Suppressing a redundant `unsafeBitCast` compiler warning

I'm adding Windows capability to a project which, as a ubiquitous need (for a few hundred functions), uses unsafeBitCast to cast the result of a dlsym(..) call, which on non-Windows platforms is an UnsafeMutableRawPointer, to the true function signature. In Swift I use the following (error checking removed and lines folded for brevity):

typealias FunctionPtr = UnsafeMutableRawPointer
typealias functionSignature = @convention(c) 
                         ( UnsafeMutablePointer<CChar> ) -> Int32

let pointer: FunctionPtr = dlsym(libHandle, functionName)
let typedFunction = unsafeBitCast(pointer, to: functionSignature.self)

This works as desired on macOS and Linux.


On Windows, the code is the same, except for the call that returns the function pointer:

typealias FunctionPtr = FARPROC
typealias functionSignature = @convention(c) 
                         ( UnsafeMutablePointer<CChar> ) -> Int32

let pointer: FunctionPtr = GetProcAddress(libHandle, functionName)
let typedFunction = unsafeBitCast(pointer, to: functionSignature.self)

However, GetProcAddress(..) does not return an UnsafeMutableRawPointer, it returns a 'generic' function pointer (because value pointers and function pointers are different types). The returned pointer is a type called FARPROC which, though used as a stand-in for a function of any signature, is actually a:

@convention(c) ( ) -> Int

This works as desired on Windows.

So, what's my problem?! When the functionSignature described by the typealias happen to be the same signature as returned by GetProcAddress, the compiler complains, with a warning, and correctly, that unsafeBitCast is not required because its two parameters, pointer and functionSignature.self are the same type.

The code on both platforms does what I want (and I can't think of another way to do it), but on Windows the compiler coughs up these warnings. This will be production level source code to be compiled by others and I want to suppress these warning to avoid worries and confusion. Is there a way I could recode this to suppress them?

Wrap unsafeBitCast in your own generic function.

typealias FunctionPtr = @convention(c) () -> Int

func unsafeFunctionSignatureCast<U>(_ value: FunctionPtr, to type: U.Type) -> U {
    return unsafeBitCast(value, to: type)
}

let f: @convention(c) () -> Int = { 123 }
let g = unsafeBitCast(f, to: FunctionPtr.self)               // warning
let h = unsafeFunctionSignatureCast(f, to: FunctionPtr.self) // no warning

Would that work?

#if WINDOWS
...
#else
...
#end

(use the proper name for that test, not sure what it is)

I'm doing that @tera. To avoiding pasting a lot of code, I just showed the essentials. The two blocks of code I used to illustrate the differences are, in fact within:

#if os(Windows)
    « windows example »
#elseif os(macOS)
    « macOS example »
#endif

.. looks nice @mayoff, I'll give it a try!

Perhaps I am missing something then:

#if os(Windows)
let pointer: FunctionPtr = GetProcAddress(libHandle, functionName)
let typedFunction: FunctionSignature = unsafeBitCast(pointer, to: FunctionSignature.self)
// 🔶 `unsafeBitCast` is not required because its two parameters, `pointer` and `functionSignature.self` are the same type

If the warning is true - then perhaps just this should work?

let type: FunctionSignature = pointer

And if it doesn't, then the warning is wrong?

Otherwise it doesn't add up.

Edit: this might work as well:

unsafeBitCast(unsafeBitCast(pointer, to: UnsafeMutableRawPointer.self), to: FunctionSignature.self)

Works as expected! I blank on the simple stuff sometimes! Thanks

There's more going on behind this. There are a lot of functions being called from the (Fortran) dynamic libraries and they are all made available to Swift by being 'wrapped' in code like I showed. The few hundred 'wrappers' are generated by a program so they are all almost exactly the same and as simple/small as possible, differing only in the function signatures being cast.

On macOS, the address of a function has no type so they all have to be cast; on Windows the function does have a type, usually wrong so those needs to be cast too, but sometimes the types match and the compiler, being a pleasant sort of chap says, "Hey, buddy, that cast wasn't necessary, you could remove it!" .. that's nice but I'm not going to make a special case for ~1% of the functions.