Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions compiler/rustc_attr_parsing/src/attributes/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,14 @@ impl NoArgsAttributeParser for RustcCoinductiveParser {
const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcCoinductive;
}

pub(crate) struct RustcAntiFundamentalParser;
impl NoArgsAttributeParser for RustcAntiFundamentalParser {
const PATH: &[Symbol] = &[sym::rustc_anti_fundamental];
const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[Allow(Target::Trait)]);
const STABILITY: AttributeStability = unstable!(rustc_attrs);
const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcAntiFundamental;
}

pub(crate) struct RustcAllowIncoherentImplParser;
impl NoArgsAttributeParser for RustcAllowIncoherentImplParser {
const PATH: &[Symbol] = &[sym::rustc_allow_incoherent_impl];
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_attr_parsing/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,7 @@ attribute_parsers!(
Single<WithoutArgs<RustcAllocatorParser>>,
Single<WithoutArgs<RustcAllocatorZeroedParser>>,
Single<WithoutArgs<RustcAllowIncoherentImplParser>>,
Single<WithoutArgs<RustcAntiFundamentalParser>>,
Single<WithoutArgs<RustcAsPtrParser>>,
Single<WithoutArgs<RustcCanonicalSymbolParser>>,
Single<WithoutArgs<RustcCaptureAnalysisParser>>,
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_feature/src/builtin_attrs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,7 @@ pub static BUILTIN_ATTRIBUTES: &[Symbol] = &[
sym::rustc_never_returns_null_ptr,
sym::rustc_no_implicit_autorefs,
sym::rustc_coherence_is_core,
sym::rustc_anti_fundamental,
sym::rustc_coinductive,
sym::rustc_comptime,
sym::rustc_allow_incoherent_impl,
Expand Down
6 changes: 6 additions & 0 deletions compiler/rustc_hir/src/attrs/data_structures.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1350,6 +1350,12 @@ pub enum AttributeKind {
/// Represents `#[rustc_allow_incoherent_impl]`.
RustcAllowIncoherentImpl(Span),

/// Represents `#[rustc_anti_fundamental]`. This marks a trait such that
/// `#[fundamental]` types (that are not local to the current crate) cannot
/// receive implementations of it. Used to reserve control over `Deref`,
/// `DispatchFromDyn`, etc. on fundamental wrappers like `Box` and `Pin`.
RustcAntiFundamental,

/// Represents `#[rustc_as_ptr]` (used by the `dangling_pointers_from_temporaries` lint).
RustcAsPtr,

Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_hir/src/attrs/encode_cross_crate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ impl AttributeKind {
RustcAllocatorZeroedVariant { .. } => Yes,
RustcAllowConstFnUnstable(..) => No,
RustcAllowIncoherentImpl(..) => No,
RustcAntiFundamental => Yes,
RustcAsPtr => Yes,
RustcAutodiff(..) => Yes,
RustcBodyStability { .. } => No,
Expand Down
15 changes: 15 additions & 0 deletions compiler/rustc_hir_analysis/src/coherence/orphan.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,10 @@ pub(crate) fn orphan_check_impl(
OrphanCheckErr::NonLocalInputType(_) => {
bug!("orphanck: shouldn't've gotten non-local input tys in compat mode")
}
OrphanCheckErr::AntiFundamentalForeignType(_) => {
// Anti-fundamental violations are always hard errors.
return Err(emit_orphan_check_error(tcx, trait_ref, impl_def_id, err));
}
},
Err(err) => return Err(emit_orphan_check_error(tcx, trait_ref, impl_def_id, err)),
},
Expand Down Expand Up @@ -378,6 +382,9 @@ fn orphan_check<'tcx>(
});
OrphanCheckErr::NonLocalInputType(tys)
}
OrphanCheckErr::AntiFundamentalForeignType(ty) => {
OrphanCheckErr::AntiFundamentalForeignType(infcx.resolve_vars_if_possible(ty))
}
})
}

