Merge pull request #21200 from ChayimFriedman2/fake-impls

perf: Do not really expand builtin derives, instead treat them specifically
This commit is contained in:
Lukas Wirth 2025-12-26 13:31:48 +00:00 committed by GitHub
commit 50ed4e91fc
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
45 changed files with 2734 additions and 802 deletions

View file

@ -188,6 +188,7 @@ fn match_attr_flags(attr_flags: &mut AttrFlags, attr: Meta) -> ControlFlow<Infal
"deprecated" => attr_flags.insert(AttrFlags::IS_DEPRECATED),
"macro_export" => attr_flags.insert(AttrFlags::IS_MACRO_EXPORT),
"no_mangle" => attr_flags.insert(AttrFlags::NO_MANGLE),
"pointee" => attr_flags.insert(AttrFlags::IS_POINTEE),
"non_exhaustive" => attr_flags.insert(AttrFlags::NON_EXHAUSTIVE),
"ignore" => attr_flags.insert(AttrFlags::IS_IGNORE),
"bench" => attr_flags.insert(AttrFlags::IS_BENCH),
@ -289,6 +290,7 @@ bitflags::bitflags! {
const RUSTC_PAREN_SUGAR = 1 << 42;
const RUSTC_COINDUCTIVE = 1 << 43;
const RUSTC_FORCE_INLINE = 1 << 44;
const IS_POINTEE = 1 << 45;
}
}

View file

@ -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)
}
}

View file

