Skip to content

Commit a2beeb8

Browse files
committed
Auto merge of #13721 - Veykril:incoherent-impls, r=Veykril
Support `rustc_has_incoherent_inherent_impls` Fixes us not resolving `<dyn Error>::downcast` now that `Error` moved to core, while that assoc function is declared in `alloc`.
2 parents 957b4bb + ca1389e commit a2beeb8

File tree

6 files changed

+153
-37
lines changed

6 files changed

+153
-37
lines changed

crates/hir-def/src/adt.rs

+18
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ pub struct StructData {
3636
pub variant_data: Arc<VariantData>,
3737
pub repr: Option<ReprData>,
3838
pub visibility: RawVisibility,
39+
pub rustc_has_incoherent_inherent_impls: bool,
3940
}
4041

4142
#[derive(Debug, Clone, PartialEq, Eq)]
@@ -44,6 +45,7 @@ pub struct EnumData {
4445
pub variants: Arena<EnumVariantData>,
4546
pub repr: Option<ReprData>,
4647
pub visibility: RawVisibility,
48+
pub rustc_has_incoherent_inherent_impls: bool,
4749
}
4850

4951
#[derive(Debug, Clone, PartialEq, Eq)]
@@ -157,6 +159,10 @@ impl StructData {
157159
let item_tree = loc.id.item_tree(db);
158160
let repr = repr_from_value(db, krate, &item_tree, ModItem::from(loc.id.value).into());
159161
let cfg_options = db.crate_graph()[loc.container.krate].cfg_options.clone();
162+
let rustc_has_incoherent_inherent_impls = item_tree
163+
.attrs(db, loc.container.krate, ModItem::from(loc.id.value).into())
164+
.by_key("rustc_has_incoherent_inherent_impls")
165+
.exists();
160166

161167
let strukt = &item_tree[loc.id.value];
162168
let (variant_data, diagnostics) = lower_fields(
@@ -175,6 +181,7 @@ impl StructData {
175181
variant_data: Arc::new(variant_data),
176182
repr,
177183
visibility: item_tree[strukt.visibility].clone(),
184+
rustc_has_incoherent_inherent_impls,
178185
}),
179186
diagnostics.into(),
180187
)
@@ -194,6 +201,11 @@ impl StructData {
194201
let repr = repr_from_value(db, krate, &item_tree, ModItem::from(loc.id.value).into());
195202
let cfg_options = db.crate_graph()[loc.container.krate].cfg_options.clone();
196203

204+
let rustc_has_incoherent_inherent_impls = item_tree
205+
.attrs(db, loc.container.krate, ModItem::from(loc.id.value).into())
206+
.by_key("rustc_has_incoherent_inherent_impls")
207+
.exists();
208+
197209
let union = &item_tree[loc.id.value];
198210
let (variant_data, diagnostics) = lower_fields(
199211
db,
@@ -211,6 +223,7 @@ impl StructData {
211223
variant_data: Arc::new(variant_data),
212224
repr,
213225
visibility: item_tree[union.visibility].clone(),
226+
rustc_has_incoherent_inherent_impls,
214227
}),
215228
diagnostics.into(),
216229
)
@@ -231,6 +244,10 @@ impl EnumData {
231244
let item_tree = loc.id.item_tree(db);
232245
let cfg_options = db.crate_graph()[krate].cfg_options.clone();
233246
let repr = repr_from_value(db, krate, &item_tree, ModItem::from(loc.id.value).into());
247+
let rustc_has_incoherent_inherent_impls = item_tree
248+
.attrs(db, loc.container.krate, ModItem::from(loc.id.value).into())
249+
.by_key("rustc_has_incoherent_inherent_impls")
250+
.exists();
234251

235252
let enum_ = &item_tree[loc.id.value];
236253
let mut variants = Arena::new();
@@ -271,6 +288,7 @@ impl EnumData {
271288
variants,
272289
repr,
273290
visibility: item_tree[enum_.visibility].clone(),
291+
rustc_has_incoherent_inherent_impls,
274292
}),
275293
diagnostics.into(),
276294
)

