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
Show file tree
Hide file tree
Changes from 43 commits
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
2ae6b6f
Merge pull request #1 from carbon-language/master
jonmeow May 20, 2020
5bba3a8
Merge remote-tracking branch 'upstream/master'
jonmeow May 20, 2020
98562a6
Merge remote-tracking branch 'upstream/master'
jonmeow May 21, 2020
a05e9c3
Merge pull request #2 from carbon-language/master
jonmeow Jun 1, 2020
dfe42cb
Merge pull request #3 from carbon-language/master
jonmeow Jun 3, 2020
3931951
Merge pull request #4 from carbon-language/master
jonmeow Jun 4, 2020
43cfde9
Merge pull request #5 from carbon-language/master
jonmeow Jun 10, 2020
b489c23
Merge pull request #6 from carbon-language/master
jonmeow Jun 12, 2020
5b10b4d
Merge pull request #7 from carbon-language/master
jonmeow Jun 12, 2020
6ce0f97
Merge remote-tracking branch 'upstream/master'
jonmeow Jun 16, 2020
16e6242
Initializing proposal
jonmeow Jun 16, 2020
5a9c853
Import interoperability design
jonmeow Jun 16, 2020
38e73de
Rename proposal
jonmeow Jun 16, 2020
4310965
tocs
jonmeow Jun 17, 2020
3b49e31
Summaries
jonmeow Jun 17, 2020
218df9f
Merge remote-tracking branch 'upstream/master'
jonmeow Jun 22, 2020
5c33fbf
Merge remote-tracking branch 'upstream/master'
jonmeow Jun 22, 2020
e4223f4
codespell
jonmeow Jun 22, 2020
471df44
Merge branch 'master' into interop-proposal
jonmeow Jun 22, 2020
21097a8
↔ -> <-> due to github emoji weirdness
jonmeow Jun 22, 2020
116edd6
De-emphasize alternatives
jonmeow Jun 22, 2020
7be07be
Lang markers
jonmeow Jun 22, 2020
72a3382
Proposal
jonmeow Jun 22, 2020
69ba0aa
Cleanup
jonmeow Jun 22, 2020
bb4678b
Restructuring
jonmeow Jun 23, 2020
93dd8b8
Progress
jonmeow Jun 23, 2020
b8e6432
Progress
jonmeow Jun 23, 2020
6eb02c7
Progress
jonmeow Jun 23, 2020
e3be8d5
Progress
jonmeow Jun 23, 2020
46e1943
Progress
jonmeow Jun 23, 2020
f935369
Comments
jonmeow Jun 24, 2020
338703a
Address a few TODOs
jonmeow Jun 2, 2020
e828a28
Remove mention of Carbon open overloads
jonmeow Jun 2, 2020
e2270a4
Progress
jonmeow Jun 2, 2020
05430cc
Progress
jonmeow Jun 2, 2020
7e44750
Apply suggestions from code review
jonmeow Jul 9, 2020
90f798c
Addressing comments
jonmeow Jul 9, 2020
74861eb
Addressing comments
jonmeow Jul 9, 2020
5f94ba9
Apply suggestions from code review
jonmeow Jul 10, 2020
0d7735c
Addressing comments
jonmeow Jul 10, 2020
acaf391
Addressing a few comments from titus on #110
jonmeow Jul 10, 2020
b43d0b3
Drop user-defined types
jonmeow Jul 10, 2020
9da1904
Drop user-defined types
jonmeow Jul 10, 2020
4a4dea6
Apply suggestions from code review
jonmeow Jul 20, 2020
521d0f0
Update docs/design/interoperability/other_syntax.md
jonmeow Jul 21, 2020
14ead29
Working on comments
jonmeow Jul 21, 2020
0c03ae4
Merge branch 'interop-proposal' of https://github.com/jonmeow/carbon-…
jonmeow Jul 21, 2020
8178a87
Addressing comments
jonmeow Jul 21, 2020
80c8698
Drop vice versa
jonmeow Jul 21, 2020
38ce17f
Apply suggestions from code review
jonmeow Jul 24, 2020
394f723
Extract out goals
jonmeow Jul 24, 2020
46aaff3
Comments
jonmeow Jul 24, 2020
b7bdcb0
Update docs/design/interoperability/vocabulary_types.md
jonmeow Jul 24, 2020
bedd467
Merge branch 'interop-proposal' of https://github.com/jonmeow/carbon-…
jonmeow Jul 25, 2020
891fb23
Comments
jonmeow Jul 25, 2020
525f503
Merge branch 'trunk' into interop-proposal
jonmeow Jul 30, 2020
7101c2e
Merge
jonmeow Jul 31, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
335 changes: 335 additions & 0 deletions docs/design/interoperability/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,335 @@
# Carbon &lt;-> C/C++ interoperability
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 I understand the role of the README file -- is it supposed to repeat and/or summarize the information from other files? If so, I'd rather not include such duplication at this stage of development, because different copies of information will very likely go out of sync. Any unique information (like goals and philosophy) would be more discoverable in a file with a name that describes the contents.

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.


<!--
Part of the Carbon Language project, under the Apache License v2.0 with LLVM
Exceptions. See /LICENSE for license information.
SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-->

## Table of contents

<!-- toc -->

- [Overview](#overview)
- [Goals](#goals)
- [Philosophy of interoperability layer](#philosophy-of-interoperability-layer)
- [Interoperability syntax elements](#interoperability-syntax-elements)
- [Details](#details)
- [Bridge code in Carbon files](#bridge-code-in-carbon-files)
- [Name mapping](#name-mapping)
- [Type mapping](#type-mapping)
- [Primitive types](#primitive-types)
- [User-defined types](#user-defined-types)
- [Vocabulary types](#vocabulary-types)
- [Enums](#enums)
- [Templates and generics](#templates-and-generics)
- [Using C++ templates from Carbon](#using-c-templates-from-carbon)
- [Using Carbon templates from C++](#using-carbon-templates-from-c)
- [Using Carbon generics from C++](#using-carbon-generics-from-c)
- [Functions and overload sets](#functions-and-overload-sets)
- [Other syntax](#other-syntax)
- [Migration examples](#migration-examples)

<!-- tocstop -->

## Overview

It's critical that Carbon can both access C++ interfaces and export C++
interfaces. Supporting C++ interoperability is a
jonmeow marked this conversation as resolved.
Show resolved Hide resolved
[key requirement for Carbon's goals](/docs/project/goals.md) and is expected to
influence the design of Carbon itself.

## Goals

The goals of Carbon's interoperability layer are heavily influenced by the
[language-level goals](/docs/project/goals.md). Notably, we prioritize
performance, and making any performance overhead visible and opt-in.

Goals:

- The majority of simple C/C++ interfaces should be usable from Carbon without
any custom bridge code and without any runtime overhead.
- There should be support for most idiomatic usage of advanced C++ features:
templates, overload sets, ADL.
Copy link
Contributor

Choose a reason for hiding this comment

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

I thought at Google we were moving to no use of ADL in C++. If that is a widespread best practice, or not necessary for interop, it seems we might not support ADL as part of Carbon/C++ interop.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

My impression is it's not really a widespread best practice, but even if it were, aren't there parts of stl that rely on it?

Copy link
Contributor

Choose a reason for hiding this comment

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

We're moving to no accidental use in ADL in Google code. The problem with ADL isn't that it's a bad practice, it's that as a language feature, it's extremely prone to accidental use in ways that constrain API evolution. There are enough legitimate and widespread uses (e.g. AbslHashValue) that Carbon will need some kind of story for how to support them.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Just to be explicit: I consider this thread resolved, without changes.

- It should be possible to map types across languages.
- Primitive types should have unsurprising mappings.
- There should be transparent, automatic translation between C++ and Carbon
non-owning vocabulary types, such as pointers and references, without
runtime overhead.
- It should be possible to expose other C++ vocabulary types with reasonable,
but potentially non-zero-cost, conversions available to map into Carbon
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.

Suggested change
subset of Carbon types and interfaces to C++ code without custom bridge
subset of Carbon types and abstract base classes 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.

This is referring to Carbon interfaces, not C++. ("Carbon types and interfaces to C++ code") Even with your "base type" comments, I assume that's still distinct from "abstract base class", which sounds C++-y. I'm assuming you read that backwards, but not sure how to phrase better.

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?

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

code, including instantiating templates.

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.

- Automatically exposing all Carbon code would impose language and API
constraints; we want to limit these constraints to where users need it.
- We do not expect to support all C/C++ corner cases: the complexity of
supporting any given construct must be balanced by the real world need for
that support.
- We may target C++17, and not keep adding interoperability support for later

Choose a reason for hiding this comment

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

Notably, this means we would not support modular 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.

Noted:
- There may be interest in supporting some C++20 features, particularly
modules. However, exhaustive support should not be assumed.

C++ features.
- There may be interest in supporting some C++20 features, particularly
modules. However, exhaustive support should not be assumed.
- For example, we might not prioritize support for non-idiomatic code,
interfaces, or patterns outside of those in widespread open source libraries
and used by key contributors.
- For example, we might not support low-level C ABIs outside of modern 64-bit
ABIs: Linux, POSIX, and a small subset of Windows' calling conventions.
- We may choose not to use the existing ABI for the C++ language or standard
library.
- It might be reasonable to eventually support these with added runtime
overhead.
- We may choose not to provide exact matches between Carbon and C++ vocabulary
types.
- We may choose not to provide full support for unwinding exceptions across
Carbon and C/C++ boundaries.

## Philosophy of interoperability layer

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.

2. A willingness to expose the idioms of C++ into Carbon code, and vice versa,
when necessary to maximize performance.
3. The use of wrappers, generic programming, and templates to minimize or
eliminate runtime overhead.

These things come together when looking at how custom data structures in C++ are
exposed into Carbon or vice versa. In both languages, it is reasonable and even
common to have customized low-level data structures such as associative
containers. Even today, there are numerous data structures for mapping from a
key to a value that might be "best": hash table, linked hash table, sorted
vector, and btree to name a few. Experience with C++ tells us that we should
also expect slow but meaningful evolution even in implementation strategies that
cause divergence.

The result is that it is often reasonable to directly expose a data structure
from C++ to Carbon without converting it to a "native" or "idiomatic" Carbon
data structure. For many data structures, code will reasonably support multiple
different implementations, even if there is an extremely good default. We can
expose C++ data structures as another implementation and then focus on wrapping
it to match whatever idioms Carbon expects of that kind of data structure.

The reverse is also true. C++ code will often not care, or can be enhanced to
not care, what specific data structure is used. Carbon data structures can be
exposed as yet another implementation in C++, and wrapped in C++ code to match
C++ idioms and be usable within templated contexts.

Another fundamental philosophy of interoperability between Carbon and C++, and
generally between any two languages, is that it requires expressing one language
as a subset of another. The approach proposed is to do this in two directions:
take specific, restricted C++ APIs and make them available using restricted
Carbon APIs, and also take specific, restricted Carbon APIs and make them
available as restricted C++ APIs. In both languages, the API restrictions on
exported interfaces and imported interfaces can be intersected. These
intersections define the interoperability layer and constrain the expressivity
of Carbon/C++ interoperability. Our goal is that these expressivity constraints
are wide enough to make the amount of bridge code sustainable and the overhead
of wrappers manageable.

## Interoperability syntax elements

> References: [Name mapping](name_mapping.md).

An `import` will be sufficient for Carbon to call most C++ APIs, with no changes
to the C++ code. However, special interoperability syntax elements will be
required when exposing Carbon code to C++.

Notable elements are:

- `$extern("Cpp")`: Indicates that Carbon code should be exposed for C++.

Choose a reason for hiding this comment

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

I know it's a placeholder text, but is there some technical reason we couldn't have the string be "C++" instead of "Cpp"?

Choose a reason for hiding this comment

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

From reading ahead a bit, it looks like that string becomes a Carbon package, which seems to be required to have a valid identifier as its name?

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 a brief sentence:

We use the name Cpp because import needs a valid identifier.

Copy link
Contributor

Choose a reason for hiding this comment

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

This seems to be a consequence of the decision to make import's first operand serve two roles simultaneously: it specifies both the language of the imported file, and the package that the file contents are imported into. That non-orthogonality has a couple of unfortunate consequences:

  • It prevents us from identifying the C++ language by its name in imports, which then means that consistency prevents us from identifying C++ by its name in exports
  • It means that imported names cannot appear in any package other than Cpp (whereas exported names can appear in any namespace, not just Carbon)

Have we considered a more orthogonal design, where the language and namespace are specified separately, e.g. import("C++") Cpp "project/file.h"?

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've added more of a discussion of options to name_mapping.md, trying to capture what you're saying - it may help to move conversation there. I agree with your point that the current syntax may just be poor (I wonder about switching away from import entirely), although I'm hesitant to directly support arbitrary namespace locations.

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

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.

- `namespace` and `name` parameters are provided to override default choices,
geoffromer marked this conversation as resolved.
Show resolved Hide resolved
particularly to assist migration of C++ APIs to Carbon.
- Externs may be #included using `.6c.h` files.
geoffromer marked this conversation as resolved.
Show resolved Hide resolved
- `import Cpp "path"`: Imports API calls from a C++ header file.
jonmeow marked this conversation as resolved.
Show resolved Hide resolved

We use the name `Cpp` because `import` needs a valid identifier.

## Details

### Bridge code in Carbon files

> TODO: We should allow writing bridge C++ code in Carbon files to ease
> maintenance of compatibility layers. Syntax needs to be proposed.
Copy link
Contributor

Choose a reason for hiding this comment

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

Also some guardrails are needed for ensuring that people don't abuse this feature to just write arbitrary C++ in Carbon source files, effectively treating Carbon as a superset of C++. For example, the C++ bridge code written in a Carbon file shouldn't be callable from Carbon 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.

I would interpret part of the point here to be calling C++ bridge code from Carbon. e.g., if there's some C++ API that isn't being exposed to Carbon well enough to call automatically, or if it's code that you don't own, and would need some markup for correct interpretation, having a bridge in the Carbon file seems like the right spot.

While I'd agree at a basic level that inserting tons of C++ in a Carbon file wouldn't be great, I'm not sure it's something that we need to actively prevent. It's already going to get a little awkward mixing file extensions. I'd hope the added friction dissuades users.

Regardless, noted.


### Name mapping

> References: [Name mapping](name_mapping.md).
>
> TODO: Add a reference for incomplete types when one is created.

C/C++ names are mapped into the `Cpp` Carbon package. C++ namespaces work the
same fundamental way as Carbon namespaces within the `Cpp` package name. Dotted
names are used when referencing these names from Carbon code. For example,
`std::exit` becomes `Cpp.std.exit`.

C++ incomplete types will be mirrored into Carbon's incomplete type behavior.

Choose a reason for hiding this comment

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

Do we have a document outlining what this behavior is? If so, a link might be appropriate 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.

I don't think so, added a TODO

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 Carbon may have much stricter requirements on incomplete types than C++ does. For example, exporting incomplete types might be illegal in Carbon. Or every Carbon incomplete type might have to have a definition in the same file.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@josh11b I'm not sure if you'd intended for changes, so I want to be explicit, I think the TODO resolves this comment thread (that there is no doc).

Users wanting to avoid differences in incomplete type behaviors should fully
define the C++ types using repeated imports.
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 the word "repeated" means here. (It can't mean importing the same header multiple times, because that won't make the imported type complete, but I can't think of another interpretation.)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Dropped "repeated"


Carbon names which are mapped into C++ will use a top-level namespace of
geoffromer marked this conversation as resolved.
Show resolved Hide resolved
`Carbon` by default, with the package name and namespaces represented as
namespaces below that. For example, the `Widget` Carbon package with a namespace
`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`.
Comment on lines +87 to +89
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.)


### Type mapping
geoffromer marked this conversation as resolved.
Show resolved Hide resolved

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?

languages.

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`.
gribozavr marked this conversation as resolved.
Show resolved Hide resolved
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"?


In some cases, there are multiple C/C++ types that map to a single Carbon type.
This is expected to generally be transparent to users, but may impose important
constraints around overload resolution and other C++ features that would "just
work" with 1:1 mappings.

#### Primitive types

> References: [Primitive types](primitive_types.md).

We'll have a simple mapping for C++ primitive types. Conversion methods will
exist for cross-platform 32-bit/64-bit compatibility.

#### User-defined types

> TODO: Handling of user-defined types should be addressed.

#### Vocabulary types

> References: [Vocabulary types](vocabulary_types.md).

There are several cases of vocabulary types that are important to consider:

- Non-owning types passed by reference or pointer, such as `std::vector<T> &&`.
- C++ references map to Carbon non-null pointers, or `T*`.
- C++ pointers map to Carbon nullable pointers, or `T*?`.
- Non-owning types passed by value, such as `std::string_view` or `std::span`.
gribozavr marked this conversation as resolved.
Show resolved Hide resolved
- We copy these to Carbon types with similar semantics. These should have
trivial construction costs.
- Owning types signaling a move of ownership, such as `std::unique_ptr`.
- 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?

- Owning types signaling a copy of data, such as `std::vector<T>`.
- Copying overhead should be expected and normal, even for a Carbon type.

### Enums

> References: [Enums](enums.md).

C++ enums will generally translate nautrally 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.

Suggested change
C++ enums will generally translate nautrally to Carbon, whether using `enum` or
C++ enums will generally translate naturally to Carbon, whether using `enum` or

`enum class`. In the other direction, we expect Carbon enums to always use
`enum class`.
Comment on lines +144 to +146
Copy link
Contributor

Choose a reason for hiding this comment

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

This sort of presupposes a design for Carbon enums (which AFAIK we don't have, even in PR #83), and assumes that design will be basically isomorphic to C++ enums, which I don't think is a safe assumption. I would expect Carbon enums to be degenerate cases of variant types (as they are in Haskell, and pretty much every other language that has built-in variant types); at the very least, I think that's a strong possibility.

I'd suggest just dropping this section; it won't be difficult to recreate it if we adopt an enum design that works with this approach.

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 the input. I'd like to hear from others if they agree.

Copy link
Contributor

Choose a reason for hiding this comment

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

While Carbon will certainly have some enum facility, how exactly far away it will be from C++ enums is yet to be decided. For example, ergonomics of some C++ APIs relies on the fact that C++ enums are integer-like, and that they implicitly convert to integers rather eagerly. Ergonomics of some other APIs relies on the fact that C++ enums are not namespaced (for example, they already incorporate a prefix), so adding another prefix would be seen as redundant and non-ergonomic.

Another issue is that Carbon enums can be more strict than C++ enums on the low level. For example, C++ enums allow all bit patterns at runtime to be passed through (even ones that are not listed in enumerators). Carbon might decide to steal unused bit patterns for bitpacking, or for optional's "none" representation, or just ensure that unused bit patterns are not used through the means of a sanitizer. We could special case C++ enums here, but then such enums are not really Carbon enums, they are C++ enums with special case Carbon syntax and semantics that mostly looks like Carbon enums except where it doesn't.

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?

Copy link
Contributor

Choose a reason for hiding this comment

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

For the "In the other direction" part, I think the narrow thing that this part of the document is saying -- that when exporting a Carbon enumeration to C++ code, the names of enumerators do not leak out into the enclosing scope -- should be expected to be true for any design of Carbon enumerations. We want the names in the C++ scope in namespace Carbon to largely match the names in the corresponding Carbon scope.

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.


For example, here is a C++ and Carbon version of a `Direction` enum, both of
which will be equivalent in either language:

```cc
enum class Direction {
East,
West,
North,
South,
};
```

```carbon
$extern("Cpp") enum Direction {
East = 0,
West = 1,
North = 2,
South = 3,
}
```

### Templates and generics

### Using C++ templates from Carbon

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

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.

Up to this point we've been talking about interoperability as if we generated C++ code from Carbon code on one side of the divide, and generated Carbon code from C++ code on the other side. But that seems to fall down now. Presumably we're not going to machine-translate the entire vector class template from C++ into Carbon, and instantiate it as a Carbon template -- I don't think we'll ever get the language semantics close enough for that to work. So that presumably means we will be processing it as a C++ class template, and doing an instantiation in the C++ world, but with template arguments that are (arbitrary, not $extern("Cpp")'d) Carbon types. So now we need to talk about what it means to map an arbitrary Carbon type into that C++ world, and we need our Carbon compiler to link in enough of a C++ compiler to do C++ template instantiation.

I think that's actually all OK, and it's the right thing to be doing, but it doesn't seem to match the remapping-by-code-generation model described in this document.

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?


gribozavr marked this conversation as resolved.
Show resolved Hide resolved
### Using Carbon templates from C++

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

Similarly to the above, we should not expect to be able to machine-translate Carbon templates into C++ code. So our C++ compiler will also need to link against enough of a Carbon compiler to be able to instantiate Carbon templates. We may be in a better position here if we don't allow exporting Carbon templates to C++, and only allow exporting Carbon generics.

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


### Using Carbon generics from C++

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

code may be written using a Carbon template, changing compatibility constraints
to match.

For example, given the Carbon code:

```carbon
fn GenericAPI[Foo:$ T](T*: x) { ... }

fn TemplateAPI[Foo:$$ T](T*: x) { GenericAPI(x); }
```

We could have C++ code that uses the template wrapper to use the generic:

```cc
CppType y;
::Carbon::TemplateAPI(&y);
```

### Functions and overload sets

> References: [Functions and overload sets](functions_and_overload_sets.md).

Non-overloaded functions may be trivially mapped between Carbon and C++, so long
as their parameter and return types have suitable mappings. If the names are
made available, then they can be called.
Comment on lines +212 to +214
Copy link
Contributor

Choose a reason for hiding this comment

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

We've talked about Carbon having different defaults for various calling-convention questions than C++. One that sticks out is the assumption that pointer parameters to functions are assumed to not be captured by default. How would that play into this? Would C++ code that's exposed to Carbon pick up the Carbon assumption, or not (presumably with some attribute to request the nocapture behavior)?


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
Comment on lines +220 to +221
Copy link
Contributor

Choose a reason for hiding this comment

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

"extremely similar" seems like a stretch to me. C++ overload resolution has at its heart the ranking of implicit conversion sequences, and pattern matching (whether we're thinking about doing it with types or values) isn't really about conversions at all. I think it would be entirely reasonable for Carbon to look for an argument of type Foo or an argument of type "convertible to Foo", but I don't think ranking Carbon candidates by "the argument converts to Foo more easily than it converts to Bar" is really in line with the pattern matching ethos.

I think the thing that makes the most sense is to say: when calling C++ from Carbon, we use the C++ overload resolution rules. And when calling Carbon from C++, we use the Carbon pattern matching rules. If we're intending to deviate from that, then I think we need an exploration of the consequences of said deviation, particularly to the more interesting overload sets in C++ (for an example case study, we could look at overloaded functions in the interfaces of containers such as vector::insert).

overloads between the two approaches.
Comment on lines +216 to +222
Copy link
Contributor

Choose a reason for hiding this comment

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

The hard part here is that to call some C++ facilities with a value of a particular type, the C++ code expects some function or type in a specific C++ namespace to be overloaded to support that type. Are you proposing being able to overload a C++ name with a Carbon implementation?

The reverse case is a little different, since Carbon isn't going to use open overloading. The equivalent Carbon extension mechanism is interface implementation. That is, we will need some way to write bridge code to implement a Carbon interface for a C++ type. Carbon look up rules currently would require those implementation to be defined either with the interface or the implementing type, but we may need another place to look for C++ bridge 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.

A lot of this is Chandler's original text, so I'm somewhat interpreting, but I think the answer is "yes". The exported API needs to walk and talk like a C++ API in order to be called by C++, regardless of whether the implementation is in C++ or Carbon.

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.


### Other syntax

> References: [Other syntax](other_syntax.md).

Beyond the above in-depth discussions, a few key syntax details to be aware of
are:

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

This approach seems reasonable to me. For the macros that we do convert to aliases, what scope do we put them into on the Carbon side? Does it depend on whether the #define appears inside a C++ namespace?

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.


### Migration examples

Large-scale migrations need to be done piecemeal. The goal of migration examples
is to use interoperability in order to produce small migration steps which can
be performed independently to a large codebase, without breaking surrounding
code.

Examples:

- [Incremental migration of APIs](example_incremental.md)
- [Framework API migration](example_framework.md)
Loading