Clang C module importer bug?

I'm using libgmp with Swift, but for some reason the Clang C module importer does not do what I'm expecting. Here's an excerpt of gmp.h, which is the main header file. Consider the following definition:

#define mpz_clear __gmpz_clear
__GMP_DECLSPEC void mpz_clear (mpz_ptr);

To use this from C, I just do mpz_clear(e). To use this from Swift, I have to do __gmpz_clear(&e).
Why is the Clang importer not honoring the #define here?

I created an example Xcode project for you to reproduce.

Am I missing something or is this a bug?

Looks like it is treated as a "complex" kind of macro :slight_smile:

When it does matter (probably not worth doing it in this case) you can introduce a wrapper function on C side and call it instead.

I think there's a new-ish way to diagnose Clang importer questions such as yours, thanks to @NuriAmari’s work in PR #39923: Improve ClangImporter Diagnostics. From reading the PR, I assumed you should be able to pass -Xfrontend -enable-experimental-clang-importer-diagnostics to the compiler, but this doesn’t work for me with your sample project with Swift 5.7 (Xcode 14.0.1 or Xcode 14.1b3) because the compiler doesn’t recognize the flag:

error: unknown argument: '-enable-experimental-clang-importer-diagnostics'

Maybe this flag is not part of the Xcode toolchains (yet?)? I haven't tested with a current nightly toolchain.

Hi all,

I think there's a new-ish way to diagnose Clang importer questions such as yours, thanks to @NuriAmari’s work in PR #39923: Improve ClangImporter Diagnostics . From reading the PR, I assumed you should be able to pass -Xfrontend -enable-experimental-clang-importer-diagnostics to the compiler, but this doesn’t work for me with your sample project with Swift 5.7 (Xcode 14.0.1 or Xcode 14.1b3) because the compiler doesn’t recognize the flag:

error: unknown argument: '-enable-experimental-clang-importer-diagnostics'

Maybe this flag is not part of the Xcode toolchains (yet?)? I haven't tested with a current nightly toolchain. I think there's a new-ish way to diagnose Clang importer questions such as yours, thanks to @NuriAmari’s work in PR #39923: Improve ClangImporter Diagnostics . From reading the PR, I assumed you should be able to pass -Xfrontend -enable-experimental-clang-importer-diagnostics to the compiler, but this doesn’t work for me with your sample project with Swift 5.7 (Xcode 14.0.1 or Xcode 14.1b3) because the compiler doesn’t recognize the flag:

error: unknown argument: '-enable-experimental-clang-importer-diagnostics'

Maybe this flag is not part of the Xcode toolchains (yet?)? I haven't tested with a current nightly toolchain.

In terms of enabling the improved ClangImporter diagnostics, they are enabled by default now: [Pitch] Lazy ClangImporter Diagnostics Enabled by Default. Unfortunately, @mickeyl the case you are facing is not covered by the diagnostics.

On swift main at least, the macro is dropped here: swift/ImportMacro.cpp at main · apple/swift · GitHub. It seems I left myself a TODO to handle this case, but never got to it. Feel free to attach this post to the issue: [SR-15429] Add Missing Macro Diagnostics to ClangImporter · Issue #57735 · apple/swift · GitHub. At this moment, I don't really have any advice aside from using the __gmpz_clear name in the interim. I don't have time to investigate further at this moment, but I will try to either improve the diagnostic or see if we can import this macro, hopefully sometime in the next week or so.

2 Likes

Ok, thanks for acknowledging that this is a bug. Unfortunately, it's not only about one single symbol, gmp.h is full of those #defines. Would be cool, if this could be fixed sooner or later.

ClangImporter's ability to import macros is extremely limited; it really only covers simple constant-like macros like #define RED ((uint32_t)0xff0000). It can import macros with identifiers that refer to other macros, but not the general case.

We're not likely to expand macro import support to handle your use case, but an alternative would be to use swift_name attributes to rename functions when they're imported into Swift. For example, if you marked this function with __attribute__((swift_name("mpz_clear"))), Swift would import it with the desired name. If you don't want to edit the header file, you can instead use API notes, a YAML format which can add attributes to clang declarations. (In an API note, the attribute will be spelled SwiftName.) If the library uses this pattern in a sufficiently regular way, you could probably write a small script to read in the header and generate API notes for you.

If you want to get fancy with this, the swift_name attribute can also map global C functions to members of types. For instance, if you used the Swift name mpz_t.clear(self:), then depending on the definition of mpz_t this method would come in as a method called clear().

6 Likes