Swift 5.9 compilation failure on linux when using system modules that import select.h

TL;DR: Swift 5.9 fails with error

/usr/include/x86_64-linux-gnu/sys/select.h:64:15: error: '(anonymous struct)::fds_bits' from module 'LinuxSys' is not present in definition of 'fd_set' in module 'CDispatch'
    __fd_mask fds_bits[__FD_SETSIZE / __NFDBITS];
              ^
/usr/include/x86_64-linux-gnu/sys/select.h:59:9: note: definition has no member 'fds_bits'
typedef struct
        ^
error: fatalError

Hello everyone,

I'm facing this weird issue with Swift compiler version 5.9, pretty much since the first development snapshot was posted. The repository is located at GitHub - smumriak/AppKid: UI toolkit for Linux in Swift. Powered by Vulkan, the simplest way to reproduce the issue is to build target TinyFoundation swift build --target TinyFoundation, since it has only four dependencies and will not require any pre-setup.
One of those dependencies is a module called LinuxSys, which is defined as a systemLibrary and its umbrella header includes OS headers. Very similar to builtin module GLibc, but covers a bit wider set of APIs.

The issue is pretty much isolated to systemLibrary definitions that have any transient include of select.h (there are a couple more libraries defined in the project, like CXlib or CCairo, all of which fail to compile with this error). As a workaround I think I can fallback to use empty C shim libraries instead of the system library definitions in Package manifest, tho it's less than ideal.

Seeing C++ style of error with (anonymous struct)::fds_bits field suggests it's somehow related to C++ interop shipping with 5.9 which makes me wonder if there's any additional flag I need to set in manifest or something like that. Any help is appreciated!

I think this is saying that Dispatch’s C headers are redeclaring a struct that’s in select.h and it’s not doing so compatibly. The fix might be for Dispatch’s C headers to include select.h on Linux systems, or it might be an accidental collision and they should rename their struct, or it might be something more complicated; nonetheless, the change will probably have to happen in Dispatch, because we can’t change system headers. (Though it’s possible the Glibc module map will also need to be modified.)

It's still going to happen whenever two separate C system modules would import "select.h", no?

Oh, I didn’t realize the “conflicts” were in the same file. That seems like the file behaves differently based on the preprocessor, which isn’t supported with modules. I’m no longer sure whether this is something Dispatch specifically should address, vs something you’d have to set in your project across all imports.

That was my first thought. The only obvious difference is check on __USE_XOPEN. So I tried to define it locally in the umbrella header before including system headers like so: #define __USE_XOPEN 1. No change. Same with undefining it at all.

I’m no longer sure whether this is something Dispatch specifically should address, vs something you’d have to set in your project across all imports.

Since this particular code was working on very long chain for Swift 5.* releases I'm inclined to think there's an internal change in 5.9 that does this. So I'm expecting to see some compiler flag or manifest setting that would bring the behavior of previous Swift releases, but I'm struggling to find it

1 Like

Ok, something definitely weird is going on. Running swift build --target TinyFoundation exactly 4 times without cleaning build artifacts (after initial clean) results in successful build

5.9 branched after the LLVM rebranch, which added the ODR for C headers and a bunch of other tightening. For example, it hit me on Android too, simply because two typedefs used different names, even though the underlying types were the same. My guess is one of those other newly added rules is affecting your two C modules here. As that author notes, that's because some C++ rules are now being applied for C headers too, by the underlying libclang that the Swift compiler uses to import C headers.

1 Like

This makes total sense!

Hey @vsapsai, could you please check if this is indeed the same issue?

It will take me some time to reproduce the issue on Linux. So far I have a question is /usr/include/x86_64-linux-gnu/sys/select.h covered both by 'CDispatch' and 'LinuxSys'?

Yes. For x86_64 systems this is a file location for <sys/select.h> imports. Both CDispatch and LinuxSys umbrella headers have it as transient include.

@vsapsai have you had a chance to look into this?

I've found that the issue is caused when using Linux system headers with _GNU_SOURCE defined. I'm using that to get access to gettid syscall from libc. I bet a lot of other libraries are using it for some other functionality, since this is the way to get access to a lot of non-POSIX API in linux.
I'm going to make a workaround via converting my LinuxSys library to regular target instead of system lib and making a wrapper function for gettid. This way I will not have to expose things hidden behind _GNU_SOURCE, but this is defeating the purpose of the library itself. I'm out of solutions really

@vsapsai if it would help I can attach the simplest swift package that reproduces the issue.

Hello,
We've been looking at a similar, if not, the same issue. I have posted a minimized reproducer here: Swift / C build error related to missing definition of `fds_bits`. · Issue #69311 · apple/swift · GitHub
I'm interested to see if this is impacting architectures other than Ubuntu 20 + ARM64 in a Docker container.
Thanks for having a look!

2 Likes