Design overview update part 4: C and C++ Interop (#1336)

This follows #1274 , #1325 , and #1328 . It fills in the "Bidirectional interoperability with C and C++" section.

Co-authored-by: Chandler Carruth <chandlerc@gmail.com>
Co-authored-by: Geoff Romer <gromer@google.com>
This commit is contained in:
josh11b
2022-06-27 17:20:24 -07:00
committed by GitHub
co-authored by Chandler Carruth Geoff Romer
parent 9ea405788e
commit fbf353afe8
+288 -7
View File
@@ -84,7 +84,16 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
- [Generic type equality and `observe` declarations](#generic-type-equality-and-observe-declarations)
- [Operator overloading](#operator-overloading)
- [Common type](#common-type)
- [Bidirectional interoperability with C/C++](#bidirectional-interoperability-with-cc)
- [Bidirectional interoperability with C and C++](#bidirectional-interoperability-with-c-and-c)
- [Goals](#goals)
- [Non-goals](#non-goals)
- [Importing and `#include`](#importing-and-include)
- [ABI and dynamic linking](#abi-and-dynamic-linking)
- [Operator overloading](#operator-overloading-1)
- [Templates](#templates)
- [Standard types](#standard-types)
- [Inheritance](#inheritance-1)
- [Enums](#enums)
- [Unfinished tales](#unfinished-tales)
- [Safety](#safety)
- [Pattern matching as function overload resolution](#pattern-matching-as-function-overload-resolution)
@@ -453,6 +462,10 @@ type `Optional(T*)`.
Pointers are the main Carbon mechanism for allowing a function to modify a
variable of the caller.
**TODO:** Perhaps Carbon will have
[stricter pointer provenance](https://www.ralfj.de/blog/2022/04/11/provenance-exposed.html)
or restrictions on casts between pointers and integers.
> References:
>
> - Question-for-leads issue
@@ -1071,7 +1084,7 @@ This is instead of
#### `match`
`match` is a control flow similar to `switch` of C/C++ and mirrors similar
`match` is a control flow similar to `switch` of C and C++ and mirrors similar
constructs in other languages, such as Swift. The `match` keyword is followed by
an expression in parentheses, whose value is matched against the `case`
declarations, each of which contains a [refutable pattern](#refutable-patterns),
@@ -1553,7 +1566,7 @@ This type can be used from another package:
```carbon
package ExampleUser;
import Geometry library("OneSide");
import Geometry library "OneSide";
fn Foo(Geometry.Shapes.Flat.Circle circle) { ... }
```
@@ -2179,6 +2192,8 @@ The interfaces that correspond to each operator are given by:
[`As(U)`](expressions/as_expressions.md#extensibility) interface
- Implicit conversions use
[`ImplicitAs(U)`](expressions/implicit_conversions.md#extensibility)
- **TODO:** [Assignment](#assignment-statements): `x = y`, `++x`, `x += y`,
and so on
- **TODO:** Dereference: `*p`
- **TODO:** Indexing: `a[3]`
- **TODO:** Function call: `f(4)`
@@ -2243,17 +2258,283 @@ The common type is required to be a type that both types have an
> - Question-for-leads issue
> [#1077: find a way to permit impls of CommonTypeWith where the LHS and RHS type overlap](https://github.com/carbon-language/carbon-lang/issues/1077)
## Bidirectional interoperability with C/C++
## Bidirectional interoperability with C and C++
> **TODO:** Needs a detailed design and a high level summary provided inline.
Interoperability, or _interop_, is the ability to call C and C++ code from
Carbon code and the other way around. This ability achieves two goals:
- Allows sharing a code and library ecosystem with C and C++.
- Allows incremental migration to Carbon from C and C++.
Carbon's approach to interopp is most similar to
[Java/Kotlin interop](interoperability/philosophy_and_goals.md#other-interoperability-layers),
where the two languages are different, but share enough of runtime model that
data from one side can be used from the other. For example, C++ and Carbon will
use the same
[memory model](https://en.cppreference.com/w/cpp/language/memory_model).
The design for interoperability between Carbon and C++ hinges on:
1. The ability to interoperate with a wide variety of code, such as
classes/structs and
[templates](<https://en.wikipedia.org/wiki/Template_(C%2B%2B)>), not just
free functions.
2. A willingness to expose the idioms of C++ into Carbon code, and the other
way around, when necessary to maximize performance of the interoperability
layer.
3. The use of wrappers and generic programming, including templates, to
minimize or eliminate runtime overhead.
This feature will have some restrictions; only a subset of Carbon APIs will be
available to C++ and a subset of C++ APIs will be available to Carbon.
- To achieve simplification in Carbon, its programming model will exclude some
rarely used and complex features of C++. For example, there will be
limitations on
[multiple inheritance](https://en.wikipedia.org/wiki/Multiple_inheritance).
- C or C++ features that compromise the performance of code that don't use
that feature, like
[RTTI](https://en.wikipedia.org/wiki/Run-time_type_information) and
[exceptions](https://en.wikipedia.org/wiki/Exception_handling), are in
particular subject to revision in Carbon.
> References:
>
> - [Bidirectional interoperability with C/C++](interoperability/README.md)
> - Proposal
> [#175: C++ interoperability goals](https://github.com/carbon-language/carbon-lang/pull/175)
### Goals
The [goals for interop](interoperability/philosophy_and_goals.md#goals) include:
- [Support mixing Carbon and C++ toolchains](interoperability/philosophy_and_goals.md#support-mixing-carbon-and-c-toolchains)
- [Compatibility with the C++ memory model](interoperability/philosophy_and_goals.md#compatibility-with-the-c-memory-model)
- [Minimize bridge code](interoperability/philosophy_and_goals.md#minimize-bridge-code)
- [Unsurprising mappings between C++ and Carbon types](interoperability/philosophy_and_goals.md#unsurprising-mappings-between-c-and-carbon-types)
- [Allow C++ bridge code in Carbon files](interoperability/philosophy_and_goals.md#allow-c-bridge-code-in-carbon-files)
- [Carbon inheritance from C++ types](interoperability/philosophy_and_goals.md#carbon-inheritance-from-c-types)
- [Support use of advanced C++ features](interoperability/philosophy_and_goals.md#support-use-of-advanced-c-features)
- [Support basic C interoperability](interoperability/philosophy_and_goals.md#support-basic-c-interoperability)
> References:
>
> **TODO:** References need to be evolved.
> - [Interoperability: Goals](interoperability/philosophy_and_goals.md#goals)
### Non-goals
The [non-goals for interop](interoperability/philosophy_and_goals.md#non-goals)
include:
- [Full parity between a Carbon-only toolchain and mixing C++/Carbon toolchains](interoperability/philosophy_and_goals.md#full-parity-between-a-carbon-only-toolchain-and-mixing-ccarbon-toolchains)
- [Never require bridge code](interoperability/philosophy_and_goals.md#never-require-bridge-code)
- [Convert all C++ types to Carbon types](interoperability/philosophy_and_goals.md#convert-all-c-types-to-carbon-types)
- [Support for C++ exceptions without bridge code](interoperability/philosophy_and_goals.md#support-for-c-exceptions-without-bridge-code)
- [Cross-language metaprogramming](interoperability/philosophy_and_goals.md#cross-language-metaprogramming)
- [Offer equivalent support for languages other than C++](interoperability/philosophy_and_goals.md#offer-equivalent-support-for-languages-other-than-c)
> References:
>
> - [Interoperability: Non-goals](interoperability/philosophy_and_goals.md#non-goals)
### Importing and `#include`
A C++ library header file may be [imported](#packages-libraries-namespaces) into
Carbon using an `import` declaration of the special `Cpp` package.
```carbon
// like `#include "circle.h"` in C++
import Cpp library "circle.h"
```
This adds the names from `circle.h` into the `Cpp` namespace. If `circle.h`
defines some names in a `namespace shapes { ... }` scope, those will be found in
Carbon's `Cpp.shapes` namespace.
In the other direction, Carbon packages can export a header file to be
`#include`d from C++ files.
```c++
// like `import Geometry` in Carbon
#include "geometry.carbon.h"
```
Generally Carbon entities will be usable from C++ and C++ entities will be
usable from Carbon. This includes types, function, and constants. Some entities,
such as Carbon interfaces, won't be able to be translated directly.
C and C++ macros that are defining constants will be imported as constants.
Otherwise, C and C++ macros will be unavailable in Carbon. C and C++ `typedef`s
would be translated into type constants, as if declared using a
[`let`](#constant-let-declarations).
Carbon functions and types that satisfy some restrictions may be annotated as
exported to C as well, like C++'s
[`extern "C"`](https://en.wikipedia.org/wiki/Compatibility_of_C_and_C%2B%2B#Linking_C_and_C++_code)
marker.
### ABI and dynamic linking
Carbon itself will not have a stable ABI for the language as a whole, and most
language features will be designed around not having any ABI stability. Instead,
we expect to add dedicated language features that are specifically designed to
provide an ABI-stable boundary between two separate parts of a Carbon program.
These ABI-resilient language features and API boundaries will be opt-in and
explicit. They may also have functionality restrictions to make them easy to
implement with strong ABI resilience.
When interoperating with already compiled C++ object code or shared libraries,
the C++ interop may be significantly less feature rich than otherwise. This is
an open area for us to explore, but we expect to require re-compiling C++ code
in order to get the full ergonomic and performance benefits when interoperating
with Carbon. For example, recompilation lets us ensure Carbon and C++ can use
the same representation for key vocabulary types.
However, we expect to have full support for the C ABI when interoperating with
already-compiled C object code or shared libraries. We expect Carbon's bridge
code functionality to cover similar use cases as C++'s
[`extern "C"`](https://en.wikipedia.org/wiki/Compatibility_of_C_and_C%2B%2B#Linking_C_and_C++_code)
marker in order to provide full bi-directional support here. The functionality
available across this interop boundary will of course be restricted to what is
expressible in the C ABI, and types may need explicit markers to have guaranteed
ABI compatibility.
### Operator overloading
[Operator overloading](#operator-overloading) is supported in Carbon, but is
done by [implementing an interface](#interfaces-and-implementations) instead of
defining a method or nonmember function as in C++.
Carbon types implementing an operator overload using an interface should get the
corresponding operator overload in C++. So implementing `ModWith(U)` in Carbon
for a type effectively implements `operator%` in C++ for that type. This also
works in the other direction, so C++ types implementing an operator overload are
automatically considered to implement the corresponding Carbon interface. So
implementing `operator%` in C++ for a type also implements interface
`ModWith(U)` in Carbon. However, there may be edge cases around implicit
conversions or overload selection that don't map completely into Carbon.
In some cases, the operation might be written differently in the two languages.
In those cases, they are matched according to which operation has the most
similar semantics rather than using the same symbols. For example, the `^x`
operation and `BitComplement` interface in Carbon corresponds to the `~x`
operation and `operator~` function in C++. Similarly, the `ImplicitAs(U)` Carbon
interface corresponds to implicit conversions in C++, which can be written in
multiple different ways. Other
[C++ customization points](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4381.html)
like `swap` will correspond to a Carbon interface, on a case-by-case basis.
Some operators will only exist or be overridable in C++, such as logical
operators or the comma operator. In the unlikely situation where those operators
need to be overridden for a Carbon type, that can be done with a nonmember C++
function.
Carbon intefaces with no C++ equivalent, such as
[`CommonTypeWith(U)`](#common-type), may be implemented for C++ types externally
in Carbon code. To satisfy the orphan rule
([1](generics/details.md#impl-lookup), [2](generics/details.md#orphan-rule)),
each C++ library will have a corresponding Carbon wrapper library that must be
imported instead of the C++ library if the Carbon wrapper exists. **TODO:**
Perhaps it will automatically be imported, so a wrapper may be added without
requiring changes to importers?
### Templates
Carbon supports both
[checked and template generics](#checked-and-template-parameters). This provides
a migration path for C++ template code:
- C++ template -> Carbon template: This involves migrating the code from C++
to Carbon. If that migration is faithful, the change should be transparent
to callers.
- -> Carbon template with constraints: Constraints may be added one at a time.
Adding a constraint never changes the meaning of the code as long as it
continues to compile. Compile errors will point to types for which an
implementation of missing interfaces is needed. A temporary template
implementation of that interface can act as a bridge during the transition.
- -> Carbon checked generic: Once all callers work after all constraints have
been added, the template parameter may be switched to a checked generic.
Carbon will also provide direct interop with C++ templates in many ways:
- Ability to call C++ templates and use C++ templated types from Carbon.
- Ability to instantiate a C++ template with a Carbon type.
- Ability to instantiate a Carbon generic with a C++ type.
We expect the best interop in these areas to be based on a Carbon-provided C++
toolchain. However, even when using Carbon's generated C++ headers for interop,
we will include the ability where possible to use a Carbon generic from C++ as
if it were a C++ template.
### Standard types
The Carbon integer types, like `i32` and `u64`, are considered equal to the
corresponding fixed-width integer types in C++, like `int32_t` and `uint64_t`,
provided by `<stdint.h>` or `<cstdint>`. The basic C and C++ integer types like
`int`, `char`, and `unsigned long` are available in Carbon inside the `Cpp`
namespace given an `import Cpp;` declaration, with names like `Cpp.int`,
`Cpp.char`, and `Cpp.unsigned_long`. C++ types are considered different if C++
considers them different, so C++ overloads are resolved the same way. Carbon
[conventions for implicit conversions between integer types](expressions/implicit_conversions.md#data-types)
apply here, allowing them whenever the numerical value for all inputs may be
preserved by the conversion.
Other C and C++ types are equal to Carbon types as follows:
| C or C++ | Carbon |
| -------- | -------------- |
| `bool` | `bool` |
| `float` | `f32` |
| `double` | `f64` |
| `T*` | `Optional(T*)` |
| `T[4]` | `[T; 4]` |
Further, C++ reference types like `T&` will be translated to `T*` in Carbon,
which is Carbon's non-null pointer type.
Carbon will work to have idiomatic vocabulary _view_ types for common data
structures, like `std::string_view` and `std::span`, map transparently between
C++ and the Carbon equivalents. This will include data layout so that even
pointers to these types translate seamlessly, contingent on a suitable C++ ABI
for those types, potentially by re-compiling the C++ code with a customized ABI.
We will also explore how to expand coverage to similar view types in other
libraries.
However, Carbon's containers will be distinct from the C++ standard library
containers in order to maximize our ability to improve performance and leverage
language features like checked generics in their design and implementation.
Where possible, we will also try to provide implementations of Carbon's standard
library container _interfaces_ for the relevant C++ container types so that they
can be directly used with generic Carbon code. This should allow generic code in
Carbon to work seamlessly with both Carbon and C++ containers without
performance loss or constraining the Carbon container implementations. In the
other direction, Carbon containers will satisfy C++ container requirements, so
templated C++ code can operate directly on Carbon containers as well.
### Inheritance
[Carbon has single inheritance](#inheritance) allowing C++ classes using
inheritance to be migrated. The data representation will be consistent so that
Carbon classes may inherit from C++ classes, and the other way around, even with
virtual methods.
C++ [multiple inheritance](https://en.wikipedia.org/wiki/Multiple_inheritance)
and [CRTP](https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern)
will be migrated using a combination of Carbon features. Carbon mixins support
implementation reuse and Carbon interfaces allow a type to implement multiple
APIs. However, there may be limits on the degree of interop available with
multiple inheritance across the C++ <-> Carbon boundaries.
Carbon dyn-safe interfaces may be exported to C++ as an
[abstract base class](<https://en.wikipedia.org/wiki/Class_(computer_programming)#Abstract_and_concrete>).
The reverse operation is also possible using a proxy object implementing a C++
abstract base class and holding a pointer to a type implementing the
corresponding interface.
### Enums
> **TODO**
## Unfinished tales
@@ -2342,7 +2623,7 @@ This leads to Carbon's incremental path to safety:
Carbon provides metaprogramming facilities that look similar to regular Carbon
code. These are structured, and do not offer arbitrary inclusion or
preprocessing of source text such as C/C++ does.
preprocessing of source text such as C and C++ do.
> References: [Metaprogramming](metaprogramming.md)