Skip to content

Commit 3e75146

Browse files
committed
Auto merge of #94963 - lcnr:inherent-impls-std, r=oli-obk,m-ou-se
allow arbitrary inherent impls for builtin types in core Part of rust-lang/compiler-team#487. Slightly adjusted after some talks with `@m-ou-se` about the requirements of `t-libs-api`. This adds a crate attribute `#![rustc_coherence_is_core]` which allows arbitrary impls for builtin types in core. For other library crates impls for builtin types should be avoided if possible. We do have to allow the existing stable impls however. To prevent us from accidentally adding more of these in the future, there is a second attribute `#[rustc_allow_incoherent_impl]` which has to be added to **all impl items**. This only supports impls for builtin types but can easily be extended to additional types in a future PR. This implementation does not check for overlaps in these impls. Perfectly checking that requires us to check the coherence of these incoherent impls in every crate, as two distinct dependencies may add overlapping methods. It should be easy enough to detect if it goes wrong and the attribute is only intended for use inside of std. The first two commits are mostly unrelated cleanups.
2 parents e50ff9b + 46340f2 commit 3e75146

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

66 files changed

+703
-851
lines changed

compiler/rustc_error_codes/src/error_codes/E0118.md

+4-25
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ enum, union, or trait object.
44
Erroneous code example:
55

66
```compile_fail,E0118
7-
impl (u8, u8) { // error: no nominal type found for inherent implementation
7+
impl fn(u8) { // error: no nominal type found for inherent implementation
88
fn get_state(&self) -> String {
99
// ...
1010
}
@@ -20,8 +20,8 @@ trait LiveLongAndProsper {
2020
fn get_state(&self) -> String;
2121
}
2222
23-
// and now you can implement it on (u8, u8)
24-
impl LiveLongAndProsper for (u8, u8) {
23+
// and now you can implement it on fn(u8)
24+
impl LiveLongAndProsper for fn(u8) {
2525
fn get_state(&self) -> String {
2626
"He's dead, Jim!".to_owned()
2727
}
@@ -33,32 +33,11 @@ For example, `NewType` is a newtype over `Foo` in `struct NewType(Foo)`.
3333
Example:
3434

3535
```
36-
struct TypeWrapper((u8, u8));
36+
struct TypeWrapper(fn(u8));
3737
3838
impl TypeWrapper {
3939
fn get_state(&self) -> String {
4040
"Fascinating!".to_owned()
4141
}
4242
}
4343
```
44-
45-
Instead of defining an inherent implementation on a reference, you could also
46-
move the reference inside the implementation:
47-
48-
```compile_fail,E0118
49-
struct Foo;
50-
51-
impl &Foo { // error: no nominal type found for inherent implementation
52-
fn bar(self, other: Self) {}
53-
}
54-
```
55-
56-
becomes
57-
58-
```
59-
struct Foo;
60-
61-
impl Foo {
62-
fn bar(&self, other: &Self) {}
63-
}
64-
```

compiler/rustc_error_codes/src/error_codes/E0390.md

+22-2
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,7 @@ struct Foo {
88
}
99
1010
impl *mut Foo {}
11-
// error: only a single inherent implementation marked with
12-
// `#[lang = "mut_ptr"]` is allowed for the `*mut T` primitive
11+
// error: cannot define inherent `impl` for primitive types
1312
```
1413

1514
This isn't allowed, but using a trait to implement a method or constant
@@ -29,3 +28,24 @@ impl Bar for *mut Foo {
2928
fn bar() {} // ok!
3029
}
3130
```
31+
32+
Instead of defining an inherent implementation on a reference, you could also
33+
move the reference inside the implementation:
34+
35+
```compile_fail,E0390
36+
struct Foo;
37+
38+
impl &Foo { // error: no nominal type found for inherent implementation
39+
fn bar(self, other: Self) {}
40+
}
41+
```
42+
43+
becomes
44+
45+
```
46+
struct Foo;
47+
48+
impl Foo {
49+
fn bar(&self, other: &Self) {}
50+
}
51+
```

compiler/rustc_feature/src/builtin_attrs.rs

