Risks of compiler warning: 'Module was not compiled with library evolution support'

I am getting a warning Module BBB was not compiled with library evolution support; using it means binary compatibility for AAA can't be guaranteed, which is a valid description of the build setup***, my question is regarding the potential implications of using AAA given this warning.
Is there a risk of runtime ABI incompatibility (i.e. crashing)?
Would this trigger a linker error when AAA is used in certain cases (which is an acceptable risk in this case)?

*** the AAA framework I am building has a Carthage library dependency BBB that doesn't have BUILD_LIBRARIES_FOR_DISTRIBUTION set.

By distributing your code as a binary framework, you’re making guarantees that clients will not need to recompile when adopting a new version of your framework. Build Libraries for Distribution increases the set of modifications you can make to your framework without breaking ABI guarantees (see Swift.org - Library Evolution in Swift for more info on library evolution).

However, if you have adopted Build Libraries for Distrbution, and you use a framework that doesn’t adopt Build Libraries for Distrbution, then that library is much more likely to break ABI when it changes slightly.

So if you distribute a compiled binary framework that relies on a framework that’s not using library evolution, your framework will have made many more assumptions about BBB, including specifics of type layout that library evolution would have abstracted.

That warning is there to ensure you don’t rely on a framework’s ABI if it hasn’t adopted library evolution.

2 Likes

Does ABI breakage have a risk of runtime memory corruption? That is, is there a possibility that the compiler/linker doesn't detect the ABI incompatibility? I ask because I recall this happening with C++.

I suspect my question will be a common issue for Carthage-supported binary frameworks. My library supplies a binary framework through Carthage, and the lib has further dependencies. These transitive dependencies are not shipped with my library, are not built with lib evolution enabled, are non-binary frameworks (i.e built from source), and the final consumer will be building and including them directly.

Stable module interfaces (-emit-module-interface flag) requires library evolution support (-enable-library-evolution). If you enable the former but not the latter, the compiler will produce an invalid module interface file that will cause runtime crashes when used.

If a library A depends on a library B, and A is built with stable module interfaces and library evolution, B must also be built with stable module interfaces and library evolution, unless A only imports B using an @_implementationOnly import, in which case it is safe for B to be built without module stability and library evolution, since in that case you know that B is not part of A's ABI.

1 Like

Yep. When you compile your framework, you're baking in assumptions about your dependencies. With library evolution enabled, those assumptions boil down mostly to "what symbols has this framework made public?" But without library evolution, those assumptions include things like exact size, alignment, and layout of types in the library. If the framework changes private implementation details, there's a huge chance that it'll invalidate assumptions your library made and cause runtime memory corruption and other issues.

There wouldn't really be a way for the compiler or linker to detect the ABI incompatibility. C++ doesn't have a notion of library evolution, so everything you put in headers is basically a contract set in stone.

Yes, this seems like a huge problem. Library evolution exists to reduce the amount of guarantees your framework needs to make about the exact size and layout of types (among other ABI details). If your library depends on something that's not built with library evolution, you inherit all of those guarantees in your framework as well, and if that framework makes ABI breaking changes, it will break you.

You should make sure all your dependencies are built with library evolution enabled, and are properly versioned.

1 Like

Thanks for the detailed answer, it is clear I should not be publishing my library to consumers while this warning is present.

1 Like