Skip to content
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

Coercion from non-capturing closure to fn ptr fails when return type is ! #66738

Open
RalfJung opened this issue Nov 25, 2019 · 5 comments
Open
Labels
A-closures Area: Closures (`|…| { … }`) A-coercions Area: implicit and explicit `expr as Type` coercions C-bug Category: This is a bug. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Comments

@RalfJung
Copy link
Member

RalfJung commented Nov 25, 2019

Usually, non-capturing closures can be coerced to fn ptrs. But somehow that seems to fail when the return type is !:

fn magic<R, F: FnOnce() -> R>(f: F) -> F { f }

fn main() {
    let f1 = magic(|| {}) as fn() -> (); // works fine
    let f2 = magic(|| loop {}) as fn() -> !; // errors
}

The error is

error[E0605]: non-primitive cast: `[closure@src/main.rs:5:21: 5:31]` as `fn() -> !`
 --> src/main.rs:5:14
  |
5 |     let f2 = magic(|| loop {}) as fn() -> !;
  |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  |
  = note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait

This fails even on nightly, where ! should be stable as a type.

Curiously, this works:

let f2: fn() -> ! = || loop {};

Cc @Centril

bors added a commit to rust-lang/miri that referenced this issue Nov 25, 2019
test closure-to-fn-ptr coercions a bit more

Also add some commented-out failing tests, Cc rust-lang/rust#66738 #1075
@jonas-schievink jonas-schievink added A-closures Area: Closures (`|…| { … }`) A-coercions Area: implicit and explicit `expr as Type` coercions C-bug Category: This is a bug. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Nov 25, 2019
@eddyb
Copy link
Member

eddyb commented Nov 26, 2019

This also works: magic(|| -> ! { loop {} }) as fn() -> !.

I'm not surprised inference of ! is finicky even with ! stabilized.

@SimonSapin
Copy link
Contributor

Could this have a common root cause with #66757? They both compile if #![feature(never_type_fallback)] is enabled.

@RalfJung
Copy link
Member Author

RalfJung commented Mar 8, 2020

Actually the error happens even on stable, entirely independent of the never_type feature.

@Mark-Simulacrum
Copy link
Member

I suspect this is not actually a bug, in some sense. Currently, the closure being passed into the function here doesn't get a return type of !. Expressions like loop {} are typed as !, but the return type of the closure is inferred for this code -- and the loop {} expression sets no requirements on the return type due to ! being coerceable to any type -- including the inference variable of the return type.

That means we basically end up with a situation where the return type of the closure is "unknown", and so falls back to (). fn() is not castable to fn() -> !, of course, so this leads to an error. In general ! is a somewhat "weak" type due to this easy coercion behavior, which leads to quite a bit of unintuitive behavior.

@RalfJung
Copy link
Member Author

RalfJung commented Jan 3, 2022

Ah, I see, that makes sense... and indeed this variant works:

#![feature(never_type)]
fn magic<F: FnOnce() -> !>(f: F) -> F { f }

fn main() {
    let f2 = magic(|| loop {}) as fn() -> !;
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-closures Area: Closures (`|…| { … }`) A-coercions Area: implicit and explicit `expr as Type` coercions C-bug Category: This is a bug. 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

5 participants