commit | 6a813346b846acd0394187d3e58745b11d052a62 | [log] [tgz] |
---|---|---|
author | Devin Jeanpierre <jeanpierreda@google.com> | Mon Aug 05 17:15:18 2024 -0700 |
committer | Copybara-Service <copybara-worker@google.com> | Mon Aug 05 17:16:23 2024 -0700 |
tree | 4e69a7d62cb3b572203af5a436786646c16022aa | |
parent | 03247396adfb3c7ed280c942e7a7d5c78431d3c0 [diff] |
Zip the HIR and MIR together, and use it to expose the `ffi` aliases. See[] (I would suggest not approving without at least in principle approving that doc.) ## What? The core of this CL is that, while recursing through the `ty::Ty`, we also recurse through the corresponding `hir::Ty` if we have one. We then get to see aliases, as long as they haven't been "lost" because we lost them in the HIR. (This can happen for at least two reasons: because the HIR is in another crate (not sure about the boundaries of this?), and because the HIR had an opaque barrier, such as an inferred type, a generic type, or an associated type). ## Why? This has the following benefits: * The direct impetus is that it allows us to map `c_char` to `char`, even though it's actually a `u8` or `i8`. Rust has two 8-bit types, but the Rust standard library uses three. This provides a _relatively_ intuitive behavior. See[] * In a future CL, zipping this will allow us to create bindings for aliases to non-public types. For instance, consider the following: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=725fa6d6140ab9894b6796c71d84abf0 ```rust pub trait MyTrait { type Type; } const _ : () = { pub struct Hidden; impl MyTrait for i32 { type Type = Hidden; } }; pub type X = <i32 as MyTrait>::Type; ``` We may need to see aliases to find the public instance of this, otherwise we are lost. The `rustc_middle::ty::Ty`, however, has no useful name attached to it so far as I know. --- This is not perfect. For `c_char`, ideally we'd use a newtype, not an alias. The HIR will get lost in many predictable places: 1. When the entity is in another crate (?). For example, a trait defined in another crate. This also might be why `pub type MyChar = core::ffi::c_char` doesn't work, see TODO -- have not yet investigated, and we'd need to fully outline the limitations of this approach before the bug is marked fixed. 2. When this is an _instantiation_. For a function `fn foo<T>()`, `foo::<i8>` and `foo::<c_char>` are the _same function_, and HIR is lost here. IMO, we will want to do this regardless of whether we can broadly embrace the use of newtypes, simply because users would _expect_ c_char etc. to work. Walking the HIR like this forces it to work in a predictable set of places: it works wherever it _can_ work, and doesn't work wherever it can't. Easy enough. (See[] PiperOrigin-RevId: 659732898 Change-Id: I700b771caa57ca792ceb656ca3a4a4be9499a1d9
NOTE: Crubit currently expects deep integration with the build system, and is difficult to deploy to environments dissimilar to Google's monorepo. We do not have our tooling set up to accept external contributions at this time.
Crubit is a bidirectional bindings generator for C++ and Rust, with the goal of integrating the C++ and Rust ecosystems.
Support for calling FFI-friendly C++ from Rust is in progress.
Support for calling Rust from C++ will arrive in 2024H2.
Consider the following C++ function:
extern "C" bool IsGreater(int lhs, int rhs);
This function, if present in a header file which is processed by Crubit, becomes callable from Rust as if it were defined as:
pub fn IsGreater(lhs: ffi::c_int, rhs: ffi::c_int) -> bool {...}
Note: There are some temporary restrictions on the API shape. For example, functions that are not extern "C"
, or that accept a type like std::string
, can't be called from Rust directly via Crubit. These restrictions will be relaxed over time.
Here are some resources for getting started with Crubit:
Rust Bindings for C++ Libraries is a detailed walkthrough on how to use C++ from Rust using Crubit.
The examples/cpp/
directory has copy-pastable examples of calling C++ from Rust, together with snapshots of what the generated Rust interface looks like.
$ apt install clang lld bazel $ git clone git@github.com:google/crubit.git $ cd crubit $ bazel build --linkopt=-fuse-ld=/usr/bin/ld.lld //rs_bindings_from_cc:rs_bindings_from_cc_impl
$ git clone https://github.com/llvm/llvm-project $ cd llvm-project $ CC=clang CXX=clang++ cmake -S llvm -B build -DLLVM_ENABLE_PROJECTS='clang' -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=install $ cmake --build build -j $ # wait... $ cmake --install build $ cd ../crubit $ LLVM_INSTALL_PATH=../llvm-project/install bazel build //rs_bindings_from_cc:rs_bindings_from_cc_impl