Skip to content

Commit 209dd2c

Browse files
committed
generalize "incoherent impls" impl for custom types
1 parent 12d3f10 commit 209dd2c

File tree

7 files changed

+85
-18
lines changed

7 files changed

+85
-18
lines changed

compiler/rustc_feature/src/builtin_attrs.rs

+5
Original file line numberDiff line numberDiff line change
@@ -644,6 +644,11 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
644644
rustc_allow_incoherent_impl, AttributeType::Normal, template!(Word), ErrorFollowing,
645645
"#[rustc_allow_incoherent_impl] has to be added to all impl items of an incoherent inherent impl."
646646
),
647+
rustc_attr!(
648+
rustc_has_incoherent_inherent_impls, AttributeType::Normal, template!(Word), ErrorFollowing,
649+
"#[rustc_has_incoherent_inherent_impls] allows the addition of incoherent inherent impls for \
650+
the given type by annotating all impl items with #[rustc_allow_incoherent_impl]."
651+
),
647652
BuiltinAttribute {
648653
name: sym::rustc_diagnostic_item,
649654
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
@@ -124,6 +124,9 @@ impl CheckAttrVisitor<'_> {
124124
sym::rustc_allow_incoherent_impl => {
125125
self.check_allow_incoherent_impl(&attr, span, target)
126126
}
127+
sym::rustc_has_incoherent_inherent_impls => {
128+
self.check_has_incoherent_inherent_impls(&attr, span, target)
129+
}
127130
sym::rustc_const_unstable
128131
| sym::rustc_const_stable
129132
| sym::unstable
@@ -1095,7 +1098,6 @@ impl CheckAttrVisitor<'_> {
10951098
}
10961099
}
10971100

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

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

compiler/rustc_span/src/symbol.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1187,6 +1187,7 @@ symbols! {
11871187
rustc_error,
11881188
rustc_evaluate_where_clauses,
11891189
rustc_expected_cgu_reuse,
1190+
rustc_has_incoherent_inherent_impls,
11901191
rustc_if_this_changed,
11911192
rustc_inherit_overflow_checks,
11921193
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

+43-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,52 @@ 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: &str =
126+
"alternatively add `#[rustc_allow_incoherent_impl]` to the relevant impl items";
127+
127128
impl<'tcx> InherentCollect<'tcx> {
128-
fn check_def_id(&mut self, item: &hir::Item<'_>, def_id: DefId) {
129+
fn check_def_id(&mut self, item: &hir::Item<'_>, self_ty: Ty<'tcx>, def_id: DefId) {
130+
let impl_def_id = item.def_id;
129131
if let Some(def_id) = def_id.as_local() {
130132
// Add the implementation to the mapping from implementation to base
131133
// type def ID, if there is a base type for this implementation and
132134
// the implementation does not have any associated traits.
133135
let vec = self.impls_map.inherent_impls.entry(def_id).or_default();
134-
vec.push(item.def_id.to_def_id());
136+
vec.push(impl_def_id.to_def_id());
137+
return;
138+
}
139+
140+
if self.tcx.features().rustc_attrs
141+
&& self.tcx.has_attr(def_id, sym::rustc_has_incoherent_inherent_impls)
142+
{
143+
let hir::ItemKind::Impl(hir::Impl { items, .. }) = item.kind else {
144+
bug!("expected `impl` item: {:?}", item);
145+
};
146+
147+
for item in items {
148+
if !self.tcx.has_attr(item.id.def_id.to_def_id(), sym::rustc_allow_incoherent_impl)
149+
{
150+
struct_span_err!(
151+
self.tcx.sess,
152+
item.span,
153+
E0390,
154+
"cannot define inherent `impl` for a type outside of crate where the type is defined",
155+
)
156+
.help(INTO_DEFINING_CRATE)
157+
.span_help(item.span, ADD_ATTR)
158+
.emit();
159+
return;
160+
}
161+
}
162+
163+
if let Some(simp) = simplify_type(self.tcx, self_ty, TreatParams::AsPlaceholders) {
164+
self.impls_map.incoherent_impls.entry(simp).or_default().push(impl_def_id);
165+
} else {
166+
bug!("unexpected self type: {:?}", self_ty);
167+
}
135168
} else {
136169
struct_span_err!(
137170
self.tcx.sess,
@@ -153,9 +186,6 @@ impl<'tcx> InherentCollect<'tcx> {
153186
items: &[hir::ImplItemRef],
154187
span: Span,
155188
) {
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";
159189
if !self.tcx.hir().rustc_coherence_is_core() {
160190
if self.tcx.features().rustc_attrs {
161191
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]`.

0 commit comments

Comments
 (0)