crates/hir-def/src/data.rs

+14-5
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,7 @@ pub struct TypeAliasData {
168168
pub type_ref: Option<Interned<TypeRef>>,
169169
pub visibility: RawVisibility,
170170
pub is_extern: bool,
171+
pub rustc_has_incoherent_inherent_impls: bool,
171172
/// Bounds restricting the type alias itself (eg. `type Ty: Bound;` in a trait or impl).
172173
pub bounds: Vec<Interned<TypeBound>>,
173174
}
@@ -186,11 +187,17 @@ impl TypeAliasData {
186187
item_tree[typ.visibility].clone()
187188
};
188189

190+
let rustc_has_incoherent_inherent_impls = item_tree
191+
.attrs(db, loc.container.module(db).krate(), ModItem::from(loc.id.value).into())
192+
.by_key("rustc_has_incoherent_inherent_impls")
193+
.exists();
194+
189195
Arc::new(TypeAliasData {
190196
name: typ.name.clone(),
191197
type_ref: typ.type_ref.clone(),
192198
visibility,
193199
is_extern: matches!(loc.container, ItemContainerId::ExternBlockId(_)),
200+
rustc_has_incoherent_inherent_impls,
194201
bounds: typ.bounds.to_vec(),
195202
})
196203
}
@@ -202,6 +209,7 @@ pub struct TraitData {
202209
pub items: Vec<(Name, AssocItemId)>,
203210
pub is_auto: bool,
204211
pub is_unsafe: bool,
212+
pub rustc_has_incoherent_inherent_impls: bool,
205213
pub visibility: RawVisibility,
206214
/// Whether the trait has `#[rust_skip_array_during_method_dispatch]`. `hir_ty` will ignore
207215
/// method calls to this trait's methods when the receiver is an array and the crate edition is
@@ -231,11 +239,11 @@ impl TraitData {
231239
let is_auto = tr_def.is_auto;
232240
let is_unsafe = tr_def.is_unsafe;
233241
let visibility = item_tree[tr_def.visibility].clone();
234-
let skip_array_during_method_dispatch = item_tree
235-
.attrs(db, module_id.krate(), ModItem::from(tree_id.value).into())
236-
.by_key("rustc_skip_array_during_method_dispatch")
237-
.exists();
238-
242+
let attrs = item_tree.attrs(db, module_id.krate(), ModItem::from(tree_id.value).into());
243+
let skip_array_during_method_dispatch =
244+
attrs.by_key("rustc_skip_array_during_method_dispatch").exists();
245+
let rustc_has_incoherent_inherent_impls =
246+
attrs.by_key("rustc_has_incoherent_inherent_impls").exists();
239247
let (items, attribute_calls, diagnostics) = match &tr_def.items {
240248
Some(items) => {
241249
let mut collector = AssocItemCollector::new(
@@ -258,6 +266,7 @@ impl TraitData {
258266
is_unsafe,
259267
visibility,
260268
skip_array_during_method_dispatch,
269+
rustc_has_incoherent_inherent_impls,
261270
}),
262271
diagnostics.into(),
263272
)

crates/hir-ty/src/db.rs

+10-5
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,13 @@
33
44
use std::sync::Arc;
55

6-
use arrayvec::ArrayVec;
76
use base_db::{impl_intern_key, salsa, CrateId, Upcast};
87
use hir_def::{
98
db::DefDatabase, expr::ExprId, BlockId, ConstId, ConstParamId, DefWithBodyId, EnumVariantId,
109
FunctionId, GenericDefId, ImplId, LifetimeParamId, LocalFieldId, TypeOrConstParamId, VariantId,
1110
};
1211
use la_arena::ArenaMap;
12+
use smallvec::SmallVec;
1313

1414
use crate::{
1515
chalk_db,
@@ -92,10 +92,15 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
9292
fn inherent_impls_in_block(&self, block: BlockId) -> Option<Arc<InherentImpls>>;
9393

9494
/// Collects all crates in the dependency graph that have impls for the
95-
/// given fingerprint. This is only used for primitive types; for
96-
/// user-defined types we just look at the crate where the type is defined.
97-
#[salsa::invoke(crate::method_resolution::inherent_impl_crates_query)]
98-
fn inherent_impl_crates(&self, krate: CrateId, fp: TyFingerprint) -> ArrayVec<CrateId, 2>;
95+
/// given fingerprint. This is only used for primitive types and types
96+
/// annotated with `rustc_has_incoherent_inherent_impls`; for other types
97+
/// we just look at the crate where the type is defined.
98+
#[salsa::invoke(crate::method_resolution::incoherent_inherent_impl_crates)]
99+
fn incoherent_inherent_impl_crates(
100+
&self,
101+
krate: CrateId,
102+
fp: TyFingerprint,
103+
) -> SmallVec<[CrateId; 2]>;
99104

100105
#[salsa::invoke(TraitImpls::trait_impls_in_crate_query)]
101106
fn trait_impls_in_crate(&self, krate: CrateId) -> Arc<TraitImpls>;

crates/hir-ty/src/method_resolution.rs

+45-27
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,17 @@
22
//! For details about how this works in rustc, see the method lookup page in the
33
//! [rustc guide](https://rust-lang.github.io/rustc-guide/method-lookup.html)
44
//! and the corresponding code mostly in rustc_hir_analysis/check/method/probe.rs.
5-
use std::{iter, ops::ControlFlow, sync::Arc};
5+
use std::{ops::ControlFlow, sync::Arc};
66

7-
use arrayvec::ArrayVec;
87
use base_db::{CrateId, Edition};
98
use chalk_ir::{cast::Cast, Mutability, UniverseIndex};
109
use hir_def::{
1110
data::ImplData, item_scope::ItemScope, nameres::DefMap, AssocItemId, BlockId, ConstId,
12-
FunctionId, GenericDefId, HasModule, ImplId, ItemContainerId, Lookup, ModuleDefId, ModuleId,
13-
TraitId,
11+
FunctionId, HasModule, ImplId, ItemContainerId, Lookup, ModuleDefId, ModuleId, TraitId,
1412
};
1513
use hir_expand::name::Name;
1614
use rustc_hash::{FxHashMap, FxHashSet};
15+
use smallvec::{smallvec, SmallVec};
1716
use stdx::never;
1817

1918
use crate::{
@@ -336,21 +335,18 @@ impl InherentImpls {
336335
}
337336
}
338337

339-
pub(crate) fn inherent_impl_crates_query(
338+
pub(crate) fn incoherent_inherent_impl_crates(
340339
db: &dyn HirDatabase,
341340
krate: CrateId,
342341
fp: TyFingerprint,
343-
) -> ArrayVec<CrateId, 2> {
342+
) -> SmallVec<[CrateId; 2]> {
344343
let _p = profile::span("inherent_impl_crates_query");
345-
let mut res = ArrayVec::new();
344+
let mut res = SmallVec::new();
346345
let crate_graph = db.crate_graph();
347346

347+
// should pass crate for finger print and do reverse deps
348+
348349
for krate in crate_graph.transitive_deps(krate) {
349-
if res.is_full() {
350-
// we don't currently look for or store more than two crates here,
351-
// so don't needlessly look at more crates than necessary.
352-
break;
353-
}
354350
let impls = db.inherent_impls_in_crate(krate);
355351
if impls.map.get(&fp).map_or(false, |v| !v.is_empty()) {
356352
res.push(krate);
@@ -392,30 +388,52 @@ pub fn def_crates(
392388
db: &dyn HirDatabase,
393389
ty: &Ty,
394390
cur_crate: CrateId,
395-
) -> Option<ArrayVec<CrateId, 2>> {
396-
let mod_to_crate_ids = |module: ModuleId| Some(iter::once(module.krate()).collect());
397-
398-
let fp = TyFingerprint::for_inherent_impl(ty);
399-
391+
) -> Option<SmallVec<[CrateId; 2]>> {
400392
match ty.kind(Interner) {
401-
TyKind::Adt(AdtId(def_id), _) => mod_to_crate_ids(def_id.module(db.upcast())),
402-
TyKind::Foreign(id) => {
403-
mod_to_crate_ids(from_foreign_def_id(*id).lookup(db.upcast()).module(db.upcast()))
393+
&TyKind::Adt(AdtId(def_id), _) => {
394+
let rustc_has_incoherent_inherent_impls = match def_id {
395+
hir_def::AdtId::StructId(id) => {
396+
db.struct_data(id).rustc_has_incoherent_inherent_impls
397+
}
398+
hir_def::AdtId::UnionId(id) => {
399+
db.union_data(id).rustc_has_incoherent_inherent_impls
400+
}
401+
hir_def::AdtId::EnumId(id) => db.enum_data(id).rustc_has_incoherent_inherent_impls,
402+
};
403+
Some(if rustc_has_incoherent_inherent_impls {
404+
db.incoherent_inherent_impl_crates(cur_crate, TyFingerprint::Adt(def_id))
405+
} else {
406+
smallvec![def_id.module(db.upcast()).krate()]
407+
})
408+
}
409+
&TyKind::Foreign(id) => {
410+
let alias = from_foreign_def_id(id);
411+
Some(if db.type_alias_data(alias).rustc_has_incoherent_inherent_impls {
412+
db.incoherent_inherent_impl_crates(cur_crate, TyFingerprint::ForeignType(id))
413+
} else {
414+
smallvec![alias.module(db.upcast()).krate()]
415+
})
416+
}
417+
TyKind::Dyn(_) => {
418+
let trait_id = ty.dyn_trait()?;
419+
Some(if db.trait_data(trait_id).rustc_has_incoherent_inherent_impls {
420+
db.incoherent_inherent_impl_crates(cur_crate, TyFingerprint::Dyn(trait_id))
421+
} else {
422+
smallvec![trait_id.module(db.upcast()).krate()]
423+
})
404424
}
405-
TyKind::Dyn(_) => ty
406-
.dyn_trait()
407-
.and_then(|trait_| mod_to_crate_ids(GenericDefId::TraitId(trait_).module(db.upcast()))),
408425
// for primitives, there may be impls in various places (core and alloc
409426
// mostly). We just check the whole crate graph for crates with impls
410427
// (cached behind a query).
411428
TyKind::Scalar(_)
412429
| TyKind::Str
413430
| TyKind::Slice(_)
414431
| TyKind::Array(..)
415-
| TyKind::Raw(..) => {
416-
Some(db.inherent_impl_crates(cur_crate, fp.expect("fingerprint for primitive")))
417-
}
418-
_ => return None,
432+
| TyKind::Raw(..) => Some(db.incoherent_inherent_impl_crates(
433+
cur_crate,
434+
TyFingerprint::for_inherent_impl(ty).expect("fingerprint for primitive"),
435+
)),
436+
_ => None,
419437
}
420438
}
421439

crates/hir-ty/src/tests/method_resolution.rs

+29
Original file line numberDiff line numberDiff line change
@@ -1867,3 +1867,32 @@ fn g<T: Trait>(a: T) {
18671867
"#,
18681868
);
18691869
}
1870+
1871+
#[test]
1872+
fn incoherent_impls() {
1873+
check(
1874+
r#"
1875+
//- minicore: error, send
1876+
pub struct Box<T>(T);
1877+
use core::error::Error;
1878+
1879+
#[rustc_allow_incoherent_impl]
1880+
impl dyn Error {
1881+
pub fn downcast<T: Error + 'static>(self: Box<Self>) -> Result<Box<T>, Box<dyn Error>> {
1882+
loop {}
1883+
}
1884+
}
1885+
#[rustc_allow_incoherent_impl]
1886+
impl dyn Error + Send {
1887+
/// Attempts to downcast the box to a concrete type.
1888+
pub fn downcast<T: Error + 'static>(self: Box<Self>) -> Result<Box<T>, Box<dyn Error + Send>> {
1889+
let err: Box<dyn Error> = self;
1890+
// ^^^^ expected Box<dyn Error>, got Box<dyn Error + Send>
1891+
// FIXME, type mismatch should not occur
1892+
<dyn Error>::downcast(err).map_err(|_| loop {})
1893+
//^^^^^^^^^^^^^^^^^^^^^ type: fn downcast<{unknown}>(Box<dyn Error>) -> Result<Box<{unknown}>, Box<dyn Error>>
1894+
}
1895+
}
1896+
"#,
1897+
);
1898+
}

crates/test-utils/src/minicore.rs

+37
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
//! derive:
2121
//! drop:
2222
//! eq: sized
23+
//! error: fmt
2324
//! fmt: result
2425
//! fn:
2526
//! from: sized
@@ -34,8 +35,10 @@
3435
//! pin:
3536
//! range:
3637
//! result:
38+
//! send: sized
3739
//! sized:
3840
//! slice:
41+
//! sync: sized
3942
//! try:
4043
//! unsize: sized
4144
@@ -47,6 +50,24 @@ pub mod marker {
4750
pub trait Sized {}
4851
// endregion:sized
4952

53+
// region:send
54+
pub unsafe auto trait Send {}
55+
56+
impl<T: ?Sized> !Send for *const T {}
57+
impl<T: ?Sized> !Send for *mut T {}
58+
// region:sync
59+
unsafe impl<T: Sync + ?Sized> Send for &T {}
60+
unsafe impl<T: Send + ?Sized> Send for &mut T {}
61+
// endregion:sync
62+
// endregion:send
63+
64+
// region:sync
65+
pub unsafe auto trait Sync {}
66+
67+
impl<T: ?Sized> !Sync for *const T {}
68+
impl<T: ?Sized> !Sync for *mut T {}
69+
// endregion:sync
70+
5071
// region:unsize
5172
#[lang = "unsize"]
5273
pub trait Unsize<T: ?Sized> {}
@@ -438,6 +459,9 @@ pub mod fmt {
438459
pub trait Debug {
439460
fn fmt(&self, f: &mut Formatter<'_>) -> Result;
440461
}
462+
pub trait Display {
463+
fn fmt(&self, f: &mut Formatter<'_>) -> Result;
464+
}
441465
}
442466
// endregion:fmt
443467

@@ -693,6 +717,17 @@ impl bool {
693717
}
694718
// endregion:bool_impl
695719

720+
// region:error
721+
pub mod error {
722+
#[rustc_has_incoherent_inherent_impls]
723+
pub trait Error: crate::fmt::Debug + crate::fmt::Display {
724+
fn source(&self) -> Option<&(dyn Error + 'static)> {
725+
None
726+
}
727+
}
728+
}
729+
// endregion:error
730+
696731
pub mod prelude {
697732
pub mod v1 {
698733
pub use crate::{
@@ -705,7 +740,9 @@ pub mod prelude {
705740
iter::{IntoIterator, Iterator}, // :iterator
706741
macros::builtin::derive, // :derive
707742
marker::Copy, // :copy
743+
marker::Send, // :send
708744
marker::Sized, // :sized
745+
marker::Sync, // :sync
709746
mem::drop, // :drop
710747
ops::Drop, // :drop
711748
ops::{Fn, FnMut, FnOnce}, // :fn

0 commit comments

Comments
 (0)