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 54 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
244 changes: 244 additions & 0 deletions docs/design/interoperability/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,244 @@
# 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)
- [Interoperability syntax elements](#interoperability-syntax-elements)
- [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++ APIs and export C++ APIs.
Supporting C++ interoperability is a
[key requirement for Carbon's goals](/docs/project/goals.md) and is expected to
influence the design of Carbon itself. The interoperability layer also has its
own [goals and philosophy](goals_and_philosophy.md) which guide the design of
individual features.

## 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. For example,
`$extern("Cpp", namespace="myproject")`.
- The Carbon toolchain will translate externed declarations to C++, and they
will be available to C++ code through `.6c.h` header files.
- `import Cpp "path"`: Imports APIs from a C++ header file.

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

## 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, and guard
> rails to prevent overuse/misuse should be considered.

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

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

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.

Where performance is critical, such as primitive types, mappings are required to
have identical memory layout between C++ and Carbon. This is necessary to
provide inteoperability calls without a conversion cost.

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

C++ class templates are directly made available in Carbon. For example, ignoring
allocators and their associated complexity, `std::vector<T>` in C++ would be
available as `Cpp.std.vector(T)` in Carbon.

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)
113 changes: 113 additions & 0 deletions docs/design/interoperability/enums.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
# Type mapping

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

- [C/C++ enums in Carbon](#cc-enums-in-carbon)
- [Carbon enums in C/C++](#carbon-enums-in-cc)

<!-- tocstop -->

We expect enums can be represented directly in the other language. All values in
the copy should be assumed to be explicit, to prevent any possible issues in
Comment on lines +25 to +26
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 "All values in the copy should be assumed to be explicit" mean?

enum semantics.
geoffromer marked this conversation as resolved.
Show resolved Hide resolved

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

`enum class`. Attributes may be used if renaming is desired to reach
conventional Carbon naming.

Given a C++ enum:

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

We would expect to generate equivalent Carbon code:

```carbon
enum Direction {
East = 0,
West = 20,
North = 21,
South = 22,
}
Comment on lines +49 to +54
Copy link
Contributor

Choose a reason for hiding this comment

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

I would expect to by default also generate

alias East = Direction.East;
alias West = Direction.West;
alias North = Direction.North;
alias South = Direction.South;`

In general I think we should aim that the Cpp. package in Carbon has the same names in the same scopes as exist in C++. The C++ code introduces a ::East, so the Carbon code should have a Cpp.East. If we want to clean up the names, that should be done when the Cpp.* names are aliased into some Carbon package and re-exported.


// Calling semantic:
var Direction: x = Direction.East;
```

Sometimes enum names may repeat the enum identifier; for example,
`DIRECTION_EAST` instead of `East`. To help with this case, we may want to
support renaming of enum entries. For example, to rename in a way that results
in a match to the above Carbon calling convention, we add `carbon_enum`:

```cc
enum Direction {
DIRECTION_EAST,
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.

```

If using enum class, we'd expect similar behavior:

```cc
enum class Direction : char {
East = 'E',
West = 'W',
North = 'N',
South = 'S',
};
```

With Carbon code:

```carbon
enum(Byte) Direction {
East = 'E',
West = 'W',
North = 'N',
South = 'S',
};
```

## Carbon enums in C/C++

Carbon enums should be expected to translate to `enum class`.

For example, given a Carbon enum:

```carbon
$extern("Cpp") enum Direction {
East,
West = 20,
North,
South,
}
```

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?


```cc
enum class Direction {
East = 0,
West = 20,
North = 21,
South = 22,
};
```
Loading