-
Notifications
You must be signed in to change notification settings - Fork 10.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add a C++ Interoperability Vision Document #60501
Conversation
docs/CppInteroperability/Roadmap.md
Outdated
| * C and Objective-C interoperability | ||
| * Specifics and trade-offs | ||
| * Goals | ||
| * The approach |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does it make sense to provide some examples of how we import various APIs? Maybe as an appendix at the end?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I think that would be helpful to many people.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be very helpful for me, and would help solidify a lot of the vocabulary.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree as well. I far too often have to help folks experimenting with C++-Interop to get swift-ide-test built just for them to see some examples like these actually.
docs/CppInteroperability/Roadmap.md
Outdated
|
|
||
| **This roadmap is** a sketch, rather than a final design for the “forward” (using C++ APIs from Swift) half of C++ and Swift interoperability. This roadmap outlines some high-level topics related to C++ interoperability, overarching goals that drive the project’s design decisions, a process for evolving C++ interoperability over time, and a collection of specific problems and their potential solutions. | ||
|
|
||
| “Reverse” interoperability (using Swift APIs from C++) is another extremely important part of the interoperability story, however, reverse interoperability has largely different goals and constraints, which necessarily mean a different design and roadmap. Alex Lorenz will be putting together a roadmap for reverse interoperability which outlines these goals and will shed light onto the differences between the two halves of the projects. In the future this roadmap will need to be updated to explain how these two halves fit together and present the “bi-directional” interoperability story that is C++ and Swift interoperability as a whole. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
different goals and constraints
Maybe expand on these constrains and goals: going from some specific Swift API to a broad language with less strong idioms. This actually gives a lot for flexibility for the interop model.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it may be better to just a) describe what "reverse" interop is, and then link a placeholder document that @hyp can fill out.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I disagree that these things are very different. As soon as you can pass function pointers or closures across the language boundary, you effectively have the problems of bidirectional interop. Not to be alarmist, but tackling the two directions independently with the assumption that they are fundamentally different seems like a disaster waiting to happen.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This also speaks to the implicit/explicit conversion discussion we had today. If I pass (in a swift file) a swift string to a C++ API that expects a std::string, that C++ implementation is going to go off and make assumptions about how it can edit and access the data. So we have to think about how C++ would use Swift data types, not just how Swift would use C++ data types.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For reverse interop I suggest eventually we link to the reverse interop document. Also, should the document explicitly call out that using Swift types from C++ code is a form of reverse interop and not the responsibility of forward interop?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can be shorter and sweeter about this, something like: ""Reverse" interoperability, which allows Swift to be used from C++, is an interesting problem, but the technical and design problems it faces are largely independent from those of forward interoperability, and it will be addressed in a separate roadmap document."
It is not correct to link two clauses with ", however,"; you can use a semicolon (or a period) before "however", or you use ", but".
I wouldn't predict anything about needing edits to this document to explain how the halves fit together. If we need that, we can just make those edits to whichever document they're appropriate for.
docs/CppInteroperability/Roadmap.md
Outdated
|
|
||
| In this section, the roadmap will discuss several of the most common C++ API patterns and how they might be imported by the Swift compiler using the goals outlined above. | ||
|
|
||
| ### Definitions |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These definitions feel verbose after reading the proceeding section. I think we can probably just delete the whole thing. Is there any useful information here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I personally appreciated it -- as someone with less understanding of these things
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we can move them to an appendix? Or these should be hoisted above the preceding sections to explain to the reader the terminology being used in the document.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, let's move them to the top of the document and define a few other things. Specifically for owned types, I want to remove the specifics about special members (that will be covered in detail below).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The title “definitions” makes it sound like this is a collection of unrelated terminology, but I believe this is really the list of “common C++ API patterns” you were talking about before. If so, you may want to retitle this section.
I also think there’s a sort of hierarchy that you might want to represent more clearly here by reorganizing and referencing back to previous definitions:
- Reference types
- Owned types
- Trivial types (a specific kind of owned type)
- Projections
- Iterators (a specific kind of projection)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMO these terms are not universally understood, cannot be assumed, and must be defined up-front.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree we should keep these. I still have a difficult time keeping them straight without some examples (even after reading this a couple times).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree, these definitions should be kept in. It really helped my reading of the document.
docs/CppInteroperability/Roadmap.md
Outdated
|
|
||
| **Reference types** have reference semantics and object identity. A reference type is a pointer (or “reference”) to some object which means there is a layer of indirection. When a reference type is copied, the pointer’s value is copied rather than the object’s storage. This means reference types can be used to represent non-copyable types in C++. | ||
|
|
||
| **Owned types** “own” some storage which can be copied and destroyed. An owned type must be copyable and destructible. The copy constructor must copy any storage that is owned by the type and the destructor must destroy that storage. Copies and destroys must balance out and these operations must not have side effects. Examples of owned types include `std::vector` and `std::string`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Swift semantics require that copies can be made all over the place, imported types must fit with these semantics until Swift does not require that types are copied all over.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would a type like the following be a owned type: (It owns some storage but not all)
struct S {
int &unownedReference;
int ownedIntegerField;
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Guess it is a trivial type as per the below definition. So owned type is a super set of trivial types right or are we expecting an explicit copy constructor (directly or transitively)? It may be worth clarifying.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was surprised not to see “value semantics” in this definition, even in a qualified way. Owned types are often value types, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's wrong to say that Swift semantics require copying all over the place. The rampant copying in Swift is almost entirely an implementation artifact, and this fact is easy to demonstrate. Nothing in the Swift documentation gives any assurance of observable differences in address between two objects. The only copies actually required by the language are those needed to make sure that mutations to one var/inout binding don't affect the values of any other bindings.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm confused again: the previous paragraph reads like it's talking about categories of Swift types, and this one reads like it's categorizing C++ types.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was surprised not to see “value semantics” in this definition, even in a qualified way. Owned types are often value types, right?
@beccadax just to clarify, the notion of owned types here is only types that has some storage (because they could potentially expose internal pointers to it through some method). It doesn't necessarily has to have "value semantics" if you are thinking of types that behaves like conceptual values e.g. like String, COW types (assuming their type parameters have value semantics), functional data structures, and the like. E.g. the following would be "owned", and so is a smart pointer because it allocates and stores a ref count and copies that storage in copy constructors.
More semantically, the property that is required is whether a type has "some storage it copies" and can expose inner pointers to it. (It can totally have any others pointer, point to any storage and may have any side-effects.)
// This is an owned type as per the above definition.
struct S {
StringRef name;
int &integerRefence
vector<int> intVector;
...
}
docs/CppInteroperability/Roadmap.md
Outdated
|
|
||
| As mentioned above, swift types fall into two categories: value types and reference types. Value types are relatively simple in Swift, C, and Objective-C, however, the same is not true for C++ where value types may have complex lifetime operations that are used to express non-native concepts (to Swift) such as value types that own memory. This case of owned value types is specifically difficult to map on to Swift as it is novel to the language and does not always work well with the existing model for Swift value types that are trivially copyable. | ||
|
|
||
| #### Trivial types |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It may be helpful to call out that std::array is an example of a trivial types (whereas std::vector is an example of an owned type).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1 -- example would be helpful :)
docs/CppInteroperability/Roadmap.md
Outdated
|
|
||
| This roadmap will refer to trivial value types that hold pointers as “pointer types.” These types include pointers themselves and types which are composed of any pointer types (potentially including other types as well). The pointers held by pointer types refer to memory that is *not owned* by the pointer type (making pointer types a “view” or “projection” into memory). While pointer types are very similar to trivial types with respect to their lifetime operations and the fact that they map similarly in these four language, they differ in the fact that while they themselves are not inherently unsafe, they may be used in unsafe APIs (discussed later). | ||
|
|
||
| #### Owned types |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it possible to have an owned type that holds a pointer and doesn't have a copy constructor?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, if I understand correctly, I don't think that is possible. There would be a copy constructor, even if implicit (and fully inlined).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, we need some better terminology here. (I will include this in the "definitions" section.) What I meant to ask is: "Is it possible to have an owned type that holds a pointer and has a trivial (implicit, compiler generated) copy constructor?"
docs/CppInteroperability/Roadmap.md
Outdated
|
|
||
| As discussed many time before, the C++ language is huge and scattered (refrase). This roadmap allows specific, focused, and self contained evolution proposals to be created for individual pieces of the language and specific programming patterns by providing goals that lend themself to this kind of incremental design and evolution (by not importing everything and requiring specific mappings for specific API patterns) and by framing interop in a larger context that these individual evolution proposals can fit into. | ||
|
|
||
| For example, “virtual type interfaces that API consumers provide implementations for” are a fairly common API pattern in C++. Currently, there is no design for such a pattern, but some Swift contributor may extend interop to provide a clear, native mapping for this API pattern through a self-contained proposal following the goals outlined by this document, and framed by “the approach” described above. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
An oddly specific example, maybe this should instead discuss casting or representing class hierarchies.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree that this is an oddly specific example and deserves some discussion. Am I correct in interpreting this as ABCs?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, exactly. I'm just giving this as an example of how we might evolve to support some specific API pattern. Maybe immortal, immutable owned types would be a better example (where we could allow projections).
docs/CppInteroperability/Roadmap.md
Outdated
| Egor Zhdan will discuss importing the standard library, the Swift C++ standard library overlay, etc. :) | ||
| * * * | ||
|
|
||
| ## Thoughts not incorporated into the above document: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not part of this document, just some things that I was thinking about but didn't know exactly where to put.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe move some of the suggested removals here, i.e. TODOs
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yippee!! love it 🚀
One general comment:
Good compiler feedback
I may have missed it but having really good compiler error messages around interop seems like it should be a high priority.
One thing people love about swift is how understandable it is and as mentioned in the article, this is not the case with C++. So like if I have a C++ class and I think its trivial and its not imported into swift, it would be nice to have a clear message explaining that the type wasn't imported because XYZ, try ABC. Or maybe I though this was a value type that should be imported but really the methods im calling is a projection and we don't import those...
Maybe "high priority" isn't the right way to phrase it for a Roadmap. Id be curious to hear what role that plays in the Roadmap and what we are looking to support in terms of education of interop via the compiler
(I know @zoecarver has been adding these and I have hit these before and found them very helpful. I have also not hit them and wished they were there :))
docs/CppInteroperability/Roadmap.md
Outdated
|
|
||
| As mentioned above, swift types fall into two categories: value types and reference types. Value types are relatively simple in Swift, C, and Objective-C, however, the same is not true for C++ where value types may have complex lifetime operations that are used to express non-native concepts (to Swift) such as value types that own memory. This case of owned value types is specifically difficult to map on to Swift as it is novel to the language and does not always work well with the existing model for Swift value types that are trivially copyable. | ||
|
|
||
| #### Trivial types |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1 -- example would be helpful :)
docs/CppInteroperability/Roadmap.md
Outdated
|
|
||
| Note: in the example above, “start” is a pointer type. The use of this pointer in the “begin” API is unsafe, but the type of start itself is not unsafe. In other words, safety restrictions need not be applied to pointer types themselves but rather their unsafe uses. | ||
|
|
||
| ### Iterators |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe there can be 2 broader sections.
Generally supported:
- Trivial types
- Reference types
Custom mappings:
- Iterators
In which case things would move form Custom mapping to "Generally Supported" as swift gets new features + we improve generally supported features to address issues that inhibit some item in Custom mapping from just being mapped
Something falls into Custom Mapping more because if we just generally imported it, it would be less safe in swift than C++ but we still want to support it, so we Custom map it
Not sure if that makes sense
docs/CppInteroperability/Roadmap.md
Outdated
| * C and Objective-C interoperability | ||
| * Specifics and trade-offs | ||
| * Goals | ||
| * The approach |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I think that would be helpful to many people.
docs/CppInteroperability/Roadmap.md
Outdated
| * The approach | ||
| * Foreign reference types | ||
| * Owned types and value lifetimes | ||
| * Iterators and ranges (by Egor) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| * Iterators and ranges (by Egor) | |
| * Iterators and ranges |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This and similar wont be in the final document.
docs/CppInteroperability/Roadmap.md
Outdated
| * Evolution and process | ||
| * Example: virtual/overridden APIs | ||
| * How interop fits into the ecosystem more generally (lldb, ide, spm) | ||
| * *The* Standard Library overlay (by Egor) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| * *The* Standard Library overlay (by Egor) | |
| * *The* Standard Library overlay |
docs/CppInteroperability/Roadmap.md
Outdated
|
|
||
| ## Introduction | ||
|
|
||
| **This roadmap is** a sketch, rather than a final design for the “forward” (using C++ APIs from Swift) half of C++ and Swift interoperability. This roadmap outlines some high-level topics related to C++ interoperability, overarching goals that drive the project’s design decisions, a process for evolving C++ interoperability over time, and a collection of specific problems and their potential solutions. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure I understand the emphasis on "This roadmap is".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agree, it's much "easier" to read without the emphasis, I also don't see a point in making it bold
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree: if this going for some sort of drop-cap-like effect, it doesn't really work and would be out of place.
More importantly, though, this first sentence shouldn't be the first sentence. The way it's written draws attention to the statement this is a "sketch" and not a "final design"; that's good to include, but it should come later in the paragraph, after you've told us what the document is. "This document is a roadmap for the development of the "forward" half of C++ and Swift interoperability: using C++ APIs from Swift. It outlines..."
"some high-level topics" is vague and suggests you're eliding something that might be important. "This roadmap describes the current state of the art of using C++ from Swift", maybe?
The parallelism between these list items as things being outlined is weak, and it would be much better if you used a verb on each: "outlines some high-level topics", "lays out overarching goals", "defines a process", and "finally, investigates a collection of specific problems in importing C++ APIs and proposes solutions for them".
"Proposes solutions for them", if you choose to use it, would lead very nicely into a sentence about how this is not a "final design" and how everything in it will need to go through evolution.
docs/CppInteroperability/Roadmap.md
Outdated
|
|
||
| **This roadmap is** a sketch, rather than a final design for the “forward” (using C++ APIs from Swift) half of C++ and Swift interoperability. This roadmap outlines some high-level topics related to C++ interoperability, overarching goals that drive the project’s design decisions, a process for evolving C++ interoperability over time, and a collection of specific problems and their potential solutions. | ||
|
|
||
| “Reverse” interoperability (using Swift APIs from C++) is another extremely important part of the interoperability story, however, reverse interoperability has largely different goals and constraints, which necessarily mean a different design and roadmap. Alex Lorenz will be putting together a roadmap for reverse interoperability which outlines these goals and will shed light onto the differences between the two halves of the projects. In the future this roadmap will need to be updated to explain how these two halves fit together and present the “bi-directional” interoperability story that is C++ and Swift interoperability as a whole. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it may be better to just a) describe what "reverse" interop is, and then link a placeholder document that @hyp can fill out.
docs/CppInteroperability/Roadmap.md
Outdated
| Egor Zhdan will discuss importing the standard library, the Swift C++ standard library overlay, etc. :) | ||
| * * * | ||
|
|
||
| ## Thoughts not incorporated into the above document: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe move some of the suggested removals here, i.e. TODOs
docs/CppInteroperability/Roadmap.md
Outdated
|
|
||
| ## Thoughts not incorporated into the above document: | ||
|
|
||
| * Maybe discuss drawing lines for other things. The patterns you cover talk about memory ownership specifically. What about virtual inheritance? What about templates? What about mutexes and file opens? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What makes mutex and files special? C has those as well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can just remove this. I was getting at another API pattern: types that use RAII to define a scope of use. But that should maybe be left for another document.
docs/CppInteroperability/Roadmap.md
Outdated
|
|
||
| * Maybe discuss drawing lines for other things. The patterns you cover talk about memory ownership specifically. What about virtual inheritance? What about templates? What about mutexes and file opens? | ||
| * Safety: It is convention in C and Objective-C to traffic in pointers. In Swift this is written as “unsafe” meaning the unsafety is clearly disclosed. C++ often prefers value types and “fancy pointers” which do not disclosure their unsafety in their name. | ||
| * Safety: because C++‘s un-oppionated APIs are funneled into Swift, they are generally forced through unsafe pointer which loses information. This loss of semantic information leads to mis-use, if more information was captured it would be more difficult, or even impossible, to make such mistakes. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| * Safety: because C++‘s un-oppionated APIs are funneled into Swift, they are generally forced through unsafe pointer which loses information. This loss of semantic information leads to mis-use, if more information was captured it would be more difficult, or even impossible, to make such mistakes. | |
| * Safety: because C++‘s un-opinionated APIs are funneled into Swift, they are generally forced through unsafe pointer which loses information. This loss of semantic information leads to misuse, if more information was captured it would be more difficult, or even impossible, to make such mistakes. |
docs/CppInteroperability/Roadmap.md
Outdated
| * Maybe discuss drawing lines for other things. The patterns you cover talk about memory ownership specifically. What about virtual inheritance? What about templates? What about mutexes and file opens? | ||
| * Safety: It is convention in C and Objective-C to traffic in pointers. In Swift this is written as “unsafe” meaning the unsafety is clearly disclosed. C++ often prefers value types and “fancy pointers” which do not disclosure their unsafety in their name. | ||
| * Safety: because C++‘s un-oppionated APIs are funneled into Swift, they are generally forced through unsafe pointer which loses information. This loss of semantic information leads to mis-use, if more information was captured it would be more difficult, or even impossible, to make such mistakes. | ||
| * Safety: the subtly different paradims for automatic lifetime management in Swift and C++ mean there is added unsafety to a language like c where lifetime is manually managed, or a language like objc where the lifetime matches swift’s paradims. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| * Safety: the subtly different paradims for automatic lifetime management in Swift and C++ mean there is added unsafety to a language like c where lifetime is manually managed, or a language like objc where the lifetime matches swift’s paradims. | |
| * Safety: the subtly different paradigms for automatic lifetime management in Swift and C++ mean there is added unsafety to a language like C where lifetime is manually managed, or a language like ObjC where the lifetime matches Swift’s paradigms. |
docs/CppInteroperability/Roadmap.md
Outdated
| * Safety: It is convention in C and Objective-C to traffic in pointers. In Swift this is written as “unsafe” meaning the unsafety is clearly disclosed. C++ often prefers value types and “fancy pointers” which do not disclosure their unsafety in their name. | ||
| * Safety: because C++‘s un-oppionated APIs are funneled into Swift, they are generally forced through unsafe pointer which loses information. This loss of semantic information leads to mis-use, if more information was captured it would be more difficult, or even impossible, to make such mistakes. | ||
| * Safety: the subtly different paradims for automatic lifetime management in Swift and C++ mean there is added unsafety to a language like c where lifetime is manually managed, or a language like objc where the lifetime matches swift’s paradims. | ||
| * Memory safety: lifetimes, reference counting, type safety: importing types as “Any” or “OpaquePointer” is not type safe. Also: use after free, bound checking, null pointer, etc. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| * Memory safety: lifetimes, reference counting, type safety: importing types as “Any” or “OpaquePointer” is not type safe. Also: use after free, bound checking, null pointer, etc. | |
| * Memory safety: lifetimes, reference counting, type safety: importing types as `Any` or `OpaquePointer` is not type safe. Also: use after free, bound checking, null pointer, etc. |
Good catch! This is an excellent observation and something that should 100% be called out in this document. I will add a section for that. Thanks @Huddie! |
docs/CppInteroperability/Roadmap.md
Outdated
|
|
||
| ## The approach | ||
|
|
||
| The approach for importing C++ APIs into Swift is derived from the fact that both our goals, and C++ as a language, necessitate that not all APIs are imported, and to import an API the user must provide semantic information in some cases. Further, in order to realize our goals (especially the goals of safety and clear, well defined mappings), it is impossible to import any arbitrary C++ API. APIs that are imported by Swift must fall into one of our understood categories or be annotated with semantic information that can be used to classify them into a Swift idiom. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
From this sentence it is not clear how much coverage of C++ types/APIs you expect from these categories. Not sure if you want to clarify that or not.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
our goals, and C++ as a language, necessitate that not all APIs are imported
I don't know that this claim has been adequately supported by arguments. I understand that importing all APIs is a hard problem and we shouldn't expect it in the near term, but nothing I've read so far would lead me to the conclusion above.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure, I think I can update this. The point I'm trying to make here is that we want to start from a place of not importing all APIs. In this initial version of interop that is outlined by the roadmap, not all APIs will be imported. This is necessitated by the fact that we want specific mappings for API patterns, and the fact that not all C++ APIs fit into one of these patterns. Does that make sense?
Of course, I'm not opposed to eventually supporting all C++ API patterns. If someone wanted to add support for all of these, I don't see any reason we shouldn't. The document outlines exactly how this can be done over time in the evolution section.
docs/CppInteroperability/Roadmap.md
Outdated
|
|
||
| **Owned types** “own” some storage which can be copied and destroyed. An owned type must be copyable and destructible. The copy constructor must copy any storage that is owned by the type and the destructor must destroy that storage. Copies and destroys must balance out and these operations must not have side effects. Examples of owned types include `std::vector` and `std::string`. | ||
|
|
||
| **An iterator** represents a point in a range. Iterator types must provide a comparison operator and increment operator. Iterators can be returned by `begin` and `end` methods to form a range. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Iterator types must provide a comparison operator and increment operator
I think we should say "equality operator" instead of "comparison operator" and "pre-increment iterator" instead of "increment operator" to make it more precise.
But can we instead say that iterator types should conform to C++ named requirements for iterators without listing the specific requirements?
docs/CppInteroperability/Roadmap.md
Outdated
|
|
||
| **C++, as a language:** C++ is an stubbornly un-opinionated language. There is little to no consensus on anything from naming, to generic programming, value categories, or error handling (to name a few things). This lack of opinion is in sharp contrast to Swift where there is generally one, specific way to do things. Even in the C++ Standard Library there is a rather un-standard set of API models. The un-opinionated nature of C++ means there are few, if any guarantees about a given C++ API, because the same functionality may be implemented using many different language features or programming paradigms. When consuming an arbitrary C++ API, you must be prepared for *anything*. When consuming Swift APIs, this is not the case. Swift is an opinionated language which means rather than consuming *any* API pattern you are consuming *some*, specific API pattern. Swift clearly defines patterns for naming, generic programming, value categories, error handling, etc. and it is rare that a codebase deviates from the standard practice. | ||
|
|
||
| **C and Objective-C interoperability:** C++ interoperability will have to build off of the previous art of C and Objective-C interoperability. Starting with the name, C++ is a superset of C, which means C++ interoperability has a great starting place to build off of. C is a simple language without many high level constructs, so importing C APIs is, relatively, simple. On the other hand, Objective-C adds some high level constructs, such as reference types and a generics model. However, unlike C++, Objective-C has strong idioms; there are only so many patterns for doing some specific thing. Further, Objective-C was able to be updated to better interoperate with Swift, the generics model is a good example of this. So, while C and Objective-C interoperability is a good starting place, the model for importing these languages cannot be taken wholesale for C++ interoperability. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| **C and Objective-C interoperability:** C++ interoperability will have to build off of the previous art of C and Objective-C interoperability. Starting with the name, C++ is a superset of C, which means C++ interoperability has a great starting place to build off of. C is a simple language without many high level constructs, so importing C APIs is, relatively, simple. On the other hand, Objective-C adds some high level constructs, such as reference types and a generics model. However, unlike C++, Objective-C has strong idioms; there are only so many patterns for doing some specific thing. Further, Objective-C was able to be updated to better interoperate with Swift, the generics model is a good example of this. So, while C and Objective-C interoperability is a good starting place, the model for importing these languages cannot be taken wholesale for C++ interoperability. | |
| **C and Objective-C interoperability:** C++ interoperability will have to build on the previous art of C and Objective-C interoperability. Starting with the name, C++ is a superset of C, which means C++ interoperability has a great starting place. C is a simple language without many high level constructs, so importing C APIs is relatively simple. On the other hand, Objective-C adds some high level constructs, such as reference types and a generics model. However, unlike C++, Objective-C has strong idioms; there are only so many patterns for doing some specific thing. Further, Objective-C was able to be updated to better interoperate with Swift: the generics model is a good example. So, while C and Objective-C interoperability is a good starting place, the model for importing these languages cannot be taken wholesale for C++ interoperability. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This paragraph gives me the same sense, of putting off the real content.
docs/CppInteroperability/Roadmap.md
Outdated
|
|
||
| Swift programmers have used C++ APIs since Swift 1.0 through an Objective-C or C bridging layer. It is important that programmers are able to *incrementally* remove these (potentially huge) bridging layers and start using their C++ APIs directly. To make this transition incremental, C++ interoperability must not change the way Swift imports any existing C or Objective-C APIs, even when C++ interoperability is enabled. | ||
|
|
||
| It is *not* a goal to import every C++ API into Swift. APIs that are imported by Swift should feel Swifty and idiomatic. There must be an easy way to add annotations so that APIs become importable, especially when updating APIs directly is not an option (because the source code is not available). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Without some idea of which kinds of APIs you intend not to import, this statement is perhaps needlessly alarming. For example, I can live without the ability to use function-style macros from Swift, but I can't think of any other category of C++ APIs that obviously should be off the table, and you can make interop arbitrarily useless to me depending on where you draw that line.
Keep in mind that vector<bool> is a specialization, and if you're going to prevent me from using vector<T> in code that's generic on T, that would be pretty hard to swallow.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I disagree that it's needlessly alarming. If the goal of the first cut of this interop is to import only a manageable subset of C++, saying so in the goals section seems reasonable. It has to be said at some point, otherwise we're back to the previous (very challenging) plan of trying to import everything in a v1 implementation, which is what led us to here in the first place.
That said, I think concrete examples of what will and won't be imported will make this whole document easier to understand. That's a big part of what's lacking from all of this, so it still seems nebulous and scary and possibly what Dave is responding to.
docs/CppInteroperability/Roadmap.md
Outdated
|
|
||
| ### Definitions | ||
|
|
||
| **Reference types** have reference semantics and object identity. A reference type is a pointer (or “reference”) to some object which means there is a layer of indirection. When a reference type is copied, the pointer’s value is copied rather than the object’s storage. This means reference types can be used to represent non-copyable types in C++. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You've set me up with the introduction that we're about to “discuss several of the most common C++ API patterns,” so when I get here I understand you to be categorizing C++ types. But the last sentence above leads to the opposite understanding.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
reference types can be used to represent non-copyable types in C++
This idea conflicts with the idea that things should not become less safe when imported into Swift. In C++, non-copyable types are idiomatically used to enforce single-ownership semantics, on which the safety of other constructs may depend. Representing them directly as copyable references in Swift undermines that. One solution could be to import a non-copyable C++ T as a class Shared<T> in Swift, but the API necessary to make the indirection evident will be ugly until Swift itself gets new features… like at least partial direct support for non-copyable types.
docs/CppInteroperability/Roadmap.md
Outdated
|
|
||
| ### Reference types | ||
|
|
||
| Swift types fall into two categories: value types and reference types. The same is true for Objective-C types, which makes importing types easy: structs are imported as value types and Objective-C classes are imported as Swift class types. The same is not true for C++. In typical C++ fashion, there is no clear idiom for defining reference types. All types in C++ are declared in the same way, but some types have reference semantics and others don’t. To be able to express reference types in Swift natively, users must annotate their reference types as such, which will tell the compiler to import them as Swift classes (which have the same semantics). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In typical C++ fashion, there is no clear idiom for defining reference types.
This is not really a distinction between Swift and C++. I can create structs with reference semantics and classes with value semantics in Swift.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, Swift classes do not have the same semantics as C++ reference types. Typically when C++ is used with reference semantics, types are handled through explicit pointers, so shared_ptr<Foo> (or Foo*) is treated as a reference to Foo even if Foo itself would otherwise have value semantics. Idiomatic C++ actually gives you quite a bit more information than idiomatic Swift does in this respect.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pls explain how it's possible to create struct with reference semantics in Swift, as from my understanding, Swift's struct always "behaves" as if it was just a value type, i.e. it's copied when passed/dereferenced. Under the hood there might be some optimizations, such as copy-on-write in collections, for example, but at the surface level structs always behave like value types.
... unless there are mutating functions and/or inout parameters, but those apply to functions and not to the structs themselves. Or am I missing something else?
0484178
to
167f7d0
Compare
|
Hello all! Thank you for all of these extremely helpful comments :) I re-worked a lot of the document, and added quite a bit more to it. I have not addressed all of the comments above, but I am planning to spend most of tomorrow doing that (so don't be worried if your comment has not been addressed yet). I will let you know once I feel that I've addressed all the above comments, at that point, please feel raise the issue again if you feel it hasn't been addressed sufficiently. Thanks again for all the feedback and thoughts! |
docs/CppInteroperability/Roadmap.md
Outdated
|
|
||
| In this section, the roadmap will discuss several of the most common C++ API patterns and how they might be imported by the Swift compiler using the goals outlined above. | ||
|
|
||
| ### Reference types |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It may be good to also discuss the possibility of having a "Swift reference type" or a foreign reference type that has Swift reference counting.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would suggest starting with (trivial and non-trivial) value types, since they're the default patterns we're going to try to recognize.
You can explain the value/reference type distinction once and then assume it later; no need to repeat it in different sections.
|
I don't know how the Swift team is reacting to the Val object model description. If the feeling is that it's too distant and speculative to be relevant to this roadmap, I certainly understand that, but I thought it was worth pointing one small finger at 👉 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for the document. I didn't get through it all (and I understand I'm not necessarily its intended audience anyway) but I appreciate this being out in the open. It makes me realise how difficult this task really is.
I added some "editorial" comments. Hope you can feel my positive intention behind them :).
Of course, thank you very much :) |
… of the document; move the 'lifetiem and safety' section to the end.
Also, rename to reflect that this is the forward-interop vision.
…paragraph about source/abi stability.
f61a0b8
to
12a67e9
Compare
…ing on the forums).
…tions Suggestions for the C++ forward interop vision
…things import automatically and have a good Swift analog.
|
Closing in favor of the Swift Evolution PR: swiftlang/swift-evolution#2057 |
The markdown document can be viewed here: https://github.com/zoecarver/swift/blob/docs/interop-roadmap/docs/CppInteroperability/ForwardVision.md