@ -9,15 +9,15 @@ use indexmap::map::Entry;
use itertools::Itertools;
use la_arena::Idx;
use rustc_hash::{FxHashMap, FxHashSet};
use smallvec::{SmallVec, smallvec};
use smallvec::SmallVec;
use span::Edition;
use stdx::format_to;
use syntax::ast;
use thin_vec::ThinVec;
use crate::{
AdtId, BuiltinType, ConstId, ExternBlockId, ExternCrateId, FxIndexMap, HasModule, ImplId,
Lookup, MacroCallStyles, MacroId, ModuleDefId, ModuleId, TraitId, UseId,
AdtId, BuiltinDeriveImplId, BuiltinType, ConstId, ExternBlockId, ExternCrateId, FxIndexMap,
HasModule, ImplId, Lookup, MacroCallStyles, MacroId, ModuleDefId, ModuleId, TraitId, UseId,
db::DefDatabase,
per_ns::{Item, MacrosItem, PerNs, TypesItem, ValuesItem},
visibility::Visibility,
@ -159,6 +159,7 @@ pub struct ItemScope {
declarations: ThinVec<ModuleDefId>,
impls: ThinVec<ImplId>,
builtin_derive_impls: ThinVec<BuiltinDeriveImplId>,
extern_blocks: ThinVec<ExternBlockId>,
unnamed_consts: ThinVec<ConstId>,
/// Traits imported via `use Trait as _;`.
@ -329,6 +330,10 @@ impl ItemScope {
self.impls.iter().copied()
}
pub fn builtin_derive_impls(&self) -> impl ExactSizeIterator<Item = BuiltinDeriveImplId> + '_ {
self.builtin_derive_impls.iter().copied()
}
pub fn all_macro_calls(&self) -> impl Iterator<Item = MacroCallId> + '_ {
self.macro_invocations.values().copied().chain(self.attr_macros.values().copied()).chain(
self.derive_macros.values().flat_map(|it| {
@ -471,6 +476,10 @@ impl ItemScope {
self.impls.push(imp);
}
pub(crate) fn define_builtin_derive_impl(&mut self, imp: BuiltinDeriveImplId) {
self.builtin_derive_impls.push(imp);
}
pub(crate) fn define_extern_block(&mut self, extern_block: ExternBlockId) {
self.extern_blocks.push(extern_block);
}
@ -522,12 +531,13 @@ impl ItemScope {
adt: AstId<ast::Adt>,
attr_id: AttrId,
attr_call_id: MacroCallId,
len: usize,
mut derive_call_ids: SmallVec<[Option<MacroCallId>; 4]>,
) {
derive_call_ids.shrink_to_fit();
self.derive_macros.entry(adt).or_default().push(DeriveMacroInvocation {
attr_id,
attr_call_id,
derive_call_ids: smallvec![None; len],
derive_call_ids,
});
}
@ -811,6 +821,7 @@ impl ItemScope {
unresolved,
declarations,
impls,
builtin_derive_impls,
unnamed_consts,
unnamed_trait_imports,
legacy_macros,
@ -834,6 +845,7 @@ impl ItemScope {
unresolved.shrink_to_fit();
declarations.shrink_to_fit();
impls.shrink_to_fit();
builtin_derive_impls.shrink_to_fit();
unnamed_consts.shrink_to_fit();
unnamed_trait_imports.shrink_to_fit();
legacy_macros.shrink_to_fit();

View file

@ -2,6 +2,7 @@
//!
//! This attribute to tell the compiler about semi built-in std library
//! features, such as Fn family of traits.
use hir_expand::name::Name;
use intern::{Symbol, sym};
use stdx::impl_from;
@ -10,7 +11,7 @@ use crate::{
StaticId, StructId, TraitId, TypeAliasId, UnionId,
attrs::AttrFlags,
db::DefDatabase,
nameres::{assoc::TraitItems, crate_def_map, crate_local_def_map},
nameres::{DefMap, assoc::TraitItems, crate_def_map, crate_local_def_map},
};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
@ -93,6 +94,10 @@ pub fn crate_lang_items(db: &dyn DefDatabase, krate: Crate) -> Option<Box<LangIt
}
}
if matches!(krate.data(db).origin, base_db::CrateOrigin::Lang(base_db::LangCrateOrigin::Core)) {
lang_items.fill_non_lang_core_traits(db, crate_def_map);
}
if lang_items.is_empty() { None } else { Some(Box::new(lang_items)) }
}
@ -135,6 +140,31 @@ impl LangItems {
}
}
fn resolve_core_trait(
db: &dyn DefDatabase,
core_def_map: &DefMap,
modules: &[Symbol],
name: Symbol,
) -> Option<TraitId> {
let mut current = &core_def_map[core_def_map.root];
for module in modules {
let Some((ModuleDefId::ModuleId(cur), _)) =
current.scope.type_(&Name::new_symbol_root(module.clone()))
else {
return None;
};
if cur.krate(db) != core_def_map.krate() || cur.block(db) != core_def_map.block_id() {
return None;
}
current = &core_def_map[cur];
}
let Some((ModuleDefId::TraitId(trait_), _)) = current.scope.type_(&Name::new_symbol_root(name))
else {
return None;
};
Some(trait_)
}
#[salsa::tracked(returns(as_deref))]
pub(crate) fn crate_notable_traits(db: &dyn DefDatabase, krate: Crate) -> Option<Box<[TraitId]>> {
let mut traits = Vec::new();
@ -158,6 +188,10 @@ macro_rules! language_item_table {
(
$LangItems:ident =>
$( $(#[$attr:meta])* $lang_item:ident, $module:ident :: $name:ident, $target:ident; )*
@non_lang_core_traits:
$( core::$($non_lang_module:ident)::*, $non_lang_trait:ident; )*
) => {
#[allow(non_snake_case)] // FIXME: Should we remove this?
#[derive(Debug, Default, Clone, PartialEq, Eq, Hash)]
@ -166,6 +200,9 @@ macro_rules! language_item_table {
$(#[$attr])*
pub $lang_item: Option<$target>,
)*
$(
pub $non_lang_trait: Option<TraitId>,
)*
}
impl LangItems {
@ -176,6 +213,7 @@ macro_rules! language_item_table {
/// Merges `self` with `other`, with preference to `self` items.
fn merge_prefer_self(&mut self, other: &Self) {
$( self.$lang_item = self.$lang_item.or(other.$lang_item); )*
$( self.$non_lang_trait = self.$non_lang_trait.or(other.$non_lang_trait); )*
}
fn assign_lang_item(&mut self, name: Symbol, target: LangItemTarget) {
@ -190,6 +228,10 @@ macro_rules! language_item_table {
_ => {}
}
}
fn fill_non_lang_core_traits(&mut self, db: &dyn DefDatabase, core_def_map: &DefMap) {
$( self.$non_lang_trait = resolve_core_trait(db, core_def_map, &[ $(sym::$non_lang_module),* ], sym::$non_lang_trait); )*
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
@ -426,4 +468,11 @@ language_item_table! { LangItems =>
String, sym::String, StructId;
CStr, sym::CStr, StructId;
Ordering, sym::Ordering, EnumId;
@non_lang_core_traits:
core::default, Default;
core::fmt, Debug;
core::hash, Hash;
core::cmp, Ord;
core::cmp, Eq;
}

View file

@ -30,6 +30,7 @@ pub mod dyn_map;
pub mod item_tree;
pub mod builtin_derive;
pub mod lang_item;
pub mod hir;
@ -63,6 +64,7 @@ use base_db::{Crate, impl_intern_key};
use hir_expand::{
AstId, ExpandResult, ExpandTo, HirFileId, InFile, MacroCallId, MacroCallKind, MacroCallStyles,
MacroDefId, MacroDefKind,
attrs::AttrId,
builtin::{BuiltinAttrExpander, BuiltinDeriveExpander, BuiltinFnLikeExpander, EagerExpander},
db::ExpandDatabase,
eager::expand_eager_macro_input,
@ -80,6 +82,7 @@ pub use hir_expand::{Intern, Lookup, tt};
use crate::{
attrs::AttrFlags,
builtin_derive::BuiltinDeriveImplTrait,
builtin_type::BuiltinType,
db::DefDatabase,
expr_store::ExpressionStoreSourceMap,
@ -331,6 +334,21 @@ impl ImplId {
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct BuiltinDeriveImplLoc {
pub adt: AdtId,
pub trait_: BuiltinDeriveImplTrait,
pub derive_attr_id: AttrId,
pub derive_index: u32,
}
#[salsa::interned(debug, no_lifetime)]
#[derive(PartialOrd, Ord)]
pub struct BuiltinDeriveImplId {
#[returns(ref)]
pub loc: BuiltinDeriveImplLoc,
}
type UseLoc = ItemLoc<ast::Use>;
impl_intern!(UseId, UseLoc, intern_use, lookup_intern_use);
@ -660,6 +678,18 @@ impl_from!(
for ModuleDefId
);
impl From<DefWithBodyId> for ModuleDefId {
#[inline]
fn from(value: DefWithBodyId) -> Self {
match value {
DefWithBodyId::FunctionId(id) => id.into(),
DefWithBodyId::StaticId(id) => id.into(),
DefWithBodyId::ConstId(id) => id.into(),
DefWithBodyId::VariantId(id) => id.into(),
}
}
}
/// A constant, which might appears as a const item, an anonymous const block in expressions
/// or patterns, or as a constant in types with const generics.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, salsa_macros::Supertype)]
@ -1009,6 +1039,20 @@ fn module_for_assoc_item_loc<'db>(
id.lookup(db).container.module(db)
}
impl HasModule for BuiltinDeriveImplLoc {
#[inline]
fn module(&self, db: &dyn DefDatabase) -> ModuleId {
self.adt.module(db)
}
}
impl HasModule for BuiltinDeriveImplId {
#[inline]
fn module(&self, db: &dyn DefDatabase) -> ModuleId {
self.loc(db).module(db)
}
}
impl HasModule for FunctionId {
#[inline]
fn module(&self, db: &dyn DefDatabase) -> ModuleId {

View file

@ -53,6 +53,8 @@ use crate::{
#[track_caller]
fn check_errors(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) {
crate::nameres::ENABLE_BUILTIN_DERIVE_FAST_PATH.set(false);
let db = TestDB::with_files(ra_fixture);
let krate = db.fetch_test_crate();
let def_map = crate_def_map(&db, krate);
@ -80,10 +82,15 @@ fn check_errors(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect)
.sorted_unstable_by_key(|(range, _)| range.start())
.format_with("\n", |(range, err), format| format(&format_args!("{range:?}: {err}")))
.to_string();
crate::nameres::ENABLE_BUILTIN_DERIVE_FAST_PATH.set(true);
expect.assert_eq(&errors);
}
fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str, mut expect: Expect) {
crate::nameres::ENABLE_BUILTIN_DERIVE_FAST_PATH.set(false);
let extra_proc_macros = vec![(
r#"
#[proc_macro_attribute]
@ -246,6 +253,8 @@ pub fn identity_when_valid(_attr: TokenStream, item: TokenStream) -> TokenStream
}
}
crate::nameres::ENABLE_BUILTIN_DERIVE_FAST_PATH.set(true);
expect.indent(false);
expect.assert_eq(&expanded_text);
}

View file

@ -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;"#]],
}"#]],
);
}

View file

@ -87,6 +87,25 @@ use crate::{
pub use self::path_resolution::ResolvePathResultPrefixInfo;
#[cfg(test)]
thread_local! {
/// HACK: In order to test builtin derive expansion, we gate their fast path with this atomic when cfg(test).
pub(crate) static ENABLE_BUILTIN_DERIVE_FAST_PATH: std::cell::Cell<bool> =
const { std::cell::Cell::new(true) };
}
#[inline]
#[cfg(test)]
fn enable_builtin_derive_fast_path() -> bool {
ENABLE_BUILTIN_DERIVE_FAST_PATH.get()
}
#[inline(always)]
#[cfg(not(test))]
fn enable_builtin_derive_fast_path() -> bool {
true
}
const PREDEFINED_TOOLS: &[SmolStr] = &[
SmolStr::new_static("clippy"),
SmolStr::new_static("rustfmt"),

View file

@ -12,7 +12,7 @@ use hir_expand::{
AttrMacroAttrIds, EditionedFileId, ErasedAstId, ExpandTo, HirFileId, InFile, MacroCallId,
MacroCallKind, MacroDefId, MacroDefKind,
attrs::{Attr, AttrId},
builtin::{find_builtin_attr, find_builtin_derive, find_builtin_macro},
builtin::{BuiltinDeriveExpander, find_builtin_attr, find_builtin_derive, find_builtin_macro},
mod_path::{ModPath, PathKind},
name::{AsName, Name},
proc_macro::CustomProcMacroExpander,
@ -23,15 +23,17 @@ use la_arena::Idx;
use rustc_hash::{FxHashMap, FxHashSet};
use smallvec::SmallVec;
use span::{Edition, FileAstId, SyntaxContext};
use stdx::always;
use syntax::ast;
use triomphe::Arc;
use crate::{
AdtId, AssocItemId, AstId, AstIdWithPath, ConstLoc, EnumLoc, ExternBlockLoc, ExternCrateId,
ExternCrateLoc, FunctionId, FunctionLoc, FxIndexMap, ImplLoc, Intern, ItemContainerId, Lookup,
Macro2Id, Macro2Loc, MacroExpander, MacroId, MacroRulesId, MacroRulesLoc, MacroRulesLocFlags,
ModuleDefId, ModuleId, ProcMacroId, ProcMacroLoc, StaticLoc, StructLoc, TraitLoc, TypeAliasLoc,
UnionLoc, UnresolvedMacro, UseId, UseLoc,
AdtId, AssocItemId, AstId, AstIdWithPath, BuiltinDeriveImplId, BuiltinDeriveImplLoc, ConstLoc,
EnumLoc, ExternBlockLoc, ExternCrateId, ExternCrateLoc, FunctionId, FunctionLoc, FxIndexMap,
ImplLoc, Intern, ItemContainerId, Lookup, Macro2Id, Macro2Loc, MacroExpander, MacroId,
MacroRulesId, MacroRulesLoc, MacroRulesLocFlags, ModuleDefId, ModuleId, ProcMacroId,
ProcMacroLoc, StaticLoc, StructLoc, TraitLoc, TypeAliasLoc, UnionLoc, UnresolvedMacro, UseId,
UseLoc,
db::DefDatabase,
item_scope::{GlobId, ImportId, ImportOrExternCrate, PerNsGlobImports},
item_tree::{
@ -104,6 +106,7 @@ pub(super) fn collect_defs(
prev_active_attrs: Default::default(),
unresolved_extern_crates: Default::default(),
is_proc_macro: krate.is_proc_macro,
deferred_builtin_derives: Default::default(),
};
if tree_id.is_block() {
collector.seed_with_inner(tree_id);
@ -214,6 +217,17 @@ enum MacroDirectiveKind<'db> {
},
}
#[derive(Debug)]
struct DeferredBuiltinDerive {
call_id: MacroCallId,
derive: BuiltinDeriveExpander,
module_id: ModuleId,
depth: usize,
container: ItemContainerId,
derive_attr_id: AttrId,
derive_index: u32,
}
/// Walks the tree of module recursively
struct DefCollector<'db> {
db: &'db dyn DefDatabase,
@ -252,6 +266,11 @@ struct DefCollector<'db> {
/// on the same item. Therefore, this holds all active attributes that we already
/// expanded.
prev_active_attrs: FxHashMap<AstId<ast::Item>, SmallVec<[AttrId; 1]>>,
/// To save memory, we do not really expand builtin derives. Instead, we save them as a `BuiltinDeriveImplId`.
///
/// However, we can only do that when the derive is directly above the item, and there is no attribute in between.
/// Otherwise, all sorts of weird things can happen, like the item name resolving to something else.
deferred_builtin_derives: FxHashMap<AstId<ast::Item>, Vec<DeferredBuiltinDerive>>,
}
impl<'db> DefCollector<'db> {
@ -1241,7 +1260,7 @@ impl<'db> DefCollector<'db> {
fn resolve_macros(&mut self) -> ReachedFixedPoint {
let mut macros = mem::take(&mut self.unresolved_macros);
let mut resolved = Vec::new();
let mut push_resolved = |directive: &MacroDirective<'_>, call_id| {
let push_resolved = |resolved: &mut Vec<_>, directive: &MacroDirective<'_>, call_id| {
let attr_macro_item = match &directive.kind {
MacroDirectiveKind::Attr { ast_id, .. } => Some(ast_id.ast_id),
MacroDirectiveKind::FnLike { .. } | MacroDirectiveKind::Derive { .. } => None,
@ -1271,8 +1290,8 @@ impl<'db> DefCollector<'db> {
MacroSubNs::Attr
}
};
let resolver = |path: &_| {
let resolved_res = self.def_map.resolve_path_fp_with_macro(
let resolver = |def_map: &DefMap, path: &_| {
let resolved_res = def_map.resolve_path_fp_with_macro(
self.crate_local_def_map.unwrap_or(&self.local_def_map),
self.db,
ResolveMode::Other,
@ -1283,7 +1302,7 @@ impl<'db> DefCollector<'db> {
);
resolved_res.resolved_def.take_macros().map(|it| (it, self.db.macro_def(it)))
};
let resolver_def_id = |path: &_| resolver(path).map(|(_, it)| it);
let resolver_def_id = |path: &_| resolver(&self.def_map, path).map(|(_, it)| it);
match &directive.kind {
MacroDirectiveKind::FnLike { ast_id, expand_to, ctxt: call_site } => {
@ -1306,7 +1325,7 @@ impl<'db> DefCollector<'db> {
.scope
.add_macro_invoc(ast_id.ast_id, call_id);
push_resolved(directive, call_id);
push_resolved(&mut resolved, directive, call_id);
res = ReachedFixedPoint::No;
return Resolved::Yes;
@ -1320,6 +1339,7 @@ impl<'db> DefCollector<'db> {
ctxt: call_site,
derive_macro_id,
} => {
// FIXME: This code is almost duplicate below.
let id = derive_macro_as_call_id(
self.db,
ast_id,
@ -1327,7 +1347,7 @@ impl<'db> DefCollector<'db> {
*derive_pos as u32,
*call_site,
self.def_map.krate,
resolver,
|path| resolver(&self.def_map, path),
*derive_macro_id,
);
@ -1354,7 +1374,8 @@ impl<'db> DefCollector<'db> {
}
}
push_resolved(directive, call_id);
push_resolved(&mut resolved, directive, call_id);
res = ReachedFixedPoint::No;
return Resolved::Yes;
}
@ -1460,29 +1481,85 @@ impl<'db> DefCollector<'db> {
let ast_id = ast_id.with_value(ast_adt_id);
let mut derive_call_ids = SmallVec::new();
match attr.parse_path_comma_token_tree(self.db) {
Some(derive_macros) => {
let call_id = call_id();
let mut len = 0;
for (idx, (path, call_site, _)) in derive_macros.enumerate() {
let ast_id = AstIdWithPath::new(
file_id,
ast_id.value,
Interned::new(path),
);
self.unresolved_macros.push(MacroDirective {
module_id: directive.module_id,
depth: directive.depth + 1,
kind: MacroDirectiveKind::Derive {
ast_id,
derive_attr: *attr_id,
derive_pos: idx,
ctxt: call_site.ctx,
derive_macro_id: call_id,
},
container: directive.container,
});
len = idx;
// Try to resolve the derive immediately. If we succeed, we can also use the fast path
// for builtin derives. If not, we cannot use it, as it can cause the ADT to become
// interned while the derive is still unresolved, which will cause it to get forgotten.
let id = derive_macro_as_call_id(
self.db,
&ast_id,
*attr_id,
idx as u32,
call_site.ctx,
self.def_map.krate,
|path| resolver(&self.def_map, path),
call_id,
);
if let Ok((macro_id, def_id, call_id)) = id {
derive_call_ids.push(Some(call_id));
// Record its helper attributes.
if def_id.krate != self.def_map.krate {
let def_map = crate_def_map(self.db, def_id.krate);
if let Some(helpers) =
def_map.data.exported_derives.get(&macro_id)
{
self.def_map
.derive_helpers_in_scope
.entry(ast_id.ast_id.map(|it| it.upcast()))
.or_default()
.extend(izip!(
helpers.iter().cloned(),
iter::repeat(macro_id),
iter::repeat(call_id),
));
}
}
if super::enable_builtin_derive_fast_path()
&& let MacroDefKind::BuiltInDerive(_, builtin_derive) =
def_id.kind
{
self.deferred_builtin_derives
.entry(ast_id.ast_id.upcast())
.or_default()
.push(DeferredBuiltinDerive {
call_id,
derive: builtin_derive,
module_id: directive.module_id,
container: directive.container,
depth: directive.depth,
derive_attr_id: *attr_id,
derive_index: idx as u32,
});
} else {
push_resolved(&mut resolved, directive, call_id);
}
} else {
derive_call_ids.push(None);
self.unresolved_macros.push(MacroDirective {
module_id: directive.module_id,
depth: directive.depth + 1,
kind: MacroDirectiveKind::Derive {
ast_id,
derive_attr: *attr_id,
derive_pos: idx,
ctxt: call_site.ctx,
derive_macro_id: call_id,
},
container: directive.container,
});
}
}
// We treat the #[derive] macro as an attribute call, but we do not resolve it for nameres collection.
@ -1491,7 +1568,12 @@ impl<'db> DefCollector<'db> {
// Check the comment in [`builtin_attr_macro`].
self.def_map.modules[directive.module_id]
.scope
.init_derive_attribute(ast_id, *attr_id, call_id, len + 1);
.init_derive_attribute(
ast_id,
*attr_id,
call_id,
derive_call_ids,
);
}
None => {
let diag = DefDiagnostic::malformed_derive(
@ -1522,12 +1604,25 @@ impl<'db> DefCollector<'db> {
}
}
// Clear deferred derives for this item, unfortunately we cannot use them due to the attribute.
if let Some(deferred_derives) = self.deferred_builtin_derives.remove(&ast_id) {
resolved.extend(deferred_derives.into_iter().map(|derive| {
(
derive.module_id,
derive.depth,
derive.container,
derive.call_id,
Some(ast_id),
)
}));
}
let call_id = call_id();
self.def_map.modules[directive.module_id]
.scope
.add_attr_macro_invoc(ast_id, call_id);
push_resolved(directive, call_id);
push_resolved(&mut resolved, directive, call_id);
res = ReachedFixedPoint::No;
return Resolved::Yes;
}
@ -1709,6 +1804,12 @@ impl<'db> DefCollector<'db> {
));
}
always!(
self.deferred_builtin_derives.is_empty(),
"self.deferred_builtin_derives={:#?}",
self.deferred_builtin_derives,
);
(self.def_map, self.local_def_map)
}
}
@ -1751,6 +1852,33 @@ impl ModCollector<'_, '_> {
}
let db = self.def_collector.db;
let module_id = self.module_id;
let consider_deferred_derives =
|file_id: HirFileId,
deferred_derives: &mut FxHashMap<_, Vec<DeferredBuiltinDerive>>,
ast_id: FileAstId<ast::Adt>,
id: AdtId,
def_map: &mut DefMap| {
let Some(deferred_derives) =
deferred_derives.remove(&InFile::new(file_id, ast_id.upcast()))
else {
return;
};
let module = &mut def_map.modules[module_id];
for deferred_derive in deferred_derives {
crate::builtin_derive::with_derive_traits(deferred_derive.derive, |trait_| {
let impl_id = BuiltinDeriveImplId::new(
db,
BuiltinDeriveImplLoc {
adt: id,
trait_,
derive_attr_id: deferred_derive.derive_attr_id,
derive_index: deferred_derive.derive_index,
},
);
module.scope.define_builtin_derive_impl(impl_id);
});
}
};
let update_def =
|def_collector: &mut DefCollector<'_>, id, name: &Name, vis, has_constructor| {
def_collector.def_map.modules[module_id].scope.declare(id);
@ -1928,11 +2056,21 @@ impl ModCollector<'_, '_> {
let it = &self.item_tree[id];
let vis = resolve_vis(def_map, local_def_map, &self.item_tree[it.visibility]);
let interned = StructLoc {
container: module_id,
id: InFile::new(self.tree_id.file_id(), id),
}
.intern(db);
consider_deferred_derives(
self.tree_id.file_id(),
&mut self.def_collector.deferred_builtin_derives,
id.upcast(),
interned.into(),
def_map,
);
update_def(
self.def_collector,
StructLoc { container: module_id, id: InFile::new(self.file_id(), id) }
.intern(db)
.into(),
interned.into(),
&it.name,
vis,
!matches!(it.shape, FieldsShape::Record),
@ -1942,15 +2080,19 @@ impl ModCollector<'_, '_> {
let it = &self.item_tree[id];
let vis = resolve_vis(def_map, local_def_map, &self.item_tree[it.visibility]);
update_def(
self.def_collector,
UnionLoc { container: module_id, id: InFile::new(self.file_id(), id) }
.intern(db)
.into(),
&it.name,
vis,
false,
let interned = UnionLoc {
container: module_id,
id: InFile::new(self.tree_id.file_id(), id),
}
.intern(db);
consider_deferred_derives(
self.tree_id.file_id(),
&mut self.def_collector.deferred_builtin_derives,
id.upcast(),
interned.into(),
def_map,
);
update_def(self.def_collector, interned.into(), &it.name, vis, false);
}
ModItemId::Enum(id) => {
let it = &self.item_tree[id];
@ -1960,6 +2102,13 @@ impl ModCollector<'_, '_> {
}
.intern(db);
consider_deferred_derives(
self.tree_id.file_id(),
&mut self.def_collector.deferred_builtin_derives,
id.upcast(),
enum_.into(),
def_map,
);
let vis = resolve_vis(def_map, local_def_map, &self.item_tree[it.visibility]);
update_def(self.def_collector, enum_.into(), &it.name, vis, false);
}

View file

@ -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),
);
}

View file

@ -28,7 +28,7 @@ use syntax::{
};
macro_rules! register_builtin {
( $($trait:ident => $expand:ident),* ) => {
( $($trait:ident => $expand:ident),* $(,)? ) => {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum BuiltinDeriveExpander {
$($trait),*
@ -48,7 +48,6 @@ macro_rules! register_builtin {
}
}
}
};
}
@ -75,7 +74,7 @@ register_builtin! {
PartialOrd => partial_ord_expand,
Eq => eq_expand,
PartialEq => partial_eq_expand,
CoercePointee => coerce_pointee_expand
CoercePointee => coerce_pointee_expand,
}
pub fn find_builtin_derive(ident: &name::Name) -> Option<BuiltinDeriveExpander> {

View file

@ -0,0 +1,599 @@
//! Implementation of builtin derive impls.
use std::ops::ControlFlow;
use hir_def::{
AdtId, BuiltinDeriveImplId, BuiltinDeriveImplLoc, HasModule, LocalFieldId, TraitId,
TypeOrConstParamId, TypeParamId,
attrs::AttrFlags,
builtin_derive::BuiltinDeriveImplTrait,
hir::generics::{GenericParams, TypeOrConstParamData},
};
use itertools::Itertools;
use la_arena::ArenaMap;
use rustc_type_ir::{
AliasTyKind, Interner, TypeFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitor, Upcast,
inherent::{GenericArgs as _, IntoKind},
};
use crate::{
GenericPredicates,
db::HirDatabase,
next_solver::{
Clause, Clauses, DbInterner, EarlyBinder, GenericArgs, ParamEnv, StoredEarlyBinder,
StoredTy, TraitRef, Ty, TyKind, fold::fold_tys, generics::Generics,
},
};
fn coerce_pointee_new_type_param(trait_id: TraitId) -> TypeParamId {
// HACK: Fake the param.
// We cannot use a dummy param here, because it can leak into the IDE layer and that'll cause panics
// when e.g. trying to display it. So we use an existing param.
TypeParamId::from_unchecked(TypeOrConstParamId {
parent: trait_id.into(),
local_id: la_arena::Idx::from_raw(la_arena::RawIdx::from_u32(1)),
})
}
pub(crate) fn generics_of<'db>(interner: DbInterner<'db>, id: BuiltinDeriveImplId) -> Generics {
let db = interner.db;
let loc = id.loc(db);
match loc.trait_ {
BuiltinDeriveImplTrait::Copy
| BuiltinDeriveImplTrait::Clone
| BuiltinDeriveImplTrait::Default
| BuiltinDeriveImplTrait::Debug
| BuiltinDeriveImplTrait::Hash
| BuiltinDeriveImplTrait::Ord
| BuiltinDeriveImplTrait::PartialOrd
| BuiltinDeriveImplTrait::Eq
| BuiltinDeriveImplTrait::PartialEq => interner.generics_of(loc.adt.into()),
BuiltinDeriveImplTrait::CoerceUnsized | BuiltinDeriveImplTrait::DispatchFromDyn => {
let mut generics = interner.generics_of(loc.adt.into());
let trait_id = loc
.trait_
.get_id(interner.lang_items())
.expect("we don't pass the impl to the solver if we can't resolve the trait");
generics.push_param(coerce_pointee_new_type_param(trait_id).into());
generics
}
}
}
pub fn generic_params_count(db: &dyn HirDatabase, id: BuiltinDeriveImplId) -> usize {
let loc = id.loc(db);
let adt_params = GenericParams::new(db, loc.adt.into());
let extra_params_count = match loc.trait_ {
BuiltinDeriveImplTrait::Copy
| BuiltinDeriveImplTrait::Clone
| BuiltinDeriveImplTrait::Default
| BuiltinDeriveImplTrait::Debug
| BuiltinDeriveImplTrait::Hash
| BuiltinDeriveImplTrait::Ord
| BuiltinDeriveImplTrait::PartialOrd
| BuiltinDeriveImplTrait::Eq
| BuiltinDeriveImplTrait::PartialEq => 0,
BuiltinDeriveImplTrait::CoerceUnsized | BuiltinDeriveImplTrait::DispatchFromDyn => 1,
};
adt_params.len() + extra_params_count
}
pub fn impl_trait<'db>(
interner: DbInterner<'db>,
id: BuiltinDeriveImplId,
) -> EarlyBinder<'db, TraitRef<'db>> {
let db = interner.db;
let loc = id.loc(db);
let trait_id = loc
.trait_
.get_id(interner.lang_items())
.expect("we don't pass the impl to the solver if we can't resolve the trait");
match loc.trait_ {
BuiltinDeriveImplTrait::Copy
| BuiltinDeriveImplTrait::Clone
| BuiltinDeriveImplTrait::Default
| BuiltinDeriveImplTrait::Debug
| BuiltinDeriveImplTrait::Hash
| BuiltinDeriveImplTrait::Ord
| BuiltinDeriveImplTrait::Eq => {
let self_ty = Ty::new_adt(
interner,
loc.adt,
GenericArgs::identity_for_item(interner, loc.adt.into()),
);
EarlyBinder::bind(TraitRef::new(interner, trait_id.into(), [self_ty]))
}
BuiltinDeriveImplTrait::PartialOrd | BuiltinDeriveImplTrait::PartialEq => {
let self_ty = Ty::new_adt(
interner,
loc.adt,
GenericArgs::identity_for_item(interner, loc.adt.into()),
);
EarlyBinder::bind(TraitRef::new(interner, trait_id.into(), [self_ty, self_ty]))
}
BuiltinDeriveImplTrait::CoerceUnsized | BuiltinDeriveImplTrait::DispatchFromDyn => {
let generic_params = GenericParams::new(db, loc.adt.into());
let interner = DbInterner::new_no_crate(db);
let args = GenericArgs::identity_for_item(interner, loc.adt.into());
let self_ty = Ty::new_adt(interner, loc.adt, args);
let Some((pointee_param_idx, _, new_param_ty)) =
coerce_pointee_params(interner, loc, &generic_params, trait_id)
else {
// Malformed derive.
return EarlyBinder::bind(TraitRef::new(
interner,
trait_id.into(),
[self_ty, self_ty],
));
};
let changed_args = replace_pointee(interner, pointee_param_idx, new_param_ty, args);
let changed_self_ty = Ty::new_adt(interner, loc.adt, changed_args);
EarlyBinder::bind(TraitRef::new(interner, trait_id.into(), [self_ty, changed_self_ty]))
}
}
}
#[salsa::tracked(returns(ref), unsafe(non_update_types))]
pub fn predicates<'db>(db: &'db dyn HirDatabase, impl_: BuiltinDeriveImplId) -> GenericPredicates {
let loc = impl_.loc(db);
let generic_params = GenericParams::new(db, loc.adt.into());
let interner = DbInterner::new_with(db, loc.module(db).krate(db));
let adt_predicates = GenericPredicates::query(db, loc.adt.into());
let trait_id = loc
.trait_
.get_id(interner.lang_items())
.expect("we don't pass the impl to the solver if we can't resolve the trait");
match loc.trait_ {
BuiltinDeriveImplTrait::Copy
| BuiltinDeriveImplTrait::Clone
| BuiltinDeriveImplTrait::Debug
| BuiltinDeriveImplTrait::Hash
| BuiltinDeriveImplTrait::Ord
| BuiltinDeriveImplTrait::PartialOrd
| BuiltinDeriveImplTrait::Eq
| BuiltinDeriveImplTrait::PartialEq => {
simple_trait_predicates(interner, loc, &generic_params, adt_predicates, trait_id)
}
BuiltinDeriveImplTrait::Default => {
if matches!(loc.adt, AdtId::EnumId(_)) {
// Enums don't have extra bounds.
GenericPredicates::from_explicit_own_predicates(StoredEarlyBinder::bind(
Clauses::new_from_slice(adt_predicates.explicit_predicates().skip_binder())
.store(),
))
} else {
simple_trait_predicates(interner, loc, &generic_params, adt_predicates, trait_id)
}
}
BuiltinDeriveImplTrait::CoerceUnsized | BuiltinDeriveImplTrait::DispatchFromDyn => {
let Some((pointee_param_idx, pointee_param_id, new_param_ty)) =
coerce_pointee_params(interner, loc, &generic_params, trait_id)
else {
// Malformed derive.
return GenericPredicates::from_explicit_own_predicates(StoredEarlyBinder::bind(
Clauses::default().store(),
));
};
let duplicated_bounds =
adt_predicates.explicit_predicates().iter_identity_copied().filter_map(|pred| {
let mentions_pointee =
pred.visit_with(&mut MentionsPointee { pointee_param_idx }).is_break();
if !mentions_pointee {
return None;
}
let transformed =
replace_pointee(interner, pointee_param_idx, new_param_ty, pred);
Some(transformed)
});
let unsize_trait = interner.lang_items().Unsize;
let unsize_bound = unsize_trait.map(|unsize_trait| {
let pointee_param_ty = Ty::new_param(interner, pointee_param_id, pointee_param_idx);
TraitRef::new(interner, unsize_trait.into(), [pointee_param_ty, new_param_ty])
.upcast(interner)
});
GenericPredicates::from_explicit_own_predicates(StoredEarlyBinder::bind(
Clauses::new_from_iter(
interner,
adt_predicates
.explicit_predicates()
.iter_identity_copied()
.chain(duplicated_bounds)
.chain(unsize_bound),
)
.store(),
))
}
}
}
/// Not cached in a query, currently used in `hir` only. If you need this in `hir-ty` consider introducing a query.
pub fn param_env<'db>(interner: DbInterner<'db>, id: BuiltinDeriveImplId) -> ParamEnv<'db> {
let predicates = predicates(interner.db, id);
crate::lower::param_env_from_predicates(interner, predicates)
}
struct MentionsPointee {
pointee_param_idx: u32,
}
impl<'db> TypeVisitor<DbInterner<'db>> for MentionsPointee {
type Result = ControlFlow<()>;
fn visit_ty(&mut self, t: Ty<'db>) -> Self::Result {
if let TyKind::Param(param) = t.kind()
&& param.index == self.pointee_param_idx
{
ControlFlow::Break(())
} else {
t.super_visit_with(self)
}
}
}
fn replace_pointee<'db, T: TypeFoldable<DbInterner<'db>>>(
interner: DbInterner<'db>,
pointee_param_idx: u32,
new_param_ty: Ty<'db>,
t: T,
) -> T {
fold_tys(interner, t, |ty| match ty.kind() {
TyKind::Param(param) if param.index == pointee_param_idx => new_param_ty,
_ => ty,
})
}
fn simple_trait_predicates<'db>(
interner: DbInterner<'db>,
loc: &BuiltinDeriveImplLoc,
generic_params: &GenericParams,
adt_predicates: &GenericPredicates,
trait_id: TraitId,
) -> GenericPredicates {
let extra_predicates = generic_params
.iter_type_or_consts()
.filter(|(_, data)| matches!(data, TypeOrConstParamData::TypeParamData(_)))
.map(|(param_idx, _)| {
let param_id = TypeParamId::from_unchecked(TypeOrConstParamId {
parent: loc.adt.into(),
local_id: param_idx,
});
let param_idx =
param_idx.into_raw().into_u32() + (generic_params.len_lifetimes() as u32);
let param_ty = Ty::new_param(interner, param_id, param_idx);
let trait_ref = TraitRef::new(interner, trait_id.into(), [param_ty]);
trait_ref.upcast(interner)
});
let mut assoc_type_bounds = Vec::new();
match loc.adt {
AdtId::StructId(id) => extend_assoc_type_bounds(
interner,
&mut assoc_type_bounds,
interner.db.field_types(id.into()),
trait_id,
),
AdtId::UnionId(id) => extend_assoc_type_bounds(
interner,
&mut assoc_type_bounds,
interner.db.field_types(id.into()),
trait_id,
),
AdtId::EnumId(id) => {
for &(variant_id, _, _) in &id.enum_variants(interner.db).variants {
extend_assoc_type_bounds(
interner,
&mut assoc_type_bounds,
interner.db.field_types(variant_id.into()),
trait_id,
)
}
}
}
GenericPredicates::from_explicit_own_predicates(StoredEarlyBinder::bind(
Clauses::new_from_iter(
interner,
adt_predicates
.explicit_predicates()
.iter_identity_copied()
.chain(extra_predicates)
.chain(assoc_type_bounds),
)
.store(),
))
}
fn extend_assoc_type_bounds<'db>(
interner: DbInterner<'db>,
assoc_type_bounds: &mut Vec<Clause<'db>>,
fields: &ArenaMap<LocalFieldId, StoredEarlyBinder<StoredTy>>,
trait_: TraitId,
) {
struct ProjectionFinder<'a, 'db> {
interner: DbInterner<'db>,
assoc_type_bounds: &'a mut Vec<Clause<'db>>,
trait_: TraitId,
}
impl<'db> TypeVisitor<DbInterner<'db>> for ProjectionFinder<'_, 'db> {
type Result = ();
fn visit_ty(&mut self, t: Ty<'db>) -> Self::Result {
if let TyKind::Alias(AliasTyKind::Projection, _) = t.kind() {
self.assoc_type_bounds.push(
TraitRef::new(self.interner, self.trait_.into(), [t]).upcast(self.interner),
);
}
t.super_visit_with(self)
}
}
let mut visitor = ProjectionFinder { interner, assoc_type_bounds, trait_ };
for (_, field) in fields.iter() {
field.get().instantiate_identity().visit_with(&mut visitor);
}
}
fn coerce_pointee_params<'db>(
interner: DbInterner<'db>,
loc: &BuiltinDeriveImplLoc,
generic_params: &GenericParams,
trait_id: TraitId,
) -> Option<(u32, TypeParamId, Ty<'db>)> {
let pointee_param = {
if let Ok((pointee_param, _)) = generic_params
.iter_type_or_consts()
.filter(|param| matches!(param.1, TypeOrConstParamData::TypeParamData(_)))
.exactly_one()
{
pointee_param
} else {
let (_, generic_param_attrs) =
AttrFlags::query_generic_params(interner.db, loc.adt.into());
generic_param_attrs
.iter()
.find(|param| param.1.contains(AttrFlags::IS_POINTEE))
.map(|(param, _)| param)
.or_else(|| {
generic_params
.iter_type_or_consts()
.find(|param| matches!(param.1, TypeOrConstParamData::TypeParamData(_)))
.map(|(idx, _)| idx)
})?
}
};
let pointee_param_id = TypeParamId::from_unchecked(TypeOrConstParamId {
parent: loc.adt.into(),
local_id: pointee_param,
});
let pointee_param_idx =
pointee_param.into_raw().into_u32() + (generic_params.len_lifetimes() as u32);
let new_param_idx = generic_params.len() as u32;
let new_param_id = coerce_pointee_new_type_param(trait_id);
let new_param_ty = Ty::new_param(interner, new_param_id, new_param_idx);
Some((pointee_param_idx, pointee_param_id, new_param_ty))
}
#[cfg(test)]
mod tests {
use expect_test::{Expect, expect};
use hir_def::nameres::crate_def_map;
use itertools::Itertools;
use stdx::format_to;
use test_fixture::WithFixture;
use crate::{builtin_derive::impl_trait, next_solver::DbInterner, test_db::TestDB};
fn check_trait_refs(#[rust_analyzer::rust_fixture] ra_fixture: &str, expectation: Expect) {
let db = TestDB::with_files(ra_fixture);
let def_map = crate_def_map(&db, db.test_crate());
let interner = DbInterner::new_with(&db, db.test_crate());
crate::attach_db(&db, || {
let mut trait_refs = Vec::new();
for (_, module) in def_map.modules() {
for derive in module.scope.builtin_derive_impls() {
let trait_ref = impl_trait(interner, derive).skip_binder();
trait_refs.push(format!("{trait_ref:?}"));
}
}
expectation.assert_eq(&trait_refs.join("\n"));
});
}
fn check_predicates(#[rust_analyzer::rust_fixture] ra_fixture: &str, expectation: Expect) {
let db = TestDB::with_files(ra_fixture);
let def_map = crate_def_map(&db, db.test_crate());
crate::attach_db(&db, || {
let mut predicates = String::new();
for (_, module) in def_map.modules() {
for derive in module.scope.builtin_derive_impls() {
let preds = super::predicates(&db, derive).all_predicates().skip_binder();
format_to!(
predicates,
"{}\n\n",
preds.iter().format_with("\n", |pred, formatter| formatter(&format_args!(
"{pred:?}"
))),
);
}
}
expectation.assert_eq(&predicates);
});
}
#[test]
fn simple_macros_trait_ref() {
check_trait_refs(
r#"
//- minicore: derive, clone, copy, eq, ord, hash, fmt
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
struct Simple;
trait Trait {}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
struct WithGenerics<'a, T: Trait, const N: usize>(&'a [T; N]);
"#,
expect![[r#"
Simple: Debug
Simple: Clone
Simple: Copy
Simple: PartialEq<[Simple]>
Simple: Eq
Simple: PartialOrd<[Simple]>
Simple: Ord
Simple: Hash
WithGenerics<#0, #1, #2>: Debug
WithGenerics<#0, #1, #2>: Clone
WithGenerics<#0, #1, #2>: Copy
WithGenerics<#0, #1, #2>: PartialEq<[WithGenerics<#0, #1, #2>]>
WithGenerics<#0, #1, #2>: Eq
WithGenerics<#0, #1, #2>: PartialOrd<[WithGenerics<#0, #1, #2>]>
WithGenerics<#0, #1, #2>: Ord
WithGenerics<#0, #1, #2>: Hash"#]],
);
}
#[test]
fn coerce_pointee_trait_ref() {
check_trait_refs(
r#"
//- minicore: derive, coerce_pointee
use core::marker::CoercePointee;
#[derive(CoercePointee)]
struct Simple<T: ?Sized>(*const T);
#[derive(CoercePointee)]
struct MultiGenericParams<'a, T, #[pointee] U: ?Sized, const N: usize>(*const U);
"#,
expect![[r#"
Simple<#0>: CoerceUnsized<[Simple<#1>]>
Simple<#0>: DispatchFromDyn<[Simple<#1>]>
MultiGenericParams<#0, #1, #2, #3>: CoerceUnsized<[MultiGenericParams<#0, #1, #4, #3>]>
MultiGenericParams<#0, #1, #2, #3>: DispatchFromDyn<[MultiGenericParams<#0, #1, #4, #3>]>"#]],
);
}
#[test]
fn simple_macros_predicates() {
check_predicates(
r#"
//- minicore: derive, clone, copy, eq, ord, hash, fmt
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
struct Simple;
trait Trait {}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
struct WithGenerics<'a, T: Trait, const N: usize>(&'a [T; N]);
"#,
expect![[r#"
Clause(Binder { value: TraitPredicate(#1: Trait, polarity:Positive), bound_vars: [] })
Clause(Binder { value: ConstArgHasType(#2, usize), bound_vars: [] })
Clause(Binder { value: TraitPredicate(#1: Sized, polarity:Positive), bound_vars: [] })
Clause(Binder { value: TraitPredicate(#1: Debug, polarity:Positive), bound_vars: [] })
Clause(Binder { value: TraitPredicate(#1: Trait, polarity:Positive), bound_vars: [] })
Clause(Binder { value: ConstArgHasType(#2, usize), bound_vars: [] })
Clause(Binder { value: TraitPredicate(#1: Sized, polarity:Positive), bound_vars: [] })
Clause(Binder { value: TraitPredicate(#1: Clone, polarity:Positive), bound_vars: [] })
Clause(Binder { value: TraitPredicate(#1: Trait, polarity:Positive), bound_vars: [] })
Clause(Binder { value: ConstArgHasType(#2, usize), bound_vars: [] })
Clause(Binder { value: TraitPredicate(#1: Sized, polarity:Positive), bound_vars: [] })
Clause(Binder { value: TraitPredicate(#1: Copy, polarity:Positive), bound_vars: [] })
Clause(Binder { value: TraitPredicate(#1: Trait, polarity:Positive), bound_vars: [] })
Clause(Binder { value: ConstArgHasType(#2, usize), bound_vars: [] })
Clause(Binder { value: TraitPredicate(#1: Sized, polarity:Positive), bound_vars: [] })
Clause(Binder { value: TraitPredicate(#1: PartialEq, polarity:Positive), bound_vars: [] })
Clause(Binder { value: TraitPredicate(#1: Trait, polarity:Positive), bound_vars: [] })
Clause(Binder { value: ConstArgHasType(#2, usize), bound_vars: [] })
Clause(Binder { value: TraitPredicate(#1: Sized, polarity:Positive), bound_vars: [] })
Clause(Binder { value: TraitPredicate(#1: Eq, polarity:Positive), bound_vars: [] })
Clause(Binder { value: TraitPredicate(#1: Trait, polarity:Positive), bound_vars: [] })
Clause(Binder { value: ConstArgHasType(#2, usize), bound_vars: [] })
Clause(Binder { value: TraitPredicate(#1: Sized, polarity:Positive), bound_vars: [] })
Clause(Binder { value: TraitPredicate(#1: PartialOrd, polarity:Positive), bound_vars: [] })
Clause(Binder { value: TraitPredicate(#1: Trait, polarity:Positive), bound_vars: [] })
Clause(Binder { value: ConstArgHasType(#2, usize), bound_vars: [] })
Clause(Binder { value: TraitPredicate(#1: Sized, polarity:Positive), bound_vars: [] })
Clause(Binder { value: TraitPredicate(#1: Ord, polarity:Positive), bound_vars: [] })
Clause(Binder { value: TraitPredicate(#1: Trait, polarity:Positive), bound_vars: [] })
Clause(Binder { value: ConstArgHasType(#2, usize), bound_vars: [] })
Clause(Binder { value: TraitPredicate(#1: Sized, polarity:Positive), bound_vars: [] })
Clause(Binder { value: TraitPredicate(#1: Hash, polarity:Positive), bound_vars: [] })
"#]],
);
}
#[test]
fn coerce_pointee_predicates() {
check_predicates(
r#"
//- minicore: derive, coerce_pointee
use core::marker::CoercePointee;
#[derive(CoercePointee)]
struct Simple<T: ?Sized>(*const T);
trait Trait<T> {}
#[derive(CoercePointee)]
struct MultiGenericParams<'a, T, #[pointee] U: ?Sized, const N: usize>(*const U)
where
T: Trait<U>,
U: Trait<U>;
"#,
expect![[r#"
Clause(Binder { value: TraitPredicate(#0: Unsize<[#1]>, polarity:Positive), bound_vars: [] })
Clause(Binder { value: TraitPredicate(#0: Unsize<[#1]>, polarity:Positive), bound_vars: [] })
Clause(Binder { value: TraitPredicate(#1: Trait<[#2]>, polarity:Positive), bound_vars: [] })
Clause(Binder { value: TraitPredicate(#2: Trait<[#2]>, polarity:Positive), bound_vars: [] })
Clause(Binder { value: ConstArgHasType(#3, usize), bound_vars: [] })
Clause(Binder { value: TraitPredicate(#1: Sized, polarity:Positive), bound_vars: [] })
Clause(Binder { value: TraitPredicate(#1: Trait<[#4]>, polarity:Positive), bound_vars: [] })
Clause(Binder { value: TraitPredicate(#4: Trait<[#4]>, polarity:Positive), bound_vars: [] })
Clause(Binder { value: TraitPredicate(#2: Unsize<[#4]>, polarity:Positive), bound_vars: [] })
Clause(Binder { value: TraitPredicate(#1: Trait<[#2]>, polarity:Positive), bound_vars: [] })
Clause(Binder { value: TraitPredicate(#2: Trait<[#2]>, polarity:Positive), bound_vars: [] })
Clause(Binder { value: ConstArgHasType(#3, usize), bound_vars: [] })
Clause(Binder { value: TraitPredicate(#1: Sized, polarity:Positive), bound_vars: [] })
Clause(Binder { value: TraitPredicate(#1: Trait<[#4]>, polarity:Positive), bound_vars: [] })
Clause(Binder { value: TraitPredicate(#4: Trait<[#4]>, polarity:Positive), bound_vars: [] })
Clause(Binder { value: TraitPredicate(#2: Unsize<[#4]>, polarity:Positive), bound_vars: [] })
"#]],
);
}
}

View file

@ -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#"

View file

@ -2,10 +2,12 @@
//! type inference-related queries.
use base_db::{Crate, target::TargetLoadError};
use either::Either;
use hir_def::{
AdtId, CallableDefId, ConstId, ConstParamId, DefWithBodyId, EnumVariantId, FunctionId,
GenericDefId, ImplId, LifetimeParamId, LocalFieldId, StaticId, TraitId, TypeAliasId, VariantId,
db::DefDatabase, hir::ExprId, layout::TargetDataLayout,
AdtId, BuiltinDeriveImplId, CallableDefId, ConstId, ConstParamId, DefWithBodyId, EnumVariantId,
FunctionId, GenericDefId, ImplId, LifetimeParamId, LocalFieldId, StaticId, TraitId,
TypeAliasId, VariantId, builtin_derive::BuiltinDeriveImplMethod, db::DefDatabase, hir::ExprId,
layout::TargetDataLayout,
};
use la_arena::ArenaMap;
use salsa::plumbing::AsId;
@ -83,7 +85,7 @@ pub trait HirDatabase: DefDatabase + std::fmt::Debug {
env: ParamEnvAndCrate<'db>,
func: FunctionId,
fn_subst: GenericArgs<'db>,
) -> (FunctionId, GenericArgs<'db>);
) -> (Either<FunctionId, (BuiltinDeriveImplId, BuiltinDeriveImplMethod)>, GenericArgs<'db>);
// endregion:mir

View file

@ -7,7 +7,7 @@ use std::{
mem,
};
use base_db::Crate;
use base_db::{Crate, FxIndexMap};
use either::Either;
use hir_def::{
FindPathConfig, GenericDefId, HasModule, LocalFieldId, Lookup, ModuleDefId, ModuleId, TraitId,
@ -143,11 +143,11 @@ impl<'db> BoundsFormattingCtx<'db> {
}
impl<'db> HirFormatter<'_, 'db> {
fn start_location_link(&mut self, location: ModuleDefId) {
pub fn start_location_link(&mut self, location: ModuleDefId) {
self.fmt.start_location_link(location);
}
fn end_location_link(&mut self) {
pub fn end_location_link(&mut self) {
self.fmt.end_location_link();
}
@ -1971,6 +1971,49 @@ fn write_bounds_like_dyn_trait<'db>(
Ok(())
}
pub fn write_params_bounds<'db>(
f: &mut HirFormatter<'_, 'db>,
predicates: &[Clause<'db>],
) -> Result {
// Use an FxIndexMap to keep user's order, as far as possible.
let mut per_type = FxIndexMap::<_, Vec<_>>::default();
for &predicate in predicates {
let base_ty = match predicate.kind().skip_binder() {
ClauseKind::Trait(clause) => Either::Left(clause.self_ty()),
ClauseKind::RegionOutlives(clause) => Either::Right(clause.0),
ClauseKind::TypeOutlives(clause) => Either::Left(clause.0),
ClauseKind::Projection(clause) => Either::Left(clause.self_ty()),
ClauseKind::ConstArgHasType(..)
| ClauseKind::WellFormed(_)
| ClauseKind::ConstEvaluatable(_)
| ClauseKind::HostEffect(..)
| ClauseKind::UnstableFeature(_) => continue,
};
per_type.entry(base_ty).or_default().push(predicate);
}
for (base_ty, clauses) in per_type {
f.write_str(" ")?;
match base_ty {
Either::Left(it) => it.hir_fmt(f)?,
Either::Right(it) => it.hir_fmt(f)?,
}
f.write_str(": ")?;
// Rudimentary approximation: type params are `Sized` by default, everything else not.
// FIXME: This is not correct, really. But I'm not sure how we can from the ty representation
// to extract the default sizedness, and if it's possible at all.
let default_sized = match base_ty {
Either::Left(ty) if matches!(ty.kind(), TyKind::Param(_)) => {
SizedByDefault::Sized { anchor: f.krate() }
}
_ => SizedByDefault::NotSized,
};
write_bounds_like_dyn_trait(f, base_ty, &clauses, default_sized)?;
f.write_str(",\n")?;
}
Ok(())
}
impl<'db> HirDisplay<'db> for TraitRef<'db> {
fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>) -> Result {
let trait_ = self.def_id.0;

View file

@ -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)]

View file

@ -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;

View file

@ -1790,6 +1790,13 @@ impl<'db> GenericPredicates {
}
impl GenericPredicates {
#[inline]
pub(crate) fn from_explicit_own_predicates(
predicates: StoredEarlyBinder<StoredClauses>,
) -> Self {
Self { predicates, own_predicates_start: 0, is_trait: false, parent_is_trait: false }
}
#[inline]
pub fn query(db: &dyn HirDatabase, def: GenericDefId) -> &GenericPredicates {
&Self::query_with_diagnostics(db, def).0
@ -1848,6 +1855,20 @@ pub(crate) fn trait_environment_for_body_query(
db.trait_environment(def)
}
pub(crate) fn param_env_from_predicates<'db>(
interner: DbInterner<'db>,
predicates: &'db GenericPredicates,
) -> ParamEnv<'db> {
let clauses = rustc_type_ir::elaborate::elaborate(
interner,
predicates.all_predicates().iter_identity_copied(),
);
let clauses = Clauses::new_from_iter(interner, clauses);
// FIXME: We should normalize projections here, like rustc does.
ParamEnv { clauses }
}
pub(crate) fn trait_environment<'db>(db: &'db dyn HirDatabase, def: GenericDefId) -> ParamEnv<'db> {
return ParamEnv { clauses: trait_environment_query(db, def).as_ref() };
@ -1858,13 +1879,8 @@ pub(crate) fn trait_environment<'db>(db: &'db dyn HirDatabase, def: GenericDefId
) -> StoredClauses {
let module = def.module(db);
let interner = DbInterner::new_with(db, module.krate(db));
let predicates = GenericPredicates::query_all(db, def);
let clauses =
rustc_type_ir::elaborate::elaborate(interner, predicates.iter_identity_copied());
let clauses = Clauses::new_from_iter(interner, clauses);
// FIXME: We should normalize projections here, like rustc does.
clauses.store()
let predicates = GenericPredicates::query(db, def);
param_env_from_predicates(interner, predicates).clauses.store()
}
}

View file

@ -13,11 +13,13 @@ use tracing::{debug, instrument};
use base_db::Crate;
use hir_def::{
AssocItemId, BlockId, ConstId, FunctionId, GenericParamId, HasModule, ImplId, ItemContainerId,
ModuleId, TraitId,
AssocItemId, BlockId, BuiltinDeriveImplId, ConstId, FunctionId, GenericParamId, HasModule,
ImplId, ItemContainerId, ModuleId, TraitId,
attrs::AttrFlags,
builtin_derive::BuiltinDeriveImplMethod,
expr_store::path::GenericArgs as HirGenericArgs,
hir::ExprId,
lang_item::LangItems,
nameres::{DefMap, block_def_map, crate_def_map},
resolver::Resolver,
};
@ -37,7 +39,7 @@ use crate::{
infer::{InferenceContext, unify::InferenceTable},
lower::GenericPredicates,
next_solver::{
Binder, ClauseKind, DbInterner, FnSig, GenericArgs, ParamEnv, PredicateKind,
AnyImplId, Binder, ClauseKind, DbInterner, FnSig, GenericArgs, ParamEnv, PredicateKind,
SimplifiedType, SolverDefId, TraitRef, Ty, TyKind, TypingMode,
infer::{
BoundRegionConversionTime, DbInternerInferExt, InferCtxt, InferOk,
@ -132,7 +134,7 @@ pub enum MethodError<'db> {
// candidate can arise. Used for error reporting only.
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum CandidateSource {
Impl(ImplId),
Impl(AnyImplId),
Trait(TraitId),
}
@ -371,9 +373,13 @@ pub fn lookup_impl_const<'db>(
};
lookup_impl_assoc_item_for_trait_ref(infcx, trait_ref, env, name)
.and_then(
|assoc| if let (AssocItemId::ConstId(id), s) = assoc { Some((id, s)) } else { None },
)
.and_then(|assoc| {
if let (Either::Left(AssocItemId::ConstId(id)), s) = assoc {
Some((id, s))
} else {
None
}
})
.unwrap_or((const_id, subs))
}
@ -419,12 +425,12 @@ pub(crate) fn lookup_impl_method_query<'db>(
env: ParamEnvAndCrate<'db>,
func: FunctionId,
fn_subst: GenericArgs<'db>,
) -> (FunctionId, GenericArgs<'db>) {
) -> (Either<FunctionId, (BuiltinDeriveImplId, BuiltinDeriveImplMethod)>, GenericArgs<'db>) {
let interner = DbInterner::new_with(db, env.krate);
let infcx = interner.infer_ctxt().build(TypingMode::PostAnalysis);
let ItemContainerId::TraitId(trait_id) = func.loc(db).container else {
return (func, fn_subst);
return (Either::Left(func), fn_subst);
};
let trait_params = db.generic_params(trait_id.into()).len();
let trait_ref = TraitRef::new_from_args(
@ -434,16 +440,19 @@ pub(crate) fn lookup_impl_method_query<'db>(
);
let name = &db.function_signature(func).name;
let Some((impl_fn, impl_subst)) = lookup_impl_assoc_item_for_trait_ref(
&infcx,
trait_ref,
env.param_env,
name,
)
.and_then(|assoc| {
if let (AssocItemId::FunctionId(id), subst) = assoc { Some((id, subst)) } else { None }
}) else {
return (func, fn_subst);
let Some((impl_fn, impl_subst)) =
lookup_impl_assoc_item_for_trait_ref(&infcx, trait_ref, env.param_env, name).and_then(
|(assoc, impl_args)| {
let assoc = match assoc {
Either::Left(AssocItemId::FunctionId(id)) => Either::Left(id),
Either::Right(it) => Either::Right(it),
_ => return None,
};
Some((assoc, impl_args))
},
)
else {
return (Either::Left(func), fn_subst);
};
(
@ -460,22 +469,33 @@ fn lookup_impl_assoc_item_for_trait_ref<'db>(
trait_ref: TraitRef<'db>,
env: ParamEnv<'db>,
name: &Name,
) -> Option<(AssocItemId, GenericArgs<'db>)> {
) -> Option<(Either<AssocItemId, (BuiltinDeriveImplId, BuiltinDeriveImplMethod)>, GenericArgs<'db>)>
{
let (impl_id, impl_subst) = find_matching_impl(infcx, env, trait_ref)?;
let impl_id = match impl_id {
AnyImplId::ImplId(it) => it,
AnyImplId::BuiltinDeriveImplId(impl_) => {
return impl_
.loc(infcx.interner.db)
.trait_
.get_method(name.symbol())
.map(|method| (Either::Right((impl_, method)), impl_subst));
}
};
let item =
impl_id.impl_items(infcx.interner.db).items.iter().find_map(|(n, it)| match *it {
AssocItemId::FunctionId(f) => (n == name).then_some(AssocItemId::FunctionId(f)),
AssocItemId::ConstId(c) => (n == name).then_some(AssocItemId::ConstId(c)),
AssocItemId::TypeAliasId(_) => None,
})?;
Some((item, impl_subst))
Some((Either::Left(item), impl_subst))
}
pub(crate) fn find_matching_impl<'db>(
infcx: &InferCtxt<'db>,
env: ParamEnv<'db>,
trait_ref: TraitRef<'db>,
) -> Option<(ImplId, GenericArgs<'db>)> {
) -> Option<(AnyImplId, GenericArgs<'db>)> {
let trait_ref = infcx.at(&ObligationCause::dummy(), env).deeply_normalize(trait_ref).ok()?;
let obligation = Obligation::new(infcx.interner, ObligationCause::dummy(), env, trait_ref);
@ -635,13 +655,13 @@ impl InherentImpls {
#[derive(Debug, PartialEq)]
struct OneTraitImpls {
non_blanket_impls: FxHashMap<SimplifiedType, Box<[ImplId]>>,
non_blanket_impls: FxHashMap<SimplifiedType, (Box<[ImplId]>, Box<[BuiltinDeriveImplId]>)>,
blanket_impls: Box<[ImplId]>,
}
#[derive(Default)]
struct OneTraitImplsBuilder {
non_blanket_impls: FxHashMap<SimplifiedType, Vec<ImplId>>,
non_blanket_impls: FxHashMap<SimplifiedType, (Vec<ImplId>, Vec<BuiltinDeriveImplId>)>,
blanket_impls: Vec<ImplId>,
}
@ -650,7 +670,9 @@ impl OneTraitImplsBuilder {
let mut non_blanket_impls = self
.non_blanket_impls
.into_iter()
.map(|(self_ty, impls)| (self_ty, impls.into_boxed_slice()))
.map(|(self_ty, (impls, builtin_derive_impls))| {
(self_ty, (impls.into_boxed_slice(), builtin_derive_impls.into_boxed_slice()))
})
.collect::<FxHashMap<_, _>>();
non_blanket_impls.shrink_to_fit();
let blanket_impls = self.blanket_impls.into_boxed_slice();
@ -691,8 +713,9 @@ impl TraitImpls {
impl TraitImpls {
fn collect_def_map(db: &dyn HirDatabase, def_map: &DefMap) -> Self {
let lang_items = hir_def::lang_item::lang_items(db, def_map.krate());
let mut map = FxHashMap::default();
collect(db, def_map, &mut map);
collect(db, def_map, lang_items, &mut map);
let mut map = map
.into_iter()
.map(|(trait_id, trait_map)| (trait_id, trait_map.finish()))
@ -703,6 +726,7 @@ impl TraitImpls {
fn collect(
db: &dyn HirDatabase,
def_map: &DefMap,
lang_items: &LangItems,
map: &mut FxHashMap<TraitId, OneTraitImplsBuilder>,
) {
for (_module_id, module_data) in def_map.modules() {
@ -727,18 +751,29 @@ impl TraitImpls {
let entry = map.entry(trait_ref.def_id.0).or_default();
match simplify_type(interner, self_ty, TreatParams::InstantiateWithInfer) {
Some(self_ty) => {
entry.non_blanket_impls.entry(self_ty).or_default().push(impl_id)
entry.non_blanket_impls.entry(self_ty).or_default().0.push(impl_id)
}
None => entry.blanket_impls.push(impl_id),
}
}
for impl_id in module_data.scope.builtin_derive_impls() {
let loc = impl_id.loc(db);
let Some(trait_id) = loc.trait_.get_id(lang_items) else { continue };
let entry = map.entry(trait_id).or_default();
let entry = entry
.non_blanket_impls
.entry(SimplifiedType::Adt(loc.adt.into()))
.or_default();
entry.1.push(impl_id);
}
// To better support custom derives, collect impls in all unnamed const items.
// const _: () = { ... };
for konst in module_data.scope.unnamed_consts() {
let body = db.body(konst.into());
for (_, block_def_map) in body.blocks(db) {
collect(db, block_def_map, map);
collect(db, block_def_map, lang_items, map);
}
}
}
@ -761,27 +796,41 @@ impl TraitImpls {
})
}
pub fn for_trait_and_self_ty(&self, trait_: TraitId, self_ty: &SimplifiedType) -> &[ImplId] {
pub fn for_trait_and_self_ty(
&self,
trait_: TraitId,
self_ty: &SimplifiedType,
) -> (&[ImplId], &[BuiltinDeriveImplId]) {
self.map
.get(&trait_)
.and_then(|map| map.non_blanket_impls.get(self_ty))
.map(|it| &**it)
.map(|it| (&*it.0, &*it.1))
.unwrap_or_default()
}
pub fn for_trait(&self, trait_: TraitId, mut callback: impl FnMut(&[ImplId])) {
pub fn for_trait(
&self,
trait_: TraitId,
mut callback: impl FnMut(Either<&[ImplId], &[BuiltinDeriveImplId]>),
) {
if let Some(impls) = self.map.get(&trait_) {
callback(&impls.blanket_impls);
callback(Either::Left(&impls.blanket_impls));
for impls in impls.non_blanket_impls.values() {
callback(impls);
callback(Either::Left(&impls.0));
callback(Either::Right(&impls.1));
}
}
}
pub fn for_self_ty(&self, self_ty: &SimplifiedType, mut callback: impl FnMut(&[ImplId])) {
pub fn for_self_ty(
&self,
self_ty: &SimplifiedType,
mut callback: impl FnMut(Either<&[ImplId], &[BuiltinDeriveImplId]>),
) {
for for_trait in self.map.values() {
if let Some(for_ty) = for_trait.non_blanket_impls.get(self_ty) {
callback(for_ty);
callback(Either::Left(&for_ty.0));
callback(Either::Right(&for_ty.1));
}
}
}

View file

@ -1001,7 +1001,7 @@ impl<'a, 'db, Choice: ProbeChoice<'db>> ProbeContext<'a, 'db, Choice> {
self.with_impl_item(impl_def_id, |this, item| {
if !this.has_applicable_self(item) {
// No receiver declared. Not a candidate.
this.record_static_candidate(CandidateSource::Impl(impl_def_id));
this.record_static_candidate(CandidateSource::Impl(impl_def_id.into()));
return;
}
this.push_candidate(
@ -1490,7 +1490,7 @@ impl<'a, 'db, Choice: ProbeChoice<'db>> ProbeContext<'a, 'db, Choice> {
/// so do not use to make a decision that may lead to a successful compilation.
fn candidate_source(&self, candidate: &Candidate<'db>, self_ty: Ty<'db>) -> CandidateSource {
match candidate.kind {
InherentImplCandidate { impl_def_id, .. } => CandidateSource::Impl(impl_def_id),
InherentImplCandidate { impl_def_id, .. } => CandidateSource::Impl(impl_def_id.into()),
ObjectCandidate(trait_ref) | WhereClauseCandidate(trait_ref) => {
CandidateSource::Trait(trait_ref.def_id().0)
}
@ -1524,7 +1524,7 @@ impl<'a, 'db, Choice: ProbeChoice<'db>> ProbeContext<'a, 'db, Choice> {
fn candidate_source_from_pick(&self, pick: &Pick<'db>) -> CandidateSource {
match pick.kind {
InherentImplPick(impl_) => CandidateSource::Impl(impl_),
InherentImplPick(impl_) => CandidateSource::Impl(impl_.into()),
ObjectPick(trait_) | TraitPick(trait_) => CandidateSource::Trait(trait_),
WhereClausePick(trait_ref) => CandidateSource::Trait(trait_ref.skip_binder().def_id.0),
}

View file

@ -77,12 +77,14 @@ macro_rules! from_bytes {
}).into())
};
}
use from_bytes;
macro_rules! not_supported {
($it: expr) => {
return Err(MirEvalError::NotSupported(format!($it)))
return Err($crate::mir::eval::MirEvalError::NotSupported(format!($it)))
};
}
use not_supported;
#[derive(Debug, Default, Clone, PartialEq, Eq, GenericTypeVisitable)]
pub struct VTableMap<'db> {
@ -2622,6 +2624,9 @@ impl<'db> Evaluator<'db> {
def,
generic_args,
);
let Either::Left(imp) = imp else {
not_supported!("evaluating builtin derive impls is not supported")
};
let mir_body = self
.db

View file

@ -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,

View file

@ -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() {

View file

@ -1,9 +1,9 @@
//! Definition of `SolverDefId`
use hir_def::{
AdtId, AttrDefId, CallableDefId, ConstId, DefWithBodyId, EnumId, EnumVariantId, FunctionId,
GeneralConstId, GenericDefId, HasModule, ImplId, ModuleId, StaticId, StructId, TraitId,
TypeAliasId, UnionId, db::DefDatabase,
AdtId, AttrDefId, BuiltinDeriveImplId, CallableDefId, ConstId, DefWithBodyId, EnumId,
EnumVariantId, FunctionId, GeneralConstId, GenericDefId, ImplId, StaticId, StructId, TraitId,
TypeAliasId, UnionId,
};
use rustc_type_ir::inherent;
use stdx::impl_from;
@ -24,6 +24,7 @@ pub enum SolverDefId {
ConstId(ConstId),
FunctionId(FunctionId),
ImplId(ImplId),
BuiltinDeriveImplId(BuiltinDeriveImplId),
StaticId(StaticId),
TraitId(TraitId),
TypeAliasId(TypeAliasId),
@ -57,6 +58,7 @@ impl std::fmt::Debug for SolverDefId {
f.debug_tuple("FunctionId").field(&db.function_signature(id).name.as_str()).finish()
}
SolverDefId::ImplId(id) => f.debug_tuple("ImplId").field(&id).finish(),
SolverDefId::BuiltinDeriveImplId(id) => f.debug_tuple("ImplId").field(&id).finish(),
SolverDefId::StaticId(id) => {
f.debug_tuple("StaticId").field(&db.static_signature(id).name.as_str()).finish()
}
@ -108,6 +110,7 @@ impl_from!(
ConstId,
FunctionId,
ImplId,
BuiltinDeriveImplId,
StaticId,
TraitId,
TypeAliasId,
@ -170,7 +173,8 @@ impl TryFrom<SolverDefId> for AttrDefId {
SolverDefId::EnumVariantId(it) => Ok(it.into()),
SolverDefId::Ctor(Ctor::Struct(it)) => Ok(it.into()),
SolverDefId::Ctor(Ctor::Enum(it)) => Ok(it.into()),
SolverDefId::InternedClosureId(_)
SolverDefId::BuiltinDeriveImplId(_)
| SolverDefId::InternedClosureId(_)
| SolverDefId::InternedCoroutineId(_)
| SolverDefId::InternedOpaqueTyId(_) => Err(()),
}
@ -191,6 +195,7 @@ impl TryFrom<SolverDefId> for DefWithBodyId {
| SolverDefId::TraitId(_)
| SolverDefId::TypeAliasId(_)
| SolverDefId::ImplId(_)
| SolverDefId::BuiltinDeriveImplId(_)
| SolverDefId::InternedClosureId(_)
| SolverDefId::InternedCoroutineId(_)
| SolverDefId::Ctor(Ctor::Struct(_))
@ -216,6 +221,7 @@ impl TryFrom<SolverDefId> for GenericDefId {
| SolverDefId::InternedCoroutineId(_)
| SolverDefId::InternedOpaqueTyId(_)
| SolverDefId::EnumVariantId(_)
| SolverDefId::BuiltinDeriveImplId(_)
| SolverDefId::Ctor(_) => return Err(()),
})
}
@ -241,28 +247,6 @@ impl SolverDefId {
}
}
impl HasModule for SolverDefId {
fn module(&self, db: &dyn DefDatabase) -> ModuleId {
match *self {
SolverDefId::AdtId(id) => id.module(db),
SolverDefId::ConstId(id) => id.module(db),
SolverDefId::FunctionId(id) => id.module(db),
SolverDefId::ImplId(id) => id.module(db),
SolverDefId::StaticId(id) => id.module(db),
SolverDefId::TraitId(id) => id.module(db),
SolverDefId::TypeAliasId(id) => id.module(db),
SolverDefId::InternedClosureId(id) => id.loc(db).0.module(db),
SolverDefId::InternedCoroutineId(id) => id.loc(db).0.module(db),
SolverDefId::InternedOpaqueTyId(id) => match id.loc(db) {
crate::ImplTraitId::ReturnTypeImplTrait(owner, _) => owner.module(db),
crate::ImplTraitId::TypeAliasImplTrait(owner, _) => owner.module(db),
},
SolverDefId::Ctor(Ctor::Enum(id)) | SolverDefId::EnumVariantId(id) => id.module(db),
SolverDefId::Ctor(Ctor::Struct(id)) => id.module(db),
}
}
}
impl<'db> inherent::DefId<DbInterner<'db>> for SolverDefId {
fn as_local(self) -> Option<SolverDefId> {
Some(self)
@ -332,7 +316,6 @@ declare_id_wrapper!(TypeAliasIdWrapper, TypeAliasId);
declare_id_wrapper!(ClosureIdWrapper, InternedClosureId);
declare_id_wrapper!(CoroutineIdWrapper, InternedCoroutineId);
declare_id_wrapper!(AdtIdWrapper, AdtId);
declare_id_wrapper!(ImplIdWrapper, ImplId);
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub struct GeneralConstIdWrapper(pub GeneralConstId);
@ -433,3 +416,40 @@ impl<'db> inherent::DefId<DbInterner<'db>> for CallableIdWrapper {
true
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum AnyImplId {
ImplId(ImplId),
BuiltinDeriveImplId(BuiltinDeriveImplId),
}
impl_from!(ImplId, BuiltinDeriveImplId for AnyImplId);
impl From<AnyImplId> for SolverDefId {
#[inline]
fn from(value: AnyImplId) -> SolverDefId {
match value {
AnyImplId::ImplId(it) => it.into(),
AnyImplId::BuiltinDeriveImplId(it) => it.into(),
}
}
}
impl TryFrom<SolverDefId> for AnyImplId {
type Error = ();
#[inline]
fn try_from(value: SolverDefId) -> Result<Self, Self::Error> {
match value {
SolverDefId::ImplId(it) => Ok(it.into()),
SolverDefId::BuiltinDeriveImplId(it) => Ok(it.into()),
_ => Err(()),
}
}
}
impl<'db> inherent::DefId<DbInterner<'db>> for AnyImplId {
fn as_local(self) -> Option<SolverDefId> {
Some(self.into())
}
fn is_local(self) -> bool {
true
}
}

View file

@ -1,8 +1,8 @@
use rustc_type_ir::{solve::GoalSource, solve::inspect::GoalEvaluation};
use serde_derive::{Deserialize, Serialize};
use crate::next_solver::infer::InferCtxt;
use crate::next_solver::inspect::{InspectCandidate, InspectGoal};
use crate::next_solver::{AnyImplId, infer::InferCtxt};
use crate::next_solver::{DbInterner, Span};
#[derive(Debug, Clone, Serialize, Deserialize)]
@ -76,14 +76,31 @@ impl<'a, 'db> ProofTreeSerializer<'a, 'db> {
use rustc_type_ir::solve::inspect::ProbeKind;
match candidate.kind() {
ProbeKind::TraitCandidate { source, .. } => {
use hir_def::{Lookup, src::HasSource};
use rustc_type_ir::solve::CandidateSource;
let db = self.infcx.interner.db;
match source {
CandidateSource::Impl(impl_def_id) => {
use hir_def::{Lookup, src::HasSource};
let db = self.infcx.interner.db;
let impl_src = impl_def_id.0.lookup(db).source(db);
Some(impl_src.value.to_string())
}
CandidateSource::Impl(impl_def_id) => match impl_def_id {
AnyImplId::ImplId(impl_def_id) => {
let impl_src = impl_def_id.lookup(db).source(db);
Some(impl_src.value.to_string())
}
AnyImplId::BuiltinDeriveImplId(impl_id) => {
let impl_loc = impl_id.loc(db);
let adt_src = match impl_loc.adt {
hir_def::AdtId::StructId(adt) => {
adt.loc(db).source(db).value.to_string()
}
hir_def::AdtId::UnionId(adt) => {
adt.loc(db).source(db).value.to_string()
}
hir_def::AdtId::EnumId(adt) => {
adt.loc(db).source(db).value.to_string()
}
};
Some(format!("#[derive(${})]\n{}", impl_loc.trait_.name(), adt_src))
}
},
_ => None,
}
}

View file

@ -4,14 +4,15 @@ use hir_def::{
ConstParamId, GenericDefId, GenericParamId, LifetimeParamId, TypeOrConstParamId, TypeParamId,
hir::generics::{GenericParams, TypeOrConstParamData},
};
use rustc_type_ir::inherent::GenericsOf;
use crate::{db::HirDatabase, generics::parent_generic_def};
use crate::generics::parent_generic_def;
use super::SolverDefId;
use super::DbInterner;
pub(crate) fn generics(db: &dyn HirDatabase, def: SolverDefId) -> Generics {
pub(crate) fn generics(interner: DbInterner<'_>, def: SolverDefId) -> Generics {
let mk_lt = |parent, index, local_id| {
let id = GenericParamId::LifetimeParamId(LifetimeParamId { parent, local_id });
GenericParamDef { index, id }
@ -50,6 +51,7 @@ pub(crate) fn generics(db: &dyn HirDatabase, def: SolverDefId) -> Generics {
result
};
let db = interner.db;
let (parent, own_params) = match (def.try_into(), def) {
(Ok(def), _) => (
parent_generic_def(db, def),
@ -66,9 +68,12 @@ pub(crate) fn generics(db: &dyn HirDatabase, def: SolverDefId) -> Generics {
}
}
}
(_, SolverDefId::BuiltinDeriveImplId(id)) => {
return crate::builtin_derive::generics_of(interner, id);
}
_ => panic!("No generics for {def:?}"),
};
let parent_generics = parent.map(|def| Box::new(generics(db, def.into())));
let parent_generics = parent.map(|def| Box::new(generics(interner, def.into())));
Generics {
parent,
@ -84,6 +89,13 @@ pub struct Generics {
pub own_params: Vec<GenericParamDef>,
}
impl Generics {
pub(crate) fn push_param(&mut self, id: GenericParamId) {
let index = self.count() as u32;
self.own_params.push(GenericParamDef { index, id });
}
}
#[derive(Debug)]
pub struct GenericParamDef {
index: u32,

View file

@ -2,7 +2,7 @@
use std::ops::ControlFlow;
use hir_def::{ImplId, TraitId};
use hir_def::TraitId;
use macros::{TypeFoldable, TypeVisitable};
use rustc_type_ir::{
Interner,
@ -12,7 +12,7 @@ use rustc_type_ir::{
use crate::{
db::InternedOpaqueTyId,
next_solver::{
Const, ErrorGuaranteed, GenericArgs, Goal, TraitRef, Ty, TypeError,
AnyImplId, Const, ErrorGuaranteed, GenericArgs, Goal, TraitRef, Ty, TypeError,
infer::{
InferCtxt,
select::EvaluationResult::*,
@ -249,7 +249,7 @@ impl<'db, N> ImplSource<'db, N> {
pub(crate) struct ImplSourceUserDefinedData<'db, N> {
#[type_visitable(ignore)]
#[type_foldable(identity)]
pub(crate) impl_def_id: ImplId,
pub(crate) impl_def_id: AnyImplId,
pub(crate) args: GenericArgs<'db>,
pub(crate) nested: Vec<N>,
}
@ -395,7 +395,7 @@ fn to_selection<'db>(cand: InspectCandidate<'_, 'db>) -> Option<Selection<'db>>
// FIXME: Remove this in favor of storing this in the tree
// For impl candidates, we do the rematch manually to compute the args.
ImplSource::UserDefined(ImplSourceUserDefinedData {
impl_def_id: impl_def_id.0,
impl_def_id,
args: cand.instantiate_impl_args(),
nested,
})

View file

@ -38,10 +38,10 @@ use crate::{
lower::GenericPredicates,
method_resolution::TraitImpls,
next_solver::{
AdtIdWrapper, BoundConst, CallableIdWrapper, CanonicalVarKind, ClosureIdWrapper,
CoroutineIdWrapper, Ctor, FnSig, FxIndexMap, GeneralConstIdWrapper, ImplIdWrapper,
OpaqueTypeKey, RegionAssumptions, SimplifiedType, SolverContext, SolverDefIds,
TraitIdWrapper, TypeAliasIdWrapper, UnevaluatedConst, util::explicit_item_bounds,
AdtIdWrapper, AnyImplId, BoundConst, CallableIdWrapper, CanonicalVarKind, ClosureIdWrapper,
CoroutineIdWrapper, Ctor, FnSig, FxIndexMap, GeneralConstIdWrapper, OpaqueTypeKey,
RegionAssumptions, SimplifiedType, SolverContext, SolverDefIds, TraitIdWrapper,
TypeAliasIdWrapper, UnevaluatedConst, util::explicit_item_bounds,
},
};
@ -1020,7 +1020,7 @@ impl<'db> Interner for DbInterner<'db> {
type CoroutineClosureId = CoroutineIdWrapper;
type CoroutineId = CoroutineIdWrapper;
type AdtId = AdtIdWrapper;
type ImplId = ImplIdWrapper;
type ImplId = AnyImplId;
type UnevaluatedConstId = GeneralConstIdWrapper;
type Span = Span;
@ -1164,7 +1164,7 @@ impl<'db> Interner for DbInterner<'db> {
}
fn generics_of(self, def_id: Self::DefId) -> Self::GenericsOf {
generics(self.db(), def_id)
generics(self, def_id)
}
fn variances_of(self, def_id: Self::DefId) -> Self::VariancesOf {
@ -1190,6 +1190,7 @@ impl<'db> Interner for DbInterner<'db> {
| SolverDefId::TraitId(_)
| SolverDefId::TypeAliasId(_)
| SolverDefId::ImplId(_)
| SolverDefId::BuiltinDeriveImplId(_)
| SolverDefId::InternedClosureId(_)
| SolverDefId::InternedCoroutineId(_) => {
return VariancesOf::empty(self);
@ -1327,6 +1328,7 @@ impl<'db> Interner for DbInterner<'db> {
| SolverDefId::AdtId(_)
| SolverDefId::TraitId(_)
| SolverDefId::ImplId(_)
| SolverDefId::BuiltinDeriveImplId(_)
| SolverDefId::EnumVariantId(..)
| SolverDefId::Ctor(..)
| SolverDefId::InternedOpaqueTyId(..) => panic!(),
@ -1445,8 +1447,7 @@ impl<'db> Interner for DbInterner<'db> {
self,
def_id: Self::DefId,
) -> EarlyBinder<Self, impl IntoIterator<Item = Self::Clause>> {
GenericPredicates::query_all(self.db, def_id.try_into().unwrap())
.map_bound(|it| it.iter().copied())
predicates_of(self.db, def_id).all_predicates().map_bound(|it| it.iter().copied())
}
#[tracing::instrument(level = "debug", skip(self), ret)]
@ -1454,8 +1455,7 @@ impl<'db> Interner for DbInterner<'db> {
self,
def_id: Self::DefId,
) -> EarlyBinder<Self, impl IntoIterator<Item = Self::Clause>> {
GenericPredicates::query_own(self.db, def_id.try_into().unwrap())
.map_bound(|it| it.iter().copied())
predicates_of(self.db, def_id).own_predicates().map_bound(|it| it.iter().copied())
}
#[tracing::instrument(skip(self), ret)]
@ -1500,32 +1500,30 @@ impl<'db> Interner for DbInterner<'db> {
}
}
GenericPredicates::query_explicit(self.db, def_id.try_into().unwrap()).map_bound(
|predicates| {
predicates
.iter()
.copied()
.filter(|p| match p.kind().skip_binder() {
ClauseKind::Trait(it) => is_self_or_assoc(it.self_ty()),
ClauseKind::TypeOutlives(it) => is_self_or_assoc(it.0),
ClauseKind::Projection(it) => is_self_or_assoc(it.self_ty()),
ClauseKind::HostEffect(it) => is_self_or_assoc(it.self_ty()),
// FIXME: Not sure is this correct to allow other clauses but we might replace
// `generic_predicates_ns` query here with something closer to rustc's
// `implied_bounds_with_filter`, which is more granular lowering than this
// "lower at once and then filter" implementation.
_ => true,
})
.map(|p| (p, Span::dummy()))
},
)
predicates_of(self.db, def_id).explicit_predicates().map_bound(|predicates| {
predicates
.iter()
.copied()
.filter(|p| match p.kind().skip_binder() {
ClauseKind::Trait(it) => is_self_or_assoc(it.self_ty()),
ClauseKind::TypeOutlives(it) => is_self_or_assoc(it.0),
ClauseKind::Projection(it) => is_self_or_assoc(it.self_ty()),
ClauseKind::HostEffect(it) => is_self_or_assoc(it.self_ty()),
// FIXME: Not sure is this correct to allow other clauses but we might replace
// `generic_predicates_ns` query here with something closer to rustc's
// `implied_bounds_with_filter`, which is more granular lowering than this
// "lower at once and then filter" implementation.
_ => true,
})
.map(|p| (p, Span::dummy()))
})
}
fn impl_super_outlives(
self,
impl_id: Self::ImplId,
) -> EarlyBinder<Self, impl IntoIterator<Item = Self::Clause>> {
let trait_ref = self.db().impl_trait(impl_id.0).expect("expected an impl of trait");
let trait_ref = self.impl_trait_ref(impl_id);
trait_ref.map_bound(|trait_ref| {
let clause: Clause<'_> = trait_ref.upcast(self);
elaborate(self, [clause]).filter(|clause| {
@ -1790,6 +1788,7 @@ impl<'db> Interner for DbInterner<'db> {
SolverDefId::ConstId(_)
| SolverDefId::FunctionId(_)
| SolverDefId::ImplId(_)
| SolverDefId::BuiltinDeriveImplId(_)
| SolverDefId::StaticId(_)
| SolverDefId::InternedClosureId(_)
| SolverDefId::InternedCoroutineId(_)
@ -1805,7 +1804,12 @@ impl<'db> Interner for DbInterner<'db> {
type_block,
trait_block,
&mut |impls| {
for &impl_ in impls.for_trait_and_self_ty(trait_def_id.0, &simp) {
let (regular_impls, builtin_derive_impls) =
impls.for_trait_and_self_ty(trait_def_id.0, &simp);
for &impl_ in regular_impls {
f(impl_.into());
}
for &impl_ in builtin_derive_impls {
f(impl_.into());
}
},
@ -1927,7 +1931,10 @@ impl<'db> Interner for DbInterner<'db> {
}
fn impl_is_default(self, impl_def_id: Self::ImplId) -> bool {
self.db.impl_signature(impl_def_id.0).is_default()
match impl_def_id {
AnyImplId::ImplId(impl_id) => self.db.impl_signature(impl_id).is_default(),
AnyImplId::BuiltinDeriveImplId(_) => false,
}
}
#[tracing::instrument(skip(self), ret)]
@ -1935,14 +1942,24 @@ impl<'db> Interner for DbInterner<'db> {
self,
impl_id: Self::ImplId,
) -> EarlyBinder<Self, rustc_type_ir::TraitRef<Self>> {
let db = self.db();
db.impl_trait(impl_id.0)
// ImplIds for impls where the trait ref can't be resolved should never reach trait solving
.expect("invalid impl passed to trait solver")
match impl_id {
AnyImplId::ImplId(impl_id) => {
let db = self.db();
db.impl_trait(impl_id)
// ImplIds for impls where the trait ref can't be resolved should never reach trait solving
.expect("invalid impl passed to trait solver")
}
AnyImplId::BuiltinDeriveImplId(impl_id) => {
crate::builtin_derive::impl_trait(self, impl_id)
}
}
}
fn impl_polarity(self, impl_id: Self::ImplId) -> rustc_type_ir::ImplPolarity {
let impl_data = self.db().impl_signature(impl_id.0);
let AnyImplId::ImplId(impl_id) = impl_id else {
return ImplPolarity::Positive;
};
let impl_data = self.db().impl_signature(impl_id);
if impl_data.flags.contains(ImplFlags::NEGATIVE) {
ImplPolarity::Negative
} else {
@ -2230,11 +2247,13 @@ impl<'db> Interner for DbInterner<'db> {
specializing_impl_def_id: Self::ImplId,
parent_impl_def_id: Self::ImplId,
) -> bool {
crate::specialization::specializes(
self.db,
specializing_impl_def_id.0,
parent_impl_def_id.0,
)
let (AnyImplId::ImplId(specializing_impl_def_id), AnyImplId::ImplId(parent_impl_def_id)) =
(specializing_impl_def_id, parent_impl_def_id)
else {
// No builtin derive allow specialization currently.
return false;
};
crate::specialization::specializes(self.db, specializing_impl_def_id, parent_impl_def_id)
}
fn next_trait_solver_globally(self) -> bool {
@ -2349,6 +2368,14 @@ impl<'db> DbInterner<'db> {
}
}
fn predicates_of(db: &dyn HirDatabase, def_id: SolverDefId) -> &GenericPredicates {
if let SolverDefId::BuiltinDeriveImplId(impl_) = def_id {
crate::builtin_derive::predicates(db, impl_)
} else {
GenericPredicates::query(db, def_id.try_into().unwrap())
}
}
macro_rules! TrivialTypeTraversalImpls {
($($ty:ty,)+) => {
$(
@ -2396,7 +2423,7 @@ TrivialTypeTraversalImpls! {
ClosureIdWrapper,
CoroutineIdWrapper,
AdtIdWrapper,
ImplIdWrapper,
AnyImplId,
GeneralConstIdWrapper,
Safety,
FnAbi,

View file

@ -12,7 +12,7 @@ use rustc_type_ir::{
use tracing::debug;
use crate::next_solver::{
AliasTy, CanonicalVarKind, Clause, ClauseKind, CoercePredicate, GenericArgs, ImplIdWrapper,
AliasTy, AnyImplId, CanonicalVarKind, Clause, ClauseKind, CoercePredicate, GenericArgs,
ParamEnv, Predicate, PredicateKind, SubtypePredicate, Ty, TyKind, fold::fold_tys,
util::sizedness_fast_path,
};
@ -174,9 +174,13 @@ impl<'db> SolverDelegate for SolverContext<'db> {
&self,
_goal_trait_ref: rustc_type_ir::TraitRef<Self::Interner>,
trait_assoc_def_id: SolverDefId,
impl_id: ImplIdWrapper,
impl_id: AnyImplId,
) -> Result<Option<SolverDefId>, ErrorGuaranteed> {
let impl_items = impl_id.0.impl_items(self.0.interner.db());
let AnyImplId::ImplId(impl_id) = impl_id else {
// Builtin derive traits don't have type/consts assoc items.
return Ok(None);
};
let impl_items = impl_id.impl_items(self.0.interner.db());
let id =
match trait_assoc_def_id {
SolverDefId::TypeAliasId(trait_assoc_id) => {

View file

@ -243,6 +243,10 @@ $0",
"parse_shim",
"real_span_map_shim",
"TraitImpls::for_crate_",
"lang_items",
"crate_lang_items",
"AttrFlags::query_",
"AttrFlags::query_",
]
"#]],
);
@ -279,6 +283,10 @@ pub struct NewStruct {
"real_span_map_shim",
"crate_local_def_map",
"TraitImpls::for_crate_",
"crate_lang_items",
"AttrFlags::query_",
"AttrFlags::query_",
"AttrFlags::query_",
]
"#]],
);
@ -314,6 +322,10 @@ $0",
"parse_shim",
"real_span_map_shim",
"TraitImpls::for_crate_",
"lang_items",
"crate_lang_items",
"AttrFlags::query_",
"AttrFlags::query_",
]
"#]],
);
@ -351,6 +363,13 @@ pub enum SomeEnum {
"real_span_map_shim",
"crate_local_def_map",
"TraitImpls::for_crate_",
"crate_lang_items",
"AttrFlags::query_",
"AttrFlags::query_",
"AttrFlags::query_",
"EnumVariants::of_",
"AttrFlags::query_",
"AttrFlags::query_",
]
"#]],
);
@ -386,6 +405,10 @@ $0",
"parse_shim",
"real_span_map_shim",
"TraitImpls::for_crate_",
"lang_items",
"crate_lang_items",
"AttrFlags::query_",
"AttrFlags::query_",
]
"#]],
);
@ -420,6 +443,9 @@ fn bar() -> f32 {
"real_span_map_shim",
"crate_local_def_map",
"TraitImpls::for_crate_",
"crate_lang_items",
"AttrFlags::query_",
"AttrFlags::query_",
]
"#]],
);
@ -459,6 +485,11 @@ $0",
"parse_shim",
"real_span_map_shim",
"TraitImpls::for_crate_",
"lang_items",
"crate_lang_items",
"AttrFlags::query_",
"AttrFlags::query_",
"AttrFlags::query_",
]
"#]],
);
@ -501,17 +532,16 @@ impl SomeStruct {
"real_span_map_shim",
"crate_local_def_map",
"TraitImpls::for_crate_",
"AttrFlags::query_",
"impl_trait_with_diagnostics_query",
"impl_signature_shim",
"impl_signature_with_source_map_shim",
"lang_items",
"crate_lang_items",
"AttrFlags::query_",
"ImplItems::of_",
"AttrFlags::query_",
"AttrFlags::query_",
"AttrFlags::query_",
"AttrFlags::query_",
"impl_trait_with_diagnostics_query",
"impl_signature_shim",
"impl_signature_with_source_map_shim",
"impl_self_ty_with_diagnostics_query",
"struct_signature_shim",
"struct_signature_with_source_map_shim",

View file

@ -35,6 +35,8 @@ pub enum AttrsOwner {
Field(FieldId),
LifetimeParam(LifetimeParamId),
TypeOrConstParam(TypeOrConstParamId),
/// Things that do not have attributes. Used for builtin derives.
Dummy,
}
impl AttrsOwner {
@ -123,7 +125,9 @@ impl AttrsWithOwner {
let owner = match self.owner {
AttrsOwner::AttrDef(it) => Either::Left(it),
AttrsOwner::Field(it) => Either::Right(it),
AttrsOwner::LifetimeParam(_) | AttrsOwner::TypeOrConstParam(_) => return &[],
AttrsOwner::LifetimeParam(_) | AttrsOwner::TypeOrConstParam(_) | AttrsOwner::Dummy => {
return &[];
}
};
self.attrs.doc_aliases(db, owner)
}
@ -133,7 +137,9 @@ impl AttrsWithOwner {
let owner = match self.owner {
AttrsOwner::AttrDef(it) => Either::Left(it),
AttrsOwner::Field(it) => Either::Right(it),
AttrsOwner::LifetimeParam(_) | AttrsOwner::TypeOrConstParam(_) => return None,
AttrsOwner::LifetimeParam(_) | AttrsOwner::TypeOrConstParam(_) | AttrsOwner::Dummy => {
return None;
}
};
self.attrs.cfgs(db, owner)
}
@ -143,7 +149,9 @@ impl AttrsWithOwner {
match self.owner {
AttrsOwner::AttrDef(it) => AttrFlags::docs(db, it).as_deref(),
AttrsOwner::Field(it) => AttrFlags::field_docs(db, it),
AttrsOwner::LifetimeParam(_) | AttrsOwner::TypeOrConstParam(_) => None,
AttrsOwner::LifetimeParam(_) | AttrsOwner::TypeOrConstParam(_) | AttrsOwner::Dummy => {
None
}
}
}
}
@ -156,6 +164,9 @@ pub trait HasAttrs: Sized {
AttrsOwner::Field(it) => AttrsWithOwner::new_field(db, it),
AttrsOwner::LifetimeParam(it) => AttrsWithOwner::new_lifetime_param(db, it),
AttrsOwner::TypeOrConstParam(it) => AttrsWithOwner::new_type_or_const_param(db, it),
AttrsOwner::Dummy => {
AttrsWithOwner { attrs: AttrFlags::empty(), owner: AttrsOwner::Dummy }
}
}
}
@ -167,7 +178,9 @@ pub trait HasAttrs: Sized {
match self.attr_id(db) {
AttrsOwner::AttrDef(it) => AttrFlags::docs(db, it).as_deref(),
AttrsOwner::Field(it) => AttrFlags::field_docs(db, it),
AttrsOwner::LifetimeParam(_) | AttrsOwner::TypeOrConstParam(_) => None,
AttrsOwner::LifetimeParam(_) | AttrsOwner::TypeOrConstParam(_) | AttrsOwner::Dummy => {
None
}
}
}
}
@ -190,12 +203,28 @@ impl_has_attrs![
(Trait, TraitId),
(TypeAlias, TypeAliasId),
(Macro, MacroId),
(Function, FunctionId),
(Adt, AdtId),
(Impl, ImplId),
(ExternCrateDecl, ExternCrateId),
];
impl HasAttrs for Function {
fn attr_id(self, _db: &dyn HirDatabase) -> AttrsOwner {
match self.id {
crate::AnyFunctionId::FunctionId(id) => AttrsOwner::AttrDef(id.into()),
crate::AnyFunctionId::BuiltinDeriveImplMethod { .. } => AttrsOwner::Dummy,
}
}
}
impl HasAttrs for Impl {
fn attr_id(self, _db: &dyn HirDatabase) -> AttrsOwner {
match self.id {
hir_ty::next_solver::AnyImplId::ImplId(id) => AttrsOwner::AttrDef(id.into()),
hir_ty::next_solver::AnyImplId::BuiltinDeriveImplId(..) => AttrsOwner::Dummy,
}
}
}
macro_rules! impl_has_attrs_enum {
($($variant:ident),* for $enum:ident) => {$(
impl HasAttrs for $variant {
@ -294,7 +323,9 @@ fn resolve_doc_path_on_(
AttrsOwner::AttrDef(AttrDefId::MacroId(it)) => it.resolver(db),
AttrsOwner::AttrDef(AttrDefId::ExternCrateId(it)) => it.resolver(db),
AttrsOwner::Field(it) => it.parent.resolver(db),
AttrsOwner::LifetimeParam(_) | AttrsOwner::TypeOrConstParam(_) => return None,
AttrsOwner::LifetimeParam(_) | AttrsOwner::TypeOrConstParam(_) | AttrsOwner::Dummy => {
return None;
}
};
let mut modpath = doc_modpath_from_str(link)?;

View file

@ -2,19 +2,22 @@
use either::Either;
use hir_def::{
AdtId, GenericDefId,
AdtId, BuiltinDeriveImplId, FunctionId, GenericDefId, ImplId, ItemContainerId,
builtin_derive::BuiltinDeriveImplMethod,
expr_store::ExpressionStore,
hir::generics::{GenericParams, TypeOrConstParamData, TypeParamProvenance, WherePredicate},
item_tree::FieldsShape,
signatures::{StaticFlags, TraitFlags},
type_ref::{TypeBound, TypeRef, TypeRefId},
};
use hir_expand::name::Name;
use hir_ty::{
GenericPredicates,
db::HirDatabase,
display::{
HirDisplay, HirDisplayWithExpressionStore, HirFormatter, Result, SizedByDefault,
hir_display_with_store, write_bounds_like_dyn_trait_with_prefix, write_visibility,
hir_display_with_store, write_bounds_like_dyn_trait_with_prefix, write_params_bounds,
write_visibility,
},
next_solver::ClauseKind,
};
@ -22,25 +25,78 @@ use itertools::Itertools;
use rustc_type_ir::inherent::IntoKind;
use crate::{
Adt, AsAssocItem, AssocItem, AssocItemContainer, Const, ConstParam, Crate, Enum,
Adt, AnyFunctionId, AsAssocItem, AssocItem, AssocItemContainer, Const, ConstParam, Crate, Enum,
ExternCrateDecl, Field, Function, GenericParam, HasCrate, HasVisibility, Impl, LifetimeParam,
Macro, Module, SelfParam, Static, Struct, StructKind, Trait, TraitRef, TupleField, Type,
TypeAlias, TypeNs, TypeOrConstParam, TypeParam, Union, Variant,
};
fn write_builtin_derive_impl_method<'db>(
f: &mut HirFormatter<'_, 'db>,
impl_: BuiltinDeriveImplId,
method: BuiltinDeriveImplMethod,
) -> Result {
let db = f.db;
let loc = impl_.loc(db);
let (adt_params, _adt_params_store) = db.generic_params_and_store(loc.adt.into());
if f.show_container_bounds() && !adt_params.is_empty() {
f.write_str("impl")?;
write_generic_params(loc.adt.into(), f)?;
f.write_char(' ')?;
let trait_id = loc.trait_.get_id(f.lang_items());
if let Some(trait_id) = trait_id {
f.start_location_link(trait_id.into());
}
write!(f, "{}", Name::new_symbol_root(loc.trait_.name()).display(db, f.edition()))?;
if trait_id.is_some() {
f.end_location_link();
}
f.write_str(" for ")?;
f.start_location_link(loc.adt.into());
write!(f, "{}", Adt::from(loc.adt).name(db).display(db, f.edition()))?;
f.end_location_link();
write_generic_args(loc.adt.into(), f)?;
f.write_char('\n')?;
}
let Some(trait_method) = method.trait_method(db, impl_) else {
return write!(f, "fn {}(…)", method.name());
};
let has_written_where = write_function(f, trait_method)?;
if f.show_container_bounds() && !adt_params.is_empty() {
if !has_written_where {
f.write_str("\nwhere")?
}
write!(f, "\n // Bounds from impl:")?;
let predicates =
hir_ty::builtin_derive::predicates(db, impl_).explicit_predicates().skip_binder();
write_params_bounds(f, predicates)?;
}
Ok(())
}
impl<'db> HirDisplay<'db> for Function {
fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>) -> Result {
let id = match self.id {
AnyFunctionId::FunctionId(id) => id,
AnyFunctionId::BuiltinDeriveImplMethod { method, impl_ } => {
return write_builtin_derive_impl_method(f, impl_, method);
}
};
let db = f.db;
let data = db.function_signature(self.id);
let container = self.as_assoc_item(db).map(|it| it.container(db));
let mut module = self.module(db);
let container = id.loc(db).container;
// Write container (trait or impl)
let container_params = match container {
Some(AssocItemContainer::Trait(trait_)) => {
let (params, params_store) = f.db.generic_params_and_store(trait_.id.into());
ItemContainerId::TraitId(trait_) => {
let (params, params_store) = f.db.generic_params_and_store(trait_.into());
if f.show_container_bounds() && !params.is_empty() {
write_trait_header(&trait_, f)?;
write_trait_header(trait_.into(), f)?;
f.write_char('\n')?;
has_disaplayable_predicates(f.db, &params, &params_store)
.then_some((params, params_store))
@ -48,10 +104,10 @@ impl<'db> HirDisplay<'db> for Function {
None
}
}
Some(AssocItemContainer::Impl(impl_)) => {
let (params, params_store) = f.db.generic_params_and_store(impl_.id.into());
ItemContainerId::ImplId(impl_) => {
let (params, params_store) = f.db.generic_params_and_store(impl_.into());
if f.show_container_bounds() && !params.is_empty() {
write_impl_header(&impl_, f)?;
write_impl_header(impl_, f)?;
f.write_char('\n')?;
has_disaplayable_predicates(f.db, &params, &params_store)
.then_some((params, params_store))
@ -59,124 +115,20 @@ 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 module_id = module.id;
write_visibility(module_id, self.visibility(db), f)?;
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()))?;
write_generic_params(GenericDefId::FunctionId(self.id), f)?;
f.write_char('(')?;
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;
}
// 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;
}
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_str(": ")?;
type_ref.hir_fmt(f, &data.store)?;
}
if data.is_varargs() {
if !first {
f.write_str(", ")?;
}
f.write_str("...")?;
}
f.write_char(')')?;
// `FunctionData::ret_type` will be `::core::future::Future<Output = ...>` for async fns.
// Use ugly pattern match to strip the Future trait.
// Better way?
let ret_type = if !data.is_async() {
data.ret_type
} else if let Some(ret_type) = data.ret_type {
match &data.store[ret_type] {
TypeRef::ImplTrait(bounds) => match &bounds[0] {
&TypeBound::Path(path, _) => Some(
*data.store[path]
.segments()
.iter()
.last()
.unwrap()
.args_and_bindings
.unwrap()
.bindings[0]
.type_ref
.as_ref()
.unwrap(),
),
_ => None,
},
_ => 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)?;
}
}
}
// Write where clauses
let has_written_where = write_where_clause(GenericDefId::FunctionId(self.id), f)?;
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.unwrap() {
AssocItemContainer::Trait(_) => "trait",
AssocItemContainer::Impl(_) => "impl",
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)?;
@ -185,14 +137,129 @@ impl<'db> HirDisplay<'db> for Function {
}
}
fn write_impl_header<'db>(impl_: &Impl, f: &mut HirFormatter<'_, 'db>) -> Result {
fn write_function<'db>(f: &mut HirFormatter<'_, 'db>, func_id: FunctionId) -> Result<bool> {
let db = f.db;
let func = Function::from(func_id);
let data = db.function_signature(func_id);
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;
write_visibility(module_id, func.visibility(db), f)?;
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()))?;
write_generic_params(GenericDefId::FunctionId(func_id), f)?;
f.write_char('(')?;
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;
}
let pat_id = body.params[param.idx - body.self_param.is_some() as usize];
let pat_str = body.pretty_print_pat(db, func_id.into(), pat_id, true, f.edition());
f.write_str(&pat_str)?;
f.write_str(": ")?;
type_ref.hir_fmt(f, &data.store)?;
}
if data.is_varargs() {
if !first {
f.write_str(", ")?;
}
f.write_str("...")?;
}
f.write_char(')')?;
// `FunctionData::ret_type` will be `::core::future::Future<Output = ...>` for async fns.
// Use ugly pattern match to strip the Future trait.
// Better way?
let ret_type = if !data.is_async() {
data.ret_type
} else if let Some(ret_type) = data.ret_type {
match &data.store[ret_type] {
TypeRef::ImplTrait(bounds) => match &bounds[0] {
&TypeBound::Path(path, _) => Some(
*data.store[path]
.segments()
.iter()
.last()
.unwrap()
.args_and_bindings
.unwrap()
.bindings[0]
.type_ref
.as_ref()
.unwrap(),
),
_ => None,
},
_ => 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)?;
}
}
}
// 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_: 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) {

View file

@ -4,14 +4,15 @@
//! are splitting the hir.
use hir_def::{
AdtId, AssocItemId, DefWithBodyId, EnumVariantId, FieldId, GenericDefId, GenericParamId,
ModuleDefId, VariantId,
AdtId, AssocItemId, BuiltinDeriveImplId, DefWithBodyId, EnumVariantId, FieldId, GenericDefId,
GenericParamId, ModuleDefId, VariantId,
hir::{BindingId, LabelId},
};
use hir_ty::next_solver::AnyImplId;
use crate::{
Adt, AssocItem, BuiltinType, DefWithBody, Field, GenericDef, GenericParam, ItemInNs, Label,
Local, ModuleDef, Variant, VariantDef,
Adt, AnyFunctionId, AssocItem, BuiltinType, DefWithBody, Field, GenericDef, GenericParam,
ItemInNs, Label, Local, ModuleDef, Variant, VariantDef,
};
macro_rules! from_id {
@ -39,8 +40,8 @@ from_id![
(hir_def::TraitId, crate::Trait),
(hir_def::StaticId, crate::Static),
(hir_def::ConstId, crate::Const),
(hir_def::FunctionId, crate::Function),
(hir_def::ImplId, crate::Impl),
(crate::AnyFunctionId, crate::Function),
(hir_ty::next_solver::AnyImplId, crate::Impl),
(hir_def::TypeOrConstParamId, crate::TypeOrConstParam),
(hir_def::TypeParamId, crate::TypeParam),
(hir_def::ConstParamId, crate::ConstParam),
@ -119,11 +120,15 @@ impl From<ModuleDefId> for ModuleDef {
}
}
impl From<ModuleDef> for ModuleDefId {
fn from(id: ModuleDef) -> Self {
match id {
impl TryFrom<ModuleDef> for ModuleDefId {
type Error = ();
fn try_from(id: ModuleDef) -> Result<Self, Self::Error> {
Ok(match id {
ModuleDef::Module(it) => ModuleDefId::ModuleId(it.into()),
ModuleDef::Function(it) => ModuleDefId::FunctionId(it.into()),
ModuleDef::Function(it) => match it.id {
AnyFunctionId::FunctionId(it) => it.into(),
AnyFunctionId::BuiltinDeriveImplMethod { .. } => return Err(()),
},
ModuleDef::Adt(it) => ModuleDefId::AdtId(it.into()),
ModuleDef::Variant(it) => ModuleDefId::EnumVariantId(it.into()),
ModuleDef::Const(it) => ModuleDefId::ConstId(it.into()),
@ -132,18 +137,22 @@ impl From<ModuleDef> for ModuleDefId {
ModuleDef::TypeAlias(it) => ModuleDefId::TypeAliasId(it.into()),
ModuleDef::BuiltinType(it) => ModuleDefId::BuiltinType(it.into()),
ModuleDef::Macro(it) => ModuleDefId::MacroId(it.into()),
}
})
}
}
impl From<DefWithBody> for DefWithBodyId {
fn from(def: DefWithBody) -> Self {
match def {
DefWithBody::Function(it) => DefWithBodyId::FunctionId(it.id),
impl TryFrom<DefWithBody> for DefWithBodyId {
type Error = ();
fn try_from(def: DefWithBody) -> Result<Self, ()> {
Ok(match def {
DefWithBody::Function(it) => match it.id {
AnyFunctionId::FunctionId(it) => it.into(),
AnyFunctionId::BuiltinDeriveImplMethod { .. } => return Err(()),
},
DefWithBody::Static(it) => DefWithBodyId::StaticId(it.id),
DefWithBody::Const(it) => DefWithBodyId::ConstId(it.id),
DefWithBody::Variant(it) => DefWithBodyId::VariantId(it.into()),
}
})
}
}
@ -168,17 +177,11 @@ impl From<AssocItemId> for AssocItem {
}
}
impl From<GenericDef> for GenericDefId {
fn from(def: GenericDef) -> Self {
match def {
GenericDef::Function(it) => GenericDefId::FunctionId(it.id),
GenericDef::Adt(it) => GenericDefId::AdtId(it.into()),
GenericDef::Trait(it) => GenericDefId::TraitId(it.id),
GenericDef::TypeAlias(it) => GenericDefId::TypeAliasId(it.id),
GenericDef::Impl(it) => GenericDefId::ImplId(it.id),
GenericDef::Const(it) => GenericDefId::ConstId(it.id),
GenericDef::Static(it) => GenericDefId::StaticId(it.id),
}
impl TryFrom<GenericDef> for GenericDefId {
type Error = ();
fn try_from(def: GenericDef) -> Result<Self, Self::Error> {
def.id().ok_or(())
}
}
@ -238,13 +241,17 @@ impl From<FieldId> for Field {
}
}
impl From<AssocItem> for GenericDefId {
fn from(item: AssocItem) -> Self {
match item {
AssocItem::Function(f) => f.id.into(),
impl TryFrom<AssocItem> for GenericDefId {
type Error = ();
fn try_from(item: AssocItem) -> Result<Self, Self::Error> {
Ok(match item {
AssocItem::Function(f) => match f.id {
AnyFunctionId::FunctionId(it) => it.into(),
AnyFunctionId::BuiltinDeriveImplMethod { .. } => return Err(()),
},
AssocItem::Const(c) => c.id.into(),
AssocItem::TypeAlias(t) => t.id.into(),
}
})
}
}
@ -270,13 +277,14 @@ impl From<hir_def::item_scope::ItemInNs> for ItemInNs {
}
}
impl From<ItemInNs> for hir_def::item_scope::ItemInNs {
fn from(it: ItemInNs) -> Self {
match it {
ItemInNs::Types(it) => Self::Types(it.into()),
ItemInNs::Values(it) => Self::Values(it.into()),
impl TryFrom<ItemInNs> for hir_def::item_scope::ItemInNs {
type Error = ();
fn try_from(it: ItemInNs) -> Result<Self, Self::Error> {
Ok(match it {
ItemInNs::Types(it) => Self::Types(it.try_into()?),
ItemInNs::Values(it) => Self::Values(it.try_into()?),
ItemInNs::Macros(it) => Self::Macros(it.into()),
}
})
}
}
@ -291,3 +299,21 @@ impl From<BuiltinType> for hir_def::builtin_type::BuiltinType {
it.inner
}
}
impl From<hir_def::ImplId> for crate::Impl {
fn from(value: hir_def::ImplId) -> Self {
crate::Impl { id: AnyImplId::ImplId(value) }
}
}
impl From<BuiltinDeriveImplId> for crate::Impl {
fn from(value: BuiltinDeriveImplId) -> Self {
crate::Impl { id: AnyImplId::BuiltinDeriveImplId(value) }
}
}
impl From<hir_def::FunctionId> for crate::Function {
fn from(value: hir_def::FunctionId) -> Self {
crate::Function { id: AnyFunctionId::FunctionId(value) }
}
}

View file

@ -7,18 +7,18 @@ use hir_def::{
src::{HasChildSource, HasSource as _},
};
use hir_expand::{EditionedFileId, HirFileId, InFile};
use hir_ty::db::InternedClosure;
use syntax::ast;
use hir_ty::{db::InternedClosure, next_solver::AnyImplId};
use syntax::{AstNode, ast};
use tt::TextRange;
use crate::{
Adt, Callee, Const, Enum, ExternCrateDecl, Field, FieldSource, Function, Impl,
Adt, AnyFunctionId, Callee, Const, Enum, ExternCrateDecl, Field, FieldSource, Function, Impl,
InlineAsmOperand, Label, LifetimeParam, LocalSource, Macro, Module, Param, SelfParam, Static,
Struct, Trait, TypeAlias, TypeOrConstParam, Union, Variant, VariantDef, db::HirDatabase,
};
pub trait HasSource {
type Ast;
pub trait HasSource: Sized {
type Ast: AstNode;
/// Fetches the definition's source node.
/// Using [`crate::SemanticsImpl::source`] is preferred when working with [`crate::Semantics`],
/// as that caches the parsed file in the semantics' cache.
@ -27,6 +27,20 @@ pub trait HasSource {
/// But we made this method `Option` to support rlib in the future
/// by <https://github.com/rust-lang/rust-analyzer/issues/6913>
fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>>;
/// Fetches the source node, along with its full range.
///
/// The reason for the separate existence of this method is that some things, notably builtin derive impls,
/// do not really have a source node, at least not of the correct type. But we still can trace them
/// to source code (the derive producing them). So this method will return the range if it is supported,
/// and if the node is supported too it will return it as well.
fn source_with_range(
self,
db: &dyn HirDatabase,
) -> Option<InFile<(TextRange, Option<Self::Ast>)>> {
let source = self.source(db)?;
Some(source.map(|node| (node.syntax().text_range(), Some(node))))
}
}
/// NB: Module is !HasSource, because it has two source nodes at the same time:
@ -146,7 +160,30 @@ impl HasSource for Variant {
impl HasSource for Function {
type Ast = ast::Fn;
fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>> {
Some(self.id.lookup(db).source(db))
match self.id {
AnyFunctionId::FunctionId(id) => Some(id.loc(db).source(db)),
// When calling `source()`, we use the trait method source, but when calling `source_with_range()`,
// we return `None` as the syntax node source. This is relying on the assumption that if you are calling
// `source_with_range()` (e.g. in navigation) you're prepared to deal with no source node, while if
// you call `source()` maybe you don't - therefore we fall back to the trait method, to not lose features.
AnyFunctionId::BuiltinDeriveImplMethod { method, impl_ } => method
.trait_method(db, impl_)
.and_then(|trait_method| Function::from(trait_method).source(db)),
}
}
fn source_with_range(
self,
db: &dyn HirDatabase,
) -> Option<InFile<(TextRange, Option<Self::Ast>)>> {
match self.id {
AnyFunctionId::FunctionId(id) => Some(
id.loc(db).source(db).map(|source| (source.syntax().text_range(), Some(source))),
),
AnyFunctionId::BuiltinDeriveImplMethod { impl_, .. } => {
Some(impl_.loc(db).source(db).map(|range| (range, None)))
}
}
}
}
impl HasSource for Const {
@ -190,7 +227,24 @@ impl HasSource for Macro {
impl HasSource for Impl {
type Ast = ast::Impl;
fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>> {
Some(self.id.lookup(db).source(db))
match self.id {
AnyImplId::ImplId(id) => Some(id.loc(db).source(db)),
AnyImplId::BuiltinDeriveImplId(_) => None,
}
}
fn source_with_range(
self,
db: &dyn HirDatabase,
) -> Option<InFile<(TextRange, Option<Self::Ast>)>> {
match self.id {
AnyImplId::ImplId(id) => Some(
id.loc(db).source(db).map(|source| (source.syntax().text_range(), Some(source))),
),
AnyImplId::BuiltinDeriveImplId(impl_) => {
Some(impl_.loc(db).source(db).map(|range| (range, None)))
}
}
}
}
@ -224,7 +278,7 @@ impl HasSource for Param<'_> {
fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>> {
match self.func {
Callee::Def(CallableDefId::FunctionId(func)) => {
let InFile { file_id, value } = Function { id: func }.source(db)?;
let InFile { file_id, value } = Function::from(func).source(db)?;
let params = value.param_list()?;
if let Some(self_param) = params.self_param() {
if let Some(idx) = self.idx.checked_sub(1) {
@ -261,7 +315,7 @@ impl HasSource for SelfParam {
type Ast = ast::SelfParam;
fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>> {
let InFile { file_id, value } = Function::from(self.func).source(db)?;
let InFile { file_id, value } = self.func.source(db)?;
value
.param_list()
.and_then(|params| params.self_param())

File diff suppressed because it is too large Load diff

View file

@ -13,7 +13,7 @@ use std::{
use base_db::FxIndexSet;
use either::Either;
use hir_def::{
DefWithBodyId, FunctionId, MacroId, StructId, TraitId, VariantId,
DefWithBodyId, MacroId, StructId, TraitId, VariantId,
attrs::parse_extra_crate_attrs,
expr_store::{Body, ExprOrPatSource, HygieneId, path::Path},
hir::{BindingId, Expr, ExprId, ExprOrPatId, Pat},
@ -34,7 +34,7 @@ use hir_ty::{
diagnostics::{unsafe_operations, unsafe_operations_for_body},
infer_query_with_inspect,
next_solver::{
DbInterner, Span,
AnyImplId, DbInterner, Span,
format_proof_tree::{ProofTreeData, dump_proof_tree_structured},
},
};
@ -53,11 +53,11 @@ use syntax::{
};
use crate::{
Adjust, Adjustment, Adt, AutoBorrow, BindingMode, BuiltinAttr, Callable, Const, ConstParam,
Crate, DefWithBody, DeriveHelper, Enum, Field, Function, GenericSubstitution, HasSource, Impl,
InFile, InlineAsmOperand, ItemInNs, Label, LifetimeParam, Local, Macro, Module, ModuleDef,
Name, OverloadedDeref, ScopeDef, Static, Struct, ToolModule, Trait, TupleField, Type,
TypeAlias, TypeParam, Union, Variant, VariantDef,
Adjust, Adjustment, Adt, AnyFunctionId, AutoBorrow, BindingMode, BuiltinAttr, Callable, Const,
ConstParam, Crate, DefWithBody, DeriveHelper, Enum, Field, Function, GenericSubstitution,
HasSource, Impl, InFile, InlineAsmOperand, ItemInNs, Label, LifetimeParam, Local, Macro,
Module, ModuleDef, Name, OverloadedDeref, ScopeDef, Static, Struct, ToolModule, Trait,
TupleField, Type, TypeAlias, TypeParam, Union, Variant, VariantDef,
db::HirDatabase,
semantics::source_to_def::{ChildContainer, SourceToDefCache, SourceToDefCtx},
source_analyzer::{SourceAnalyzer, resolve_hir_path},
@ -106,7 +106,10 @@ impl PathResolution {
| PathResolution::DeriveHelper(_)
| PathResolution::ConstParam(_) => None,
PathResolution::TypeParam(param) => Some(TypeNs::GenericParam((*param).into())),
PathResolution::SelfType(impl_def) => Some(TypeNs::SelfType((*impl_def).into())),
PathResolution::SelfType(impl_def) => match impl_def.id {
AnyImplId::ImplId(id) => Some(TypeNs::SelfType(id)),
AnyImplId::BuiltinDeriveImplId(_) => None,
},
}
}
}
@ -345,23 +348,23 @@ impl<DB: HirDatabase + ?Sized> Semantics<'_, DB> {
}
pub fn resolve_await_to_poll(&self, await_expr: &ast::AwaitExpr) -> Option<Function> {
self.imp.resolve_await_to_poll(await_expr).map(Function::from)
self.imp.resolve_await_to_poll(await_expr)
}
pub fn resolve_prefix_expr(&self, prefix_expr: &ast::PrefixExpr) -> Option<Function> {
self.imp.resolve_prefix_expr(prefix_expr).map(Function::from)
self.imp.resolve_prefix_expr(prefix_expr)
}
pub fn resolve_index_expr(&self, index_expr: &ast::IndexExpr) -> Option<Function> {
self.imp.resolve_index_expr(index_expr).map(Function::from)
self.imp.resolve_index_expr(index_expr)
}
pub fn resolve_bin_expr(&self, bin_expr: &ast::BinExpr) -> Option<Function> {
self.imp.resolve_bin_expr(bin_expr).map(Function::from)
self.imp.resolve_bin_expr(bin_expr)
}
pub fn resolve_try_expr(&self, try_expr: &ast::TryExpr) -> Option<Function> {
self.imp.resolve_try_expr(try_expr).map(Function::from)
self.imp.resolve_try_expr(try_expr)
}
pub fn resolve_variant(&self, record_lit: ast::RecordExpr) -> Option<VariantDef> {
@ -1749,6 +1752,7 @@ impl<'db> SemanticsImpl<'db> {
func: Function,
subst: impl IntoIterator<Item = Type<'db>>,
) -> Option<Function> {
let AnyFunctionId::FunctionId(func) = func.id else { return Some(func) };
let interner = DbInterner::new_no_crate(self.db);
let mut subst = subst.into_iter();
let substs =
@ -1757,7 +1761,12 @@ impl<'db> SemanticsImpl<'db> {
subst.next().expect("too few subst").ty.into()
});
assert!(subst.next().is_none(), "too many subst");
Some(self.db.lookup_impl_method(env.env, func.into(), substs).0.into())
Some(match self.db.lookup_impl_method(env.env, func, substs).0 {
Either::Left(it) => it.into(),
Either::Right((impl_, method)) => {
Function { id: AnyFunctionId::BuiltinDeriveImplMethod { method, impl_ } }
}
})
}
fn resolve_range_pat(&self, range_pat: &ast::RangePat) -> Option<StructId> {
@ -1768,23 +1777,23 @@ impl<'db> SemanticsImpl<'db> {
self.analyze(range_expr.syntax())?.resolve_range_expr(self.db, range_expr)
}
fn resolve_await_to_poll(&self, await_expr: &ast::AwaitExpr) -> Option<FunctionId> {
fn resolve_await_to_poll(&self, await_expr: &ast::AwaitExpr) -> Option<Function> {
self.analyze(await_expr.syntax())?.resolve_await_to_poll(self.db, await_expr)
}
fn resolve_prefix_expr(&self, prefix_expr: &ast::PrefixExpr) -> Option<FunctionId> {
fn resolve_prefix_expr(&self, prefix_expr: &ast::PrefixExpr) -> Option<Function> {
self.analyze(prefix_expr.syntax())?.resolve_prefix_expr(self.db, prefix_expr)
}
fn resolve_index_expr(&self, index_expr: &ast::IndexExpr) -> Option<FunctionId> {
fn resolve_index_expr(&self, index_expr: &ast::IndexExpr) -> Option<Function> {
self.analyze(index_expr.syntax())?.resolve_index_expr(self.db, index_expr)
}
fn resolve_bin_expr(&self, bin_expr: &ast::BinExpr) -> Option<FunctionId> {
fn resolve_bin_expr(&self, bin_expr: &ast::BinExpr) -> Option<Function> {
self.analyze(bin_expr.syntax())?.resolve_bin_expr(self.db, bin_expr)
}
fn resolve_try_expr(&self, try_expr: &ast::TryExpr) -> Option<FunctionId> {
fn resolve_try_expr(&self, try_expr: &ast::TryExpr) -> Option<Function> {
self.analyze(try_expr.syntax())?.resolve_try_expr(self.db, try_expr)
}
@ -1861,7 +1870,9 @@ impl<'db> SemanticsImpl<'db> {
}
pub fn get_unsafe_ops(&self, def: DefWithBody) -> FxHashSet<ExprOrPatSource> {
let def = DefWithBodyId::from(def);
let Ok(def) = DefWithBodyId::try_from(def) else {
return FxHashSet::default();
};
let (body, source_map) = self.db.body_with_source_map(def);
let infer = InferenceResult::for_body(self.db, def);
let mut res = FxHashSet::default();
@ -1877,7 +1888,9 @@ impl<'db> SemanticsImpl<'db> {
always!(block.unsafe_token().is_some());
let block = self.wrap_node_infile(ast::Expr::from(block));
let Some(def) = self.body_for(block.syntax()) else { return Vec::new() };
let def = def.into();
let Ok(def) = def.try_into() else {
return Vec::new();
};
let (body, source_map) = self.db.body_with_source_map(def);
let infer = InferenceResult::for_body(self.db, def);
let Some(ExprOrPatId::ExprId(block)) = source_map.node_expr(block.as_ref()) else {
@ -2023,16 +2036,22 @@ impl<'db> SemanticsImpl<'db> {
}
/// Search for a definition's source and cache its syntax tree
pub fn source<Def: HasSource>(&self, def: Def) -> Option<InFile<Def::Ast>>
where
Def::Ast: AstNode,
{
pub fn source<Def: HasSource>(&self, def: Def) -> Option<InFile<Def::Ast>> {
// FIXME: source call should go through the parse cache
let res = def.source(self.db)?;
self.cache(find_root(res.value.syntax()), res.file_id);
Some(res)
}
pub fn source_with_range<Def: HasSource>(
&self,
def: Def,
) -> Option<InFile<(TextRange, Option<Def::Ast>)>> {
let res = def.source_with_range(self.db)?;
self.parse_or_expand(res.file_id);
Some(res)
}
pub fn body_for(&self, node: InFile<&SyntaxNode>) -> Option<DefWithBody> {
let container = self.with_ctx(|ctx| ctx.find_container(node))?;
@ -2162,9 +2181,10 @@ impl<'db> SemanticsImpl<'db> {
let def = match &enclosing_item {
Either::Left(ast::Item::Fn(it)) if it.unsafe_token().is_some() => return true,
Either::Left(ast::Item::Fn(it)) => {
self.to_def(it).map(<_>::into).map(DefWithBodyId::FunctionId)
}
Either::Left(ast::Item::Fn(it)) => (|| match self.to_def(it)?.id {
AnyFunctionId::FunctionId(id) => Some(DefWithBodyId::FunctionId(id)),
AnyFunctionId::BuiltinDeriveImplMethod { .. } => None,
})(),
Either::Left(ast::Item::Const(it)) => {
self.to_def(it).map(<_>::into).map(DefWithBodyId::ConstId)
}
@ -2201,7 +2221,11 @@ impl<'db> SemanticsImpl<'db> {
}
pub fn impl_generated_from_derive(&self, impl_: Impl) -> Option<Adt> {
let source = hir_def::src::HasSource::ast_ptr(&impl_.id.loc(self.db), self.db);
let id = match impl_.id {
AnyImplId::ImplId(id) => id,
AnyImplId::BuiltinDeriveImplId(id) => return Some(id.loc(self.db).adt.into()),
};
let source = hir_def::src::HasSource::ast_ptr(&id.loc(self.db), self.db);
let mut file_id = source.file_id;
let adt_ast_id = loop {
let macro_call = file_id.macro_file()?;

View file

@ -57,9 +57,9 @@ use syntax::{
use triomphe::Arc;
use crate::{
Adt, AssocItem, BindingMode, BuiltinAttr, BuiltinType, Callable, Const, DeriveHelper, Field,
Function, GenericSubstitution, Local, Macro, ModuleDef, Static, Struct, ToolModule, Trait,
TupleField, Type, TypeAlias, Variant,
Adt, AnyFunctionId, AssocItem, BindingMode, BuiltinAttr, BuiltinType, Callable, Const,
DeriveHelper, Field, Function, GenericSubstitution, Local, Macro, ModuleDef, Static, Struct,
ToolModule, Trait, TupleField, Type, TypeAlias, Variant,
db::HirDatabase,
semantics::{PathResolution, PathResolutionPerNs},
};
@ -431,7 +431,7 @@ impl<'db> SourceAnalyzer<'db> {
let expr_id = self.expr_id(call.clone().into())?.as_expr()?;
let (f_in_trait, substs) = self.infer()?.method_resolution(expr_id)?;
Some(self.resolve_impl_method_or_trait_def(db, f_in_trait, substs).into())
Some(self.resolve_impl_method_or_trait_def(db, f_in_trait, substs))
}
pub(crate) fn resolve_method_call_fallback(
@ -446,8 +446,8 @@ impl<'db> SourceAnalyzer<'db> {
let (fn_, subst) =
self.resolve_impl_method_or_trait_def_with_subst(db, f_in_trait, substs);
Some((
Either::Left(fn_.into()),
Some(GenericSubstitution::new(fn_.into(), subst, self.trait_environment(db))),
Either::Left(fn_),
GenericSubstitution::new_from_fn(fn_, subst, self.trait_environment(db)),
))
}
None => {
@ -519,8 +519,8 @@ impl<'db> SourceAnalyzer<'db> {
None => inference_result.method_resolution(expr_id).map(|(f, substs)| {
let (f, subst) = self.resolve_impl_method_or_trait_def_with_subst(db, f, substs);
(
Either::Right(f.into()),
Some(GenericSubstitution::new(f.into(), subst, self.trait_environment(db))),
Either::Right(f),
GenericSubstitution::new_from_fn(f, subst, self.trait_environment(db)),
)
}),
}
@ -569,7 +569,7 @@ impl<'db> SourceAnalyzer<'db> {
&self,
db: &'db dyn HirDatabase,
await_expr: &ast::AwaitExpr,
) -> Option<FunctionId> {
) -> Option<Function> {
let mut ty = self.ty_of_expr(await_expr.expr()?)?;
let into_future_trait = self
@ -605,7 +605,7 @@ impl<'db> SourceAnalyzer<'db> {
&self,
db: &'db dyn HirDatabase,
prefix_expr: &ast::PrefixExpr,
) -> Option<FunctionId> {
) -> Option<Function> {
let (_op_trait, op_fn) = match prefix_expr.op_kind()? {
ast::UnaryOp::Deref => {
// This can be either `Deref::deref` or `DerefMut::deref_mut`.
@ -650,7 +650,7 @@ impl<'db> SourceAnalyzer<'db> {
&self,
db: &'db dyn HirDatabase,
index_expr: &ast::IndexExpr,
) -> Option<FunctionId> {
) -> Option<Function> {
let base_ty = self.ty_of_expr(index_expr.base()?)?;
let index_ty = self.ty_of_expr(index_expr.index()?)?;
@ -679,7 +679,7 @@ impl<'db> SourceAnalyzer<'db> {
&self,
db: &'db dyn HirDatabase,
binop_expr: &ast::BinExpr,
) -> Option<FunctionId> {
) -> Option<Function> {
let op = binop_expr.op_kind()?;
let lhs = self.ty_of_expr(binop_expr.lhs()?)?;
let rhs = self.ty_of_expr(binop_expr.rhs()?)?;
@ -699,7 +699,7 @@ impl<'db> SourceAnalyzer<'db> {
&self,
db: &'db dyn HirDatabase,
try_expr: &ast::TryExpr,
) -> Option<FunctionId> {
) -> Option<Function> {
let ty = self.ty_of_expr(try_expr.expr()?)?;
let op_fn = self.lang_items(db).TryTraitBranch?;
@ -905,7 +905,7 @@ impl<'db> SourceAnalyzer<'db> {
subs,
self.trait_environment(db),
);
(AssocItemId::from(f_in_trait), subst)
(AssocItem::Function(f_in_trait.into()), Some(subst))
}
Some(func_ty) => {
if let TyKind::FnDef(_fn_def, subs) = func_ty.kind() {
@ -913,19 +913,19 @@ impl<'db> SourceAnalyzer<'db> {
.resolve_impl_method_or_trait_def_with_subst(
db, f_in_trait, subs,
);
let subst = GenericSubstitution::new(
fn_.into(),
let subst = GenericSubstitution::new_from_fn(
fn_,
subst,
self.trait_environment(db),
);
(fn_.into(), subst)
(AssocItem::Function(fn_), subst)
} else {
let subst = GenericSubstitution::new(
f_in_trait.into(),
subs,
self.trait_environment(db),
);
(f_in_trait.into(), subst)
(AssocItem::Function(f_in_trait.into()), Some(subst))
}
}
}
@ -938,11 +938,11 @@ impl<'db> SourceAnalyzer<'db> {
subst,
self.trait_environment(db),
);
(konst.into(), subst)
(AssocItem::Const(konst.into()), Some(subst))
}
};
return Some((PathResolution::Def(AssocItem::from(assoc).into()), Some(subst)));
return Some((PathResolution::Def(assoc.into()), subst));
}
if let Some(VariantId::EnumVariantId(variant)) =
infer.variant_resolution_for_expr_or_pat(expr_id)
@ -1401,7 +1401,7 @@ impl<'db> SourceAnalyzer<'db> {
db: &'db dyn HirDatabase,
func: FunctionId,
substs: GenericArgs<'db>,
) -> FunctionId {
) -> Function {
self.resolve_impl_method_or_trait_def_with_subst(db, func, substs).0
}
@ -1410,13 +1410,19 @@ impl<'db> SourceAnalyzer<'db> {
db: &'db dyn HirDatabase,
func: FunctionId,
substs: GenericArgs<'db>,
) -> (FunctionId, GenericArgs<'db>) {
) -> (Function, GenericArgs<'db>) {
let owner = match self.resolver.body_owner() {
Some(it) => it,
None => return (func, substs),
None => return (func.into(), substs),
};
let env = self.param_and(db.trait_environment_for_body(owner));
db.lookup_impl_method(env, func, substs)
let (func, args) = db.lookup_impl_method(env, func, substs);
match func {
Either::Left(func) => (func.into(), args),
Either::Right((impl_, method)) => {
(Function { id: AnyFunctionId::BuiltinDeriveImplMethod { method, impl_ } }, args)
}
}
}
fn resolve_impl_const_or_trait_def_with_subst(

View file

@ -734,11 +734,11 @@
FileSymbol {
name: "generic_impl_fn",
def: Function(
Function {
id: FunctionId(
FunctionId(
FunctionId(
6402,
),
},
),
),
loc: DeclarationLocation {
hir_file_id: FileId(
@ -769,11 +769,11 @@
FileSymbol {
name: "impl_fn",
def: Function(
Function {
id: FunctionId(
FunctionId(
FunctionId(
6401,
),
},
),
),
loc: DeclarationLocation {
hir_file_id: FileId(
@ -839,11 +839,11 @@
FileSymbol {
name: "main",
def: Function(
Function {
id: FunctionId(
FunctionId(
FunctionId(
6400,
),
},
),
),
loc: DeclarationLocation {
hir_file_id: FileId(
@ -907,11 +907,11 @@
FileSymbol {
name: "trait_fn",
def: Function(
Function {
id: FunctionId(
FunctionId(
FunctionId(
6403,
),
},
),
),
loc: DeclarationLocation {
hir_file_id: FileId(

View file

@ -767,14 +767,30 @@ fn label_of_ty(
)
});
let module_def_location = |label_builder: &mut InlayHintLabelBuilder<'_>,
def: ModuleDef,
name| {
let def = def.try_into();
if let Ok(def) = def {
label_builder.start_location_link(def);
}
#[expect(
clippy::question_mark,
reason = "false positive; replacing with `?` leads to 'type annotations needed' error"
)]
if let Err(err) = label_builder.write_str(name) {
return Err(err);
}
if def.is_ok() {
label_builder.end_location_link();
}
Ok(())
};
label_builder.write_str(LABEL_START)?;
label_builder.start_location_link(ModuleDef::from(iter_trait).into());
label_builder.write_str(LABEL_ITERATOR)?;
label_builder.end_location_link();
module_def_location(label_builder, ModuleDef::from(iter_trait), LABEL_ITERATOR)?;
label_builder.write_str(LABEL_MIDDLE)?;
label_builder.start_location_link(ModuleDef::from(item).into());
label_builder.write_str(LABEL_ITEM)?;
label_builder.end_location_link();
module_def_location(label_builder, ModuleDef::from(item), LABEL_ITEM)?;
label_builder.write_str(LABEL_MIDDLE2)?;
rec(sema, famous_defs, max_length, &ty, label_builder, config, display_target)?;
label_builder.write_str(LABEL_END)?;

View file

@ -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();

View file

@ -6,7 +6,7 @@ use arrayvec::ArrayVec;
use either::Either;
use hir::{
AssocItem, Crate, FieldSource, HasContainer, HasCrate, HasSource, HirDisplay, HirFileId,
InFile, LocalSource, ModuleSource, Semantics, Symbol, db::ExpandDatabase, sym,
InFile, LocalSource, ModuleSource, Name, Semantics, Symbol, db::ExpandDatabase, sym,
symbols::FileSymbol,
};
use ide_db::{
@ -204,6 +204,22 @@ impl NavigationTarget {
)
}
pub(crate) fn from_named_with_range(
db: &RootDatabase,
ranges: InFile<(TextRange, Option<TextRange>)>,
name: Option<Name>,
kind: SymbolKind,
) -> UpmappingResult<NavigationTarget> {
let InFile { file_id, value: (full_range, focus_range) } = ranges;
let name = name.map(|name| name.symbol().clone()).unwrap_or_else(|| sym::underscore);
orig_range_with_focus_r(db, file_id, full_range, focus_range).map(
|(FileRange { file_id, range: full_range }, focus_range)| {
NavigationTarget::from_syntax(file_id, name.clone(), focus_range, full_range, kind)
},
)
}
pub(crate) fn from_syntax(
file_id: FileId,
name: Symbol,
@ -414,7 +430,13 @@ impl ToNavFromAst for hir::Trait {
impl<D> TryToNav for D
where
D: HasSource + ToNavFromAst + Copy + HasDocs + for<'db> HirDisplay<'db> + HasCrate,
D: HasSource
+ ToNavFromAst
+ Copy
+ HasDocs
+ for<'db> HirDisplay<'db>
+ HasCrate
+ hir::HasName,
D::Ast: ast::HasName,
{
fn try_to_nav(
@ -422,11 +444,19 @@ where
sema: &Semantics<'_, RootDatabase>,
) -> Option<UpmappingResult<NavigationTarget>> {
let db = sema.db;
let src = self.source(db)?;
let src = self.source_with_range(db)?;
Some(
NavigationTarget::from_named(
NavigationTarget::from_named_with_range(
db,
src.as_ref().map(|it| it as &dyn ast::HasName),
src.map(|(full_range, node)| {
(
full_range,
node.and_then(|node| {
Some(ast::HasName::name(&node)?.syntax().text_range())
}),
)
}),
self.name(db),
D::KIND,
)
.map(|mut res| {
@ -477,16 +507,16 @@ impl TryToNav for hir::Impl {
sema: &Semantics<'_, RootDatabase>,
) -> Option<UpmappingResult<NavigationTarget>> {
let db = sema.db;
let InFile { file_id, value } = self.source(db)?;
let derive_path = self.as_builtin_derive_path(db);
let InFile { file_id, value: (full_range, source) } = self.source_with_range(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()),
};
Some(orig_range_with_focus(db, file_id, syntax, focus).map(
|(FileRange { file_id, range: full_range }, focus_range)| {
Some(
orig_range_with_focus_r(
db,
file_id,
full_range,
source.and_then(|source| Some(source.self_ty()?.syntax().text_range())),
)
.map(|(FileRange { file_id, range: full_range }, focus_range)| {
NavigationTarget::from_syntax(
file_id,
sym::kw_impl,
@ -494,8 +524,8 @@ impl TryToNav for hir::Impl {
full_range,
SymbolKind::Impl,
)
},
))
}),
)
}
}

View file

@ -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
"#]],

View file

@ -1679,11 +1679,11 @@ mod r#mod {
[
"(TestMod, NavigationTarget { file_id: FileId(0), full_range: 1..461, focus_range: 5..10, name: \"mod\", kind: Module, description: \"mod r#mod\" })",
"(Test, NavigationTarget { file_id: FileId(0), full_range: 17..41, focus_range: 32..36, name: \"r#fn\", kind: Function })",
"(DocTest, NavigationTarget { file_id: FileId(0), full_range: 47..84, name: \"r#for\", container_name: \"mod\" })",
"(DocTest, NavigationTarget { file_id: FileId(0), full_range: 90..146, name: \"r#struct\", container_name: \"mod\" })",
"(DocTest, NavigationTarget { file_id: FileId(0), full_range: 47..84, name: \"for\", container_name: \"mod\" })",
"(DocTest, NavigationTarget { file_id: FileId(0), full_range: 90..146, name: \"struct\", container_name: \"mod\" })",
"(DocTest, NavigationTarget { file_id: FileId(0), full_range: 152..266, focus_range: 189..205, name: \"impl\", kind: Impl })",
"(DocTest, NavigationTarget { file_id: FileId(0), full_range: 216..260, name: \"r#fn\" })",
"(DocTest, NavigationTarget { file_id: FileId(0), full_range: 323..367, name: \"r#fn\" })",
"(DocTest, NavigationTarget { file_id: FileId(0), full_range: 216..260, name: \"fn\" })",
"(DocTest, NavigationTarget { file_id: FileId(0), full_range: 323..367, name: \"fn\" })",
"(DocTest, NavigationTarget { file_id: FileId(0), full_range: 401..459, focus_range: 445..456, name: \"impl\", kind: Impl })",
]
"#]],

View file

@ -525,5 +525,10 @@ define_symbols! {
arbitrary_self_types,
arbitrary_self_types_pointers,
supertrait_item_shadowing,
hash,
partial_cmp,
cmp,
CoerceUnsized,
DispatchFromDyn,
define_opaque,
}

View file

@ -693,21 +693,24 @@ impl flags::AnalysisStats {
let mut sw = self.stop_watch();
let mut all = 0;
let mut fail = 0;
for &body_id in bodies {
for &body in bodies {
bar.set_message(move || {
format!("mir lowering: {}", full_name(db, body_id, body_id.module(db)))
format!("mir lowering: {}", full_name(db, body, body.module(db)))
});
bar.inc(1);
if matches!(body_id, DefWithBody::Variant(_)) {
if matches!(body, DefWithBody::Variant(_)) {
continue;
}
let module = body_id.module(db);
if !self.should_process(db, body_id, module) {
let module = body.module(db);
if !self.should_process(db, body, module) {
continue;
}
all += 1;
let Err(e) = db.mir_body(body_id.into()) else {
let Ok(body_id) = body.try_into() else {
continue;
};
let Err(e) = db.mir_body(body_id) else {
continue;
};
if verbosity.is_spammy() {
@ -716,7 +719,7 @@ impl flags::AnalysisStats {
.into_iter()
.rev()
.filter_map(|it| it.name(db))
.chain(Some(body_id.name(db).unwrap_or_else(Name::missing)))
.chain(Some(body.name(db).unwrap_or_else(Name::missing)))
.map(|it| it.display(db, Edition::LATEST).to_string())
.join("::");
bar.println(format!("Mir body for {full_name} failed due {e:?}"));
@ -747,11 +750,12 @@ impl flags::AnalysisStats {
if self.parallel {
let mut inference_sw = self.stop_watch();
let bodies = bodies.iter().filter_map(|&body| body.try_into().ok()).collect::<Vec<_>>();
bodies
.par_iter()
.map_with(db.clone(), |snap, &body| {
snap.body(body.into());
InferenceResult::for_body(snap, body.into());
snap.body(body);
InferenceResult::for_body(snap, body);
})
.count();
eprintln!("{:<20} {}", "Parallel Inference:", inference_sw.elapsed());
@ -769,6 +773,7 @@ impl flags::AnalysisStats {
let mut num_pat_type_mismatches = 0;
let mut panics = 0;
for &body_id in bodies {
let Ok(body_def_id) = body_id.try_into() else { continue };
let name = body_id.name(db).unwrap_or_else(Name::missing);
let module = body_id.module(db);
let display_target = module.krate(db).to_display_target(db);
@ -807,9 +812,9 @@ impl flags::AnalysisStats {
bar.println(msg());
}
bar.set_message(msg);
let body = db.body(body_id.into());
let body = db.body(body_def_id);
let inference_result =
catch_unwind(AssertUnwindSafe(|| InferenceResult::for_body(db, body_id.into())));
catch_unwind(AssertUnwindSafe(|| InferenceResult::for_body(db, body_def_id)));
let inference_result = match inference_result {
Ok(inference_result) => inference_result,
Err(p) => {
@ -826,7 +831,7 @@ impl flags::AnalysisStats {
}
};
// This query is LRU'd, so actually calling it will skew the timing results.
let sm = || db.body_with_source_map(body_id.into()).1;
let sm = || db.body_with_source_map(body_def_id).1;
// region:expressions
let (previous_exprs, previous_unknown, previous_partially_unknown) =
@ -1081,6 +1086,7 @@ impl flags::AnalysisStats {
let mut sw = self.stop_watch();
bar.tick();
for &body_id in bodies {
let Ok(body_def_id) = body_id.try_into() else { continue };
let module = body_id.module(db);
if !self.should_process(db, body_id, module) {
continue;
@ -1114,7 +1120,7 @@ impl flags::AnalysisStats {
bar.println(msg());
}
bar.set_message(msg);
db.body(body_id.into());
db.body(body_def_id);
bar.inc(1);
}