Undeclared use of identifier in generated Module-Swift file

I have a mixed project containing Swift, Objective-C and C++ code. I've recently been removing some of the Objective-C bridging code in favour of direct interop from Swift -> C++.

I recently stumbled into an issue where I was getting a compilation issue in the generated Module-Swift.h file. The class in question was a C++ class. Initially I was confused since my understanding of the Module-Swift.h is that it exposes your Swift API to Objective-C (and now C++). But in this case it was trying to expose a C++ interface :thinking:

I created a sample project to reproduce the issue and discovered the problem comes when having a public function/extension return the C++ type. Either making the surrounding struct/class an enum or, reducing the visibility modifier down to internal avoid the C++ class from entering into the Module-Swift.h thus resolving the compilation issue.

The problem is, I need this function to have public visibility and I can't use an enum type. It's almost as if there needs to be a way of annotating a function or type to stop it getting added to the Module-Swift.h file...perhaps there is and I'm not aware of this?

Here's a super simple sample project which demonstrates the issue:

C++ class incorrectly added to Module-Swift.h

Has anybody else run into a similar issue and found other workarounds or is there perhaps something I'm overlooking here? Thanks!

1 Like

Hey, thanks for trying out C++ interoperability. I've looked at your project , and there are two issues that can be worked around fortunately to get your sample project working.

The use of the C++ HelloCxxWorld class return type in Swift lets Swift emit extra metadata in the header that reference the C++ HelloCxxWorld class type. This is expected and needed for some of the interoperability features, e.g. in case your Swift code decides to return a Swift generic type that is parametrized by the C++ HelloCxxWorld class type. Although that doesn't happen in your case, the metadata is still emitted in the generated header. This leads to two issues like I mentioned.

The first issue is a known issue. It basically boils down to the fact that the generated header currently doesn't include back references back to the C++ headers that contain. class definitions that might be referenced in the header. Specifically in your sample the generated header doesn't include #include "HelloCxxWorld.hpp" which provides the definition of the HelloCxxWorld class. You can work around this issue by swapping the order in which the generated header is included in your C++ code:

#include "HelloCxxWorld.hpp" // include this header first
#import "TestCxx-Swift.h"

This issue is tracked on GitHub by and we're planning to fix in the near term.

The second issue occurred after I worked around the first issue above. It shows up as a linker error like this:

ld: Undefined symbols:
  type metadata accessor for __C.HelloCxxWorld, referenced from:
      swift::TypeMetadataTrait<HelloCxxWorld>::getTypeMetadata() in HelloHello.o

This is caused by how the C++ getTypeMetadata inline function is annotated in the generated header. It uses the SWIFT_INLINE_THUNK annotation which in debug mode in Xcode (when DEBUG is defined as a preprocessor macro/Swift build condition) is forced to be emitted into the object file, even when it's not used. This is wrong, as it's not supposed to be linked in forcefully and should only be needed when it's actually used. You can workaround this issue by building the project in release mode, or dropping the DEBUG Swift build condition or processor macro from the Xcode build settings for the sample project.

This second issue is tracked by on GitHub.

Once I worked around these two issues I was able to build the project successfully.

Thanks so much for looking into this issue and for the thorough explanations.

The shuffling of the includes seems obvious now you point, I should have tried this. All the sane, it's great to hear that a fix for this will likely land shortly.

Regarding the second workaround, I've tried this and it does indeed seem to work. However, I'm unfortunately not in a position to be able to just build in release or to knock out the DEBUG preprocessor as that would inevitably result in path of execution changes elsewhere in the codebase where the DEBUG macro is in use. So this secondary issue remains a blocker for me.

Thanks for linking to the issues in GitHub, I'll be sure to keep a track of those. I can see just now both issues have been tagged with Swift 5.11 so that gives me some indication of when they'll get addressed.

Thanks again @Alex_L :pray: