Debugging types not being imported via C++ interop

I'm working on enabling Swift interop in MLIR, and am running into an issue where the import statement works, but I'm not able to use the type I am trying to import (mlir::MLIRContext). I'm on swift-DEVELOPMENT-SNAPSHOT-2022-07-06-a on ubuntu 22.04 (The swift-main-jammy docker image).
I'm wondering how I would go about debugging this, is there an easy way to see what API has been imported? Or to see a log of API that has failed to import?

My test case is simple (line 19 produces an error that milr-a C++ namespace-does not have a member called MLIRContext):

as is the modulemap I am using:

I think this specific type is not getting imported because its copy constructor is deleted. Swift generally imports C++ types as struct, which means that Swift might copy them around in memory. For C++ types with a deleted copy constructor, that won't work.

We do have a solution for such types though, you can apply an attribute on the type: __attribute__((swift_attr("import_as_ref"))). That way the type will get imported as a class, and Swift won't try to copy it, so the copy constructor won't be needed.

I'm wondering how I would go about debugging this, is there an easy way to see what API has been imported? Or to see a log of API that has failed to import?

Swift can emit a diagnostic when you're attempting to use a method that wasn't imported (credits to @NuriAmari for that work!), however, I don't think it works for types at the moment.
The logic that determines if we can import a given C++ type is right here.

1 Like

Thanks!

Is __attribute__((swift_attr("import_as_ref"))) something we are trying to replace with API notes currently? i.e. should it be possible in the fullness of time to specify which types should be imported as refs without modifying the source?

Also, thanks for the link to the code for determining if a given C++ type can be imported... is there a mechanism to do a verbose log of all types for which an import attempt was made and the outcome (for a precompiled toolchain)?

Finally, in this case MLIRContext is used as a namespace for another type Threading. Would it make sense to still import the type but not allow the compiler to declare values of that type (so it can still be used as a namespace)?

Yeap, definitely. We would first need to make API notes work with namespaces, and once that is done, we'll be able to apply this attribute via API notes without modifying the source.

I don't think there is a way to do that right now, unfortunately. I think we would probably want to extend the compiler mechanism that emits diagnostics for unimported C++ methods to also work for C++ types.

That would definitely be useful, thanks for discovering this! I filed an issue to track this.

1 Like

There is actually a mode for this, but I'll warn you that it can get pretty verbose. -enable-experimental-eager-clang-module-diagnostics will "eagerly" print out information about any APIs that it cannot import (even APIs that you don't use). Unfortunately, I think it only works for methods, properties, function, and macros (not records/types). It would be great to improve this, though so you get a nice warning about why we can't import things like MLIRContext and what to do if it has reference semantics, etc.

4 Likes

Update on this: the most recent (july 20th) Swift nightly toolchains should have much better diagnostics when we can't import something. I'd be very interested to hear your feedback after using those toolchains (if the errors/notes are easy to understand and helpful, and if not how we might improve them).

Here's the user manual that accompanied those changes: swift/UserManual.md at main · apple/swift · GitHub

Hopefully adoption/testing is going well on your end. Don't be shy to ask questions or suggest improvements, we'd love to help!

1 Like