From b2d604c9085f718aa0667740c554374eb4ca783e Mon Sep 17 00:00:00 2001 From: Dnreikronos Date: Fri, 12 Jun 2026 16:55:44 -0300 Subject: [PATCH] Expand free alias types in the auto-trait orphan check The auto-trait branch of the orphan check inspected the unexpanded self type, so an impl whose self type was a free (lazy) type alias was rejected even when the alias resolved to an otherwise valid nominal type, e.g. `unsafe impl Sync for Alias {}` with `type Alias = Local;`. Expand free alias types before classifying the self type, mirroring the locality check above. Expansion still reveals genuinely problematic self types (trait objects, opaque types, type parameters), so those keep being rejected. --- .../src/coherence/orphan.rs | 8 ++- tests/ui/lazy-type-alias/auto-trait-impls.rs | 53 +++++++++++++++++++ .../lazy-type-alias/auto-trait-impls.stderr | 17 ++++++ .../impl_for_weak_alias.rs | 6 ++- .../impl_for_weak_alias.stderr | 11 ---- 5 files changed, 82 insertions(+), 13 deletions(-) create mode 100644 tests/ui/lazy-type-alias/auto-trait-impls.rs create mode 100644 tests/ui/lazy-type-alias/auto-trait-impls.stderr delete mode 100644 tests/ui/type-alias-impl-trait/impl_for_weak_alias.stderr diff --git a/compiler/rustc_hir_analysis/src/coherence/orphan.rs b/compiler/rustc_hir_analysis/src/coherence/orphan.rs index bacc1f1549973..96f657f0963ce 100644 --- a/compiler/rustc_hir_analysis/src/coherence/orphan.rs +++ b/compiler/rustc_hir_analysis/src/coherence/orphan.rs @@ -83,7 +83,13 @@ pub(crate) fn orphan_check_impl( ); if tcx.trait_is_auto(trait_def_id) { - let self_ty = trait_ref.self_ty(); + // Expand free alias types (e.g. lazy type aliases) so that the checks below operate + // on the type the alias resolves to rather than the alias itself. Otherwise an impl + // whose self type is an alias expanding to an otherwise valid nominal type would be + // wrongly rejected, e.g. `unsafe impl Sync for Alias {}` where `type Alias = Local;`. + // Expansion also reveals genuinely problematic self types (trait objects, opaque + // types, type parameters), so those keep being rejected. See issue #157756. + let self_ty = tcx.expand_free_alias_tys(trait_ref.self_ty()); // If the impl is in the same crate as the auto-trait, almost anything // goes. diff --git a/tests/ui/lazy-type-alias/auto-trait-impls.rs b/tests/ui/lazy-type-alias/auto-trait-impls.rs new file mode 100644 index 0000000000000..e5d9c988f0d5f --- /dev/null +++ b/tests/ui/lazy-type-alias/auto-trait-impls.rs @@ -0,0 +1,53 @@ +//! Auto-trait impls whose self type is a free (lazy) type alias are checked against the +//! type the alias *expands* to, not the alias itself. An alias resolving to an otherwise +//! valid nominal type is accepted, just as if the underlying type had been written +//! directly; an alias resolving to a problematic type is still rejected. +//! +//! Regression test for . + +#![feature(lazy_type_alias, auto_traits, negative_impls)] +#![allow(incomplete_features)] + +struct Local; + +auto trait Marker {} + +// Aliases expanding to a local nominal type are accepted for both a cross-crate auto trait +// (`Sync`) and a local one (`Marker`), exactly as if `Local` had been named directly. +type ToLocal = Local; +unsafe impl Sync for ToLocal {} +impl Marker for ToLocal {} + +// Nested aliases are expanded transitively, so an alias chain ending in a local nominal type +// is accepted as well. +struct Inner; +type Mid = Inner; +type Nested = Mid; +impl Marker for Nested {} + +// A generic alias instantiated with a local nominal type is accepted. +struct Generic; +type Id = T; +impl Marker for Id {} + +// Negative auto-trait impls go through the same orphan check, so an alias to a local nominal +// type is accepted for them too. +struct Negated; +type ToNeg = Negated; +impl !Marker for ToNeg {} + +// An alias expanding to a reference is still rejected for the cross-crate auto trait. Using +// `&'static NotSync` (with `NotSync: !Sync`) avoids overlapping std's `Send for &T` impl, so +// the only error is the auto-trait nominal-type restriction. +struct NotSync; +impl !Sync for NotSync {} +type ToRef = &'static NotSync; +unsafe impl Send for ToRef {} +//~^ ERROR cross-crate traits with a default impl, like `Send`, can only be implemented for a struct/enum type, not `&'static NotSync` + +// An alias expanding to a trait object is still rejected for the local auto trait. +type ToDyn = dyn Send; +impl Marker for ToDyn {} +//~^ ERROR traits with a default impl, like `Marker`, cannot be implemented for trait object `(dyn Send + 'static)` + +fn main() {} diff --git a/tests/ui/lazy-type-alias/auto-trait-impls.stderr b/tests/ui/lazy-type-alias/auto-trait-impls.stderr new file mode 100644 index 0000000000000..f2ab6db19fb2b --- /dev/null +++ b/tests/ui/lazy-type-alias/auto-trait-impls.stderr @@ -0,0 +1,17 @@ +error[E0321]: traits with a default impl, like `Marker`, cannot be implemented for trait object `(dyn Send + 'static)` + --> $DIR/auto-trait-impls.rs:50:1 + | +LL | impl Marker for ToDyn {} + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: a trait object implements `Marker` if and only if `Marker` is one of the trait object's trait bounds + +error[E0321]: cross-crate traits with a default impl, like `Send`, can only be implemented for a struct/enum type, not `&'static NotSync` + --> $DIR/auto-trait-impls.rs:45:1 + | +LL | unsafe impl Send for ToRef {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ can't implement cross-crate trait with a default impl for non-struct/enum type + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0321`. diff --git a/tests/ui/type-alias-impl-trait/impl_for_weak_alias.rs b/tests/ui/type-alias-impl-trait/impl_for_weak_alias.rs index b50345bb637c3..57a092a682aaf 100644 --- a/tests/ui/type-alias-impl-trait/impl_for_weak_alias.rs +++ b/tests/ui/type-alias-impl-trait/impl_for_weak_alias.rs @@ -1,3 +1,8 @@ +//! The orphan check expands a free alias used as the self type of an auto-trait impl, so an +//! alias resolving to a tuple is accepted just like the tuple written directly. See #157756. + +//@ check-pass + #![feature(type_alias_impl_trait)] #![feature(auto_traits)] @@ -5,7 +10,6 @@ type Alias = (impl Sized, u8); auto trait Trait {} impl Trait for Alias {} -//~^ ERROR traits with a default impl, like `Trait`, cannot be implemented for type alias `Alias` #[define_opaque(Alias)] fn _def() -> Alias { diff --git a/tests/ui/type-alias-impl-trait/impl_for_weak_alias.stderr b/tests/ui/type-alias-impl-trait/impl_for_weak_alias.stderr deleted file mode 100644 index a13e9eaab3add..0000000000000 --- a/tests/ui/type-alias-impl-trait/impl_for_weak_alias.stderr +++ /dev/null @@ -1,11 +0,0 @@ -error[E0321]: traits with a default impl, like `Trait`, cannot be implemented for type alias `Alias` - --> $DIR/impl_for_weak_alias.rs:7:1 - | -LL | impl Trait for Alias {} - | ^^^^^^^^^^^^^^^^^^^^ - | - = note: a trait object implements `Trait` if and only if `Trait` is one of the trait object's trait bounds - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0321`.