Support rustc_has_incoherent_inherent_impls

This commit is contained in:
Lukas Wirth 2022-12-04 20:12:11 +01:00
parent 957b4bb216
commit ca1389ef9f
6 changed files with 153 additions and 37 deletions

View file

@ -36,6 +36,7 @@ pub struct StructData {
pub variant_data: Arc<VariantData>, pub variant_data: Arc<VariantData>,
pub repr: Option<ReprData>, pub repr: Option<ReprData>,
pub visibility: RawVisibility, pub visibility: RawVisibility,
pub rustc_has_incoherent_inherent_impls: bool,
} }
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
@ -44,6 +45,7 @@ pub struct EnumData {
pub variants: Arena<EnumVariantData>, pub variants: Arena<EnumVariantData>,
pub repr: Option<ReprData>, pub repr: Option<ReprData>,
pub visibility: RawVisibility, pub visibility: RawVisibility,
pub rustc_has_incoherent_inherent_impls: bool,
} }
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
@ -157,6 +159,10 @@ impl StructData {
let item_tree = loc.id.item_tree(db); let item_tree = loc.id.item_tree(db);
let repr = repr_from_value(db, krate, &item_tree, ModItem::from(loc.id.value).into()); let repr = repr_from_value(db, krate, &item_tree, ModItem::from(loc.id.value).into());
let cfg_options = db.crate_graph()[loc.container.krate].cfg_options.clone(); let cfg_options = db.crate_graph()[loc.container.krate].cfg_options.clone();
let rustc_has_incoherent_inherent_impls = item_tree
.attrs(db, loc.container.krate, ModItem::from(loc.id.value).into())
.by_key("rustc_has_incoherent_inherent_impls")
.exists();
let strukt = &item_tree[loc.id.value]; let strukt = &item_tree[loc.id.value];
let (variant_data, diagnostics) = lower_fields( let (variant_data, diagnostics) = lower_fields(
@ -175,6 +181,7 @@ impl StructData {
variant_data: Arc::new(variant_data), variant_data: Arc::new(variant_data),
repr, repr,
visibility: item_tree[strukt.visibility].clone(), visibility: item_tree[strukt.visibility].clone(),
rustc_has_incoherent_inherent_impls,
}), }),
diagnostics.into(), diagnostics.into(),
) )
@ -194,6 +201,11 @@ impl StructData {
let repr = repr_from_value(db, krate, &item_tree, ModItem::from(loc.id.value).into()); let repr = repr_from_value(db, krate, &item_tree, ModItem::from(loc.id.value).into());
let cfg_options = db.crate_graph()[loc.container.krate].cfg_options.clone(); let cfg_options = db.crate_graph()[loc.container.krate].cfg_options.clone();
let rustc_has_incoherent_inherent_impls = item_tree
.attrs(db, loc.container.krate, ModItem::from(loc.id.value).into())
.by_key("rustc_has_incoherent_inherent_impls")
.exists();
let union = &item_tree[loc.id.value]; let union = &item_tree[loc.id.value];
let (variant_data, diagnostics) = lower_fields( let (variant_data, diagnostics) = lower_fields(
db, db,
@ -211,6 +223,7 @@ impl StructData {
variant_data: Arc::new(variant_data), variant_data: Arc::new(variant_data),
repr, repr,
visibility: item_tree[union.visibility].clone(), visibility: item_tree[union.visibility].clone(),
rustc_has_incoherent_inherent_impls,
}), }),
diagnostics.into(), diagnostics.into(),
) )
@ -231,6 +244,10 @@ impl EnumData {
let item_tree = loc.id.item_tree(db); let item_tree = loc.id.item_tree(db);
let cfg_options = db.crate_graph()[krate].cfg_options.clone(); let cfg_options = db.crate_graph()[krate].cfg_options.clone();
let repr = repr_from_value(db, krate, &item_tree, ModItem::from(loc.id.value).into()); let repr = repr_from_value(db, krate, &item_tree, ModItem::from(loc.id.value).into());
let rustc_has_incoherent_inherent_impls = item_tree
.attrs(db, loc.container.krate, ModItem::from(loc.id.value).into())
.by_key("rustc_has_incoherent_inherent_impls")
.exists();
let enum_ = &item_tree[loc.id.value]; let enum_ = &item_tree[loc.id.value];
let mut variants = Arena::new(); let mut variants = Arena::new();
@ -271,6 +288,7 @@ impl EnumData {
variants, variants,
repr, repr,
visibility: item_tree[enum_.visibility].clone(), visibility: item_tree[enum_.visibility].clone(),
rustc_has_incoherent_inherent_impls,
}), }),
diagnostics.into(), diagnostics.into(),
) )

