-
Notifications
You must be signed in to change notification settings - Fork 1.5k
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
Carbon <-> C/C++ interoperability #80
Conversation
Move firebase support into a src directory. (#15)
Update fork
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 the record, I don't consider my top-level concerns with this PR resolved, and I'm not confident I'll be able to affirm this proposal until they are, but I think for now they're best discussed in the context of PR #83 (see forum post).
- Carbon must be able to compile C++ headers in order to translate names and | ||
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.
This is certainly true, but the toolchain used to compile C++ object files doesn't need to be the same as the toolchain that compiles Carbon object files. Allowing them to differ could permit a much more incremental migration: rather than a massive up-front project to switch C++ toolchains (and standard libraries) across the codebase before you even start using Carbon, you could incrementally migrate C++ headers if and when they are included from Carbon.
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.
Noted, added "even if it's not used to compile the C++ object files"
I think your statement about migrating headers is off though, as if you don't have the underlying .cc files compiling with the Carbon toolchain, then Carbon tools shouldn't be expected to be able to migrate the code. Similarly, that cc code may not be able to call into Carbon, for the reasons noted in the next bullet.
- While arbitrary C++ code may be able to call into Carbon code that has been | ||
pre-compiled into a library, a more complex interaction like C++ code | ||
calling Carbon templates requires compilation of _both_ languages together. |
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.
Could this take the form of e.g. compiling the Carbon template to portable C++ (rather than Clang AST), for use by the user's C++ toolchain?
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 seems like an infeasible constraint on Carbon to me, considering it's mainly for templates. Noted as an alternative approach.
$if platform == LP64 | ||
fn ToCLong(var Int64: val) -> Int64 { return val; } | ||
$else | ||
fn ToCLong(var Int64: val) -> Int32 { return (Int32)val; } |
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.
My comment isn't about the particular usage depicted in the example, it's about any possible usage. As far as I can tell my comment is still accurate, but evidently it's still not clear. Maybe we should set up a VC to try to get on the same page about this?
- For example, users may still write platform-specific code like | ||
`var Cpp.long: x = ...; var Int32: y = (Int32)x;`. |
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.
How is this any different from the user writing code like var Int32: x = CppCompat.FromCLong(...)
, or var auto: x = (Int32)...;
?
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.
Pragmatically, it's not that different. That's why "may still" instead of "uniquely" -- the point is to emphasize that this solution has problems, too.
- If platform-specific types are added, it may be worth considering whether we | ||
should promote these to Carbon primitive 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.
We might as well start considering it now, because I guarantee you that people will introduce types like Cpp.long
if Carbon doesn't provide them. I can't be sure how prevalent they will be, but I'm not going to be the only one who will be uncomfortable using an inherently type-unsafe API like the one proposed 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.
Thank you for being explicit, and I think I'd correctly guessed your leaning from your other comments. I think my response is captured on the line above.
- These types are likely to leak beyond C++ interoperability code, creating | ||
friction in using APIs that are designed either only for variable-size types | ||
or for fixed-size types, hindering API reuse. Overlapping implementations | ||
and increased maintenance costs are a likely result. |
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 arguments still seem too vague and speculative to be persuasive to me. Won't the very friction you're concerned about tend to discourage the API leakage that would cause it? A concrete example might help me envision why this leakage is likely to happen, and why it's likely to be harmful.
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.
Sorry, I'm not sure I can provide concrete examples, other than to say that it's my understanding Swift has seen similar problems with their Obj-C layer that they didn't anticipate.
I view this as something that could flip in the decision, but my sense is variable size types probably won't be accepted -- thus the choice of defaults. Unless there's more discussion in the review that clearly indicates a leaning in the other direction, I'll leave it as is.
### Mapping similar built-in types | ||
|
||
When it is not possible to convert a non-owning reference or pointer to a C++ | ||
data structure or vocabulary type into a suitable Carbon type, the actual 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.
See 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.
Sending the comments that I wrote so far, only reviewed two files.
|
||
## Acknowledgements | ||
|
||
This borrows significantly from the structure of Swift's interoperability plan |
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 borrows significantly from the structure of Swift's interoperability plan | |
This proposal borrows significantly from the structure of Swift's interoperability plan |
vocabulary types. | ||
- Mappings should be easy to maintain. | ||
- We should provide a syntax for transparently, automatically exposing a | ||
subset of Carbon types and interfaces to C++ code without custom bridge |
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.
Do you really mean Carbon interfaces or just Carbon APIs in general, including free functions and constants that are not nested within a type?
|
||
Non-goals: | ||
|
||
- We will not make Carbon -> C++ migrations as easy as C++ -> Carbon migrations. |
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 feel like you meant "interop" instead of "migration". I don't think we are designing any support for Carbon -> C++ migration (so I find it surprising to even mention it here), but calling Carbon from C++ is necessary for C++ -> Carbon migration.
So to avoid confusion I'd say "We prioritize calling C++ from Carbon. Calling Carbon from C++ will not be necessarily as easy."
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.
Done, roughly.
|
||
The design for interoperation between Carbon and C++ hinges on: | ||
|
||
1. A focus on types, and simple overload sets built from those 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'm not sure what "a focus on types" means. Does it mean that interop for free functions would be not as good as interop for structs?
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 either. :)
How about:
- The ability to interoperate with a wide variety of code, such as
classes/structs, not just free functions.
|
||
> References: [Templates and generics](templates_and_generics.md). | ||
|
||
Carbon generics will require bridge code that hides the generic. This bridge |
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 why -- is there some implementation difficulty that you foresee? Exposing a Carbon generic as a C++ template should be rather doable even if Carbon generics are compiled separately.
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 clear that's that straightforward, given how generics are done. But maybe a template could be auto-generated? I haven't really thought that through. Added an open question to templates_and_generics.md
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.
Indeed, it seems substantially harder to expose Carbon templates than to expose Carbon generics. This makes me wonder whether "template" and "generic" somehow got reversed in these two sections, and what we mean is that Carbon templates must be wrapped in a Carbon generic before they can be exposed?
DIRECTION_WEST, | ||
DIRECTION_NORTH, | ||
DIRECTION_SOUTH, | ||
} __attribute__((carbon_enum("East:West:North:South")); |
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 Swift we instead strip a common prefix from enumerators.
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.
Good point, added a note.
|
||
## C/C++ enums in Carbon | ||
|
||
C++ enums will generally translate naturally to Carbon, whether using `enum` or |
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 good to mention the cases that don't fall into the general pattern.
C and C++ APIs sometimes (how often? IDK) rely on enums being implicitly convertible to integers. Ignoring this issue will lead to some APIs being non-ergonomic so it is OK to punt on it being ergonomic, but we should provide a technical ability for C++ enums specifically. It might be the case that by default Carbon enums will not be convertible to integers at all to avoid even a remote possibility of anyone relying on numeric values (I'd certainly argue for it).
C and C++ APIs also sometimes cast arbitrary bit patterns into enum values, which Carbon enums might decide to prohibit. Ignoring this issue will lead to miscompiles, so I think we have to think about it. Maybe the Carbon enums imported from C++ should not be assumed to have free bit patterns.
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.
Adding open questions to cover this. Is that sufficient for now?
} | ||
``` | ||
|
||
We would expect to generate equivalent C++ code: |
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 C++ code will likely need an attribute to specify the size of the enum (sizeof(Direction)
), because the size should match the Carbon ABI exactly to enable direct bridging, and Carbon should feel free to choose and change its rules about determining the enum size.
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.
BTW, I'd really like this proposal to introduce some terms around bridging, specifically:
(1) a term that denotes zero-cost bridging of types that have identical memory layout in C++ and Carbon,
(2) a term that denotes bridging that involves running code that converts the memory layout (the code can be either compiler-written or user-defined, does not matter),
(3) (maybe) a more specific version of (2) where we create an independent copy of the value,
(4) (maybe) a more specific version of (2) where we destroy the source of the value being bridged.
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 the attribute something that exists? For now, I've added an open question. Although, would setting appropriate types on enum classes actually solve it?
For terms, any suggestions for a glossary? Does Swift have such terms?
Co-authored-by: Dmitri Gribenko <[email protected]> Co-authored-by: Geoff Romer <[email protected]>
Co-authored-by: Geoff Romer <[email protected]>
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.
Partly through comments, aware I still have some left.
vocabulary types. | ||
- Mappings should be easy to maintain. | ||
- We should provide a syntax for transparently, automatically exposing a | ||
subset of Carbon types and interfaces to C++ code without custom bridge |
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 it's fine to change this to APIs
@@ -0,0 +1,346 @@ | |||
# Carbon <-> C/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 is trying to echo the BLUF style, so I think it's better to keep, even though it can get out of date. The intent is to summarize, and help people get a picture in their head before they delve into details (if they even choose to).
I can move the goals and philosophy out, though.
|
||
Non-goals: | ||
|
||
- We will not make Carbon -> C++ migrations as easy as C++ -> Carbon migrations. |
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.
Done, roughly.
- Carbon must be able to compile C++ headers in order to translate names and | ||
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.
Noted, added "even if it's not used to compile the C++ object files"
I think your statement about migrating headers is off though, as if you don't have the underlying .cc files compiling with the Carbon toolchain, then Carbon tools shouldn't be expected to be able to migrate the code. Similarly, that cc code may not be able to call into Carbon, for the reasons noted in the next bullet.
- While arbitrary C++ code may be able to call into Carbon code that has been | ||
pre-compiled into a library, a more complex interaction like C++ code | ||
calling Carbon templates requires compilation of _both_ languages together. |
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 seems like an infeasible constraint on Carbon to me, considering it's mainly for templates. Noted as an alternative approach.
C++ enums will generally translate nautrally to Carbon, whether using `enum` or | ||
`enum class`. In the other direction, we expect Carbon enums to always use | ||
`enum class`. |
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.
@gribozavr, for clarity, would you voice in support of leaving this for now pending further enum design, or would you prefer it be removed?
|
||
> References: [Templates and generics](templates_and_generics.md). | ||
|
||
Carbon generics will require bridge code that hides the generic. This bridge |
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 clear that's that straightforward, given how generics are done. But maybe a template could be auto-generated? I haven't really thought that through. Added an open question to templates_and_generics.md
Carbon will provide specialized operator template functions for C++ types which | ||
are implemented as-if calling a C++ function template in bridge code which in | ||
turn did the exact operator call, including ADL-based name lookup and overload | ||
resolution. Carbon code can then override this behavior by providing specialized | ||
patterns for operators when interacting with Carbon 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.
Honestly, I'm not familiar enough with ADL to give a good example. @chandlerc can you help 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.
A few more addressed...
$if platform == LP64 | ||
fn ToCLong(var Int64: val) -> Int64 { return val; } | ||
$else | ||
fn ToCLong(var Int64: val) -> Int32 { return (Int32)val; } |
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, feel free to grab a time.
- These types are likely to leak beyond C++ interoperability code, creating | ||
friction in using APIs that are designed either only for variable-size types | ||
or for fixed-size types, hindering API reuse. Overlapping implementations | ||
and increased maintenance costs are a likely result. |
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.
Sorry, I'm not sure I can provide concrete examples, other than to say that it's my understanding Swift has seen similar problems with their Obj-C layer that they didn't anticipate.
I view this as something that could flip in the decision, but my sense is variable size types probably won't be accepted -- thus the choice of defaults. Unless there's more discussion in the review that clearly indicates a leaning in the other direction, I'll leave it as is.
- If platform-specific types are added, it may be worth considering whether we | ||
should promote these to Carbon primitive 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.
Thank you for being explicit, and I think I'd correctly guessed your leaning from your other comments. I think my response is captured on the line above.
- For example, users may still write platform-specific code like | ||
`var Cpp.long: x = ...; var Int32: y = (Int32)x;`. |
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.
Pragmatically, it's not that different. That's why "may still" instead of "uniquely" -- the point is to emphasize that this solution has problems, too.
…lang into interop-proposal
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.
And now I think I've addressed comments...
|
||
## C/C++ enums in Carbon | ||
|
||
C++ enums will generally translate naturally to Carbon, whether using `enum` or |
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.
Adding open questions to cover this. Is that sufficient for now?
DIRECTION_WEST, | ||
DIRECTION_NORTH, | ||
DIRECTION_SOUTH, | ||
} __attribute__((carbon_enum("East:West:North:South")); |
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.
Good point, added a note.
} | ||
``` | ||
|
||
We would expect to generate equivalent C++ code: |
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 the attribute something that exists? For now, I've added an open question. Although, would setting appropriate types on enum classes actually solve it?
For terms, any suggestions for a glossary? Does Swift have such terms?
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 we should be seriously considering removing the $extern
mechanism, and instead making the interoperability much more symmetric: permit Carbon libraries to be imported into C++ just as we permit C++ libraries to be imported into Carbon. I don't think it's feasible to ask for any Carbon type that's used with a C++ template to be $extern
'd; that would create problems for the use of C++ templates from Carbon generics and from Carbon templates, where the requirement to $extern
would be imposed on the facet type or on the user of the Carbon template, respectively.
I think a lot of the detail here is in areas where we can't really agree on that level of detail yet, because we don't know how that language feature of Carbon should work. So I think what we really need to decide first is the form we want interoperability to take: do we have the full bidirectional interoperability I described above, or do we treat use of Carbon from C++ as somewhat second-class, per this document, or something else? Do we expect to have a single toolchain that can understand and generate code for both Carbon and C++ sources, or do we expect the Carbon compiler to spit out a header file that a C++ compiler that doesn't understand Carbon can consume? I would want the outcome of this PR to be that we have clarity and agreement on those kinds of questions.
The exploration of details here is useful, but once we have the high-level decisions about the form of interoperability, it seem to me that we should be feeding that into the design review of all the other aspects of Carbon rather than trying to handle it centrally. To that end, I would suggest keeping all of the specific areas of design details, to flesh out the direction you're describing, but marking them as "to be finalized later", much like was done for the overall design document.
- `$extern("Cpp")`: Indicates that Carbon code should be exposed for C++. | ||
Similarly, `$extern("Swift")` might be used to indicate exposure for Swift at | ||
some point in the future. |
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 description wasn't enough for me to infer what this syntax means or where it would appear. From reading ahead, it appears the intent is that this is an annotation attached to individual Carbon declarations, and that it causes the C++ wrapper header to provide a corresponding declaration to C++ code that matches the Carbon declaration.
Does this in any way change the meaning of the Carbon declaration, or only expose it? For example, extern "C"
in C++ can change the calling convention and name mangling. Is the idea that $extern("Cpp")
changes the calling convention and provides a C++-compatible symbol name? Or does it just indicate that the entity is exposed, and leave the compatibility / interoperability to the generated .6c.h
file?
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.
My hope would be that the Carbon declaration doesn't change meaning from within Carbon code.
Instead, any semantic differences should be only in the exposed form of the API as it is accessed from that language.
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 to @chandlerc. I also think it is better to make any necessary calling convention changes only in symbols usable from C++, while leaving symbols used by Carbon unchanged. That can mean that a function might have two entry points, one with the Carbon calling convention, one with the C++ calling convention.
|
||
Notable elements are: | ||
|
||
- `$extern("Cpp")`: Indicates that Carbon code should be exposed for 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.
Are you proposing concrete syntax here, or is this just a placeholder? If it's concrete syntax, I find it a bit strange: what is a prefix $
operator doing here? (Also I think we can find a better word for this than extern
).
`Foo` would become `::Carbon::Widget::Foo` in C++. This may be renamed for | ||
backwards compatibility for C++ callers when migrating code, for example | ||
`$extern("Cpp", namespace="widget")` for `::widget`. |
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.
Do you imagine this as declaring the entity in namespace widget
(only), or as still declaring Carbon::Widget
and then pulling it into namespace widget
by using-declaration or similar? (The former would be more consistent and would let people incrementally stop using the backwards-compatibility names, whereas the latter would provide more consistent ADL behavior, in particular if there are C++ functions in namespace widget
.)
The behavior of mapped types will not always be identical; they need only be | ||
similar. For example, we expect Carbon's `UInt32` to map to C++'s `uint32_t`. | ||
While behavior is mostly equivalent, where C++ would use modulo wrapping, Carbon | ||
will instead have trapping behavior. |
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 ("will") seems definitive but I don't think we've agreed that. Maybe "may"?
- We will try to transfer ownership to a Carbon type where possible, but may | ||
need to copy to the Carbon type in complex cases. |
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 would this "copy" option entail?
Carbon will provide specialized operator template functions for C++ types which | ||
are implemented as-if calling a C++ function template in bridge code which in | ||
turn did the exact operator call, including ADL-based name lookup and overload | ||
resolution. Carbon code can then override this behavior by providing specialized | ||
patterns for operators when interacting with Carbon 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.
If I'm understanding right, the idea is that C++ types exposed to Carbon implement the corresponding Carbon operator interfaces by calling bridge code in C++ that uses the C++ operator. (I think this was written before we decided we probably want to use interfaces for operator overloading, which is why it talks about "specialized operator template functions" instead.)
Presumably when exposing a Carbon type to C++, if the Carbon type implements operator interfaces, we'll inject a suitable declaration of a C++ overloaded operator to match too?
vocabulary types. | ||
- Mappings should be easy to maintain. | ||
- We should provide a syntax for transparently, automatically exposing a | ||
subset of Carbon types and APIs to C++ code without custom bridge code, |
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.
By "types and APIs" do you mean "types and functions"? "APIs" isn't really a well-defined term in this context, but if you want to use it to preserve some amount of imprecision, it presumably includes types.
- We prioritize making it easy to call C++ APIs from Carbon. Calling Carbon APIs | ||
from C++ must be possible, but need not be as easy. |
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.
Do you mean "function" instead of "API" here? Or "use" instead of "call"?
- We may choose not to provide full support for unwinding exceptions across | ||
Carbon and C/C++ boundaries. | ||
- Interoperability features should not be expected to work for arbitrary C++ | ||
toolchains. While pre-compiled C++ libraries may be callable, the Carbon |
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 there's a key decision to be made here. If we require all Carbon and C++ code in project to be built with the same toolchain, then we can essentially require all the C++ code to be written in a Carbon-flavored C++, which is backwards-compatible with normal C++ but has extensions to work better with Carbon, uses Carbon-specific ABI rules, and can directly talk to Carbon types. We don't need to generate C++ headers from Carbon code, or anything like that; we just have one toolchain that speaks both languages. We may not even need explicit $extern
syntax in that case, and could instead let the C++ code import Carbon like we let Carbon code import C++.
But if we want to support use of Carbon from C++ code that's built with an unmodified C++ toolchain using a regular C++ ABI, then our situation is very different.
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 really is a huge point, and thanks for surfacing it.
I have a bunch of thoughts here, but I think this discussion is important enough to optimize it a bit so I started a Discourse thread here:
https://forums.carbon-lang.dev/t/interop-implementation-strategies/108
Notably, I think several other in-file comments end up tying back to the same core point.
That said, I think half of the $extern
syntax is actually not tied up in this. I'll talk a bit about the $extern
syntax more broadly in response to your top-level comment.
Similarly, `float` and `double` may end up being different sizes on particular | ||
platforms. |
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 we said in another doc that we're not interested in supporting such platforms.
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 we should be seriously considering removing the
$extern
mechanism, and instead making the interoperability much more symmetric: permit Carbon libraries to be imported into C++ just as we permit C++ libraries to be imported into Carbon. I don't think it's feasible to ask for any Carbon type that's used with a C++ template to be$extern
'd; that would create problems for the use of C++ templates from Carbon generics and from Carbon templates, where the requirement to$extern
would be imposed on the facet type or on the user of the Carbon template, respectively.
FWIW, I agree that we should make the interop much more symmetric and not require the $extern
stuff when, for example, nistantiating C++ templates with Carbon types.
However, I want to point out that there are two different uses of $extern
. One of them is to address the consumption direction where it involves templates and thus Carbon types need to be visible to C++. I agree that we should make that 100% transparent (see my comment on your implementation question below).
But the other use is to designated what parts of a Carbon API become available for export to C++ consumers. Not as part of templates (which would still be within the purview of a Carbon compilation), but generically to a layer of completely C++ code. There, I think the utility remains. That is where we are likely to want it to be explicit that the Carbon interface may have constraints on what it can do (for example, Carbon templates might not work). Those constraints shouldn't apply to the prior case. Concretely: Carbon code consuming a C++ template and then instantiating it on a Carbon template type should be fine. We started in Carbon code, and so we can instantiate Carbon templates. It is when the root consumer is C++ code that restrictions enter the picture.
But I also don't think this will need much special syntax. We will want explicit syntax to control which Carbon APIs are exported from a library for any consumption. We should take that syntax and build on it to designate when that export includes C++ export. Not sure we have clearly thought through what that syntax is, but if the $export
stuff in this proposal were reduced to a placeholder for "building on whatever normal export syntax we end up with..." for this purpose, I'd be fine with it.
I think a lot of the detail here is in areas where we can't really agree on that level of detail yet, because we don't know how that language feature of Carbon should work. So I think what we really need to decide first is the form we want interoperability to take: do we have the full bidirectional interoperability I described above, or do we treat use of Carbon from C++ as somewhat second-class, per this document, or something else? Do we expect to have a single toolchain that can understand and generate code for both Carbon and C++ sources, or do we expect the Carbon compiler to spit out a header file that a C++ compiler that doesn't understand Carbon can consume? I would want the outcome of this PR to be that we have clarity and agreement on those kinds of questions.
I've started a forum thread to dive into the requirements we want here:
https://forums.carbon-lang.dev/t/interop-implementation-strategies/108
The exploration of details here is useful, but once we have the high-level decisions about the form of interoperability, it seem to me that we should be feeding that into the design review of all the other aspects of Carbon rather than trying to handle it centrally. To that end, I would suggest keeping all of the specific areas of design details, to flesh out the direction you're describing, but marking them as "to be finalized later", much like was done for the overall design document.
+1 (But I also think it would be useful to try to get some clear directionality on the high level decision.)
- `$extern("Cpp")`: Indicates that Carbon code should be exposed for C++. | ||
Similarly, `$extern("Swift")` might be used to indicate exposure for Swift at | ||
some point in the future. |
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.
My hope would be that the Carbon declaration doesn't change meaning from within Carbon code.
Instead, any semantic differences should be only in the exposed form of the API as it is accessed from that language.
C++ enums will generally translate nautrally to Carbon, whether using `enum` or | ||
`enum class`. In the other direction, we expect Carbon enums to always use | ||
`enum class`. |
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.
FWIW, I do have lots fo qusetions around what enums will actually end up looking like in Carbon -- I'd not want to really speculate too far about that.
But I think @zygoloid hits on a nice point that we can telegraph meaningfully -- we don't intend to have the name leakage by default, whatever it is that ends up forming the basis of mapped-to-enums.
Simple C++ class templates are directly made available as Carbon templates. For | ||
example, ignoring allocators and their associated complexity, `std::vector<T>` | ||
in C++ would be available as `Cpp.std.vector(T)` in Carbon. More complex C++ | ||
templates may need explicit bridge code. |
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.
Huge plus one, but I wonder if there is an effective way to basically telegraph this, and come in with a revision that specifically tries to address this? I think this is one of the most complex aspects of interop and it might be helpful to have a focused discussion just around that. Thoughts?
|
||
> References: [Templates and generics](templates_and_generics.md). | ||
|
||
Carbon templates should be usable from 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.
We should really dig into this strategy question you raised... Going to do that in a separate thread -- in particular, I think it might make sense to pop that discussion out to its own Discourse thread:
https://forums.carbon-lang.dev/t/interop-implementation-strategies/108
However, function overloading is supported in both languages, and presents a | ||
much more complex surface to translate. Carbon's overloading is designed to be | ||
largely compatible with C++ so that this can be done reasonably well, but it | ||
isn't expected to be precisely identical. Carbon formalizes the idea of overload | ||
resolution into pattern matching. C++ already works in an extremely similar way, | ||
although without the formalization. We expect to be able to mirror most function | ||
overloads between the two approaches. |
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.
FWIW, I wrote this before we had really explored interfaces as the primary extension point mechanism.
I think it might be worthwhile to revisit much of this and think about whether there is a better way to bridge C++ overloads (at least those intending to be extension points) and Carbon interfaces.
|
||
- C typedefs are generally mapped to Carbon aliases. | ||
- C/C++ macros that are defined as constants will be imported as constants. | ||
Otherwise, macros will be unavailable in Carbon. |
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.
Just my two cents, but I wouldn't try to overly infer semantics or structure here.
They aren't namespaced in C++ and so I'd expect them to be named in a way that copes with that.
For a collection of Carbon function patterns to be exposed to C++ code, all the | ||
types involved must also be exposed to C++ code. These patterns will be | ||
expressed by synthesizing an overload set in C++ code that as accurately as | ||
possible reflects the expected pattern match that would occur with native Carbon | ||
code. | ||
|
||
There is no need to rely on the complexity of C++ conversion sequences to | ||
precisely match any conversions triggered by the Carbon pattern match. Instead, | ||
this logic can be produced by explicitly generating all the necessary C++ | ||
overloads and managing conversion within them. |
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.
To be fair, technically that forms an overload set. ;]
I think the text here is somewhat written from the perspective of the user of the API, not the implementation strategy? And I don't think we'd want users to interact with the function as a function template, but as an overload that does deduction, which is the usual way to call function templates like this...
supported, Carbon needs strong support for most common and idiomatic overload | ||
sets it encounters. | ||
|
||
Overload sets will be translated in a series of steps: |
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 an interesting idea...
On first glance, I don't really see strong motivation for either direction over the other here... That's ok, we can pick one and see how it goes, and I'm reasonably happy with either. But maybe you have some motivating factors in mind that would be worth capturing in the document?
Carbon will provide specialized operator template functions for C++ types which | ||
are implemented as-if calling a C++ function template in bridge code which in | ||
turn did the exact operator call, including ADL-based name lookup and overload | ||
resolution. Carbon code can then override this behavior by providing specialized | ||
patterns for operators when interacting with Carbon 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.
@zygoloid's memory is correct -- this long predates the interface based stuff. And I agree with his suggestion of how this is likely to work: by implementing the Carbon operator interface with calls to the C++ operator overloads.
I also agree about how the reverse will work: injecting overloads based on the interfaces implemented and dispatching from the overload through the interface.
- We may choose not to provide full support for unwinding exceptions across | ||
Carbon and C/C++ boundaries. | ||
- Interoperability features should not be expected to work for arbitrary C++ | ||
toolchains. While pre-compiled C++ libraries may be callable, the Carbon |
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 really is a huge point, and thanks for surfacing it.
I have a bunch of thoughts here, but I think this discussion is important enough to optimize it a bit so I started a Discourse thread here:
https://forums.carbon-lang.dev/t/interop-implementation-strategies/108
Notably, I think several other in-file comments end up tying back to the same core point.
That said, I think half of the $extern
syntax is actually not tied up in this. I'll talk a bit about the $extern
syntax more broadly in response to your top-level comment.
- `$extern("Cpp")`: Indicates that Carbon code should be exposed for C++. | ||
Similarly, `$extern("Swift")` might be used to indicate exposure for Swift at | ||
some point in the future. |
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 to @chandlerc. I also think it is better to make any necessary calling convention changes only in symbols usable from C++, while leaving symbols used by Carbon unchanged. That can mean that a function might have two entry points, one with the Carbon calling convention, one with the C++ calling convention.
|
||
## Type mapping | ||
|
||
Carbon and C/C++ will have a number of types with direct mappings between the |
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 does the word "direct" mean here?
Per discussion regarding implementation strategies and what to do with the RFC, I'm going to pause here. This proposal is back in WIP. |
Should we continue reviewing? |
#83) Co-authored by: chandlerc - Based on [PR 22](#83) - [Idea topic](https://forums.carbon-lang.dev/t/proposal-for-an-incomplete-rough-high-level-overview-ready-for-early-feedback/52) - [RFC](https://forums.carbon-lang.dev/t/rfc-an-incomplete-early-and-in-progress-overview-of-the-language-design/73) - [Decision announcement](https://forums.carbon-lang.dev/t/accepted-an-incomplete-early-and-in-progress-overview-of-the-language-design/110) This proposal should be considered a starting point of the language design. It's not intended to be final; language details may change. This is intended to offer a reasonable starting point for: - Example code. - Conceptualizing Carbon at a high level. - Reasonable, but not necessarily final, approaches to features in README.md. - If any idea is obviously bad, we can clean it up here. This proposal is not intended to achieve: - A whole language design. - This is way too much work for a single proposal; this is a skeletal framework only. - As we work on feature-specific designs, we may decide to use other approaches. That's fine: we only need somewhere to start. - The summaries in README.md may be expected to change over time. - Feature-specific files aren't intended to be well-written or comprehensive. They are a quick jot of prior thoughts. - We want to avoid getting stuck on language details that we should consider more carefully regardless. If you're passionate about a feature, please feel free to start a new proposal for it. - Each and every aspect of the suggested overview should be subject to careful examination and justification before it becomes a settled plan of record. Chandler started this with #22. I've taken it over with the following changes: - More of a directory hierarchy. - Trying to thin out the main file (now README.md) to lighter summaries of features. - Details/rationale/alternatives should be in feature-specific files. - Draft files are linked as references where added. For an example of how we may proceed with feature-specific designs, see #80. In this structure: - docs/design/README.md mentions interoperability, with a light overview. - The light overview is not yet in #80. - docs/design/interoperability/README.md goes into more depth on interoperability, covering key points of the approach. - Individual files in docs/design/interoperability/* go into more depth on interoperability. Simple designs may not have a subdirectory. All current feature-specific designs do not -- they may be moved later.
Related threads: - From austern, [Initial draft of C++ Interoperability principles doc #62](#62) - From me, [Carbon <-> C/C++ interoperability #80](#80) - [Doc](https://docs.google.com/document/d/1va8VgvDdA966WG3znJyUrlComYqNfNBV7__hUd9XxxU/edit) - [Ideas topic](https://forums.carbon-lang.dev/t/draft-carbon-c-c-interoperability/77) - [RFC topic](https://forums.carbon-lang.dev/t/rfc-carbon-c-c-interoperability/89) - From chandlerc, [Interop implementation strategies](https://forums.carbon-lang.dev/t/interop-implementation-strategies/108) For this PR: - [RFC topic](https://forums.carbon-lang.dev/t/rfc-c-interoperability-goals-175/156) - [Decision topic](https://forums.carbon-lang.dev/t/request-for-decision-c-interoperability-goals/171) - [Decision announcement](https://forums.carbon-lang.dev/t/accepted-c-interoperability-goals/175) - [Decision PR](#200)
Co-authored by: chandlerc
This includes the core structure of an interoperability design. It is not complete, and I've tried to indicate key missing pieces with TODOs. These portions would ideally be addressed in the future, as part of other proposals.