Skip to content

Commit 74cea9f

Browse files
committed
Auto merge of rust-lang#96520 - lcnr:general-incoherent-impls, r=petrochenkov
generalize "incoherent impls" impl for user defined types To allow the move of `trait Error` into core. continues the work from rust-lang#94963, finishes rust-lang/compiler-team#487 r? `@petrochenkov` cc `@yaahc`
2 parents 30f3860 + 321162b commit 74cea9f

File tree

12 files changed

+326
-18
lines changed

12 files changed

+326
-18
lines changed

compiler/rustc_feature/src/builtin_attrs.rs

+5
Original file line numberDiff line numberDiff line change
@@ -650,6 +650,11 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
650650
rustc_allow_incoherent_impl, AttributeType::Normal, template!(Word), ErrorFollowing,
651651
"#[rustc_allow_incoherent_impl] has to be added to all impl items of an incoherent inherent impl."
652652
),
653+
rustc_attr!(
654+
rustc_has_incoherent_inherent_impls, AttributeType::Normal, template!(Word), ErrorFollowing,
655+
"#[rustc_has_incoherent_inherent_impls] allows the addition of incoherent inherent impls for \
656+
the given type by annotating all impl items with #[rustc_allow_incoherent_impl]."
657+
),
653658
BuiltinAttribute {
654659
name: sym::rustc_diagnostic_item,
655660
type_: Normal,

compiler/rustc_hir/src/lang_items.rs

-2
Original file line numberDiff line numberDiff line change
@@ -327,8 +327,6 @@ language_item_table! {
327327
Range, sym::Range, range_struct, Target::Struct, GenericRequirement::None;
328328
RangeToInclusive, sym::RangeToInclusive, range_to_inclusive_struct, Target::Struct, GenericRequirement::None;
329329
RangeTo, sym::RangeTo, range_to_struct, Target::Struct, GenericRequirement::None;
330-
331-
CStr, sym::CStr, c_str, Target::Struct, GenericRequirement::None;
332330
}
333331

334332
pub enum GenericRequirement {

compiler/rustc_passes/src/check_attr.rs

+27-1
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,9 @@ impl CheckAttrVisitor<'_> {
125125
sym::rustc_allow_incoherent_impl => {
126126
self.check_allow_incoherent_impl(&attr, span, target)
127127
}
128+
sym::rustc_has_incoherent_inherent_impls => {
129+
self.check_has_incoherent_inherent_impls(&attr, span, target)
130+
}
128131
sym::rustc_const_unstable
129132
| sym::rustc_const_stable
130133
| sym::unstable
@@ -1096,7 +1099,6 @@ impl CheckAttrVisitor<'_> {
10961099
}
10971100
}
10981101

1099-
/// Warns against some misuses of `#[pass_by_value]`
11001102
fn check_allow_incoherent_impl(&self, attr: &Attribute, span: Span, target: Target) -> bool {
11011103
match target {
11021104
Target::Method(MethodKind::Inherent) => true,
@@ -1114,6 +1116,30 @@ impl CheckAttrVisitor<'_> {
11141116
}
11151117
}
11161118