View file

@ -168,6 +168,7 @@ pub struct TypeAliasData {
pub type_ref: Option<Interned<TypeRef>>, pub type_ref: Option<Interned<TypeRef>>,
pub visibility: RawVisibility, pub visibility: RawVisibility,
pub is_extern: bool, pub is_extern: bool,
pub rustc_has_incoherent_inherent_impls: bool,
/// Bounds restricting the type alias itself (eg. `type Ty: Bound;` in a trait or impl). /// Bounds restricting the type alias itself (eg. `type Ty: Bound;` in a trait or impl).
pub bounds: Vec<Interned<TypeBound>>, pub bounds: Vec<Interned<TypeBound>>,
} }
@ -186,11 +187,17 @@ impl TypeAliasData {
item_tree[typ.visibility].clone() item_tree[typ.visibility].clone()
}; };
let rustc_has_incoherent_inherent_impls = item_tree
.attrs(db, loc.container.module(db).krate(), ModItem::from(loc.id.value).into())
.by_key("rustc_has_incoherent_inherent_impls")
.exists();
Arc::new(TypeAliasData { Arc::new(TypeAliasData {
name: typ.name.clone(), name: typ.name.clone(),
type_ref: typ.type_ref.clone(), type_ref: typ.type_ref.clone(),
visibility, visibility,
is_extern: matches!(loc.container, ItemContainerId::ExternBlockId(_)), is_extern: matches!(loc.container, ItemContainerId::ExternBlockId(_)),
rustc_has_incoherent_inherent_impls,
bounds: typ.bounds.to_vec(), bounds: typ.bounds.to_vec(),
}) })
} }
@ -202,6 +209,7 @@ pub struct TraitData {
pub items: Vec<(Name, AssocItemId)>, pub items: Vec<(Name, AssocItemId)>,
pub is_auto: bool, pub is_auto: bool,
pub is_unsafe: bool, pub is_unsafe: bool,
pub rustc_has_incoherent_inherent_impls: bool,
pub visibility: RawVisibility, pub visibility: RawVisibility,
/// Whether the trait has `#[rust_skip_array_during_method_dispatch]`. `hir_ty` will ignore /// Whether the trait has `#[rust_skip_array_during_method_dispatch]`. `hir_ty` will ignore
/// method calls to this trait's methods when the receiver is an array and the crate edition is /// method calls to this trait's methods when the receiver is an array and the crate edition is
@ -231,11 +239,11 @@ impl TraitData {
let is_auto = tr_def.is_auto; let is_auto = tr_def.is_auto;
let is_unsafe = tr_def.is_unsafe; let is_unsafe = tr_def.is_unsafe;
let visibility = item_tree[tr_def.visibility].clone(); let visibility = item_tree[tr_def.visibility].clone();
let skip_array_during_method_dispatch = item_tree let attrs = item_tree.attrs(db, module_id.krate(), ModItem::from(tree_id.value).into());
.attrs(db, module_id.krate(), ModItem::from(tree_id.value).into()) let skip_array_during_method_dispatch =
.by_key("rustc_skip_array_during_method_dispatch") attrs.by_key("rustc_skip_array_during_method_dispatch").exists();
.exists(); let rustc_has_incoherent_inherent_impls =
attrs.by_key("rustc_has_incoherent_inherent_impls").exists();
let (items, attribute_calls, diagnostics) = match &tr_def.items { let (items, attribute_calls, diagnostics) = match &tr_def.items {
Some(items) => { Some(items) => {
let mut collector = AssocItemCollector::new( let mut collector = AssocItemCollector::new(
@ -258,6 +266,7 @@ impl TraitData {
is_unsafe, is_unsafe,
visibility, visibility,
skip_array_during_method_dispatch, skip_array_during_method_dispatch,
rustc_has_incoherent_inherent_impls,
}), }),
diagnostics.into(), diagnostics.into(),
) )

View file

@ -3,13 +3,13 @@
use std::sync::Arc; use std::sync::Arc;
use arrayvec::ArrayVec;
use base_db::{impl_intern_key, salsa, CrateId, Upcast}; use base_db::{impl_intern_key, salsa, CrateId, Upcast};
use hir_def::{ use hir_def::{
db::DefDatabase, expr::ExprId, BlockId, ConstId, ConstParamId, DefWithBodyId, EnumVariantId, db::DefDatabase, expr::ExprId, BlockId, ConstId, ConstParamId, DefWithBodyId, EnumVariantId,
FunctionId, GenericDefId, ImplId, LifetimeParamId, LocalFieldId, TypeOrConstParamId, VariantId, FunctionId, GenericDefId, ImplId, LifetimeParamId, LocalFieldId, TypeOrConstParamId, VariantId,
}; };
use la_arena::ArenaMap; use la_arena::ArenaMap;
use smallvec::SmallVec;
use crate::{ use crate::{
chalk_db, chalk_db,
@ -92,10 +92,15 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
fn inherent_impls_in_block(&self, block: BlockId) -> Option<Arc<InherentImpls>>; fn inherent_impls_in_block(&self, block: BlockId) -> Option<Arc<InherentImpls>>;
/// Collects all crates in the dependency graph that have impls for the /// Collects all crates in the dependency graph that have impls for the
/// given fingerprint. This is only used for primitive types; for /// given fingerprint. This is only used for primitive types and types
/// user-defined types we just look at the crate where the type is defined. /// annotated with `rustc_has_incoherent_inherent_impls`; for other types
#[salsa::invoke(crate::method_resolution::inherent_impl_crates_query)] /// we just look at the crate where the type is defined.
fn inherent_impl_crates(&self, krate: CrateId, fp: TyFingerprint) -> ArrayVec<CrateId, 2>; #[salsa::invoke(crate::method_resolution::incoherent_inherent_impl_crates)]
fn incoherent_inherent_impl_crates(
&self,
krate: CrateId,
fp: TyFingerprint,
) -> SmallVec<[CrateId; 2]>;
#[salsa::invoke(TraitImpls::trait_impls_in_crate_query)] #[salsa::invoke(TraitImpls::trait_impls_in_crate_query)]
fn trait_impls_in_crate(&self, krate: CrateId) -> Arc<TraitImpls>; fn trait_impls_in_crate(&self, krate: CrateId) -> Arc<TraitImpls>;

View file

@ -2,18 +2,17 @@
//! For details about how this works in rustc, see the method lookup page in the //! For details about how this works in rustc, see the method lookup page in the
//! [rustc guide](https://rust-lang.github.io/rustc-guide/method-lookup.html) //! [rustc guide](https://rust-lang.github.io/rustc-guide/method-lookup.html)
//! and the corresponding code mostly in rustc_hir_analysis/check/method/probe.rs. //! and the corresponding code mostly in rustc_hir_analysis/check/method/probe.rs.
use std::{iter, ops::ControlFlow, sync::Arc}; use std::{ops::ControlFlow, sync::Arc};
use arrayvec::ArrayVec;
use base_db::{CrateId, Edition}; use base_db::{CrateId, Edition};
use chalk_ir::{cast::Cast, Mutability, UniverseIndex}; use chalk_ir::{cast::Cast, Mutability, UniverseIndex};
use hir_def::{ use hir_def::{
data::ImplData, item_scope::ItemScope, nameres::DefMap, AssocItemId, BlockId, ConstId, data::ImplData, item_scope::ItemScope, nameres::DefMap, AssocItemId, BlockId, ConstId,
FunctionId, GenericDefId, HasModule, ImplId, ItemContainerId, Lookup, ModuleDefId, ModuleId, FunctionId, HasModule, ImplId, ItemContainerId, Lookup, ModuleDefId, ModuleId, TraitId,
TraitId,
}; };
use hir_expand::name::Name; use hir_expand::name::Name;
use rustc_hash::{FxHashMap, FxHashSet}; use rustc_hash::{FxHashMap, FxHashSet};
use smallvec::{smallvec, SmallVec};
use stdx::never; use stdx::never;
use crate::{ use crate::{
@ -336,21 +335,18 @@ impl InherentImpls {
} }
} }
pub(crate) fn inherent_impl_crates_query( pub(crate) fn incoherent_inherent_impl_crates(
db: &dyn HirDatabase, db: &dyn HirDatabase,
krate: CrateId, krate: CrateId,
fp: TyFingerprint, fp: TyFingerprint,
) -> ArrayVec<CrateId, 2> { ) -> SmallVec<[CrateId; 2]> {
let _p = profile::span("inherent_impl_crates_query"); let _p = profile::span("inherent_impl_crates_query");
let mut res = ArrayVec::new(); let mut res = SmallVec::new();
let crate_graph = db.crate_graph(); let crate_graph = db.crate_graph();
// should pass crate for finger print and do reverse deps
for krate in crate_graph.transitive_deps(krate) { for krate in crate_graph.transitive_deps(krate) {
if res.is_full() {
// we don't currently look for or store more than two crates here,
// so don't needlessly look at more crates than necessary.
break;
}
let impls = db.inherent_impls_in_crate(krate); let impls = db.inherent_impls_in_crate(krate);
if impls.map.get(&fp).map_or(false, |v| !v.is_empty()) { if impls.map.get(&fp).map_or(false, |v| !v.is_empty()) {
res.push(krate); res.push(krate);
@ -392,19 +388,40 @@ pub fn def_crates(
db: &dyn HirDatabase, db: &dyn HirDatabase,
ty: &Ty, ty: &Ty,
cur_crate: CrateId, cur_crate: CrateId,
) -> Option<ArrayVec<CrateId, 2>> { ) -> Option<SmallVec<[CrateId; 2]>> {
let mod_to_crate_ids = |module: ModuleId| Some(iter::once(module.krate()).collect());
let fp = TyFingerprint::for_inherent_impl(ty);
match ty.kind(Interner) { match ty.kind(Interner) {
TyKind::Adt(AdtId(def_id), _) => mod_to_crate_ids(def_id.module(db.upcast())), &TyKind::Adt(AdtId(def_id), _) => {
TyKind::Foreign(id) => { let rustc_has_incoherent_inherent_impls = match def_id {
mod_to_crate_ids(from_foreign_def_id(*id).lookup(db.upcast()).module(db.upcast())) hir_def::AdtId::StructId(id) => {
db.struct_data(id).rustc_has_incoherent_inherent_impls
}
hir_def::AdtId::UnionId(id) => {
db.union_data(id).rustc_has_incoherent_inherent_impls
}
hir_def::AdtId::EnumId(id) => db.enum_data(id).rustc_has_incoherent_inherent_impls,
};
Some(if rustc_has_incoherent_inherent_impls {
db.incoherent_inherent_impl_crates(cur_crate, TyFingerprint::Adt(def_id))
} else {
smallvec![def_id.module(db.upcast()).krate()]
})
}
&TyKind::Foreign(id) => {
let alias = from_foreign_def_id(id);
Some(if db.type_alias_data(alias).rustc_has_incoherent_inherent_impls {
db.incoherent_inherent_impl_crates(cur_crate, TyFingerprint::ForeignType(id))
} else {
smallvec![alias.module(db.upcast()).krate()]
})
}
TyKind::Dyn(_) => {
let trait_id = ty.dyn_trait()?;
Some(if db.trait_data(trait_id).rustc_has_incoherent_inherent_impls {
db.incoherent_inherent_impl_crates(cur_crate, TyFingerprint::Dyn(trait_id))
} else {
smallvec![trait_id.module(db.upcast()).krate()]
})
} }
TyKind::Dyn(_) => ty
.dyn_trait()
.and_then(|trait_| mod_to_crate_ids(GenericDefId::TraitId(trait_).module(db.upcast()))),
// for primitives, there may be impls in various places (core and alloc // for primitives, there may be impls in various places (core and alloc
// mostly). We just check the whole crate graph for crates with impls // mostly). We just check the whole crate graph for crates with impls
// (cached behind a query). // (cached behind a query).
@ -412,10 +429,11 @@ pub fn def_crates(
| TyKind::Str | TyKind::Str
| TyKind::Slice(_) | TyKind::Slice(_)
| TyKind::Array(..) | TyKind::Array(..)
| TyKind::Raw(..) => { | TyKind::Raw(..) => Some(db.incoherent_inherent_impl_crates(
Some(db.inherent_impl_crates(cur_crate, fp.expect("fingerprint for primitive"))) cur_crate,
} TyFingerprint::for_inherent_impl(ty).expect("fingerprint for primitive"),
_ => return None, )),
_ => None,
} }
} }

View file

@ -1867,3 +1867,32 @@ fn g<T: Trait>(a: T) {
"#, "#,
); );
} }
#[test]
fn incoherent_impls() {
check(
r#"
//- minicore: error, send
pub struct Box<T>(T);
use core::error::Error;
#[rustc_allow_incoherent_impl]
impl dyn Error {
pub fn downcast<T: Error + 'static>(self: Box<Self>) -> Result<Box<T>, Box<dyn Error>> {
loop {}
}
}
#[rustc_allow_incoherent_impl]
impl dyn Error + Send {
/// Attempts to downcast the box to a concrete type.
pub fn downcast<T: Error + 'static>(self: Box<Self>) -> Result<Box<T>, Box<dyn Error + Send>> {
let err: Box<dyn Error> = self;
// ^^^^ expected Box<dyn Error>, got Box<dyn Error + Send>
// FIXME, type mismatch should not occur
<dyn Error>::downcast(err).map_err(|_| loop {})
//^^^^^^^^^^^^^^^^^^^^^ type: fn downcast<{unknown}>(Box<dyn Error>) -> Result<Box<{unknown}>, Box<dyn Error>>
}
}
"#,
);
}

View file

@ -20,6 +20,7 @@
//! derive: //! derive:
//! drop: //! drop:
//! eq: sized //! eq: sized
//! error: fmt
//! fmt: result //! fmt: result
//! fn: //! fn:
//! from: sized //! from: sized
@ -34,8 +35,10 @@
//! pin: //! pin:
//! range: //! range:
//! result: //! result:
//! send: sized
//! sized: //! sized:
//! slice: //! slice:
//! sync: sized
//! try: //! try:
//! unsize: sized //! unsize: sized
@ -47,6 +50,24 @@ pub mod marker {
pub trait Sized {} pub trait Sized {}
// endregion:sized // endregion:sized
// region:send
pub unsafe auto trait Send {}
impl<T: ?Sized> !Send for *const T {}
impl<T: ?Sized> !Send for *mut T {}
// region:sync
unsafe impl<T: Sync + ?Sized> Send for &T {}
unsafe impl<T: Send + ?Sized> Send for &mut T {}
// endregion:sync
// endregion:send
// region:sync
pub unsafe auto trait Sync {}
impl<T: ?Sized> !Sync for *const T {}
impl<T: ?Sized> !Sync for *mut T {}
// endregion:sync
// region:unsize // region:unsize
#[lang = "unsize"] #[lang = "unsize"]
pub trait Unsize<T: ?Sized> {} pub trait Unsize<T: ?Sized> {}
@ -438,6 +459,9 @@ pub mod fmt {
pub trait Debug { pub trait Debug {
fn fmt(&self, f: &mut Formatter<'_>) -> Result; fn fmt(&self, f: &mut Formatter<'_>) -> Result;
} }
pub trait Display {
fn fmt(&self, f: &mut Formatter<'_>) -> Result;
}
} }
// endregion:fmt // endregion:fmt
@ -693,6 +717,17 @@ impl bool {
} }
// endregion:bool_impl // endregion:bool_impl
// region:error
pub mod error {
#[rustc_has_incoherent_inherent_impls]
pub trait Error: crate::fmt::Debug + crate::fmt::Display {
fn source(&self) -> Option<&(dyn Error + 'static)> {
None
}
}
}
// endregion:error
pub mod prelude { pub mod prelude {
pub mod v1 { pub mod v1 {
pub use crate::{ pub use crate::{
@ -705,7 +740,9 @@ pub mod prelude {
iter::{IntoIterator, Iterator}, // :iterator iter::{IntoIterator, Iterator}, // :iterator
macros::builtin::derive, // :derive macros::builtin::derive, // :derive
marker::Copy, // :copy marker::Copy, // :copy
marker::Send, // :send
marker::Sized, // :sized marker::Sized, // :sized
marker::Sync, // :sync
mem::drop, // :drop mem::drop, // :drop
ops::Drop, // :drop ops::Drop, // :drop
ops::{Fn, FnMut, FnOnce}, // :fn ops::{Fn, FnMut, FnOnce}, // :fn