Skip to content
183 changes: 182 additions & 1 deletion docs/design/interoperability/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,26 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

- [Philosophy and goals](#philosophy-and-goals)
- [Overview](#overview)
- [C++ interoperability model: introduction and principles](#c-interoperability-model-introduction-and-principles)
- [The "successor language" mandate](#the-successor-language-mandate)
- [The C++ interop type](#the-c-interop-type)
- [Importing C++ APIs into Carbon](#importing-c-apis-into-carbon)
- [Importing C++ libraries (header-based)](#importing-c-libraries-header-based)
- [TODO: Importing C++ code (inline)](#todo-importing-c-code-inline)
- [Accessing built-in C++ entities (file-less)](#accessing-built-in-c-entities-file-less)
- [The `Cpp` package](#the-cpp-package)
- [TODO: Importing C++ macros](#todo-importing-c-macros)
- [Calling C++ code from Carbon](#calling-c-code-from-carbon)
- [Function call syntax and semantics](#function-call-syntax-and-semantics)
- [TODO: Overload resolution](#todo-overload-resolution)
- [TODO: Constructors](#todo-constructors)
- [TODO: Struct literals](#todo-struct-literals)
- [TODO: Accessing C++ classes, structs, and members](#todo-accessing-c-classes-structs-and-members)
- [TODO: Accessing global variables](#todo-accessing-global-variables)
- [TODO: Bi-directional type mapping: primitives and core types](#todo-bi-directional-type-mapping-primitives-and-core-types)
- [TODO: Advanced type mapping: pointers, references, and `const`](#todo-advanced-type-mapping-pointers-references-and-const)
- [TODO: Bi-directional type mapping: standard library types](#todo-bi-directional-type-mapping-standard-library-types)
- [TODO: The operator interoperability model](#todo-the-operator-interoperability-model)

<!-- tocstop -->

Expand All @@ -29,4 +49,165 @@ more detail.

## Overview

TODO
Carbon's bidirectional interoperability with C++ is
[a cornerstone of its design](/docs/project/goals.md#interoperability-with-and-migration-from-existing-c-code),
enabling a gradual transition from existing C++ codebases. The goal is not just
a foreign function interface (FFI), but a seamless, high-fidelity integration
that supports advanced C++ features, from templates to class hierarchies.

C++ APIs are imported into Carbon using an `import Cpp` directive, which makes
C++ declarations available within a dedicated `Cpp` package in Carbon. This
prevents name collisions and makes the origin of symbols explicit. Carbon code
can then call C++ functions, instantiate C++ classes, and use C++ types, while
respecting C++'s semantics, including its complex overload resolution rules and
preserving the nominal distinctions between C++ types like `long` and
`long long`, or `T*` and `T&`, which is critical for correct overload resolution
and template instantiation.

Similarly, Carbon APIs can be designed to be callable from C++. The
interoperability layer is designed to be zero-cost, avoiding unnecessary
allocations or copies when calling between the two languages.

## C++ interoperability model: introduction and principles

### The "successor language" mandate

The design of Carbon's C++ interoperability is governed by its foundational
goal: [to be a successor language](/README.md), not merely a language with a
foreign function interface (FFI). This mandate dictates a design that moves
beyond the C-style FFI adopted by most modern languages and instead provides
"seamless, bidirectional interoperability". The objective is to support deep
integration with existing C++ code, encompassing its most complex features,
"from inheritance to templates".

This goal has profound implications for the Carbon compiler and language
semantics. It requires that C++ is not treated as a "foreign" entity. Instead,
Carbon's semantic model must be _co-designed_ to understand, map, and interact
with C++'s semantic constructs—including templates, class hierarchies, and
complex overload resolution—with high fidelity. The interoperability layer must,
therefore, operate at the semantic analysis level, not just at the linking (ABI)
level. This document specifies the design of this semantic contract.
Comment on lines 75 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.

I would drop all the double-quotes here. While these quotes are pulled from the main README, I don't think that's worth calling out, especially since we're quoting ourselves.

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.


### The C++ interop type

A core mechanism in this design is the C++ interop type. This concept defines
the "trigger" that activates C++-specific semantic rules within the Carbon
compiler. Any operation involving a type that is designated as a C++ interop
type could invoke the specialized interoperability logic, such as C++ overload
resolution or operator overload resolution that involves both Carbon and C++
operator overloads..
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
operator overloads..
operator overloads.

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.


A type is considered a C++ interop type if its definition involves an imported
C++ type in any of the following ways:

1. A C++ imported type (for example, `Cpp.Widget`).
2. A pointer to a C++ interop type (for example, `Cpp.Widget*`).
3. A Carbon generic type parameterized with a C++ interop type (for example,
`MyCarbonVector(Cpp.Widget)`).
4. A Carbon struct or class containing a C++ interop type as a member (for
example, `MyCarbonStruct { x: Cpp.Widget }`).
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we want this fourth case? It sounds expensive to check whether Carbon classes are compounded from C++ types in general, and I'm not sure we know yet what Carbon struct types would map into in C++, but I don't think it's likely that there'll be C++ operations defined for either case.

Maybe we can narrow this down to just Carbon class types that extend a C++ 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.

Done.


This "pervasive" model of C++-awareness is a fundamental design choice. The C++
semantics are not confined to a specific `unsafe` or `extern "C++"` block; they
affect any Carbon type that composes them. For example, when the Carbon compiler
instantiates a _Carbon_ generic type like `MyCarbonVector(Cpp.Widget)`, its type
system must be aware that the `Cpp.Widget` parameter carries C++-specific rules.
This mandates that Carbon's own generic system, struct layout logic, overload
resolution and operator lookup must query the type system for the presence of a
C++ interop type. If present, Carbon must consider C++ rules when operating over
C++ interop types. This design prioritizes the goal of a "seamless" and
"intuitive" user experience.
Copy link
Contributor

Choose a reason for hiding this comment

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

As above, I'd drop the double-quotes 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.

Done.


## Importing C++ APIs into Carbon

### Importing C++ libraries (header-based)

The primary mechanism for importing existing, user-defined C++ code is through
header file inclusion. Carbon must be able to parse and analyze C++ header files
to make their declarations available within Carbon.

**Syntax:** The syntax for this operation is `import Cpp library "header_name"`.
This syntax is used for both C-style standard libraries and C++ headers:

- **C Standard Library:**
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
This syntax is used for both C-style standard libraries and C++ headers:
- **C Standard Library:**
This syntax is used for both standard library headers and user-defined headers:
- **Standard Library:**

I think we should try to avoid mentioning C 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.

Done.


```carbon
import Cpp library "<cstdio>";
```

This import makes entities like `putchar` available.

- **C++ User-Defined Header:**
```carbon
import Cpp library "circle.h";
```
This import makes user-defined declarations and definitions available.

### TODO: Importing C++ code (inline)

### Accessing built-in C++ entities (file-less)

Some C++ entities, particularly built-in primitive types, are not defined in any
header file. They are "intrinsic" to the C++ language. These entities are
available in Carbon without an explicit `import` declaration.

### The `Cpp` package

A critical design choice for managing C++ imports is the mandatory use of a
containing package, `Cpp`. All imported C++ named entities (functions, types,
namespaces) are contained in the `Cpp` package.

- **Functions:** `Cpp.putchar(...)`
- **Classes/Types:** `Cpp.Circle`, `Cpp.Point`
- **Constructors:** `Cpp.Circle.Circle()`

The `Cpp.` prefix makes the _origin_ of every symbol explicit and unambiguous.
It ensures that C++ entities cannot collide with Carbon code.

### TODO: Importing C++ macros

## Calling C++ code from Carbon

### Function call syntax and semantics

Once imported, C++ functions are invoked using standard Carbon function call
syntax, prefixed with the `Cpp` name. The Carbon compiler is responsible for
mapping the Carbon arguments to the types expected by the C++ function's
signature.

This often requires explicit casting on the Carbon side, using the `as` keyword,
to satisfy the C++ function's parameter types.

**Example:** The following example imports `cstdio` and calls the C function
`putchar`. The Carbon `Core.Char` variable `n` must be cast first to `u8` and
then to `i32` to match the `int` parameter expected by `putchar`.

```carbon
import Cpp library "<cstdio>";

fn Run() {
let hello: array(Core.Char, 6) = ('H', 'e', 'l', 'l', 'o', '!');
for (n: Core.Char in hello) {
// Carbon 'as' casting is used to match the C++ signature
Cpp.putchar((n as u8) as i32);
}
}
```

### TODO: Overload resolution

### TODO: Constructors

### TODO: Struct literals

## TODO: Accessing C++ classes, structs, and members

## TODO: Accessing global variables

## TODO: Bi-directional type mapping: primitives and core types

## TODO: Advanced type mapping: pointers, references, and `const`

## TODO: Bi-directional type mapping: standard library types

## TODO: The operator interoperability model
Loading