Skip to content
Open
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: 7 additions & 1 deletion compiler/rustc_hir_analysis/src/coherence/orphan.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
53 changes: 53 additions & 0 deletions tests/ui/lazy-type-alias/auto-trait-impls.rs
Original file line number Diff line number Diff line change
@@ -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 <https://github.com/rust-lang/rust/issues/157756>.

#![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> = T;
impl Marker for Id<Generic> {}

// 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() {}
17 changes: 17 additions & 0 deletions tests/ui/lazy-type-alias/auto-trait-impls.stderr
Original file line number Diff line number Diff line change
@@ -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`.
6 changes: 5 additions & 1 deletion tests/ui/type-alias-impl-trait/impl_for_weak_alias.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
//! 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)]

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 {
Expand Down
11 changes: 0 additions & 11 deletions tests/ui/type-alias-impl-trait/impl_for_weak_alias.stderr

This file was deleted.

Loading