From bc98c080e185c2d959057dbbf7d0c45a3aa36825 Mon Sep 17 00:00:00 2001 From: Clara Engler Date: Sat, 3 Jan 2026 17:27:40 +0100 Subject: [PATCH] Allow incoherent trait implementations This commit adds two new attributes to rustc: * `#[rustc_has_incoherent_trait_impls]` * `#[rustc_allow_incoherent_trait_impl]` They are intended to behave similar to the `#[rustc_has_incoherent_inherent_impls]` pedants and serve the purpose to allow the implementation of incoherent trait implementations in the standard library. According to the Rust reference, "a trait is considered incoherent if either the orphan rules check fails or there are overlapping implementation instances". For now, this patch only circumvents orphan rule checks, hence why it is still a draft and because to my understanding, the problem of overlapping implementation instances is a larger one (see specializations). Of course, circumventing the orphan rule is generally a **bad idea**, as it is to circumvent the coherence for inherent implementations. But just like the incoherence for inherent implementations, there are valid use-cases in the hacking with the standard library that would justify its use. Personally, my motivation for this patch stems from rust-lang/libs-team#519, which describes a proposal for moving `ToOwned` from `alloc` to `core` and was theoretically accepted by libs team but ultimately dropped due to `ToOwned` implementations for types defined in `core` with associated types defined in `alloc`, thereby making it effectively impossible. I would like to keep continue working on this, but for this, a modification to `rustc` would probably be necessary. --- .../src/attributes/traits.rs | 12 ++++++++ compiler/rustc_attr_parsing/src/context.rs | 9 +++--- compiler/rustc_feature/src/builtin_attrs.rs | 10 +++++++ .../rustc_hir/src/attrs/data_structures.rs | 3 ++ .../rustc_hir/src/attrs/encode_cross_crate.rs | 1 + .../src/coherence/orphan.rs | 30 ++++++++++++++++++- compiler/rustc_passes/messages.ftl | 4 +++ compiler/rustc_passes/src/check_attr.rs | 16 ++++++++++ compiler/rustc_passes/src/errors.rs | 9 ++++++ compiler/rustc_span/src/symbol.rs | 2 ++ 10 files changed, 91 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/attributes/traits.rs b/compiler/rustc_attr_parsing/src/attributes/traits.rs index ee5895a6efd0f..ef27fd60dce10 100644 --- a/compiler/rustc_attr_parsing/src/attributes/traits.rs +++ b/compiler/rustc_attr_parsing/src/attributes/traits.rs @@ -139,6 +139,18 @@ impl NoArgsAttributeParser for AllowIncoherentImplParser { const CREATE: fn(Span) -> AttributeKind = AttributeKind::AllowIncoherentImpl; } +pub(crate) struct AllowIncoherentTraitImplParser; +impl NoArgsAttributeParser for AllowIncoherentTraitImplParser { + const PATH: &[Symbol] = &[sym::rustc_allow_incoherent_trait_impl]; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ + Allow(Target::Method(MethodKind::TraitImpl)), + Allow(Target::AssocConst), + Allow(Target::AssocTy), + ]); + const CREATE: fn(Span) -> AttributeKind = AttributeKind::AllowIncoherentTraitImpl; +} + pub(crate) struct FundamentalParser; impl NoArgsAttributeParser for FundamentalParser { const PATH: &[Symbol] = &[sym::fundamental]; diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index b85bb6c6c89d4..d4bf295d5181f 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -76,10 +76,10 @@ use crate::attributes::stability::{ }; use crate::attributes::test_attrs::{IgnoreParser, ShouldPanicParser}; use crate::attributes::traits::{ - AllowIncoherentImplParser, CoinductiveParser, DenyExplicitImplParser, - DoNotImplementViaObjectParser, FundamentalParser, MarkerParser, ParenSugarParser, - PointeeParser, SkipDuringMethodDispatchParser, SpecializationTraitParser, TypeConstParser, - UnsafeSpecializationMarkerParser, + AllowIncoherentImplParser, AllowIncoherentTraitImplParser, CoinductiveParser, + DenyExplicitImplParser, DoNotImplementViaObjectParser, FundamentalParser, MarkerParser, + ParenSugarParser, PointeeParser, SkipDuringMethodDispatchParser, SpecializationTraitParser, + TypeConstParser, UnsafeSpecializationMarkerParser, }; use crate::attributes::transparency::TransparencyParser; use crate::attributes::{AttributeParser as _, Combine, Single, WithoutArgs}; @@ -231,6 +231,7 @@ attribute_parsers!( Single, Single, Single>, + Single>, Single>, Single>, Single>, diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index a7e8515e415f0..5a476e981ed73 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -1308,6 +1308,10 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ rustc_allow_incoherent_impl, AttributeType::Normal, template!(Word), ErrorFollowing, EncodeCrossCrate::No, "`#[rustc_allow_incoherent_impl]` has to be added to all impl items of an incoherent inherent impl." ), + rustc_attr!( + rustc_allow_incoherent_trait_impl, AttributeType::Normal, template!(Word), ErrorFollowing, EncodeCrossCrate::No, + "`#[rustc_allow_incoherent_trait_impl]` has to be added to all impl items of an incoherent trait impl." + ), rustc_attr!( rustc_preserve_ub_checks, AttributeType::CrateLevel, template!(Word), ErrorFollowing, EncodeCrossCrate::No, "`#![rustc_preserve_ub_checks]` prevents the designated crate from evaluating whether UB checks are enabled when optimizing MIR", @@ -1335,6 +1339,12 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ "`#[rustc_has_incoherent_inherent_impls]` allows the addition of incoherent inherent impls for \ the given type by annotating all impl items with `#[rustc_allow_incoherent_impl]`." ), + rustc_attr!( + rustc_has_incoherent_trait_impls, AttributeType::Normal, template!(Word), + ErrorFollowing, EncodeCrossCrate::Yes, + "`#[rustc_has_incoherent_trait_impls]` allows the addition of incoherent trait impls for \ + the given trait by annotating all impl items with `#[rustc_allow_incoherent_trait_impl]`." + ), BuiltinAttribute { name: sym::rustc_diagnostic_item, diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index f418c391ece7a..44fcf454ff42d 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -694,6 +694,9 @@ pub enum AttributeKind { /// Represents `#[rustc_allow_incoherent_impl]`. AllowIncoherentImpl(Span), + /// Represents `#[rustc_allow_incoherent_trait_impl]`. + AllowIncoherentTraitImpl(Span), + /// Represents `#[allow_internal_unsafe]`. AllowInternalUnsafe(Span), diff --git a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs index 57d2d6c5875ed..3505fff8eb5b9 100644 --- a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs +++ b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs @@ -21,6 +21,7 @@ impl AttributeKind { Align { .. } => No, AllowConstFnUnstable(..) => No, AllowIncoherentImpl(..) => No, + AllowIncoherentTraitImpl(..) => No, AllowInternalUnsafe(..) => Yes, AllowInternalUnstable(..) => Yes, AsPtr(..) => Yes, diff --git a/compiler/rustc_hir_analysis/src/coherence/orphan.rs b/compiler/rustc_hir_analysis/src/coherence/orphan.rs index f1e138dbcb97a..25c9106ce6db9 100644 --- a/compiler/rustc_hir_analysis/src/coherence/orphan.rs +++ b/compiler/rustc_hir_analysis/src/coherence/orphan.rs @@ -3,6 +3,8 @@ use rustc_data_structures::fx::FxIndexSet; use rustc_errors::ErrorGuaranteed; +use rustc_hir::attrs::AttributeKind; +use rustc_hir::find_attr; use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt, TyCtxtInferExt}; use rustc_lint_defs::builtin::UNCOVERED_PARAM_IN_PROJECTION; use rustc_middle::ty::{ @@ -10,6 +12,7 @@ use rustc_middle::ty::{ }; use rustc_middle::{bug, span_bug}; use rustc_span::def_id::{DefId, LocalDefId}; +use rustc_span::sym; use rustc_trait_selection::traits::{ self, IsFirstInputType, OrphanCheckErr, OrphanCheckMode, UncoveredTyParams, }; @@ -36,7 +39,32 @@ pub(crate) fn orphan_check_impl( bug!("orphanck: shouldn't've gotten non-local input tys in compat mode") } }, - Err(err) => return Err(emit_orphan_check_error(tcx, trait_ref, impl_def_id, err)), + Err(err) => { + // The orphan check failed fully, see if we may tolerate it ... + if tcx.has_attr(trait_ref.def_id, sym::rustc_has_incoherent_trait_impls) { + // `#[rustc_has_incoherent_trait_impls]` was specified, + // meaning we tolerate a violation here; now ensure that all + // associated methods, types, and constants have the + // `#[rustc_allow_incoherent_trait_impl]` attribute. + let items = tcx.associated_item_def_ids(impl_def_id); + for item in items { + if !find_attr!( + tcx.get_all_attrs(*item), + AttributeKind::AllowIncoherentTraitImpl(_) + ) { + // Missing `#[rustc_allow_incoherent_trait_impl]`. + return Err(emit_orphan_check_error(tcx, trait_ref, impl_def_id, err)); + } + + // Item is fine, continue. + } + // All items are fine, let's tolerate. + } else { + // We do not tolerate orphan violations, usually the default + // except for some standard library hacks. + return Err(emit_orphan_check_error(tcx, trait_ref, impl_def_id, err)); + } + } }, } diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl index 94996c0adb470..a17d036876b76 100644 --- a/compiler/rustc_passes/messages.ftl +++ b/compiler/rustc_passes/messages.ftl @@ -248,6 +248,10 @@ passes_has_incoherent_inherent_impl = `rustc_has_incoherent_inherent_impls` attribute should be applied to types or traits .label = only adts, extern types and traits are supported +passes_has_incoherent_trait_impl = + `rustc_has_incoherent_trait_impls` attribute should be applied to traits + .label = only traits are supported + passes_ignored_derived_impls = `{$name}` has {$trait_list_len -> [one] a derived impl diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 2370a7d1dd5c6..8df4b326bd7e7 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -246,6 +246,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | AttributeKind::UnsafeSpecializationMarker(..) | AttributeKind::ParenSugar(..) | AttributeKind::AllowIncoherentImpl(..) + | AttributeKind::AllowIncoherentTraitImpl(..) | AttributeKind::Confusables { .. } | AttributeKind::TypeConst{..} // `#[doc]` is actually a lot more than just doc comments, so is checked below @@ -326,6 +327,9 @@ impl<'tcx> CheckAttrVisitor<'tcx> { [sym::rustc_has_incoherent_inherent_impls, ..] => { self.check_has_incoherent_inherent_impls(attr, span, target) } + [sym::rustc_has_incoherent_trait_impls, ..] => { + self.check_has_incoherent_trait_impls(attr, span, target) + } [sym::autodiff_forward, ..] | [sym::autodiff_reverse, ..] => { self.check_autodiff(hir_id, attr, span, target) } @@ -1169,6 +1173,18 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } } + /// Checks if `#[rustc_has_incoherent_trait_impls]` is applied to a trait. + fn check_has_incoherent_trait_impls(&self, attr: &Attribute, span: Span, target: Target) { + match target { + Target::Trait => {} + _ => { + self.tcx + .dcx() + .emit_err(errors::HasIncoherentTraitImpl { attr_span: attr.span(), span }); + } + } + } + fn check_ffi_pure(&self, attr_span: Span, attrs: &[Attribute]) { if find_attr!(attrs, AttributeKind::FfiConst(_)) { // `#[ffi_const]` functions cannot be `#[ffi_pure]` diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index af5cb29b83d04..b193b6f6ec49b 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -196,6 +196,15 @@ pub(crate) struct HasIncoherentInherentImpl { pub span: Span, } +#[derive(Diagnostic)] +#[diag(passes_has_incoherent_trait_impl)] +pub(crate) struct HasIncoherentTraitImpl { + #[primary_span] + pub attr_span: Span, + #[label] + pub span: Span, +} + #[derive(Diagnostic)] #[diag(passes_both_ffi_const_and_pure, code = E0757)] pub(crate) struct BothFfiConstAndPure { diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 72709753b1dff..69439a45e57f8 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1912,6 +1912,7 @@ symbols! { rustc_allocator_zeroed_variant, rustc_allow_const_fn_unstable, rustc_allow_incoherent_impl, + rustc_allow_incoherent_trait_impl, rustc_allowed_through_unstable_modules, rustc_as_ptr, rustc_attrs, @@ -1951,6 +1952,7 @@ symbols! { rustc_expected_cgu_reuse, rustc_force_inline, rustc_has_incoherent_inherent_impls, + rustc_has_incoherent_trait_impls, rustc_hidden_type_of_opaques, rustc_if_this_changed, rustc_inherit_overflow_checks,