Skip to content

! cannot be used as impl Trait when ! implements Trait #105284

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
zachs18 opened this issue Dec 5, 2022 · 8 comments
Open

! cannot be used as impl Trait when ! implements Trait #105284

zachs18 opened this issue Dec 5, 2022 · 8 comments
Labels
C-bug Category: This is a bug. F-never_type `#![feature(never_type)]` requires-nightly This issue requires a nightly compiler in some way. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Comments

@zachs18
Copy link
Contributor

zachs18 commented Dec 5, 2022

I tried this code (playground link):

#![feature(never_type)]
#![allow(unreachable_code)]

pub trait Trait {}
impl Trait for ! {}

pub fn test() -> impl Trait {
    let x: ! = panic!();
    x
}

I expected to see this happen: The code should compile. Because ! implements Trait, ! should be a valid return type for fn test (and no fallback should occur).

Instead, this happened: The code does not compile. x "falls back" to (), (which does not implement Trait), so the function does not typecheck, giving the following error message:

error[E0277]: the trait bound `(): Trait` is not satisfied
 --> src/lib.rs:7:18
  |
7 | pub fn test() -> impl Trait {
  |                  ^^^^^^^^^^ the trait `Trait` is not implemented for `()`
  |
  = help: the trait `Trait` is implemented for `!`

For more information about this error, try `rustc --explain E0277`.
error: could not compile `playground` due to previous error

See also:

Meta

rustc +nightly --version --verbose:

rustc 1.67.0-nightly (32e613bba 2022-12-02)
binary: rustc
commit-hash: 32e613bbaafee1bcabba48a2257b838f8d1c03d3
commit-date: 2022-12-02
host: x86_64-unknown-linux-gnu
release: 1.67.0-nightly
LLVM version: 15.0.4
Backtrace

<backtrace>

@zachs18 zachs18 added the C-bug Category: This is a bug. label Dec 5, 2022
@fmease
Copy link
Member

fmease commented Dec 5, 2022

It does compile if you add #![feature(never_type_fallback)]. As far as I know, changing the fallback from () to ! is very difficult without breaking backward compatibility & without introducing unsoundness to some crates that use unsafe and precisely the reason why ! hasn't been stabilized yet (as a normal type).

If I am not mistaken, this issue is essentially a duplicate of the tracking issue of never_type_fallback. I am on mobile rn, and can't link to the relevant issues.

@zachs18
Copy link
Contributor Author

zachs18 commented Dec 5, 2022

If I am not mistaken, this issue is essentially a duplicate of the tracking issue of never_type_fallback.

#65992 was closed in favor of #35121, if that is the issue you are referring to.

// compiler/rustc_hir_typeck/fallback.rs:130
// ...
/// "Diverging" type variables are variables created when we
/// coerce a `!` type into an unbound type variable `?X`. 
// ...

Do I understand correctly that this (currently) means any coercion of an expression of type ! to (e.g.) impl Trait is considered a coercion from a diverging type variable (and thus resolves to () without #![feature(never_type_fallback)]), regardless of whether ! could coerce to that type "normally"?

@zachs18
Copy link
Contributor Author

zachs18 commented Dec 5, 2022

Workaround for traits such that Box<T>: Trait where T: Trait, since coercing Box<!> does not create a diverging type variable:

#![feature(never_type)]
#[allow(unreachable_code)]

pub trait Trait {}
impl Trait for ! {}
impl<T: Trait + ?Sized> Trait for Box<T> {}

pub fn test() -> impl Trait {
    let x: ! = panic!();
    let x: Box<!> = Box::new(x);
    x
}

@fmease
Copy link
Member

fmease commented Dec 6, 2022

Do I understand correctly that this (currently) means any coercion of an expression of type ! to (e.g.) impl Trait is considered a coercion from a diverging type variable (and thus resolves to () without #![feature(never_type_fallback)]), regardless of whether ! could coerce to that type "normally"?

I am not deeply familiar with the type inference process, so I sadly cannot answer your question.

Workaround for traits such that Box: Trait where T: Trait, since coercing Box<!> does not create a diverging type variable

That's unfortunate :/ I don't know of any better workaround.

@Nemo157
Copy link
Member

Nemo157 commented Dec 7, 2022

If the trait doesn't require ownership, you can also rely on static promotion to avoid needing alloc:

#![feature(never_type)]

pub trait Trait {}
impl Trait for ! {}
impl<T: Trait + ?Sized> Trait for &T {}

pub fn test() -> impl Trait {
    #[allow(unreachable_code)]
    &panic!()
}

@KisaragiEffective
Copy link
Contributor

KisaragiEffective commented Aug 22, 2023

@rustbot label +requires-nightly

@rustbot
Copy link
Collaborator

rustbot commented Aug 22, 2023

Error: Label Fnever_type can only be set by Rust team members

Please file an issue on GitHub at triagebot if there's a problem with this bot, or reach out on #t-infra on Zulip.

@rustbot rustbot added the requires-nightly This issue requires a nightly compiler in some way. label Aug 22, 2023
@fmease fmease added T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. F-never_type `#![feature(never_type)]` and removed needs-triage-legacy labels Sep 6, 2023
@WaffleLapkin
Copy link
Member

This will be fixed once we stop falling back to (), see #123748. (the original example compiles with 2024 edition enabled: play)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
C-bug Category: This is a bug. F-never_type `#![feature(never_type)]` requires-nightly This issue requires a nightly compiler in some way. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
Development

No branches or pull requests

7 participants