Feature proposal: Accessing non-public C++ members from Swift

Hey folks, I'm working on a feature that will allow Swift to access non-public C++ members, and I'm interested in hearing your feedback.

You can find the full pitch here.

The tl;dr is to introduce a C++ class annotation, SWIFT_PRIVATE_FILEID, which will specify where Swift extensions of that class will be allowed to access its non-public members:

class SWIFT_PRIVATE_FILEID("MyModule/MyFile.swift") Foo { ... };

The goal of this feature is to help C++ developers incrementally migrate the implementation of their C++ classes to Swift, without breaking encapsulation and indiscriminately exposing those classes' private and protected fields.

Note that the specific feature I am proposing here is not @implementation for C++: it does not allow Swift extensions to directly implement C++ methods declared in a header file. That is still unsupported today (no matter the access level), but something like @implementation for C++ would certainly require non-public member access in order to be useful.

8 Likes

Would the implementation as you've spelled it out allow removal of attributes like SWIFT_CONFORMS_TO_PROTOCOL?

It sounds like what you're planning to do involves telling the Swift compiler that the synthesized "Clang module" for the module from the modulemap is the "same" file as MyModule/MyFile.swift, in the context of class Foo.

Would that allow asserting protocol conformance in a more direct way than an annotation on the C++ source file? If we are claiming that MyFile.swift is the "same" file as the one that introduced Foo, it seems natural to me to extend MyFile.swift all of the special rights given to the Clang module as far as protocol conformance goes.

I think this is a great approach, extending Swift's notion of private/fileprivate out to specific C++ classes.

Doug

Hey all, I'd like to add some critiques and voice some concerns I had about this (and the general direction of how similar features are approached).

First, strictly about this proposal, I believe this is the first of these SWIFT_ macros that directly mention a Swift file/module by name (questions of how these paths should be handled aside):

class SWIFT_PRIVATE_FILEID("MyModule/MyFile.swift") Foo { ... };

While this is absolutely the nicest of the alternatives considered, I do not like how it "leaks" Swift into the C++ source - for lack of a better description on my part; as a stark contrast to most other SWIFT_ macros that only make explicit some behaviour or constraint that stems from C++ itself (e.g. "returns independent value")

To offer a couple other alternatives for consideration:

  • A more explicit @cxxPrivate (something like @cxxPrivate(fileprivate Foo)); this doesn't address the "access is authoritatively specified at the site where an entity is defined" point in the proposal, but I'd argue that importing something external into swift is its declaration site within swift (disregarding the modulemap)
  • speaking of which, the modulemap is the point of introduction for a C++ header/object into swift, further syntax there or a supplemental module.access.modulemap file, perhaps?

That said, some general feedback on the "direction" as it were (this isn't directly relevant to this proposal, but I feel it's necessary to communicate across why I don't like "leaking" one language into the other when doing interop):

It seems to me that c++interop is very one-sided (not in the sense that only one side can call the other, but rather in the sense that the C++ source is forced to supply any information that cannot be inferred automatically); that's not what interopability should be like.
The Ideal :tm: interop model would have both sources completely oblivious of the other (which would also make it simple to straight up replace foo.cpp with Foo.swift, or the other way around). This is (of course) unreasonable, but once (what I think is) the mistake of shoving a bunch of information that cannot be communicated cleanly across the interop bridge only into one side is made, replacing either side becomes increasingly difficult.
This is true for other attributes like SWIFT_CONFORMS_TO_PROTOCOL as well.

I think Swift has done a stellar job of keeping the C++ from leaking into the Swift source so far, I'd love to see it not leak Swift into C++ as well.

Sorry for the overly long post.

2 Likes