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`.