If that's the case, then why does this matter at all to Swift's own language design? When people use C, they know C's limitations, and I think they can accept Swift isn't going to magically lift them. If a C module transitively imports a Swift or mixed-source module, I would expect it to leak the C interface of that module through the C module, but it doesn't seem fundamental to me that it'd affect the Swift side of things; we could still conceivably say that to get the Swift part of the interface, the Swift code has to explicitly import it.
Unless I misunderstand, what it gives us is the ability to use C without having to re-export everything. As @Joe_Groff mentioned upthread, sometimes there are reasons to use C right now where people might prefer to stay in Swift-only land. If they had the option to do that without the downside of having to re-export it might be very useful.
I understand that it's not a regression but it is something I think we should fix if we can (and it sounds like we can, the only question is whether we should given source compatibility, etc). The behavior of (2) and (3) is extremely undesirable IMO even if it is what we have today.
Isn't there a path forward that might break source compatibility but allows for automatic migration (at least via a temporary / deprecated
@leaky import I mentioned earlier).
The compiler can't import "just the C part" of a Swift module because it doesn't know how the types match up with the Swift types. (The way this works in the importer is by basically throwing away the C declarations once they've been mapped back to Swift declarations.) I guess we could treat that as "loaded but not visible", though?
It's the kind of thing that's technically feasible but definitely non-trivial, and I haven't thought too much about the tricky bits. I was hoping we could define the problem away with the work on "implementation-only imports" and "re-exported imports", but that doesn't seem to be the case, and that means it's just a separate issue. Which means I won't be working on it right now.
Would this mean it's available to the library but not the client? I think this is pretty much the direction I'm suggesting, isn't it?
I see. We've lived with it this long so I suppose that's ok. I'll be happy as long as it eventually gets some attention...
Yeah, "loaded but not visible" is my attempt to describe what the current Swift
Okay, hm. I'll have to think about how to do this C thing.
FWIW, I would interpret "not visible" to include operators and extensions (which current Swift
import does include)...
:-) I'm describing where we want to be rather than where we are, but since I did just say that probably won't get fixed as part of this work, maybe I should stop.
I’d like to note that this is not my expectation as an Objective-C developer. Clang modules have explicit export lists, and while
export * is commonplace, it isn’t the only way to do things.
It is in fact exactly how the Clang modules dialect of C works.
@import UIKit only exposes
NSObject because UIKit’s module map says so.
On top of that (regardless of modules), C and Objective-C libraries can distinguish between public imports (ones that are in their public headers) and private ones (ones in private headers or implementation files only). They can also reduce the number of public imports required by using forward declarations. Swift doesn’t have either of these things.
It seems to me that C and Objective-C compatibility is being used to argue for a semantics that’s leakier than C or Objective-C require.
I don’t have the expertise that @jrose does in this area so I have been accepting his statement of the problem. If we can get away with more controlled semantics when dealing with C and Objective-C interop I would of course be very happy with that!
Ooh, I was hoping no one would bring up
export *. Swift is currently pretty broken when people don't do
export *, and I'm not sure Clang works well with it either. But on top of that, the module map for an Xcode-built framework containing Swift code currently always includes
export * for the Swift part of the module, with no way to configure it. Part of the reason for this is because it's never a good thing when C code behaves differently with and without modules enabled, and while Swift generated headers currently don't support modules-off very well, they should be able to (SR-805).
We may not get forward declarations in Swift (for probably dozens of reasons), but this thread definitely covers adding the implementation-only imports. The tricky bits are around the things you want to expose as API, and then around the things you want to expose as ABI but not API. (The latter is where forward declarations are "usually good enough" in C.)
My bit to the discussion from user of the Swift and SPM.
I use SPM and I have plenty of targets. SPM requires you to explicitly specify the dependencies for each target inside
Package.swift, but on the other side, Swift allows me to use transitive dependencies in my source code without updating
Package.swift. Sometimes this breaks the build phase, because, as I think, the compiler meets some
import-s of not yet built modules/targets. Then I have to dig into the problem, find the missing dependency and update my module inside
Package.swift so build system would build the libraries in the right order. Usually this is a pain, because
swift build will just print some not very helpful error about
I agree that
NSObject should be usable if you import
UIKit, but for non-Apple modules, I think, it will be beneficial to have the ability to disable the use of the transitive deps.