Skip to content

Conversation

@xymus
Copy link
Contributor

@xymus xymus commented Jul 22, 2025

The use of the C++ interop changes dependencies imported by Swift modules, as such the binary swiftmodule produced for the same Swift code is different when the interop is enabled or not. This was partially enforced by rebuilding from swiftinterface when the C++ interop is enabled in clients. However a few pieces of the puzzle were missing with implicit module builds which lead to loading incompatible swiftmodule files.

This PR fixes the module cache collision by considering the C++ interop mode in the module cache key. This preserves the rebuilt swiftmodules separately for clients with and without the C++ interop.

Not in this PR is rejecting swiftmodules built in a different C++ interop modes outside of the cache, both local and distributed modules. Rejecting them would force rebuilding them from swiftinterface or error non non-library-evolution modules. We should consider this in the future.

@xymus
Copy link
Contributor Author

xymus commented Jul 22, 2025

@swift-ci Please smoke test

@nkcsgexi
Copy link
Contributor

cc: @ravikandhadai

Copy link
Contributor

@egorzhdan egorzhdan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you! A design question from me would be: why is using a non-C++-interop module from a C++-interop module something we need to prohibit?

@xymus
Copy link
Contributor Author

xymus commented Jul 24, 2025

@egorzhdan It becomes a problem with the shared dependencies of those two modules, a typical case would be a build graph in a diamond pattern. If two non-library-evolution modules with and without the C++ interop (let’s say A and B) import the same library-evolution dependency (C), each will rebuild C from the swiftinterface and will see the API of C differently. These differences are reflected in the swiftmodule files in inlinable code and such. Then when building a 4th module that imports both A and B, we can only load one version of C. Meaning that one of A or B will expect a different API and may have broken references encoded in their swiftmodule file.

So we need all non-library-evolution modules in the same build graph to be in the same mode. With implicit builds we can only enforce this by rejecting the swiftmodule.

That being said, I may hold back this change for now. It would be much preferable to avoid having the C++ interop affect the API of imported modules instead if that’s possible.

It is expected to rebuild modules from swiftinterface when the C++
interop is enabled in clients. The swiftmodule produced is different.
Make sure the rebuilt module is kept under a distinct cache key to avoid
reusing previously compiled modules in an incompatible mode.
@xymus xymus force-pushed the cxx-swiftmodules branch from 51041e4 to f8d6bed Compare August 4, 2025 17:57
@xymus xymus changed the title Serialization: Consider C++ interop use in swiftmodules, both in the cache and for modules built from source Serialization: Consider C++ interop use in module cache hash, preventing collisions Aug 4, 2025
@xymus
Copy link
Contributor Author

xymus commented Aug 4, 2025

I removed the second commit, using this PR only for the module cache change. Marking mismatching swiftmodules as incompatible has notable implications.

@xymus
Copy link
Contributor Author

xymus commented Aug 4, 2025

@swift-ci Please smoke test

1 similar comment
@xymus
Copy link
Contributor Author

xymus commented Aug 4, 2025

@swift-ci Please smoke test

@xymus xymus merged commit 2ae485a into swiftlang:main Aug 5, 2025
3 checks passed
@xymus xymus deleted the cxx-swiftmodules branch August 5, 2025 16:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants