Skip to content
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

Closed
wants to merge 57 commits into from
Closed

Carbon <-> C/C++ interoperability #80

wants to merge 57 commits into from

Conversation

jonmeow
Copy link
Contributor

@jonmeow jonmeow commented Jun 16, 2020

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.

@jonmeow jonmeow added proposal A proposal WIP labels Jun 16, 2020
@jonmeow jonmeow changed the title Carbon: Carbon ↔ C/C++ interoperability Carbon ↔ C/C++ interoperability Jun 16, 2020
@googlebot googlebot added the cla: yes PR meets CLA requirements according to bot. label Jun 19, 2020
@jonmeow jonmeow changed the title Carbon ↔ C/C++ interoperability Carbon <-> C/C++ interoperability Jun 22, 2020
Copy link
Contributor

@geoffromer geoffromer left a 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).

Comment on lines 96 to 97
- Carbon must be able to compile C++ headers in order to translate names and
types.
Copy link
Contributor

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.

Copy link
Contributor Author

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.

Comment on lines 98 to 100
- 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.
Copy link
Contributor

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?

Copy link
Contributor Author

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; }
Copy link
Contributor

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?

Comment on lines +159 to +160
- For example, users may still write platform-specific code like
`var Cpp.long: x = ...; var Int32: y = (Int32)x;`.
Copy link
Contributor

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)...;?

Copy link
Contributor Author

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.

Comment on lines +153 to +154
- If platform-specific types are added, it may be worth considering whether we
should promote these to Carbon primitive types.
Copy link
Contributor

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.

Copy link
Contributor Author

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.

Comment on lines +149 to +152
- 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.
Copy link
Contributor

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.

Copy link
Contributor Author

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.

docs/design/interoperability/vocabulary_types.md Outdated Show resolved Hide resolved
docs/design/interoperability/vocabulary_types.md Outdated Show resolved Hide resolved
### 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++
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See below.

Copy link
Contributor

@gribozavr gribozavr left a 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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
This borrows significantly from the structure of Swift's interoperability plan
This proposal borrows significantly from the structure of Swift's interoperability plan

docs/design/interoperability/README.md Outdated Show resolved Hide resolved
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
Copy link
Contributor

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.
Copy link
Contributor

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."

Copy link
Contributor Author

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.
Copy link
Contributor

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?

Copy link
Contributor Author

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:

  1. The ability to interoperate with a wide variety of code, such as
    classes/structs, not just free functions.

docs/design/interoperability/README.md Show resolved Hide resolved

> References: [Templates and generics](templates_and_generics.md).

Carbon generics will require bridge code that hides the generic. This bridge
Copy link
Contributor

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.

Copy link
Contributor Author

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

Copy link
Contributor

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"));
Copy link
Contributor

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.

Copy link
Contributor Author

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
Copy link
Contributor

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.

Copy link
Contributor Author

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:
Copy link
Contributor

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.

Copy link
Contributor

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.

Copy link
Contributor Author

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?

Copy link
Contributor Author

@jonmeow jonmeow left a 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
Copy link
Contributor Author

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 &lt;-> C/C++ interoperability
Copy link
Contributor Author

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.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done, roughly.

Comment on lines 96 to 97
- Carbon must be able to compile C++ headers in order to translate names and
types.
Copy link
Contributor Author

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.

Comment on lines 98 to 100
- 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.
Copy link
Contributor Author

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.

docs/design/interoperability/README.md Outdated Show resolved Hide resolved
Comment on lines +278 to +280
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`.
Copy link
Contributor Author

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?

docs/design/interoperability/README.md Show resolved Hide resolved

> References: [Templates and generics](templates_and_generics.md).

Carbon generics will require bridge code that hides the generic. This bridge
Copy link
Contributor Author

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

Comment on lines +101 to +105
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.
Copy link
Contributor Author

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?

Copy link
Contributor Author

@jonmeow jonmeow left a 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; }
Copy link
Contributor Author

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.

Comment on lines +149 to +152
- 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.
Copy link
Contributor Author

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.

Comment on lines +153 to +154
- If platform-specific types are added, it may be worth considering whether we
should promote these to Carbon primitive types.
Copy link
Contributor Author

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.

Comment on lines +159 to +160
- For example, users may still write platform-specific code like
`var Cpp.long: x = ...; var Int32: y = (Int32)x;`.
Copy link
Contributor Author

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.

Copy link
Contributor Author

@jonmeow jonmeow left a 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
Copy link
Contributor Author

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"));
Copy link
Contributor Author

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:
Copy link
Contributor Author

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?

Copy link
Contributor

@zygoloid zygoloid left a 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.

Comment on lines 51 to 53
- `$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.
Copy link
Contributor

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?

Copy link
Contributor

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.

Copy link
Contributor

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++.
Copy link
Contributor

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).

Comment on lines +86 to +88
`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`.
Copy link
Contributor

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.
Copy link
Contributor

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"?

Comment on lines 129 to 130
- We will try to transfer ownership to a Carbon type where possible, but may
need to copy to the Carbon type in complex cases.
Copy link
Contributor

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?

Comment on lines +101 to +105
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.
Copy link
Contributor

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,
Copy link
Contributor

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.

Comment on lines +49 to +50
- 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.
Copy link
Contributor

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
Copy link
Contributor

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.

Copy link
Contributor

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.

Comment on lines +101 to +102
Similarly, `float` and `double` may end up being different sizes on particular
platforms.
Copy link
Contributor

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.

Copy link
Contributor

@chandlerc chandlerc left a 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.)

Comment on lines 51 to 53
- `$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.
Copy link
Contributor

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.

Comment on lines +278 to +280
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`.
Copy link
Contributor

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.

Comment on lines 169 to 172
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.
Copy link
Contributor

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++.
Copy link
Contributor

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

Comment on lines +354 to +360
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.
Copy link
Contributor

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.
Copy link
Contributor

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.

Comment on lines +27 to +36
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.
Copy link
Contributor

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:
Copy link
Contributor

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?

Comment on lines +101 to +105
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.
Copy link
Contributor

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
Copy link
Contributor

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.

Comment on lines 51 to 53
- `$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.
Copy link
Contributor

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
Copy link
Contributor

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?

@jonmeow jonmeow added WIP and removed proposal rfc Proposal with request-for-comment sent out comment deadline labels Jul 28, 2020
@jonmeow jonmeow changed the title Carbon <-> C/C++ interoperability (WIP) Carbon <-> C/C++ interoperability Jul 28, 2020
@jonmeow jonmeow changed the title (WIP) Carbon <-> C/C++ interoperability Carbon <-> C/C++ interoperability Jul 28, 2020
@jonmeow
Copy link
Contributor Author

jonmeow commented Jul 28, 2020

Per discussion regarding implementation strategies and what to do with the RFC, I'm going to pause here. This proposal is back in WIP.

@gribozavr
Copy link
Contributor

This proposal is back in WIP.

Should we continue reviewing?

jonmeow added a commit that referenced this pull request Jul 30, 2020
#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.
@jonmeow jonmeow closed this Sep 23, 2020
@jonmeow jonmeow deleted the interop-proposal branch September 23, 2020 17:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
cla: yes PR meets CLA requirements according to bot. proposal A proposal
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants