SR-15946: Reach out for help in Mandatory SIL Linker pass

https://bugs.swift.org/browse/SR-15946

top-level value not found
Cross-reference to module 'ucrt'
... memcmp
... with type (UnsafeRawPointer, UnsafeRawPointer, Int) -> Int32
Notes:
* 'memcmp' in module 'SwiftShims' was filtered out.
* 'memcmp' in module 'Foundation' was filtered out.
* 'memcmp' in module 'CRT' was filtered out.
* 'memcmp' in module 'SwiftOverlayShims' was filtered out.
* 'memcmp' in module 'ucrt' was filtered out.
* 'memcmp' in module 'CDispatch' was filtered out.
* 'memcmp' in module 'WinSDK' was filtered out.

This is a really weird bug I’ve met when trying to move ucrt.modulemap around on Windows. Everything seems okay except for Foundation.memcmp, which cannot be deserialized during "MandatorySILLinker" pass. I’ve been testing around this bug for a while, only to find out it may be related to string.h being referenced with absolute path (because how we refer to string.h in ucrt.modulemap is the only variable here).

I’m reaching out for help regarding this SIL pass since I’m not expert at the Swift compiler. Hopefully there could be someone fixing this up, or giving me some view on how this works and how I can find out the problem. Huge thanks!

2 Likes

CC @Slava_Pestov who brought this feature to us

Trying hard to make progress...

expected type '(UnsafeRawPointer, UnsafeRawPointer, Int) -> Int32' not equal to '(UnsafeRawPointer?, UnsafeRawPointer?, Int) -> Int32'

It turns out that Mandatory SIL Linker wrongly regard the type as (UnsafeRawPointer?, UnsafeRawPointer?, Int) -> Int32, and doesn't agree with SILGen which should be the correct result because the original definition was int __cdecl memcmp(void const* _Buf1, void const* _Buf2, size_t _Size);.

Anyone having idea on this? Why is the behavior related to how we reference string.h header?

For anyone who’re willing to reproduce the bug, here’s an easy way:

  • Rename %UniversalCRTSdkDir%\Include\%UCRTVersion%\ucrt\module.modulemap to ucrt.modulemap;
  • Append extern module ucrt "../ucrt/ucrt.modulemap" to %UniversalCRTSdkDir%\Include\%UCRTVersion%\um\module.modulemap

Now, try compile:

import Foundation
print(type(of: memcmp))

Ah, it seems a bigger problem: any pointer parameter (with type const void* or void*) in ucrt is becoming Optional. Then why only memcmp crashes the compiler?


UPDATE: Guess what? It turns out that only memcmp is having the correct signature in ucrt!

C:\Users\stevapple\swift-test>type x.swift
import ucrt
print("memcmp: \(type(of: memcmp))")
print("memcpy: \(type(of: memcpy))")

C:\Users\stevapple\swift-test>.\x.exe
memcmp: (UnsafeRawPointer, UnsafeRawPointer, Int) -> Int32
memcpy: (Optional<UnsafeMutableRawPointer>, Optional<UnsafeRawPointer>, Int) -> Optional<UnsafeMutableRawPointer>

Okay then, it appears to be related with false memcmp type explicitly declared in SwiftShims.

Just opened [stdlib] Fix nullability logic of `memcmp` by stevapple · Pull Request #41797 · apple/swift · GitHub to fix it.

I suppose diagnostic should be improved on such circumstances… Though it’s rather unlikely to be triggered?

I need to apologize for my ignorance of how Swift determines the type of C function — Optional is the right behavior here, and I was wrong about this in SR-15946: Reach out for help in Mandatory SIL Linker pass - #3 by stevapple and SR-15946: Reach out for help in Mandatory SIL Linker pass - #5 by stevapple.