+8
Original file line numberDiff line numberDiff line change
@@ -636,6 +636,14 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
636636
template!(Word), ErrorFollowing,
637637
"#[rustc_pass_by_value] is used to mark types that must be passed by value instead of reference."
638638
),
639+
rustc_attr!(
640+
rustc_coherence_is_core, AttributeType::CrateLevel, template!(Word), ErrorFollowing,
641+
"#![rustc_coherence_is_core] allows inherent methods on builtin types, only intended to be used in `core`."
642+
),
643+
rustc_attr!(
644+
rustc_allow_incoherent_impl, AttributeType::Normal, template!(Word), ErrorFollowing,
645+
"#[rustc_allow_incoherent_impl] has to be added to all impl items of an incoherent inherent impl."
646+
),
639647
BuiltinAttribute {
640648
name: sym::rustc_diagnostic_item,
641649
type_: Normal,

compiler/rustc_hir/src/lang_items.rs

-30
Original file line numberDiff line numberDiff line change
@@ -166,36 +166,6 @@ pub fn extract(attrs: &[ast::Attribute]) -> Option<(Symbol, Span)> {
166166

167167
language_item_table! {
168168
// Variant name, Name, Method name, Target Generic requirements;
169-
Bool, sym::bool, bool_impl, Target::Impl, GenericRequirement::None;
170-
Char, sym::char, char_impl, Target::Impl, GenericRequirement::None;
171-
Str, sym::str, str_impl, Target::Impl, GenericRequirement::None;
172-
Array, sym::array, array_impl, Target::Impl, GenericRequirement::None;
173-
Slice, sym::slice, slice_impl, Target::Impl, GenericRequirement::None;
174-
SliceU8, sym::slice_u8, slice_u8_impl, Target::Impl, GenericRequirement::None;
175-
StrAlloc, sym::str_alloc, str_alloc_impl, Target::Impl, GenericRequirement::None;
176-
SliceAlloc, sym::slice_alloc, slice_alloc_impl, Target::Impl, GenericRequirement::None;
177-
SliceU8Alloc, sym::slice_u8_alloc, slice_u8_alloc_impl, Target::Impl, GenericRequirement::None;
178-
ConstPtr, sym::const_ptr, const_ptr_impl, Target::Impl, GenericRequirement::None;
179-
MutPtr, sym::mut_ptr, mut_ptr_impl, Target::Impl, GenericRequirement::None;
180-
ConstSlicePtr, sym::const_slice_ptr, const_slice_ptr_impl, Target::Impl, GenericRequirement::None;
181-
MutSlicePtr, sym::mut_slice_ptr, mut_slice_ptr_impl, Target::Impl, GenericRequirement::None;
182-
I8, sym::i8, i8_impl, Target::Impl, GenericRequirement::None;
183-
I16, sym::i16, i16_impl, Target::Impl, GenericRequirement::None;
184-
I32, sym::i32, i32_impl, Target::Impl, GenericRequirement::None;
185-
I64, sym::i64, i64_impl, Target::Impl, GenericRequirement::None;
186-
I128, sym::i128, i128_impl, Target::Impl, GenericRequirement::None;
187-
Isize, sym::isize, isize_impl, Target::Impl, GenericRequirement::None;
188-
U8, sym::u8, u8_impl, Target::Impl, GenericRequirement::None;
189-
U16, sym::u16, u16_impl, Target::Impl, GenericRequirement::None;
190-
U32, sym::u32, u32_impl, Target::Impl, GenericRequirement::None;
191-
U64, sym::u64, u64_impl, Target::Impl, GenericRequirement::None;
192-
U128, sym::u128, u128_impl, Target::Impl, GenericRequirement::None;
193-
Usize, sym::usize, usize_impl, Target::Impl, GenericRequirement::None;
194-
F32, sym::f32, f32_impl, Target::Impl, GenericRequirement::None;
195-
F64, sym::f64, f64_impl, Target::Impl, GenericRequirement::None;
196-
F32Runtime, sym::f32_runtime, f32_runtime_impl, Target::Impl, GenericRequirement::None;
197-
F64Runtime, sym::f64_runtime, f64_runtime_impl, Target::Impl, GenericRequirement::None;
198-
199169
Sized, sym::sized, sized_trait, Target::Trait, GenericRequirement::Exact(0);
200170
Unsize, sym::unsize, unsize_trait, Target::Trait, GenericRequirement::Minimum(1);
201171
/// Trait injected by `#[derive(PartialEq)]`, (i.e. "Partial EQ").

compiler/rustc_metadata/src/rmeta/decoder.rs

+36-8
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,11 @@ crate struct CrateMetadata {
9595
/// FIXME: Used only from queries and can use query cache,
9696
/// so pre-decoding can probably be avoided.
9797
trait_impls: FxHashMap<(u32, DefIndex), Lazy<[(DefIndex, Option<SimplifiedType>)]>>,
98+
/// Inherent impls which do not follow the normal coherence rules.
99+
///
100+
/// These can be introduced using either `#![rustc_coherence_is_core]`
101+
/// or `#[rustc_allow_incoherent_impl]`.
102+
incoherent_impls: FxHashMap<SimplifiedType, Lazy<[DefIndex]>>,
98103
/// Proc macro descriptions for this crate, if it's a proc macro crate.
99104
raw_proc_macros: Option<&'static [ProcMacro]>,
100105
/// Source maps for code from the crate.
@@ -1028,11 +1033,13 @@ impl<'a, 'tcx> CrateMetadataRef<'a> {
10281033
}
10291034

10301035
/// Iterates over the language items in the given crate.
1031-
fn get_lang_items(self) -> impl Iterator<Item = (DefId, usize)> + 'a {
1032-
self.root
1033-
.lang_items
1034-
.decode(self)
1035-
.map(move |(def_index, index)| (self.local_def_id(def_index), index))
1036+
fn get_lang_items(self, tcx: TyCtxt<'tcx>) -> &'tcx [(DefId, usize)] {
1037+
tcx.arena.alloc_from_iter(
1038+
self.root
1039+
.lang_items
1040+
.decode(self)
1041+
.map(move |(def_index, index)| (self.local_def_id(def_index), index)),
1042+
)
10361043
}
10371044

10381045
/// Iterates over the diagnostic items in the given crate.
@@ -1327,17 +1334,32 @@ impl<'a, 'tcx> CrateMetadataRef<'a> {
13271334

13281335
/// Decodes all trait impls in the crate (for rustdoc).
13291336
fn get_trait_impls(self) -> impl Iterator<Item = (DefId, DefId, Option<SimplifiedType>)> + 'a {
1330-
self.cdata.trait_impls.iter().flat_map(move |((trait_cnum_raw, trait_index), impls)| {
1337+
self.cdata.trait_impls.iter().flat_map(move |(&(trait_cnum_raw, trait_index), impls)| {
13311338
let trait_def_id = DefId {
1332-
krate: self.cnum_map[CrateNum::from_u32(*trait_cnum_raw)],
1333-
index: *trait_index,
1339+
krate: self.cnum_map[CrateNum::from_u32(trait_cnum_raw)],
1340+
index: trait_index,
13341341
};
13351342
impls.decode(self).map(move |(impl_index, simplified_self_ty)| {
13361343
(trait_def_id, self.local_def_id(impl_index), simplified_self_ty)
13371344
})
13381345
})
13391346
}
13401347

1348+
fn get_all_incoherent_impls(self) -> impl Iterator<Item = DefId> + 'a {
1349+
self.cdata
1350+
.incoherent_impls
1351+
.values()
1352+
.flat_map(move |impls| impls.decode(self).map(move |idx| self.local_def_id(idx)))
1353+
}
1354+
1355+
fn get_incoherent_impls(self, tcx: TyCtxt<'tcx>, simp: SimplifiedType) -> &'tcx [DefId] {
1356+
if let Some(impls) = self.cdata.incoherent_impls.get(&simp) {
1357+
tcx.arena.alloc_from_iter(impls.decode(self).map(|idx| self.local_def_id(idx)))
1358+
} else {
1359+
&[]
1360+
}
1361+
}
1362+
13411363
fn get_implementations_of_trait(
13421364
self,
13431365
tcx: TyCtxt<'tcx>,
@@ -1754,6 +1776,11 @@ impl CrateMetadata {
17541776
.decode((&blob, sess))
17551777
.map(|trait_impls| (trait_impls.trait_id, trait_impls.impls))
17561778
.collect();
1779+
let incoherent_impls = root
1780+
.incoherent_impls
1781+
.decode((&blob, sess))
1782+
.map(|incoherent_impls| (incoherent_impls.self_ty, incoherent_impls.impls))
1783+
.collect();
17571784
let alloc_decoding_state =
17581785
AllocDecodingState::new(root.interpret_alloc_index.decode(&blob).collect());
17591786
let dependencies = Lock::new(cnum_map.iter().cloned().collect());
@@ -1766,6 +1793,7 @@ impl CrateMetadata {
17661793
blob,
17671794
root,
17681795
trait_impls,
1796+
incoherent_impls,
17691797
raw_proc_macros,
17701798
source_map_import_info: OnceCell::new(),
17711799
def_path_hash_map,

compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs

+27-12
Original file line numberDiff line numberDiff line change
@@ -81,30 +81,42 @@ macro_rules! provide {
8181
// small trait to work around different signature queries all being defined via
8282
// the macro above.
8383
trait IntoArgs {
84-
fn into_args(self) -> (DefId, DefId);
84+
type Other;
85+
fn into_args(self) -> (DefId, Self::Other);
8586
}
8687

8788
impl IntoArgs for DefId {
88-
fn into_args(self) -> (DefId, DefId) {
89-
(self, self)
89+
type Other = ();
90+
fn into_args(self) -> (DefId, ()) {
91+
(self, ())
9092
}
9193
}
9294

9395
impl IntoArgs for CrateNum {
94-
fn into_args(self) -> (DefId, DefId) {
95-
(self.as_def_id(), self.as_def_id())
96+
type Other = ();
97+
fn into_args(self) -> (DefId, ()) {
98+
(self.as_def_id(), ())
9699
}
97100
}
98101

99102
impl IntoArgs for (CrateNum, DefId) {
103+
type Other = DefId;
100104
fn into_args(self) -> (DefId, DefId) {
101105
(self.0.as_def_id(), self.1)
102106
}
103107
}
104108

105109
impl<'tcx> IntoArgs for ty::InstanceDef<'tcx> {
106-
fn into_args(self) -> (DefId, DefId) {
107-
(self.def_id(), self.def_id())
110+
type Other = ();
111+
fn into_args(self) -> (DefId, ()) {
112+
(self.def_id(), ())
113+
}
114+
}
115+
116+
impl IntoArgs for (CrateNum, SimplifiedType) {
117+
type Other = SimplifiedType;
118+
fn into_args(self) -> (DefId, SimplifiedType) {
119+
(self.0.as_def_id(), self.1)
108120
}
109121
}
110122

@@ -199,6 +211,7 @@ provide! { <'tcx> tcx, def_id, other, cdata,
199211

200212
traits_in_crate => { tcx.arena.alloc_from_iter(cdata.get_traits()) }
201213
implementations_of_trait => { cdata.get_implementations_of_trait(tcx, other) }
214+
crate_incoherent_impls => { cdata.get_incoherent_impls(tcx, other) }
202215

203216
dep_kind => {
204217
let r = *cdata.dep_kind.lock();
@@ -210,7 +223,7 @@ provide! { <'tcx> tcx, def_id, other, cdata,
210223
tcx.arena.alloc_slice(&result)
211224
}
212225
defined_lib_features => { cdata.get_lib_features(tcx) }
213-
defined_lang_items => { tcx.arena.alloc_from_iter(cdata.get_lang_items()) }
226+
defined_lang_items => { cdata.get_lang_items(tcx) }
214227
diagnostic_items => { cdata.get_diagnostic_items() }
215228
missing_lang_items => { cdata.get_missing_lang_items(tcx) }
216229

@@ -371,7 +384,6 @@ pub(in crate::rmeta) fn provide(providers: &mut Providers) {
371384
.alloc_slice(&CStore::from_tcx(tcx).crate_dependencies_in_postorder(LOCAL_CRATE))
372385
},
373386
crates: |tcx, ()| tcx.arena.alloc_from_iter(CStore::from_tcx(tcx).crates_untracked()),
374-
375387
..*providers
376388
};
377389
}
@@ -511,9 +523,12 @@ impl CStore {
511523
self.get_crate_data(cnum).get_inherent_impls()
512524
}
513525

514-
/// Decodes all lang items in the crate (for rustdoc).
515-
pub fn lang_items_untracked(&self, cnum: CrateNum) -> impl Iterator<Item = DefId> + '_ {
516-
self.get_crate_data(cnum).get_lang_items().map(|(def_id, _)| def_id)
526+
/// Decodes all incoherent inherent impls in the crate (for rustdoc).
527+
pub fn incoherent_impls_in_crate_untracked(
528+
&self,
529+
cnum: CrateNum,
530+
) -> impl Iterator<Item = DefId> + '_ {
531+
self.get_crate_data(cnum).get_all_incoherent_impls()
517532
}
518533
}
519534

compiler/rustc_metadata/src/rmeta/encoder.rs

+35-3
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@ use crate::rmeta::def_path_hash_map::DefPathHashMapRef;
22
use crate::rmeta::table::{FixedSizeEncoding, TableBuilder};
33
use crate::rmeta::*;
44

5+
use rustc_data_structures::fingerprint::Fingerprint;
56
use rustc_data_structures::fx::{FxHashMap, FxIndexSet};
6-
use rustc_data_structures::stable_hasher::StableHasher;
7+
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
78
use rustc_data_structures::sync::{join, par_iter, Lrc, ParallelIterator};
89
use rustc_hir as hir;
910
use rustc_hir::def::DefKind;
@@ -578,6 +579,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
578579
}
579580

580581
fn encode_crate_root(&mut self) -> Lazy<CrateRoot<'tcx>> {
582+
let tcx = self.tcx;
581583
let mut i = self.position();
582584

583585
// Encode the crate deps
@@ -623,8 +625,9 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
623625
let impls = self.encode_impls();
624626
let impls_bytes = self.position() - i;
625627

626-
let tcx = self.tcx;
627-
628+
i = self.position();
629+
let incoherent_impls = self.encode_incoherent_impls();
630+
let incoherent_impls_bytes = self.position() - i;
628631
// Encode MIR.
629632
i = self.position();
630633
self.encode_mir();
@@ -734,6 +737,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
734737
source_map,
735738
traits,
736739
impls,
740+
incoherent_impls,
737741
exported_symbols,
738742
interpret_alloc_index,
739743
tables,
@@ -762,6 +766,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
762766
eprintln!(" source_map bytes: {}", source_map_bytes);
763767
eprintln!(" traits bytes: {}", traits_bytes);
764768
eprintln!(" impls bytes: {}", impls_bytes);
769+
eprintln!("incoherent_impls bytes: {}", incoherent_impls_bytes);
765770
eprintln!(" exp. symbols bytes: {}", exported_symbols_bytes);
766771
eprintln!(" def-path table bytes: {}", def_path_table_bytes);
767772
eprintln!(" def-path hashes bytes: {}", def_path_hash_map_bytes);
@@ -1813,6 +1818,33 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
18131818
self.lazy(&all_impls)
18141819
}
18151820

1821+
fn encode_incoherent_impls(&mut self) -> Lazy<[IncoherentImpls]> {
1822+
debug!("EncodeContext::encode_traits_and_impls()");
1823+
empty_proc_macro!(self);
1824+
let tcx = self.tcx;
1825+
let mut ctx = tcx.create_stable_hashing_context();
1826+
let mut all_impls: Vec<_> = tcx.crate_inherent_impls(()).incoherent_impls.iter().collect();
1827+
all_impls.sort_by_cached_key(|&(&simp, _)| {
1828+
let mut hasher = StableHasher::new();
1829+
simp.hash_stable(&mut ctx, &mut hasher);
1830+
hasher.finish::<Fingerprint>();
1831+
});
1832+
let all_impls: Vec<_> = all_impls
1833+
.into_iter()
1834+
.map(|(&simp, impls)| {
1835+
let mut impls: Vec<_> =
1836+
impls.into_iter().map(|def_id| def_id.local_def_index).collect();
1837+
impls.sort_by_cached_key(|&local_def_index| {
1838+
tcx.hir().def_path_hash(LocalDefId { local_def_index })
1839+
});
1840+
1841+
IncoherentImpls { self_ty: simp, impls: self.lazy(impls) }
1842+
})
1843+
.collect();
1844+
1845+
self.lazy(&all_impls)
1846+
}
1847+
18161848
// Encodes all symbols exported from this crate into the metadata.
18171849
//
18181850
// This pass is seeded off the reachability list calculated in the

0 commit comments

Comments
 (0)