Expand Down Expand Up @@ -488,6 +495,14 @@ fn emit_orphan_check_error<'tcx>(
}
guar.unwrap()
}
traits::OrphanCheckErr::AntiFundamentalForeignType(ty) => {
let span = tcx.def_span(impl_def_id);
tcx.dcx().emit_err(diagnostics::AntiFundamentalForeignImpl {
span,
trait_name: tcx.def_path_str(trait_ref.def_id),
fundamental_ty: ty,
})
}
}
}

Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_hir_analysis/src/collect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -979,6 +979,7 @@ fn trait_def(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::TraitDef {

let deny_explicit_impl = find_attr!(attrs, RustcDenyExplicitImpl);
let force_dyn_incompatible = find_attr!(attrs, RustcDynIncompatibleTrait(span) => *span);
let is_anti_fundamental = find_attr!(attrs, RustcAntiFundamental);

ty::TraitDef {
def_id: def_id.to_def_id(),
Expand All @@ -996,6 +997,7 @@ fn trait_def(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::TraitDef {
must_implement_one_of,
force_dyn_incompatible,
deny_explicit_impl,
is_anti_fundamental,
}
}

Expand Down
14 changes: 14 additions & 0 deletions compiler/rustc_hir_analysis/src/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2045,3 +2045,17 @@ pub(crate) struct OnlyStructsCanBeViewedAdt<'tcx> {
pub article: &'static str,
pub kind: &'static str,
}

#[derive(Diagnostic)]
#[diag("cannot implement `{$trait_name}` on the fundamental type `{$fundamental_ty}`")]
#[note(
"`{$trait_name}` is marked `#[rustc_anti_fundamental]`, which means it \
cannot be implemented on `#[fundamental]` types from another crate"
)]
pub(crate) struct AntiFundamentalForeignImpl<'tcx> {
#[primary_span]
#[label("impl of `{$trait_name}` not allowed on `{$fundamental_ty}`")]
pub(crate) span: Span,
pub(crate) trait_name: String,
pub(crate) fundamental_ty: Ty<'tcx>,
}
4 changes: 4 additions & 0 deletions compiler/rustc_middle/src/ty/context/impl_interner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -613,6 +613,10 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
self.trait_def(def_id).is_fundamental
}

fn trait_is_anti_fundamental(self, def_id: DefId) -> bool {
self.trait_def(def_id).is_anti_fundamental
}

fn trait_is_unsafe(self, trait_def_id: Self::DefId) -> bool {
self.trait_def(trait_def_id).safety.is_unsafe()
}
Expand Down
5 changes: 5 additions & 0 deletions compiler/rustc_middle/src/ty/trait_def.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,11 @@ pub struct TraitDef {
/// This only applies to built-in traits, and is marked via
/// `#[rustc_deny_explicit_impl]`.
pub deny_explicit_impl: bool,

/// If `true`, then this trait has the `#[rustc_anti_fundamental]` attribute.
/// This prevents non-local `#[fundamental]` types from receiving impls of
/// this trait. Used for `Deref`, `DispatchFromDyn`, `CoerceUnsized`, etc.
pub is_anti_fundamental: bool,
}

/// Whether this trait is treated specially by the standard library
Expand Down
40 changes: 40 additions & 0 deletions compiler/rustc_next_trait_solver/src/coherence.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,9 @@ impl From<bool> for IsFirstInputType {
pub enum OrphanCheckErr<I: Interner, T> {
NonLocalInputType(Vec<(I::Ty, IsFirstInputType)>),
UncoveredTyParams(UncoveredTyParams<I, T>),
/// The trait is `#[rustc_anti_fundamental]` and the Self type's head is a
/// non-local `#[fundamental]` type. `fundamental_ty` is the offending type.
AntiFundamentalForeignType(I::Ty),
}

#[derive_where(Debug; I: Interner, T: Debug)]
Expand Down Expand Up @@ -216,6 +219,13 @@ pub struct UncoveredTyParams<I: Interner, T> {
/// the above requirement is sufficient, and is necessary in "open world"
/// cases).
///
/// In addition to the orphan rules above, this also enforces
/// `#[rustc_anti_fundamental]`: when the trait carries that attribute, an impl
/// whose `Self` type has a non-local `#[fundamental]` type at its head is
/// rejected (in `InCrate::Local` mode). This lets the standard library reserve
/// control over traits like `Deref` and `DispatchFromDyn` on fundamental
/// wrappers such as `Box` and `Pin`.
///
/// Note that this function is never called for types that have both type
/// parameters and inference variables.
#[instrument(level = "trace", skip(infcx, lazily_normalize_ty), ret)]
Expand All @@ -234,6 +244,20 @@ where
panic!("orphan check only expects inference variables: {trait_ref:?}");
}

// Anti-fundamental check: if the trait is marked `#[rustc_anti_fundamental]`,
// reject impls where the head of the Self type is a non-local fundamental type.
// This prevents downstream crates from implementing traits like `Deref` on
// fundamental wrappers like `Box` or `Pin`.
if matches!(in_crate, InCrate::Local { .. }) {
let cx = infcx.cx();
if cx.trait_is_anti_fundamental(trait_ref.def_id) {
let self_ty = infcx.shallow_resolve(trait_ref.self_ty());
if let Some(err_ty) = check_anti_fundamental_head::<I>(self_ty) {
return Ok(Err(OrphanCheckErr::AntiFundamentalForeignType(err_ty)));
}
}
}

let mut checker = OrphanChecker::new(infcx, in_crate, lazily_normalize_ty);
Ok(match trait_ref.visit_with(&mut checker) {
ControlFlow::Continue(()) => Err(OrphanCheckErr::NonLocalInputType(checker.non_local_tys)),
Expand All @@ -256,6 +280,22 @@ where
})
}

