Open Pr0methean opened 9 months ago
Can you provide a minimal reproducible example for this issue?
Here's one:
#![feature(never_type)]
fn maybe_run<T: Fn()>(optional_func: Option<T>) {
match optional_func {
Some(func) => func(),
None => {}
}
}
fn main() {
maybe_run::<!>(None)
}
Here's one
That one just demonstrates that !
doesn't impl Fn
!
is not the bottom type – the subtype of all types. !
can only be coerced to T
for any type T
. This coercion isn't transitive: You can't coerce (!,)
to (T,)
for any type T
for example. Same with Option<!>
and Option<T>
for any type T
.
If you want to coerce Option<!>
to Option<T>
for any type T
, you'll need to do it manually (and “lift the coercion”), e.g. via map
: o.map(|x| x)
.
If !
isn't the bottom type (as I'd understood it to be), then how is the actual bottom type (one that e.g. when used as a field type in an enum variant, deletes that variant during monomorphization) represented?
IMO the !
type won't be doing its job properly until rustc --release
can make Vec::<!>::len()
and size_of::<Vec<!>>()
and size_of::<Option<!>>()
all return zero and fold that zero as a compile-time constant.
And this is certainly doable, since for example Kotlin has a Nothing
type (used as the return type for methods that are provably non-halting or that always throw an exception) even when compiling for the JVM (which doesn't have a bottom type).
Fn() -> SourceType
(from the first example you posted) is just a trait, with fn() -> SourceType
only being one of the possible types it could be. So implicitly casting None::<!>
to None::<fn() -> SourceType>
– just based on the requirement that the type has to implement Fn() -> SourceType
– wouldn't make any sense to me.
Especially because an implementation of Fn(..) -> _ for !
could be added at some point which doesn't sound like a bad idea actually. Is that what you were trying to suggest with this issue?
But maybe what you meant is something like this:
#![feature(never_type)]
fn maybe_run(optional_func: Option<fn()>) {
match optional_func {
Some(func) => func(),
None => {}
}
}
fn main() {
maybe_run(None::<!>)
}
In which case fmease's answer applies.
size_of::<Vec<!>>()
won't be zero anytime soon because you can't currently have specialized structs for specific type parameters. size_of::<Option<!>>()
is already zero as a compile-time constant because the Some
variant is deleted as you say.
I agree that impl Fn() -> _ for !
is what I meant to suggest, since the first example was designed to monomorphize for both method references and closures with and without captured variables.
I tried this code:
I expected to see this happen: the code would compile.
Option<!>
would be a valid type with zero size, and would cast toOption<T>
for all T, because theSome
variant would be eliminated during monomorphization. In factNone
would always be of typeOption<!>
unless and until the compiler found a way for it to becomeSome
.Instead, this happened: it didn't compile until I changed the turbofish to
::<fn(_) -> _>
Meta
rustc --version --verbose
:Backtrace
```
```