1119+
fn check_has_incoherent_inherent_impls(
1120+
&self,
1121+
attr: &Attribute,
1122+
span: Span,
1123+
target: Target,
1124+
) -> bool {
1125+
match target {
1126+
Target::Trait | Target::Struct | Target::Enum | Target::Union | Target::ForeignTy => {
1127+
true
1128+
}
1129+
_ => {
1130+
self.tcx
1131+
.sess
1132+
.struct_span_err(
1133+
attr.span,
1134+
"`rustc_has_incoherent_inherent_impls` attribute should be applied to types or traits.",
1135+
)
1136+
.span_label(span, "only adts, extern types and traits are supported")
1137+
.emit();
1138+
false
1139+
}
1140+
}
1141+
}
1142+
11171143
/// Warns against some misuses of `#[must_use]`
11181144
fn check_must_use(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) -> bool {
11191145
let node = self.tcx.hir().get(hir_id);

compiler/rustc_span/src/symbol.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1189,6 +1189,7 @@ symbols! {
11891189
rustc_error,
11901190
rustc_evaluate_where_clauses,
11911191
rustc_expected_cgu_reuse,
1192+
rustc_has_incoherent_inherent_impls,
11921193
rustc_if_this_changed,
11931194
rustc_inherit_overflow_checks,
11941195
rustc_insignificant_dtor,

compiler/rustc_typeck/src/check/method/probe.rs

+8-1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ use rustc_span::def_id::LocalDefId;
2727
use rustc_span::lev_distance::{
2828
find_best_match_for_name_with_substrings, lev_distance_with_substrings,
2929
};
30+
use rustc_span::symbol::sym;
3031
use rustc_span::{symbol::Ident, Span, Symbol, DUMMY_SP};
3132
use rustc_trait_selection::autoderef::{self, Autoderef};
3233
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
@@ -642,16 +643,22 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
642643

643644
self.assemble_inherent_candidates_from_object(generalized_self_ty);
644645
self.assemble_inherent_impl_candidates_for_type(p.def_id());
646+
if self.tcx.has_attr(p.def_id(), sym::rustc_has_incoherent_inherent_impls) {
647+
self.assemble_inherent_candidates_for_incoherent_ty(raw_self_ty);
648+
}
645649
}
646650
ty::Adt(def, _) => {
647651
let def_id = def.did();
648652
self.assemble_inherent_impl_candidates_for_type(def_id);
649-
if Some(def_id) == self.tcx.lang_items().c_str() {
653+
if self.tcx.has_attr(def_id, sym::rustc_has_incoherent_inherent_impls) {
650654
self.assemble_inherent_candidates_for_incoherent_ty(raw_self_ty);
651655
}
652656
}
653657
ty::Foreign(did) => {
654658
self.assemble_inherent_impl_candidates_for_type(did);
659+
if self.tcx.has_attr(did, sym::rustc_has_incoherent_inherent_impls) {
660+
self.assemble_inherent_candidates_for_incoherent_ty(raw_self_ty);
661+
}
655662
}
656663
ty::Param(p) => {
657664
self.assemble_inherent_candidates_from_param(p);

compiler/rustc_typeck/src/coherence/inherent_impls.rs

+58-13
Original file line numberDiff line numberDiff line change
@@ -55,18 +55,13 @@ impl<'tcx> ItemLikeVisitor<'_> for InherentCollect<'tcx> {
5555
let self_ty = self.tcx.type_of(item.def_id);
5656
match *self_ty.kind() {
5757
ty::Adt(def, _) => {
58-
let def_id = def.did();
59-
if !def_id.is_local() && Some(def_id) == self.tcx.lang_items().c_str() {
60-
self.check_primitive_impl(item.def_id, self_ty, items, ty.span)
61-
} else {
62-
self.check_def_id(item, def_id);
63-
}
58+
self.check_def_id(item, self_ty, def.did());
6459
}
6560
ty::Foreign(did) => {
66-
self.check_def_id(item, did);
61+
self.check_def_id(item, self_ty, did);
6762
}
6863
ty::Dynamic(data, ..) if data.principal_def_id().is_some() => {
69-
self.check_def_id(item, data.principal_def_id().unwrap());
64+
self.check_def_id(item, self_ty, data.principal_def_id().unwrap());
7065
}
7166
ty::Dynamic(..) => {
7267
struct_span_err!(
@@ -124,14 +119,67 @@ impl<'tcx> ItemLikeVisitor<'_> for InherentCollect<'tcx> {
124119
fn visit_foreign_item(&mut self, _foreign_item: &hir::ForeignItem<'_>) {}
125120
}
126121

122+
const INTO_CORE: &str = "consider moving this inherent impl into `core` if possible";
123+
const INTO_DEFINING_CRATE: &str =
124+
"consider moving this inherent impl into the crate defining the type if possible";
125+
const ADD_ATTR_TO_TY: &str = "alternatively add `#[rustc_has_incoherent_inherent_impls]` to the type \
126+
and `#[rustc_allow_incoherent_impl]` to the relevant impl items";
127+
const ADD_ATTR: &str =
128+
"alternatively add `#[rustc_allow_incoherent_impl]` to the relevant impl items";
129+
127130
impl<'tcx> InherentCollect<'tcx> {
128-
fn check_def_id(&mut self, item: &hir::Item<'_>, def_id: DefId) {
131+
fn check_def_id(&mut self, item: &hir::Item<'_>, self_ty: Ty<'tcx>, def_id: DefId) {
132+
let impl_def_id = item.def_id;
129133
if let Some(def_id) = def_id.as_local() {
130134
// Add the implementation to the mapping from implementation to base
131135
// type def ID, if there is a base type for this implementation and
132136
// the implementation does not have any associated traits.
133137
let vec = self.impls_map.inherent_impls.entry(def_id).or_default();
134-
vec.push(item.def_id.to_def_id());
138+
vec.push(impl_def_id.to_def_id());
139+
return;
140+
}
141+
142+
if self.tcx.features().rustc_attrs {
143+
let hir::ItemKind::Impl(&hir::Impl { items, .. }) = item.kind else {
144+
bug!("expected `impl` item: {:?}", item);
145+
};
146+
147+
if !self.tcx.has_attr(def_id, sym::rustc_has_incoherent_inherent_impls) {
148+
struct_span_err!(
149+
self.tcx.sess,
150+
item.span,
151+
E0390,
152+
"cannot define inherent `impl` for a type outside of the crate where the type is defined",
153+
)
154+
.help(INTO_DEFINING_CRATE)
155+
.span_help(item.span, ADD_ATTR_TO_TY)
156+
.emit();
157+
return;
158+
}
159+
160+
for impl_item in items {
161+
if !self
162+
.tcx
163+
.has_attr(impl_item.id.def_id.to_def_id(), sym::rustc_allow_incoherent_impl)
164+
{
165+
struct_span_err!(
166+
self.tcx.sess,
167+
item.span,
168+
E0390,
169+
"cannot define inherent `impl` for a type outside of the crate where the type is defined",
170+
)
171+
.help(INTO_DEFINING_CRATE)
172+
.span_help(impl_item.span, ADD_ATTR)
173+
.emit();
174+
return;
175+
}
176+
}
177+
178+
if let Some(simp) = simplify_type(self.tcx, self_ty, TreatParams::AsPlaceholders) {
179+
self.impls_map.incoherent_impls.entry(simp).or_default().push(impl_def_id);
180+
} else {
181+
bug!("unexpected self type: {:?}", self_ty);
182+
}
135183
} else {
136184
struct_span_err!(
137185
self.tcx.sess,
@@ -153,9 +201,6 @@ impl<'tcx> InherentCollect<'tcx> {
153201
items: &[hir::ImplItemRef],
154202
span: Span,
155203
) {
156-
const INTO_CORE: &str = "consider moving this inherent impl into `core` if possible";
157-
const ADD_ATTR: &str =
158-
"alternatively add `#[rustc_allow_incoherent_impl]` to the relevant impl items";
159204
if !self.tcx.hir().rustc_coherence_is_core() {
160205
if self.tcx.features().rustc_attrs {
161206
for item in items {

library/core/src/ffi/c_str.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ use crate::str;
7777
#[derive(Hash)]
7878
#[cfg_attr(not(test), rustc_diagnostic_item = "CStr")]
7979
#[unstable(feature = "core_c_str", issue = "94079")]
80-
#[cfg_attr(not(bootstrap), lang = "CStr")]
80+
#[cfg_attr(not(bootstrap), rustc_has_incoherent_inherent_impls)]
8181
// FIXME:
8282
// `fn from` in `impl From<&CStr> for Box<CStr>` current implementation relies
8383
// on `CStr` being layout-compatible with `[u8]`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#![feature(rustc_attrs)]
2+
3+
#[rustc_has_incoherent_inherent_impls]
4+
pub struct StructWithAttr;
5+
pub struct StructNoAttr;
6+
7+
#[rustc_has_incoherent_inherent_impls]
8+
pub enum EnumWithAttr {}
9+
pub enum EnumNoAttr {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// aux-build:extern-crate.rs
2+
#![feature(rustc_attrs)]
3+
extern crate extern_crate;
4+
5+
impl extern_crate::StructWithAttr {
6+
//~^ ERROR cannot define inherent `impl` for a type outside of the crate
7+
fn foo() {}
8+
}
9+
impl extern_crate::StructWithAttr {
10+
#[rustc_allow_incoherent_impl]
11+
fn bar() {}
12+
}
13+
impl extern_crate::StructNoAttr {
14+
//~^ ERROR cannot define inherent `impl` for a type outside of the crate
15+
fn foo() {}
16+
}
17+
impl extern_crate::StructNoAttr {
18+
//~^ ERROR cannot define inherent `impl` for a type outside of the crate
19+
#[rustc_allow_incoherent_impl]
20+
fn bar() {}
21+
}
22+
impl extern_crate::EnumWithAttr {
23+
//~^ ERROR cannot define inherent `impl` for a type outside of the crate
24+
fn foo() {}
25+
}
26+
impl extern_crate::EnumWithAttr {
27+
#[rustc_allow_incoherent_impl]
28+
fn bar() {}
29+
}
30+
impl extern_crate::EnumNoAttr {
31+
//~^ ERROR cannot define inherent `impl` for a type outside of the crate
32+
fn foo() {}
33+
}
34+
impl extern_crate::EnumNoAttr {
35+
//~^ ERROR cannot define inherent `impl` for a type outside of the crate
36+
#[rustc_allow_incoherent_impl]
37+
fn bar() {}
38+
}
39+
40+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
error[E0390]: cannot define inherent `impl` for a type outside of the crate where the type is defined
2+
--> $DIR/needs-has-incoherent-impls.rs:5:1
3+
|
4+
LL | / impl extern_crate::StructWithAttr {
5+
LL | |
6+
LL | | fn foo() {}
7+
LL | | }
8+
| |_^
9+
|
10+
= help: consider moving this inherent impl into the crate defining the type if possible
11+
help: alternatively add `#[rustc_allow_incoherent_impl]` to the relevant impl items
12+
--> $DIR/needs-has-incoherent-impls.rs:7:5
13+
|
14+
LL | fn foo() {}
15+
| ^^^^^^^^^^^
16+
17+
error[E0390]: cannot define inherent `impl` for a type outside of the crate where the type is defined
18+
--> $DIR/needs-has-incoherent-impls.rs:13:1
19+
|
20+
LL | / impl extern_crate::StructNoAttr {
21+
LL | |
22+
LL | | fn foo() {}
23+
LL | | }
24+
| |_^
25+
|
26+
= help: consider moving this inherent impl into the crate defining the type if possible
27+
help: alternatively add `#[rustc_has_incoherent_inherent_impls]` to the type and `#[rustc_allow_incoherent_impl]` to the relevant impl items
28+
--> $DIR/needs-has-incoherent-impls.rs:13:1
29+
|
30+
LL | / impl extern_crate::StructNoAttr {
31+
LL | |
32+
LL | | fn foo() {}
33+
LL | | }
34+
| |_^
35+
36+
error[E0390]: cannot define inherent `impl` for a type outside of the crate where the type is defined
37+
--> $DIR/needs-has-incoherent-impls.rs:17:1
38+
|
39+
LL | / impl extern_crate::StructNoAttr {
40+
LL | |
41+
LL | | #[rustc_allow_incoherent_impl]
42+
LL | | fn bar() {}
43+
LL | | }
44+
| |_^
45+
|
46+
= help: consider moving this inherent impl into the crate defining the type if possible
47+
help: alternatively add `#[rustc_has_incoherent_inherent_impls]` to the type and `#[rustc_allow_incoherent_impl]` to the relevant impl items
48+
--> $DIR/needs-has-incoherent-impls.rs:17:1
49+
|
50+
LL | / impl extern_crate::StructNoAttr {
51+
LL | |
52+
LL | | #[rustc_allow_incoherent_impl]
53+
LL | | fn bar() {}
54+
LL | | }
55+
| |_^
56+
57+
error[E0390]: cannot define inherent `impl` for a type outside of the crate where the type is defined
58+
--> $DIR/needs-has-incoherent-impls.rs:22:1
59+
|
60+
LL | / impl extern_crate::EnumWithAttr {
61+
LL | |
62+
LL | | fn foo() {}
63+
LL | | }
64+
| |_^
65+
|
66+
= help: consider moving this inherent impl into the crate defining the type if possible
67+
help: alternatively add `#[rustc_allow_incoherent_impl]` to the relevant impl items
68+
--> $DIR/needs-has-incoherent-impls.rs:24:5
69+
|
70+
LL | fn foo() {}
71+
| ^^^^^^^^^^^
72+
73+
error[E0390]: cannot define inherent `impl` for a type outside of the crate where the type is defined
74+
--> $DIR/needs-has-incoherent-impls.rs:30:1
75+
|
76+
LL | / impl extern_crate::EnumNoAttr {
77+
LL | |
78+
LL | | fn foo() {}
79+
LL | | }
80+
| |_^
81+
|
82+
= help: consider moving this inherent impl into the crate defining the type if possible
83+
help: alternatively add `#[rustc_has_incoherent_inherent_impls]` to the type and `#[rustc_allow_incoherent_impl]` to the relevant impl items
84+
--> $DIR/needs-has-incoherent-impls.rs:30:1
85+
|
86+
LL | / impl extern_crate::EnumNoAttr {
87+
LL | |
88+
LL | | fn foo() {}
89+
LL | | }
90+
| |_^
91+
92+
error[E0390]: cannot define inherent `impl` for a type outside of the crate where the type is defined
93+
--> $DIR/needs-has-incoherent-impls.rs:34:1
94+
|
95+
LL | / impl extern_crate::EnumNoAttr {
96+
LL | |
97+
LL | | #[rustc_allow_incoherent_impl]
98+
LL | | fn bar() {}
99+
LL | | }
100+
| |_^
101+
|
102+
= help: consider moving this inherent impl into the crate defining the type if possible
103+
help: alternatively add `#[rustc_has_incoherent_inherent_impls]` to the type and `#[rustc_allow_incoherent_impl]` to the relevant impl items
104+
--> $DIR/needs-has-incoherent-impls.rs:34:1
105+
|
106+
LL | / impl extern_crate::EnumNoAttr {
107+
LL | |
108+
LL | | #[rustc_allow_incoherent_impl]
109+
LL | | fn bar() {}
110+
LL | | }
111+
| |_^
112+
113+
error: aborting due to 6 previous errors
114+
115+
For more information about this error, try `rustc --explain E0390`.

0 commit comments

Comments
 (0)