From 078bf5b3d4ca7dea10a3a24ac8806178954a992f Mon Sep 17 00:00:00 2001 From: Boaz Brickner Date: Tue, 21 Oct 2025 11:26:26 +0200 Subject: [PATCH 1/3] Filling out template with PR 6254 --- proposals/p6254.md | 70 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 proposals/p6254.md diff --git a/proposals/p6254.md b/proposals/p6254.md new file mode 100644 index 0000000000000..4a3fce5e13a2d --- /dev/null +++ b/proposals/p6254.md @@ -0,0 +1,70 @@ +# Calling C++ Functions + + + +[Pull request](https://github.com/carbon-language/carbon-lang/pull/6254) + + + +## Table of contents + +- [Abstract](#abstract) +- [Problem](#problem) +- [Background](#background) +- [Proposal](#proposal) +- [Details](#details) +- [Rationale](#rationale) +- [Alternatives considered](#alternatives-considered) + + + +## Abstract + +TODO: Describe, in a succinct paragraph, the gist of this document. This +paragraph should be reproduced verbatim in the PR summary. + +## Problem + +TODO: What problem are you trying to solve? How important is that problem? Who +is impacted by it? + +## Background + +TODO: Is there any background that readers should consider to fully understand +this problem and your approach to solving it? + +## Proposal + +TODO: Briefly and at a high level, how do you propose to solve the problem? Why +will that in fact solve it? + +## Details + +TODO: Fully explain the details of the proposed solution. + +## Rationale + +TODO: How does this proposal effectively advance Carbon's goals? Rather than +re-stating the full motivation, this should connect that motivation back to +Carbon's stated goals and principles. This may evolve during review. Use links +to appropriate sections of [`/docs/project/goals.md`](/docs/project/goals.md), +and/or to documents in [`/docs/project/principles`](/docs/project/principles). +For example: + +- [Community and culture](/docs/project/goals.md#community-and-culture) +- [Language tools and ecosystem](/docs/project/goals.md#language-tools-and-ecosystem) +- [Performance-critical software](/docs/project/goals.md#performance-critical-software) +- [Software and language evolution](/docs/project/goals.md#software-and-language-evolution) +- [Code that is easy to read, understand, and write](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write) +- [Practical safety and testing mechanisms](/docs/project/goals.md#practical-safety-and-testing-mechanisms) +- [Fast and scalable development](/docs/project/goals.md#fast-and-scalable-development) +- [Modern OS platforms, hardware architectures, and environments](/docs/project/goals.md#modern-os-platforms-hardware-architectures-and-environments) +- [Interoperability with and migration from existing C++ code](/docs/project/goals.md#interoperability-with-and-migration-from-existing-c-code) + +## Alternatives considered + +TODO: What alternative solutions have you considered? From 1703594cf6227c44c56deb78ffbc01ee4f6eb7ba Mon Sep 17 00:00:00 2001 From: Boaz Brickner Date: Fri, 24 Oct 2025 10:51:50 +0200 Subject: [PATCH 2/3] =?UTF-8?q?Calling=20C++=20Functions=20This=20proposal?= =?UTF-8?q?=20details=20the=20mechanism=20for=20calling=20imported=20C++?= =?UTF-8?q?=20functions=20from=20Carbon=20code.=20It=20covers=20how=20C++?= =?UTF-8?q?=20overload=20sets=20are=20handled,=20the=20process=20of=20over?= =?UTF-8?q?load=20resolution=20leveraging=20Clang,=20and=20the=20generatio?= =?UTF-8?q?n=20of=20"thunks"=20=E2=80=93=20intermediate=20functions=20?= =?UTF-8?q?=E2=80=93=20when=20necessary=20to=20bridge=20Application=20Bina?= =?UTF-8?q?ry=20Interface=20(ABI)=20differences=20between=20Carbon=20and?= =?UTF-8?q?=20C++.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- proposals/p6254.md | 229 ++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 203 insertions(+), 26 deletions(-) diff --git a/proposals/p6254.md b/proposals/p6254.md index 4a3fce5e13a2d..2ddd5afd27f2d 100644 --- a/proposals/p6254.md +++ b/proposals/p6254.md @@ -17,54 +17,231 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - [Background](#background) - [Proposal](#proposal) - [Details](#details) + - [Importing C++ Functions](#importing-c-functions) + - [Overload Resolution](#overload-resolution) + - [Direct Calls versus Thunks](#direct-calls-versus-thunks) + - [Thunk Generation](#thunk-generation) + - [Parameter and Return Value Handling](#parameter-and-return-value-handling) + - [Member Function Calls](#member-function-calls) + - [Operator Calls](#operator-calls) - [Rationale](#rationale) - [Alternatives considered](#alternatives-considered) + - [Require Manual C++ Wrappers](#require-manual-c-wrappers) + - [Mandate Carbon ABI Compatibility with C++](#mandate-carbon-abi-compatibility-with-c) ## Abstract -TODO: Describe, in a succinct paragraph, the gist of this document. This -paragraph should be reproduced verbatim in the PR summary. +This proposal details the mechanism for calling imported C++ functions from +Carbon code. It covers how C++ overload sets are handled, the process of +overload resolution leveraging Clang, and the generation of "thunks" – +intermediate functions – when necessary to bridge Application Binary Interface +(ABI) differences between Carbon and C++. ## Problem -TODO: What problem are you trying to solve? How important is that problem? Who -is impacted by it? +Seamless, high-performance interoperability with C++ +[is a fundamental goal of Carbon](https://github.com/carbon-language/carbon-lang/blob/f9bd01536b97961039257cc10fb20b495f7a9b33/docs/project/goals.md#interoperability-with-and-migration-from-existing-c-code). +To achieve this, Carbon code must be able to call C++ functions naturally. +Several challenges arise: + +- C++ supports function overloading, requiring Carbon to resolve calls to the + correct C++ function within an overload set. +- C++ types do not always have identical representations or ABIs to their + Carbon counterparts (see + [Carbon <-> C++ Interop: Primitive Types](https://github.com/carbon-language/carbon-lang/blob/44b2f60c90df5c1b0ce86f97bb0ece2a94eb50ea/proposals/p5448.md)). + For example, parameter passing conventions (by value, by pointer) or return + value handling (direct return versus return slot) might differ. +- C++ member functions require handling of the `this` pointer. +- C++ supports features like default arguments which need a defined mapping. + +A clear, robust mechanism is needed to handle these complexities, ensuring both +correctness and performance while providing a good developer experience. ## Background -TODO: Is there any background that readers should consider to fully understand -this problem and your approach to solving it? +[Carbon's C++ interoperability philosophy](https://github.com/carbon-language/carbon-lang/blob/01e12111a8a685694ccd2c9deb2779f907917543/docs/design/interoperability/philosophy_and_goals.md) +aims to minimize bridge code and provide unsurprising mappings. When Carbon code +imports a C++ header, the functions declared within become potentially callable +entities. C++ overload resolution rules are complex, and replicating them +perfectly within Carbon would be difficult and likely divergent over time. +Furthermore, direct calls are only possible when the ABI conventions of the +Carbon call site precisely match the expectations of the C++ callee. ## Proposal -TODO: Briefly and at a high level, how do you propose to solve the problem? Why -will that in fact solve it? +1. **Import:** C++ functions and methods, including overload sets, are imported + into Carbon and represented internally (conceptually, as specific overload + set instructions in SemIR). +2. **Overload Resolution:** When a call to an imported C++ function or overload + set occurs in Carbon, Carbon leverages Clang's overload resolution + mechanism. Carbon argument types are mapped to hypothetical C++ types / + expressions, and Clang's `Sema` determines the best viable function. +3. **ABI Bridging (Thunks):** + - If the selected C++ function's ABI (parameter types, return type + handling, calling convention) matches the Carbon call site's ABI based + on defined type mappings, a direct call is generated. + - If the ABIs mismatch, Carbon generates an intermediate function, called + a **C++ thunk**. This thunk has a "simple" ABI callable directly from + Carbon (typically using only pointers and basic integer types like + `i32`/`i64`). The thunk internally calls the actual C++ function, + performing necessary argument conversions (for example, loading a value + from a pointer) and handling return value conventions (for example, + managing a return slot). +4. **Call Execution:** The Carbon code either calls the C++ function directly + or calls the generated C++ thunk. ## Details -TODO: Fully explain the details of the proposed solution. +### Importing C++ Functions + +When a C++ header is imported using `import Cpp`, declarations within that +header are made available. Function declarations, including member functions and +overloaded functions, are represented internally within Carbon's SemIR. An +overload set from C++ is represented as a single callable entity in Carbon, +associated with the set of C++ candidate functions. + +### Overload Resolution + +To resolve a call like `Cpp.MyNamespace.MyFunc(arg1, arg2)` where `MyFunc` might +be an overload set imported from C++: + +1. **Map Arguments:** Carbon argument instructions (`arg1`, `arg2`) are mapped + to placeholder C++ expressions (conceptually similar to + [`clang::OpaqueValueExpr`](https://github.com/llvm/llvm-project/blob/1e99026b45b048a52f8372399ab83d488132842e/clang/include/clang/AST/Expr.h#L1178)). + The types of these expressions are determined by mapping the Carbon argument + types to corresponding C++ types + ([Carbon <-> C++ Interop: Primitive Types](https://github.com/carbon-language/carbon-lang/blob/44b2f60c90df5c1b0ce86f97bb0ece2a94eb50ea/proposals/p5448.md)). +2. **Invoke Clang Sema:** Carbon invokes Clang's overload resolution logic + ([`clang::OverloadCandidateSet::BestViableFunction()`](https://github.com/llvm/llvm-project/blob/1e99026b45b048a52f8372399ab83d488132842e/clang/include/clang/Sema/Overload.h#L1456)) + with the mapped C++ name, the candidate functions from the imported overload + set, and the placeholder argument expressions. +3. **Select Candidate:** Clang determines the best viable C++ function based on + C++ rules (implicit conversions, template argument deduction if applicable + later, etc.). If resolution fails (no viable function, ambiguity), Clang's + diagnostics are surfaced as Carbon diagnostics. +4. **Access Check:** After selecting a function, Carbon checks if the function + is accessible based on C++ access specifiers (`public`, `protected`, + `private`) in the context of the call. + +### Direct Calls versus Thunks + +A direct call from Carbon to C++ is possible only if the ABI matches exactly. A +**C++ thunk** is required if: + +- **Type Representation Mismatch:** A parameter or the return type has a + different representation in Carbon than expected by the C++ ABI, requiring + conversion. For example, a Carbon `bool` (`i1`) passed to a C++ `bool` + (often `i8`), or complex struct types. +- **Return Convention Mismatch:** The C++ function returns a non-trivial type + by value, which typically requires a hidden return slot parameter in the + ABI, whereas Carbon might expect a direct return value. +- **Parameter Convention Mismatch:** C++ expects a parameter by way of + pointer/reference where Carbon provides a value, or vice-versa. +- **Default Arguments:** The Carbon call omits arguments that have default + values in C++. The thunk provides the default values. +- **Variadic arguments:** (Future work) Calling + [C++ variadic arguments](https://en.cppreference.com/w/cpp/language/variadic_arguments.html) + functions. + +If a thunk is _not_ required, Carbon emits a direct call instruction targeting +the mangled name of the C++ function. + +### Thunk Generation + +If a thunk is required for a C++ function `CppOriginalFunc()`, Carbon generates +a new internal function, conceptually `CppOriginalFunc__carbon_thunk()`: + +1. **Signature:** The thunk has an ABI that is simple and directly callable + from Carbon. + - Parameters corresponding to C++ parameters with complex ABIs are passed + by pointer (`T*`). + - Parameters with simple ABIs (like `i32`, `i64`, raw pointers) are passed + directly. + - If `CppOriginalFunc` uses a return slot, the thunk takes a pointer + parameter for the return slot. Its LLVM return type becomes `void`. + - If `CppOriginalFunc` returns a simple type directly, the thunk returns + the same simple type directly. +2. **Body:** The thunk body performs the following: + - Loads values from pointer arguments passed by Carbon where necessary. + - Performs necessary type conversions between Carbon simple ABI types and + C++ expected types (for example, `i1` to `i8` for `bool`). + - Calls `CppOriginalFunc` with the converted arguments, potentially + passing the return slot address. + - If `CppOriginalFunc` returned directly, the thunk returns that value. If + it used a return slot, the thunk returns `void`. +3. **Attributes:** The thunk is typically marked `always_inline` to encourage + the optimizer to remove the indirection. It is given a predictable mangled + name based on the original function's mangled name plus a suffix. + +The Carbon call site then calls the thunk instead of the original C++ function. + +### Parameter and Return Value Handling + +- **Arguments:** When calling a C++ function (directly or by way of a thunk), + Carbon arguments undergo implicit conversions as needed to match the + parameter types determined by overload resolution. For calls requiring a + thunk, additional conversions might occur at the call site (for example, + taking the address of an object to pass by pointer to the thunk) and within + the thunk (for example, loading the object from the pointer). +- **Return Values:** If the C++ function returns `void`, the Carbon call + expression has type `()`. If it returns a simple type directly, the Carbon + call has the corresponding mapped Carbon type. If the C++ function uses a + return slot, the Carbon call is modeled as initializing the storage + designated by the return slot argument (often a temporary created at the + call site), and the overall call expression typically results in the + initialized value. + +### Member Function Calls + +- **Instance Methods:** When `object.CppMethod()` is called, `object` becomes + the implicit `this` argument. Clang's overload resolution handles the + qualification (for example, `const`). The `this` pointer is passed as the + first argument, either directly or to the thunk. +- **Static Methods:** Calls like `CppClass::StaticMethod()` are treated like + free function calls; no `this` pointer is involved. + +### Operator Calls + +Calls to overloaded C++ operators are handled similarly to function calls. +Carbon identifies the operator call, looks up potential C++ operator functions +(both member and non-member), and uses Clang's overload resolution to select the +best candidate. Thunks may be generated if required by the selected operator +function's ABI. ## Rationale -TODO: How does this proposal effectively advance Carbon's goals? Rather than -re-stating the full motivation, this should connect that motivation back to -Carbon's stated goals and principles. This may evolve during review. Use links -to appropriate sections of [`/docs/project/goals.md`](/docs/project/goals.md), -and/or to documents in [`/docs/project/principles`](/docs/project/principles). -For example: - -- [Community and culture](/docs/project/goals.md#community-and-culture) -- [Language tools and ecosystem](/docs/project/goals.md#language-tools-and-ecosystem) -- [Performance-critical software](/docs/project/goals.md#performance-critical-software) -- [Software and language evolution](/docs/project/goals.md#software-and-language-evolution) -- [Code that is easy to read, understand, and write](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write) -- [Practical safety and testing mechanisms](/docs/project/goals.md#practical-safety-and-testing-mechanisms) -- [Fast and scalable development](/docs/project/goals.md#fast-and-scalable-development) -- [Modern OS platforms, hardware architectures, and environments](/docs/project/goals.md#modern-os-platforms-hardware-architectures-and-environments) -- [Interoperability with and migration from existing C++ code](/docs/project/goals.md#interoperability-with-and-migration-from-existing-c-code) +- **Leverages Clang:** Reusing Clang's overload resolution avoids + reimplementing complex C++ rules and ensures consistency. +- **Performance:** Direct calls are used when possible. Thunks are designed to + be minimal and aggressively inlined, minimizing overhead. +- **Correctness:** Thunks handle ABI mismatches systematically, ensuring + correct data marshalling between Carbon and C++. +- **Developer Experience:** Aims for C++ calls to feel natural in Carbon, + hiding much of the complexity of ABI bridging. +- **Interop Goal:** Directly supports the core goal of seamless C++ + interoperability. ## Alternatives considered -TODO: What alternative solutions have you considered? +### Require Manual C++ Wrappers + +Instead of generating thunks automatically, Carbon could require developers to +write C++ wrapper functions with simple C-like ABIs for any C++ function whose +ABI doesn't directly match Carbon's expectations. + +- **Rejected because:** This places a significant burden on the developer, + increases boilerplate, hinders rapid iteration, and makes C++ libraries feel + less integrated. It violates the goal of minimizing bridge code. + +### Mandate Carbon ABI Compatibility with C++ + +Carbon could define its types and calling conventions to always match a specific +C++ ABI (for example, Itanium). + +- **Rejected because:** This would heavily constrain Carbon's own evolution + and design choices. It wouldn't solve the problem entirely, as C++ ABIs + themselves vary (for example, between platforms, compilers, or even + libraries like libc++ vs libstdc++ for `string_view`). It conflicts with the + goal of software and language evolution. From 68a598ebcd32a25271bfe6324d9fc853decdeca9 Mon Sep 17 00:00:00 2001 From: Boaz Brickner Date: Mon, 17 Nov 2025 13:34:47 +0100 Subject: [PATCH 3/3] Fix header casing --- proposals/p6254.md | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/proposals/p6254.md b/proposals/p6254.md index 2ddd5afd27f2d..594358473973f 100644 --- a/proposals/p6254.md +++ b/proposals/p6254.md @@ -17,17 +17,17 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - [Background](#background) - [Proposal](#proposal) - [Details](#details) - - [Importing C++ Functions](#importing-c-functions) - - [Overload Resolution](#overload-resolution) - - [Direct Calls versus Thunks](#direct-calls-versus-thunks) - - [Thunk Generation](#thunk-generation) - - [Parameter and Return Value Handling](#parameter-and-return-value-handling) - - [Member Function Calls](#member-function-calls) - - [Operator Calls](#operator-calls) + - [Importing C++ functions](#importing-c-functions) + - [Overload resolution](#overload-resolution) + - [Direct calls versus thunks](#direct-calls-versus-thunks) + - [Thunk generation](#thunk-generation) + - [Parameter and return value handling](#parameter-and-return-value-handling) + - [Member function calls](#member-function-calls) + - [Operator calls](#operator-calls) - [Rationale](#rationale) - [Alternatives considered](#alternatives-considered) - - [Require Manual C++ Wrappers](#require-manual-c-wrappers) - - [Mandate Carbon ABI Compatibility with C++](#mandate-carbon-abi-compatibility-with-c) + - [Require manual C++ wrappers](#require-manual-c-wrappers) + - [Mandate Carbon ABI compatibility with C++](#mandate-carbon-abi-compatibility-with-c) @@ -94,7 +94,7 @@ Carbon call site precisely match the expectations of the C++ callee. ## Details -### Importing C++ Functions +### Importing C++ functions When a C++ header is imported using `import Cpp`, declarations within that header are made available. Function declarations, including member functions and @@ -102,7 +102,7 @@ overloaded functions, are represented internally within Carbon's SemIR. An overload set from C++ is represented as a single callable entity in Carbon, associated with the set of C++ candidate functions. -### Overload Resolution +### Overload resolution To resolve a call like `Cpp.MyNamespace.MyFunc(arg1, arg2)` where `MyFunc` might be an overload set imported from C++: @@ -125,7 +125,7 @@ be an overload set imported from C++: is accessible based on C++ access specifiers (`public`, `protected`, `private`) in the context of the call. -### Direct Calls versus Thunks +### Direct calls versus thunks A direct call from Carbon to C++ is possible only if the ABI matches exactly. A **C++ thunk** is required if: @@ -148,7 +148,7 @@ A direct call from Carbon to C++ is possible only if the ABI matches exactly. A If a thunk is _not_ required, Carbon emits a direct call instruction targeting the mangled name of the C++ function. -### Thunk Generation +### Thunk generation If a thunk is required for a C++ function `CppOriginalFunc()`, Carbon generates a new internal function, conceptually `CppOriginalFunc__carbon_thunk()`: @@ -177,7 +177,7 @@ a new internal function, conceptually `CppOriginalFunc__carbon_thunk()`: The Carbon call site then calls the thunk instead of the original C++ function. -### Parameter and Return Value Handling +### Parameter and return value handling - **Arguments:** When calling a C++ function (directly or by way of a thunk), Carbon arguments undergo implicit conversions as needed to match the @@ -193,7 +193,7 @@ The Carbon call site then calls the thunk instead of the original C++ function. call site), and the overall call expression typically results in the initialized value. -### Member Function Calls +### Member function calls - **Instance Methods:** When `object.CppMethod()` is called, `object` becomes the implicit `this` argument. Clang's overload resolution handles the @@ -202,7 +202,7 @@ The Carbon call site then calls the thunk instead of the original C++ function. - **Static Methods:** Calls like `CppClass::StaticMethod()` are treated like free function calls; no `this` pointer is involved. -### Operator Calls +### Operator calls Calls to overloaded C++ operators are handled similarly to function calls. Carbon identifies the operator call, looks up potential C++ operator functions @@ -225,7 +225,7 @@ function's ABI. ## Alternatives considered -### Require Manual C++ Wrappers +### Require manual C++ wrappers Instead of generating thunks automatically, Carbon could require developers to write C++ wrapper functions with simple C-like ABIs for any C++ function whose @@ -235,7 +235,7 @@ ABI doesn't directly match Carbon's expectations. increases boilerplate, hinders rapid iteration, and makes C++ libraries feel less integrated. It violates the goal of minimizing bridge code. -### Mandate Carbon ABI Compatibility with C++ +### Mandate Carbon ABI compatibility with C++ Carbon could define its types and calling conventions to always match a specific C++ ABI (for example, Itanium).