/// Checks the head of the Self type for a non-local fundamental type.
/// If the head is a reference (`&`/`&mut`), unwrap and check again (references are fundamental).
/// Returns `Some(ty)` with the offending fundamental type if the check fails.
fn check_anti_fundamental_head<I: Interner>(mut ty: I::Ty) -> Option<I::Ty> {
// Unwrap through references (which are fundamental but undocumented as such).
while let ty::Ref(_, inner, _) = ty.kind() {
ty = inner;
}

// The head is offending only if it is a fundamental ADT that is not local to the
// current crate. All other types are fine — they're either local, non-fundamental,
// or primitive (which can't be fundamental).
matches!(ty.kind(), ty::Adt(def, _) if def.is_fundamental() && !def.def_id().is_local())
.then_some(ty)
}

struct OrphanChecker<'a, Infcx, I: Interner, F> {
infcx: &'a Infcx,
in_crate: InCrate,
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_passes/src/check_attr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
AttributeKind::RustcAllocatorZeroed => (),
AttributeKind::RustcAllocatorZeroedVariant { .. } => (),
AttributeKind::RustcAllowIncoherentImpl(..) => (),
AttributeKind::RustcAntiFundamental => (),
AttributeKind::RustcAsPtr => (),
AttributeKind::RustcAutodiff(..) => (),
AttributeKind::RustcBodyStability { .. } => (),
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_span/src/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,6 @@ symbols! {
PartialEq,
PartialOrd,
Pending,
PinDerefMutHelper,
PinMacroHelper,
Pointer,
Poll,
Expand Down Expand Up @@ -1762,6 +1761,7 @@ symbols! {
rustc_allow_const_fn_unstable,
rustc_allow_incoherent_impl,
rustc_allowed_through_unstable_modules,
rustc_anti_fundamental,
rustc_as_ptr,
rustc_attrs,
rustc_autodiff,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4168,24 +4168,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
// can do about it. As far as they are concerned, `?` is compiler magic.
return;
}
if tcx.is_diagnostic_item(sym::PinDerefMutHelper, parent_def_id) {
let parent_predicate =
self.resolve_vars_if_possible(data.derived.parent_trait_pred);

// Skip PinDerefMutHelper in suggestions, but still show downstream suggestions.
ensure_sufficient_stack(|| {
self.note_obligation_cause_code(
body_def_id,
err,
parent_predicate,
param_env,
&data.derived.parent_code,
obligated_types,
seen_requirements,
)
});
return;
}
let self_ty_str =
tcx.short_string(parent_trait_pred.skip_binder().self_ty(), err.long_ty_path());
let trait_name = tcx.short_string(
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_type_ir/src/interner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -435,6 +435,8 @@ pub trait Interner:

fn trait_is_fundamental(self, def_id: Self::TraitId) -> bool;

fn trait_is_anti_fundamental(self, def_id: Self::TraitId) -> bool;

/// Returns `true` if this is an `unsafe trait`.
fn trait_is_unsafe(self, trait_def_id: Self::TraitId) -> bool;

Expand Down
3 changes: 3 additions & 0 deletions library/core/src/ops/deref.rs
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ use crate::marker::PointeeSized;
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_diagnostic_item = "Deref"]
#[rustc_const_unstable(feature = "const_convert", issue = "143773")]
#[rustc_anti_fundamental]
pub const trait Deref: PointeeSized {
/// The resulting type after dereferencing.
#[stable(feature = "rust1", since = "1.0.0")]
Expand Down Expand Up @@ -267,6 +268,7 @@ const impl<T: ?Sized> Deref for &mut T {
#[doc(alias = "*")]
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_const_unstable(feature = "const_convert", issue = "143773")]
#[rustc_anti_fundamental]
pub const trait DerefMut: [const] Deref + PointeeSized {
/// Mutably dereferences the value.
#[stable(feature = "rust1", since = "1.0.0")]
Expand Down Expand Up @@ -367,6 +369,7 @@ unsafe impl<T: ?Sized> DerefPure for &mut T {}
/// ```
#[lang = "receiver"]
#[unstable(feature = "arbitrary_self_types", issue = "44874")]
#[rustc_anti_fundamental]
pub trait Receiver: PointeeSized {
/// The target type on which the method may be called.
#[rustc_diagnostic_item = "receiver_target"]
Expand Down
2 changes: 2 additions & 0 deletions library/core/src/ops/unsize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ use crate::marker::{PointeeSized, Unsize};
/// [nomicon-coerce]: ../../nomicon/coercions.html
#[unstable(feature = "coerce_unsized", issue = "18598")]
#[lang = "coerce_unsized"]
#[rustc_anti_fundamental]
pub trait CoerceUnsized<T: PointeeSized>: Sized {
// Empty.
}
Expand Down Expand Up @@ -119,6 +120,7 @@ impl<T: PointeeSized + Unsize<U>, U: PointeeSized> CoerceUnsized<*const U> for *
/// [^1]: Formerly known as *object safety*.
#[unstable(feature = "dispatch_from_dyn", issue = "none")]
#[lang = "dispatch_from_dyn"]
#[rustc_anti_fundamental]
pub trait DispatchFromDyn<T>: Sized {
// Empty.
}
Expand Down
71 changes: 2 additions & 69 deletions library/core/src/pin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1687,84 +1687,17 @@ const impl<Ptr: [const] Deref> Deref for Pin<Ptr> {
}
}

mod helper {
/// Helper that prevents downstream crates from implementing `DerefMut` for `Pin`.
///
/// The `Pin` type implements the unsafe trait `PinCoerceUnsized`, which essentially requires
/// that the type does not have a malicious `Deref` or `DerefMut` impl. However, without this
/// helper module, downstream crates are able to write `impl DerefMut for Pin<LocalType>` as
/// long as it does not overlap with the impl provided by stdlib. This is because `Pin` is
/// `#[fundamental]`, so stdlib promises to never implement traits for `Pin` that it does not
/// implement today.
///
/// However, this is problematic. Downstream crates could implement `DerefMut` for
/// `Pin<&LocalType>`, and they could do so maliciously. To prevent this, the implementation for
/// `Pin` delegates to this helper module. Since `helper::Pin` is not `#[fundamental]`, the
/// orphan rules assume that stdlib might implement `helper::DerefMut` for `helper::Pin<&_>` in
/// the future. Because of this, downstream crates can no longer provide an implementation of
/// `DerefMut` for `Pin<&_>`, as it might overlap with a trait impl that, according to the
/// orphan rules, the stdlib could introduce without a breaking change in a future release.
///
/// See <https://github.com/rust-lang/rust/issues/85099> for the issue this fixes.
#[repr(transparent)]
#[unstable(feature = "pin_derefmut_internals", issue = "none")]
#[allow(missing_debug_implementations)]
pub struct PinHelper<Ptr> {
pointer: Ptr,
}

#[unstable(feature = "pin_derefmut_internals", issue = "none")]
#[rustc_const_unstable(feature = "const_convert", issue = "143773")]
#[rustc_diagnostic_item = "PinDerefMutHelper"]
pub const trait PinDerefMutHelper {
type Target: ?Sized;
fn deref_mut(&mut self) -> &mut Self::Target;
}

#[unstable(feature = "pin_derefmut_internals", issue = "none")]
#[rustc_const_unstable(feature = "const_convert", issue = "143773")]
const impl<Ptr: [const] super::DerefMut> PinDerefMutHelper for PinHelper<Ptr>
where
Ptr::Target: crate::marker::Unpin,
{
type Target = Ptr::Target;

#[inline(always)]
fn deref_mut(&mut self) -> &mut Ptr::Target {
&mut self.pointer
}
}
}

#[stable(feature = "pin", since = "1.33.0")]
#[rustc_const_unstable(feature = "const_convert", issue = "143773")]
#[cfg(not(doc))]
const impl<Ptr> DerefMut for Pin<Ptr>
where
Ptr: [const] Deref,
helper::PinHelper<Ptr>: [const] helper::PinDerefMutHelper<Target = Self::Target>,
{
#[inline]
fn deref_mut(&mut self) -> &mut Ptr::Target {
// SAFETY: Pin and PinHelper have the same layout, so this is equivalent to
// `&mut self.pointer` which is safe because `Target: Unpin`.
helper::PinDerefMutHelper::deref_mut(unsafe {
&mut *(self as *mut Pin<Ptr> as *mut helper::PinHelper<Ptr>)
})
}
}

/// The `Target` type is restricted to `Unpin` types as it's not safe to obtain a mutable reference
/// to a pinned value.
///
/// For soundness reasons, implementations of `DerefMut` for `Pin<T>` are rejected even when `T` is
/// a local type not covered by this impl block. (Since `Pin` is [fundamental], such implementations
/// would normally be possible.)
/// would normally be possible.) This is enforced by the `#[rustc_anti_fundamental]` attribute on
/// the `DerefMut` trait.
///
/// [fundamental]: ../../reference/items/implementations.html#r-items.impl.trait.fundamental
#[stable(feature = "pin", since = "1.33.0")]
#[rustc_const_unstable(feature = "const_convert", issue = "143773")]
#[cfg(doc)]
const impl<Ptr> DerefMut for Pin<Ptr>
where
Ptr: [const] DerefMut,
Expand Down
Loading
Loading