Skip to content

Transmute between functions with different return types, where one function never returns #266

Open
@Michael-F-Bryan

Description

@Michael-F-Bryan

A question came up on the community Discord today and I'd like to know if my reasoning was correct.

This was the original question:

Why is it impossible to cast fn() -> ! to fn() and would it be safe to perform the conversion with a transmute?

And the following conversation ensued:

Michael-F-Bryan: I think the only valid casts/coersions for a function are extending/shrinking lifetimes (e.g. between fn(&'static str) and fn<'a>(&'a str)) and non-capturing closures to function pointers.

#![cfg_attr(goat, no_std)]: yes, they are covariant over the return type
! is a subtype of all types, isn't it

Michael-F-Bryan: transmuting between them could be unsound because of things like Return Value Optimisation... Imagine you had a fn() -> [u8; 256] and LLVM decided to apply RVO, turning it into fn(&mut [u8; 256]) to elide the copy. Now you would be transmuting a function which accepts no arguments(fn() -> !) into a function that accepts one argument, and that may mess up your stack/registers depending on your calling convention.
just hypothesizing

#![cfg_attr(goat, no_std)]: the return type is ()
it's not subject to RVO
besides I think it is safe to ignore parameters in most if not all calling conventions
since you never return, you never observe side effects of failed RVO

(I then posted a playground example which runs the transmute with Miri, and Miri complains that it's UB)

#![cfg_attr(goat, no_std)]
oh
so ! is not memory layout compatible with any type

Michael-F-Bryan: I believe it's because the general case of transmuting between functions that return different things is unsound unless the returned value is a pointer
which is why it's valid for compilers to apply things like RVO
Miri was happy for me to turn a fn() -> *mut u8 into a fn() -> mut String

Is my reasoning correct that, generally, optimisations like RVO being sound is what makes this transmute is UB?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions