diff options
author | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2022-05-10 07:03:19 +0000 |
---|---|---|
committer | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2022-05-10 07:03:19 +0000 |
commit | 8ba26ef8bb01fa9a548050cea3717a88fcf70145 (patch) | |
tree | 7074b4eac93437c9400ada0c51f46843f16c60f7 | |
parent | 44796ba17fff8dca24585e68db7b9cd47789f07c (diff) | |
parent | f427b2a756cc7e3acbf5123e3fcdd7cd93b2465f (diff) | |
download | cxx-android13-mainline-resolv-release.tar.gz |
Snap for 8564071 from f427b2a756cc7e3acbf5123e3fcdd7cd93b2465f to mainline-resolv-releaseaml_res_331820000aml_res_331611010aml_res_331512000aml_res_331314010aml_res_331114000aml_res_331011050aml_res_330910000aml_res_330810000android13-mainline-resolv-release
Change-Id: I2a63578c68d4dc4de9eac07ee1e0a81663554a13
99 files changed, 2167 insertions, 639 deletions
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3fafd9ba..0b82cd99 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -54,6 +54,7 @@ jobs: buck: name: Buck runs-on: ubuntu-latest + if: github.event_name != 'pull_request' steps: - uses: actions/checkout@v2 - uses: dtolnay/rust-toolchain@stable @@ -80,6 +81,7 @@ jobs: bazel: name: Bazel runs-on: ubuntu-latest + if: github.event_name != 'pull_request' steps: - uses: actions/checkout@v2 - name: Install Bazel @@ -51,6 +51,13 @@ rust_library { ], shared_libs: ["libc++"], host_supported: true, + apex_available: [ + "//apex_available:platform", + "com.android.bluetooth", + "com.android.compos", + "com.android.virt", + ], + min_sdk_version: "29", } cc_library_static { @@ -60,8 +67,11 @@ cc_library_static { srcs: ["src/cxx.cc"], apex_available: [ "//apex_available:platform", - "com.android.bluetooth.updatable", + "com.android.bluetooth", + "com.android.compos", + "com.android.virt", ], + min_sdk_version: "29", } cc_library_static { @@ -25,19 +25,19 @@ rust_binary( cxx_library( name = "core", srcs = ["src/cxx.cc"], - visibility = ["PUBLIC"], - header_namespace = "rust", exported_headers = { "cxx.h": "include/cxx.h", }, exported_linker_flags = ["-lstdc++"], + header_namespace = "rust", + visibility = ["PUBLIC"], ) rust_library( name = "macro", srcs = glob(["macro/src/**"]), - proc_macro = True, crate = "cxxbridge_macro", + proc_macro = True, deps = [ "//third-party:proc-macro2", "//third-party:quote", @@ -1,5 +1,5 @@ load("@rules_cc//cc:defs.bzl", "cc_library") -load("//tools/bazel:rust.bzl", "rust_binary", "rust_library") +load("@rules_rust//rust:rust.bzl", "rust_binary", "rust_library") rust_library( name = "cxx", @@ -1,6 +1,6 @@ [package] name = "cxx" -version = "1.0.42" # remember to update html_root_url +version = "1.0.54" # remember to update html_root_url authors = ["David Tolnay <dtolnay@gmail.com>"] edition = "2018" links = "cxxbridge1" @@ -21,15 +21,15 @@ default = ["cxxbridge-flags/default"] # c++11 "c++20" = ["cxxbridge-flags/c++20"] [dependencies] -cxxbridge-macro = { version = "=1.0.42", path = "macro" } +cxxbridge-macro = { version = "=1.0.54", path = "macro" } link-cplusplus = "1.0" [build-dependencies] cc = "1.0.49" -cxxbridge-flags = { version = "=1.0.42", path = "flags", default-features = false } +cxxbridge-flags = { version = "=1.0.54", path = "flags", default-features = false } [dev-dependencies] -cxx-build = { version = "=1.0.42", path = "gen/build" } +cxx-build = { version = "=1.0.54", path = "gen/build" } cxx-gen = { version = "0.7", path = "gen/lib" } cxx-test-suite = { version = "0", path = "tests/ffi" } rustversion = "1.0" @@ -1,7 +1,5 @@ name: "cxx" -description: - "Safe interop between Rust and C++" - +description: "Safe interop between Rust and C++" third_party { url { type: HOMEPAGE @@ -11,7 +9,11 @@ third_party { type: GIT value: "https://github.com/dtolnay/cxx.git" } - version: "4ab117115718908768df8366ac130bbbb3478331" - last_upgrade_date { year: 2021 month: 2 day: 17 } + version: "36d9ac1fab726e14305ce1919ebf8a3d64949d30" license_type: NOTICE + last_upgrade_date { + year: 2021 + month: 9 + day: 23 + } } @@ -1,26 +1,30 @@ workspace(name = "cxx.rs") load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") -load("//tools/bazel:vendor.bzl", "vendor") http_archive( name = "rules_rust", - sha256 = "e6d835ee673f388aa5b62dc23d82db8fc76497e93fa47d8a4afe97abaf09b10d", - strip_prefix = "rules_rust-f37b9d6a552e9412285e627f30cb124e709f4f7a", + sha256 = "697a6f4f2adbd1b00f792346d6eca4cd45f691be63069c7d7ebf4fcf82a377a8", + strip_prefix = "rules_rust-8bad4c5e4e53d9f6f8d4d5228e26a44d92f37ab2", urls = [ - # Master branch as of 2021-01-27 - "https://github.com/bazelbuild/rules_rust/archive/f37b9d6a552e9412285e627f30cb124e709f4f7a.tar.gz", + # Master branch as of 2021-04-11 + "https://github.com/bazelbuild/rules_rust/archive/8bad4c5e4e53d9f6f8d4d5228e26a44d92f37ab2.tar.gz", ], ) load("@rules_rust//rust:repositories.bzl", "rust_repositories") +RUST_VERSION = "1.54.0" + rust_repositories( edition = "2018", - version = "1.50.0", + version = RUST_VERSION, ) +load("//tools/bazel:vendor.bzl", "vendor") + vendor( name = "third-party", lockfile = "//third-party:Cargo.lock", + cargo_version = RUST_VERSION, ) diff --git a/book/src/binding/str.md b/book/src/binding/str.md index a4820aaf..9c1e0a77 100644 --- a/book/src/binding/str.md +++ b/book/src/binding/str.md @@ -30,6 +30,7 @@ public: const char *data() const noexcept; size_t size() const noexcept; size_t length() const noexcept; + bool empty() const noexcept; using iterator = const char *; using const_iterator = const char *; diff --git a/book/src/binding/string.md b/book/src/binding/string.md index a7d0790b..d564e00c 100644 --- a/book/src/binding/string.md +++ b/book/src/binding/string.md @@ -23,6 +23,10 @@ public: String(const char *); String(const char *, size_t); + // Throws std::invalid_argument if not utf-16. + String(const char16_t *); + String(const char16_t *, size_t); + String &operator=(const String &) noexcept; String &operator=(String &&) noexcept; @@ -32,9 +36,13 @@ public: const char *data() const noexcept; size_t size() const noexcept; size_t length() const noexcept; + bool empty() const noexcept; const char *c_str() noexcept; + size_t capacity() const noexcept; + void reserve(size_t new_cap) noexcept; + using iterator = char *; iterator begin() noexcept; iterator end() noexcept; diff --git a/book/src/build/bazel.md b/book/src/build/bazel.md index 08edb19f..8f910550 100644 --- a/book/src/build/bazel.md +++ b/book/src/build/bazel.md @@ -70,7 +70,7 @@ def rust_cxx_bridge(name, src, deps = []): # demo/BUILD load("@rules_cc//cc:defs.bzl", "cc_library") -load("//tools/bazel:rust.bzl", "rust_binary") +load("@rules_rust//rust:rust.bzl", "rust_binary") load("//tools/bazel:rust_cxx_bridge.bzl", "rust_cxx_bridge") rust_binary( diff --git a/book/src/build/cmake.md b/book/src/build/cmake.md index ed3cb61d..4b50746c 100644 --- a/book/src/build/cmake.md +++ b/book/src/build/cmake.md @@ -22,3 +22,10 @@ what is available in these links, feel free to make a PR adding it to this list. - Tested on Windows 10 with MSVC, and on Linux --- + +- **<https://github.com/trondhe/rusty_cmake>** + + - Alias target that can be linked into a C++ project + - Tested on Windows 10 with GNU target, and on Linux + +--- @@ -28,6 +28,12 @@ fn main() { rustc.version, ); } + + if rustc.minor < 52 { + // #![deny(unsafe_op_in_unsafe_fn)]. + // https://github.com/rust-lang/rust/issues/71668 + println!("cargo:rustc-cfg=no_unsafe_op_in_unsafe_fn_lint"); + } } } @@ -1,5 +1,5 @@ load("@rules_cc//cc:defs.bzl", "cc_library") -load("//tools/bazel:rust.bzl", "rust_binary") +load("@rules_rust//rust:rust.bzl", "rust_binary") load("//tools/bazel:rust_cxx_bridge.bzl", "rust_cxx_bridge") rust_binary( diff --git a/flags/Cargo.toml b/flags/Cargo.toml index 87c796cb..baf1157a 100644 --- a/flags/Cargo.toml +++ b/flags/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxxbridge-flags" -version = "1.0.42" +version = "1.0.54" authors = ["David Tolnay <dtolnay@gmail.com>"] edition = "2018" license = "MIT OR Apache-2.0" diff --git a/gen/build/Cargo.toml b/gen/build/Cargo.toml index 33f17dff..393fc1ce 100644 --- a/gen/build/Cargo.toml +++ b/gen/build/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxx-build" -version = "1.0.42" +version = "1.0.54" authors = ["David Tolnay <dtolnay@gmail.com>"] edition = "2018" license = "MIT OR Apache-2.0" @@ -21,7 +21,7 @@ lazy_static = "1.4" proc-macro2 = { version = "1.0.26", default-features = false, features = ["span-locations"] } quote = { version = "1.0", default-features = false } scratch = "1.0" -syn = { version = "1.0.68", default-features = false, features = ["parsing", "printing", "clone-impls", "full"] } +syn = { version = "1.0.70", default-features = false, features = ["parsing", "printing", "clone-impls", "full"] } [dev-dependencies] cxx-gen = { version = "0.7", path = "../lib" } diff --git a/gen/build/src/lib.rs b/gen/build/src/lib.rs index 63b2cf63..b8a463c3 100644 --- a/gen/build/src/lib.rs +++ b/gen/build/src/lib.rs @@ -51,6 +51,7 @@ clippy::doc_markdown, clippy::drop_copy, clippy::enum_glob_use, + clippy::if_same_then_else, clippy::inherent_to_string, clippy::items_after_statements, clippy::let_underscore_drop, diff --git a/gen/cmd/Cargo.toml b/gen/cmd/Cargo.toml index 7db02062..4b3a299f 100644 --- a/gen/cmd/Cargo.toml +++ b/gen/cmd/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxxbridge-cmd" -version = "1.0.42" +version = "1.0.54" authors = ["David Tolnay <dtolnay@gmail.com>"] edition = "2018" license = "MIT OR Apache-2.0" @@ -20,7 +20,7 @@ clap = "2.33" codespan-reporting = "0.11" proc-macro2 = { version = "1.0.26", default-features = false, features = ["span-locations"] } quote = { version = "1.0", default-features = false } -syn = { version = "1.0.68", default-features = false, features = ["parsing", "printing", "clone-impls", "full"] } +syn = { version = "1.0.70", default-features = false, features = ["parsing", "printing", "clone-impls", "full"] } [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] diff --git a/gen/cmd/src/main.rs b/gen/cmd/src/main.rs index f419dad4..1c15db63 100644 --- a/gen/cmd/src/main.rs +++ b/gen/cmd/src/main.rs @@ -3,6 +3,7 @@ clippy::cognitive_complexity, clippy::default_trait_access, clippy::enum_glob_use, + clippy::if_same_then_else, clippy::inherent_to_string, clippy::items_after_statements, clippy::large_enum_variant, diff --git a/gen/lib/Cargo.toml b/gen/lib/Cargo.toml index ff33d979..37d81687 100644 --- a/gen/lib/Cargo.toml +++ b/gen/lib/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxx-gen" -version = "0.7.42" +version = "0.7.54" authors = ["Adrian Taylor <adetaylor@chromium.org>"] edition = "2018" license = "MIT OR Apache-2.0" @@ -15,7 +15,7 @@ cc = "1.0.49" codespan-reporting = "0.11" proc-macro2 = { version = "1.0.26", default-features = false, features = ["span-locations"] } quote = { version = "1.0", default-features = false } -syn = { version = "1.0.68", default-features = false, features = ["parsing", "printing", "clone-impls", "full"] } +syn = { version = "1.0.70", default-features = false, features = ["parsing", "printing", "clone-impls", "full"] } [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] diff --git a/gen/lib/src/lib.rs b/gen/lib/src/lib.rs index e3eca5e7..411953b2 100644 --- a/gen/lib/src/lib.rs +++ b/gen/lib/src/lib.rs @@ -12,6 +12,7 @@ clippy::cast_sign_loss, clippy::default_trait_access, clippy::enum_glob_use, + clippy::if_same_then_else, clippy::inherent_to_string, clippy::items_after_statements, clippy::match_bool, diff --git a/gen/src/builtin.rs b/gen/src/builtin.rs index eaaa08d1..7ac9209c 100644 --- a/gen/src/builtin.rs +++ b/gen/src/builtin.rs @@ -30,6 +30,7 @@ pub struct Builtins<'a> { pub relocatable: bool, pub friend_impl: bool, pub is_complete: bool, + pub destroy: bool, pub deleter_if: bool, pub content: Content<'a>, } @@ -334,6 +335,14 @@ pub(super) fn write(out: &mut OutFile) { writeln!(out, "}};"); } + if builtin.destroy { + out.next_section(); + writeln!(out, "template <typename T>"); + writeln!(out, "void destroy(T *ptr) {{"); + writeln!(out, " ptr->~T();"); + writeln!(out, "}}"); + } + if builtin.deleter_if { out.next_section(); writeln!(out, "template <bool> struct deleter_if {{"); diff --git a/gen/src/check.rs b/gen/src/check.rs index 35929ad3..15add20a 100644 --- a/gen/src/check.rs +++ b/gen/src/check.rs @@ -4,7 +4,7 @@ use crate::syntax::{error, Api}; use quote::{quote, quote_spanned}; use std::path::{Component, Path}; -pub(super) use crate::syntax::check::typecheck; +pub(super) use crate::syntax::check::{typecheck, Generator}; pub(super) fn precheck(cx: &mut Errors, apis: &[Api], opt: &Opt) { if !opt.allow_dot_includes { diff --git a/gen/src/error.rs b/gen/src/error.rs index 2c8287f3..3672e26e 100644 --- a/gen/src/error.rs +++ b/gen/src/error.rs @@ -87,12 +87,11 @@ pub(crate) fn report(error: impl StdError) -> impl Display { impl<E: StdError> Display for Report<E> { fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - Display::fmt(&self.0, formatter)?; + write!(formatter, "{}", self.0)?; let mut error: &dyn StdError = &self.0; while let Some(cause) = error.source() { - formatter.write_str("\n\nCaused by:\n ")?; - Display::fmt(cause, formatter)?; + write!(formatter, "\n\nCaused by:\n {}", cause)?; error = cause; } diff --git a/gen/src/mod.rs b/gen/src/mod.rs index 3d12c71d..d8b90d0d 100644 --- a/gen/src/mod.rs +++ b/gen/src/mod.rs @@ -130,7 +130,8 @@ pub(super) fn generate(syntax: File, opt: &Opt) -> Result<GeneratedCode> { let ref types = Types::collect(errors, apis); check::precheck(errors, apis, opt); errors.propagate()?; - check::typecheck(errors, apis, types); + let generator = check::Generator::Build; + check::typecheck(errors, apis, types, generator); errors.propagate()?; // Some callers may wish to generate both header and implementation from the diff --git a/gen/src/write.rs b/gen/src/write.rs index 9f9c0391..a3b9c9fa 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -9,8 +9,8 @@ use crate::syntax::set::UnorderedSet; use crate::syntax::symbol::Symbol; use crate::syntax::trivial::{self, TrivialReason}; use crate::syntax::{ - derive, mangle, Api, Enum, ExternFn, ExternType, Pair, Signature, Struct, Trait, Type, - TypeAlias, Types, Var, + derive, mangle, Api, Doc, Enum, EnumRepr, ExternFn, ExternType, Pair, Signature, Struct, Trait, + Type, TypeAlias, Types, Var, }; use proc_macro2::Ident; @@ -101,10 +101,10 @@ fn write_data_structures<'a>(out: &mut OutFile<'a>, apis: &'a [Api]) { } Api::Enum(enm) => { out.next_section(); - if out.types.cxx.contains(&enm.name.rust) { - check_enum(out, enm); - } else { + if !out.types.cxx.contains(&enm.name.rust) { write_enum(out, enm); + } else if !enm.variants_from_header { + check_enum(out, enm); } } Api::RustType(ety) => { @@ -249,14 +249,24 @@ fn write_struct<'a>(out: &mut OutFile<'a>, strct: &'a Struct, methods: &[&Extern writeln!(out, "{};", field.name.cxx); } - writeln!(out); + out.next_section(); for method in methods { + if !method.doc.is_empty() { + out.next_section(); + } + for line in method.doc.to_string().lines() { + writeln!(out, " //{}", line); + } write!(out, " "); let sig = &method.sig; let local_name = method.name.cxx.to_string(); - write_rust_function_shim_decl(out, &local_name, sig, false); + let indirect_call = false; + write_rust_function_shim_decl(out, &local_name, sig, indirect_call); writeln!(out, ";"); + if !method.doc.is_empty() { + out.next_section(); + } } if operator_eq { @@ -307,8 +317,12 @@ fn write_struct_decl(out: &mut OutFile, ident: &Pair) { } fn write_enum_decl(out: &mut OutFile, enm: &Enum) { + let repr = match &enm.repr { + EnumRepr::Foreign { .. } => return, + EnumRepr::Native { atom, .. } => *atom, + }; write!(out, "enum class {} : ", enm.name.cxx); - write_atom(out, enm.repr); + write_atom(out, repr); writeln!(out, ";"); } @@ -332,12 +346,22 @@ fn write_opaque_type<'a>(out: &mut OutFile<'a>, ety: &'a ExternType, methods: &[ ety.name.cxx, ); - for method in methods { + for (i, method) in methods.iter().enumerate() { + if i > 0 && !method.doc.is_empty() { + out.next_section(); + } + for line in method.doc.to_string().lines() { + writeln!(out, " //{}", line); + } write!(out, " "); let sig = &method.sig; let local_name = method.name.cxx.to_string(); - write_rust_function_shim_decl(out, &local_name, sig, false); + let indirect_call = false; + write_rust_function_shim_decl(out, &local_name, sig, indirect_call); writeln!(out, ";"); + if !method.doc.is_empty() { + out.next_section(); + } } writeln!(out, " ~{}() = delete;", ety.name.cxx); @@ -356,6 +380,10 @@ fn write_opaque_type<'a>(out: &mut OutFile<'a>, ety: &'a ExternType, methods: &[ } fn write_enum<'a>(out: &mut OutFile<'a>, enm: &'a Enum) { + let repr = match &enm.repr { + EnumRepr::Foreign { .. } => return, + EnumRepr::Native { atom, .. } => *atom, + }; out.set_namespace(&enm.name.namespace); let guard = format!("CXXBRIDGE1_ENUM_{}", enm.name.to_symbol()); writeln!(out, "#ifndef {}", guard); @@ -364,7 +392,7 @@ fn write_enum<'a>(out: &mut OutFile<'a>, enm: &'a Enum) { writeln!(out, "//{}", line); } write!(out, "enum class {} : ", enm.name.cxx); - write_atom(out, enm.repr); + write_atom(out, repr); writeln!(out, " {{"); for variant in &enm.variants { for line in variant.doc.to_string().lines() { @@ -377,6 +405,10 @@ fn write_enum<'a>(out: &mut OutFile<'a>, enm: &'a Enum) { } fn check_enum<'a>(out: &mut OutFile<'a>, enm: &'a Enum) { + let repr = match &enm.repr { + EnumRepr::Foreign { .. } => return, + EnumRepr::Native { atom, .. } => *atom, + }; out.set_namespace(&enm.name.namespace); out.include.type_traits = true; writeln!( @@ -385,11 +417,11 @@ fn check_enum<'a>(out: &mut OutFile<'a>, enm: &'a Enum) { enm.name.cxx, ); write!(out, "static_assert(sizeof({}) == sizeof(", enm.name.cxx); - write_atom(out, enm.repr); + write_atom(out, repr); writeln!(out, "), \"incorrect size\");"); for variant in &enm.variants { write!(out, "static_assert(static_cast<"); - write_atom(out, enm.repr); + write_atom(out, repr); writeln!( out, ">({}::{}) == {}, \"disagrees with the value in #[cxx::bridge]\");", @@ -824,7 +856,8 @@ fn write_function_pointer_trampoline(out: &mut OutFile, efn: &ExternFn, var: &Pa out.next_section(); let c_trampoline = mangle::c_trampoline(efn, var, out.types).to_string(); - write_rust_function_shim_impl(out, &c_trampoline, f, &r_trampoline, indirect_call); + let doc = Doc::new(); + write_rust_function_shim_impl(out, &c_trampoline, f, &doc, &r_trampoline, indirect_call); } fn write_rust_function_decl<'a>(out: &mut OutFile<'a>, efn: &'a ExternFn) { @@ -894,9 +927,6 @@ fn write_rust_function_decl_impl( fn write_rust_function_shim<'a>(out: &mut OutFile<'a>, efn: &'a ExternFn) { out.set_namespace(&efn.name.namespace); - for line in efn.doc.to_string().lines() { - writeln!(out, "//{}", line); - } let local_name = match &efn.sig.receiver { None => efn.name.cxx.to_string(), Some(receiver) => format!( @@ -905,9 +935,10 @@ fn write_rust_function_shim<'a>(out: &mut OutFile<'a>, efn: &'a ExternFn) { efn.name.cxx, ), }; + let doc = &efn.doc; let invoke = mangle::extern_fn(efn, out.types); let indirect_call = false; - write_rust_function_shim_impl(out, &local_name, efn, &invoke, indirect_call); + write_rust_function_shim_impl(out, &local_name, efn, doc, &invoke, indirect_call); } fn write_rust_function_shim_decl( @@ -947,6 +978,7 @@ fn write_rust_function_shim_impl( out: &mut OutFile, local_name: &str, sig: &Signature, + doc: &Doc, invoke: &Symbol, indirect_call: bool, ) { @@ -954,6 +986,12 @@ fn write_rust_function_shim_impl( // We've already defined this inside the struct. return; } + if sig.receiver.is_none() { + // Member functions already documented at their declaration. + for line in doc.to_string().lines() { + writeln!(out, "//{}", line); + } + } write_rust_function_shim_decl(out, local_name, sig, indirect_call); if out.header { writeln!(out, ";"); @@ -1429,7 +1467,7 @@ fn write_rust_vec_extern(out: &mut OutFile, key: NamedImplKey) { ); writeln!( out, - "void cxxbridge1$rust_vec${}$reserve_total(::rust::Vec<{}> *ptr, ::std::size_t cap) noexcept;", + "void cxxbridge1$rust_vec${}$reserve_total(::rust::Vec<{}> *ptr, ::std::size_t new_cap) noexcept;", instance, inner, ); writeln!( @@ -1524,12 +1562,12 @@ fn write_rust_vec_impl(out: &mut OutFile, key: NamedImplKey) { begin_function_definition(out); writeln!( out, - "void Vec<{}>::reserve_total(::std::size_t cap) noexcept {{", + "void Vec<{}>::reserve_total(::std::size_t new_cap) noexcept {{", inner, ); writeln!( out, - " return cxxbridge1$rust_vec${}$reserve_total(this, cap);", + " return cxxbridge1$rust_vec${}$reserve_total(this, new_cap);", instance, ); writeln!(out, "}}"); @@ -1566,11 +1604,7 @@ fn write_unique_ptr_common(out: &mut OutFile, ty: UniquePtr) { // know at code generation time, so we generate both C++ and Rust side // bindings for a "new" method anyway. But the Rust code can't be called // for Opaque types because the 'new' method is not implemented. - UniquePtr::Ident(ident) => { - out.types.structs.contains_key(ident) - || out.types.enums.contains_key(ident) - || out.types.aliases.contains_key(ident) - } + UniquePtr::Ident(ident) => out.types.is_maybe_trivial(ident), UniquePtr::CxxVector(_) => false, }; @@ -1678,9 +1712,7 @@ fn write_shared_ptr(out: &mut OutFile, key: NamedImplKey) { // know at code generation time, so we generate both C++ and Rust side // bindings for a "new" method anyway. But the Rust code can't be called for // Opaque types because the 'new' method is not implemented. - let can_construct_from_value = out.types.structs.contains_key(ident) - || out.types.enums.contains_key(ident) - || out.types.aliases.contains_key(ident); + let can_construct_from_value = out.types.is_maybe_trivial(ident); writeln!( out, @@ -1803,6 +1835,8 @@ fn write_cxx_vector(out: &mut OutFile, key: NamedImplKey) { let instance = element.to_mangled(out.types); out.include.cstddef = true; + out.include.utility = true; + out.builtin.destroy = true; writeln!( out, @@ -1811,6 +1845,7 @@ fn write_cxx_vector(out: &mut OutFile, key: NamedImplKey) { ); writeln!(out, " return s.size();"); writeln!(out, "}}"); + writeln!( out, "{} *cxxbridge1$std$vector${}$get_unchecked(::std::vector<{}> *s, ::std::size_t pos) noexcept {{", @@ -1819,6 +1854,26 @@ fn write_cxx_vector(out: &mut OutFile, key: NamedImplKey) { writeln!(out, " return &(*s)[pos];"); writeln!(out, "}}"); + if out.types.is_maybe_trivial(element) { + writeln!( + out, + "void cxxbridge1$std$vector${}$push_back(::std::vector<{}> *v, {} *value) noexcept {{", + instance, inner, inner, + ); + writeln!(out, " v->push_back(::std::move(*value));"); + writeln!(out, " ::rust::destroy(value);"); + writeln!(out, "}}"); + + writeln!( + out, + "void cxxbridge1$std$vector${}$pop_back(::std::vector<{}> *v, {} *out) noexcept {{", + instance, inner, inner, + ); + writeln!(out, " ::new (out) {}(::std::move(v->back()));", inner); + writeln!(out, " v->pop_back();"); + writeln!(out, "}}"); + } + out.include.memory = true; write_unique_ptr_common(out, UniquePtr::CxxVector(element)); } diff --git a/include/cxx.h b/include/cxx.h index cdc63fbf..dffcb01a 100644 --- a/include/cxx.h +++ b/include/cxx.h @@ -43,6 +43,8 @@ public: String(const std::string &); String(const char *); String(const char *, std::size_t); + String(const char16_t *); + String(const char16_t *, std::size_t); String &operator=(const String &) &noexcept; String &operator=(String &&) &noexcept; @@ -53,9 +55,13 @@ public: const char *data() const noexcept; std::size_t size() const noexcept; std::size_t length() const noexcept; + bool empty() const noexcept; const char *c_str() noexcept; + std::size_t capacity() const noexcept; + void reserve(size_t new_cap) noexcept; + using iterator = char *; iterator begin() noexcept; iterator end() noexcept; @@ -105,6 +111,7 @@ public: const char *data() const noexcept; std::size_t size() const noexcept; std::size_t length() const noexcept; + bool empty() const noexcept; // Important in order for System V ABI to pass in registers. Str(const Str &) noexcept = default; @@ -334,7 +341,7 @@ public: Vec(unsafe_bitcopy_t, const Vec &) noexcept; private: - void reserve_total(std::size_t cap) noexcept; + void reserve_total(std::size_t new_cap) noexcept; void set_len(std::size_t len) noexcept; void drop() noexcept; @@ -475,7 +482,7 @@ void panic [[noreturn]] (const char *msg); #define CXXBRIDGE1_RUST_FN template <typename Ret, typename... Args> Ret Fn<Ret(Args...)>::operator()(Args... args) const noexcept { - return (*this->trampoline)(std::move(args)..., this->fn); + return (*this->trampoline)(std::forward<Args>(args)..., this->fn); } template <typename Ret, typename... Args> @@ -536,8 +543,8 @@ bool Slice<T>::empty() const noexcept { template <typename T> T &Slice<T>::operator[](std::size_t n) const noexcept { assert(n < this->size()); - auto pos = static_cast<char *>(slicePtr(this)) + size_of<T>() * n; - return *reinterpret_cast<T *>(pos); + auto ptr = static_cast<char *>(slicePtr(this)) + size_of<T>() * n; + return *reinterpret_cast<T *>(ptr); } template <typename T> @@ -575,8 +582,8 @@ Slice<T>::iterator::operator->() const noexcept { template <typename T> typename Slice<T>::iterator::reference Slice<T>::iterator::operator[]( typename Slice<T>::iterator::difference_type n) const noexcept { - auto pos = static_cast<char *>(this->pos) + this->stride * n; - return *reinterpret_cast<T *>(pos); + auto ptr = static_cast<char *>(this->pos) + this->stride * n; + return *reinterpret_cast<T *>(ptr); } template <typename T> diff --git a/macro/Cargo.toml b/macro/Cargo.toml index a3df1018..65f15c3a 100644 --- a/macro/Cargo.toml +++ b/macro/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cxxbridge-macro" -version = "1.0.42" +version = "1.0.54" authors = ["David Tolnay <dtolnay@gmail.com>"] edition = "2018" license = "MIT OR Apache-2.0" @@ -14,10 +14,20 @@ categories = ["development-tools::ffi"] [lib] proc-macro = true +[features] +experimental = ["clang-ast", "flate2", "memmap", "serde", "serde_json"] + [dependencies] proc-macro2 = "1.0" quote = "1.0.4" -syn = { version = "1.0.68", features = ["full"] } +syn = { version = "1.0.70", features = ["full"] } + +# optional dependencies +clang-ast = { version = "0.1", optional = true } +flate2 = { version = "1.0", optional = true } +memmap = { version = "0.7", optional = true } +serde = { version = "1.0", optional = true, features = ["derive"] } +serde_json = { version = "1.0", optional = true } [dev-dependencies] cxx = { version = "1.0", path = ".." } diff --git a/macro/src/clang.rs b/macro/src/clang.rs new file mode 100644 index 00000000..099d5a68 --- /dev/null +++ b/macro/src/clang.rs @@ -0,0 +1,51 @@ +use serde::{Deserialize, Serialize}; + +pub type Node = clang_ast::Node<Clang>; + +#[derive(Deserialize, Serialize)] +pub enum Clang { + NamespaceDecl(NamespaceDecl), + EnumDecl(EnumDecl), + EnumConstantDecl(EnumConstantDecl), + ImplicitCastExpr, + ConstantExpr(ConstantExpr), + Unknown, +} + +#[derive(Deserialize, Serialize)] +pub struct NamespaceDecl { + #[serde(skip_serializing_if = "Option::is_none")] + pub name: Option<Box<str>>, +} + +#[derive(Deserialize, Serialize)] +pub struct EnumDecl { + #[serde(skip_serializing_if = "Option::is_none")] + pub name: Option<Box<str>>, + #[serde( + rename = "fixedUnderlyingType", + skip_serializing_if = "Option::is_none" + )] + pub fixed_underlying_type: Option<Type>, +} + +#[derive(Deserialize, Serialize)] +pub struct EnumConstantDecl { + pub name: Box<str>, +} + +#[derive(Deserialize, Serialize)] +pub struct ConstantExpr { + pub value: Box<str>, +} + +#[derive(Deserialize, Serialize)] +pub struct Type { + #[serde(rename = "qualType")] + pub qual_type: Box<str>, + #[serde(rename = "desugaredQualType", skip_serializing_if = "Option::is_none")] + pub desugared_qual_type: Option<Box<str>>, +} + +#[cfg(all(test, target_pointer_width = "64"))] +const _: [(); std::mem::size_of::<Node>()] = [(); 88]; diff --git a/macro/src/derive.rs b/macro/src/derive.rs index 2f770951..ea36e3e3 100644 --- a/macro/src/derive.rs +++ b/macro/src/derive.rs @@ -151,6 +151,7 @@ fn struct_default(strct: &Struct, span: Span) -> TokenStream { let fields = strct.fields.iter().map(|field| &field.name.rust); quote_spanned! {span=> + #[allow(clippy::derivable_impls)] // different spans than the derived impl impl #generics ::std::default::Default for #ident #generics { fn default() -> Self { #ident { diff --git a/macro/src/expand.rs b/macro/src/expand.rs index 7c194ad2..5a879cbd 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -14,7 +14,7 @@ use crate::{derive, generics}; use proc_macro2::{Ident, Span, TokenStream}; use quote::{format_ident, quote, quote_spanned, ToTokens}; use std::mem; -use syn::{parse_quote, punctuated, Lifetime, Result, Token}; +use syn::{parse_quote, punctuated, Generics, Lifetime, Result, Token}; pub fn bridge(mut ffi: Module) -> Result<TokenStream> { let ref mut errors = Errors::new(); @@ -32,10 +32,14 @@ pub fn bridge(mut ffi: Module) -> Result<TokenStream> { let content = mem::take(&mut ffi.content); let trusted = ffi.unsafety.is_some(); let namespace = &ffi.namespace; - let ref apis = syntax::parse_items(errors, content, trusted, namespace); + let ref mut apis = syntax::parse_items(errors, content, trusted, namespace); + #[cfg(feature = "experimental")] + crate::load::load(errors, apis); let ref types = Types::collect(errors, apis); errors.propagate()?; - check::typecheck(errors, apis, types); + + let generator = check::Generator::Macro; + check::typecheck(errors, apis, types, generator); errors.propagate()?; Ok(expand(ffi, doc, attrs, apis, types)) @@ -130,8 +134,9 @@ fn expand(ffi: Module, doc: Doc, attrs: OtherAttrs, apis: &[Api], types: &Types) quote! { #doc #attrs - #[deny(improper_ctypes)] - #[allow(non_camel_case_types, non_snake_case, clippy::upper_case_acronyms, clippy::unknown_clippy_lints)] + #[deny(improper_ctypes, improper_ctypes_definitions)] + #[allow(clippy::unknown_clippy_lints)] + #[allow(non_camel_case_types, non_snake_case, clippy::upper_case_acronyms)] #vis #mod_token #ident #expanded } } @@ -288,7 +293,7 @@ fn expand_enum(enm: &Enum) -> TokenStream { let ident = &enm.name.rust; let doc = &enm.doc; let attrs = &enm.attrs; - let repr = enm.repr; + let repr = &enm.repr; let type_id = type_id(&enm.name); let variants = enm.variants.iter().map(|variant| { let doc = &variant.doc; @@ -419,18 +424,19 @@ fn expand_cxx_function_decl(efn: &ExternFn, types: &Types) -> TokenStream { quote!(_: #receiver_type) }); let args = efn.args.iter().map(|arg| { - let ident = &arg.name.rust; + let var = &arg.name.rust; + let colon = arg.colon_token; let ty = expand_extern_type(&arg.ty, types, true); if arg.ty == RustString { - quote!(#ident: *const #ty) + quote!(#var #colon *const #ty) } else if let Type::RustVec(_) = arg.ty { - quote!(#ident: *const #ty) + quote!(#var #colon *const #ty) } else if let Type::Fn(_) = arg.ty { - quote!(#ident: ::cxx::private::FatFunction) + quote!(#var #colon ::cxx::private::FatFunction) } else if types.needs_indirect_abi(&arg.ty) { - quote!(#ident: *mut #ty) + quote!(#var #colon *mut #ty) } else { - quote!(#ident: #ty) + quote!(#var #colon #ty) } }); let all_args = receiver.chain(args); @@ -459,8 +465,9 @@ fn expand_cxx_function_shim(efn: &ExternFn, types: &Types) -> TokenStream { let receiver = efn.receiver.iter().map(|receiver| { let var = receiver.var; if receiver.pinned { + let colon = receiver.colon_token; let ty = receiver.ty_self(); - quote!(#var: #ty) + quote!(#var #colon #ty) } else { let ampersand = receiver.ampersand; let lifetime = &receiver.lifetime; @@ -486,53 +493,66 @@ fn expand_cxx_function_shim(efn: &ExternFn, types: &Types) -> TokenStream { .map(|receiver| receiver.var.to_token_stream()); let arg_vars = efn.args.iter().map(|arg| { let var = &arg.name.rust; + let span = var.span(); match &arg.ty { Type::Ident(ident) if ident.rust == RustString => { - quote!(#var.as_mut_ptr() as *const ::cxx::private::RustString) + quote_spanned!(span=> #var.as_mut_ptr() as *const ::cxx::private::RustString) } - Type::RustBox(_) => quote!(::std::boxed::Box::into_raw(#var)), - Type::UniquePtr(_) => quote!(::cxx::UniquePtr::into_raw(#var)), - Type::RustVec(_) => quote!(#var.as_mut_ptr() as *const ::cxx::private::RustVec<_>), + Type::RustBox(ty) => { + if types.is_considered_improper_ctype(&ty.inner) { + quote_spanned!(span=> ::std::boxed::Box::into_raw(#var).cast()) + } else { + quote_spanned!(span=> ::std::boxed::Box::into_raw(#var)) + } + } + Type::UniquePtr(ty) => { + if types.is_considered_improper_ctype(&ty.inner) { + quote_spanned!(span=> ::cxx::UniquePtr::into_raw(#var).cast()) + } else { + quote_spanned!(span=> ::cxx::UniquePtr::into_raw(#var)) + } + } + Type::RustVec(_) => quote_spanned!(span=> #var.as_mut_ptr() as *const ::cxx::private::RustVec<_>), Type::Ref(ty) => match &ty.inner { Type::Ident(ident) if ident.rust == RustString => match ty.mutable { - false => quote!(::cxx::private::RustString::from_ref(#var)), - true => quote!(::cxx::private::RustString::from_mut(#var)), + false => quote_spanned!(span=> ::cxx::private::RustString::from_ref(#var)), + true => quote_spanned!(span=> ::cxx::private::RustString::from_mut(#var)), }, Type::RustVec(vec) if vec.inner == RustString => match ty.mutable { - false => quote!(::cxx::private::RustVec::from_ref_vec_string(#var)), - true => quote!(::cxx::private::RustVec::from_mut_vec_string(#var)), + false => quote_spanned!(span=> ::cxx::private::RustVec::from_ref_vec_string(#var)), + true => quote_spanned!(span=> ::cxx::private::RustVec::from_mut_vec_string(#var)), }, Type::RustVec(_) => match ty.mutable { - false => quote!(::cxx::private::RustVec::from_ref(#var)), - true => quote!(::cxx::private::RustVec::from_mut(#var)), + false => quote_spanned!(span=> ::cxx::private::RustVec::from_ref(#var)), + true => quote_spanned!(span=> ::cxx::private::RustVec::from_mut(#var)), }, inner if types.is_considered_improper_ctype(inner) => { let var = match ty.pinned { false => quote!(#var), - true => quote!(::std::pin::Pin::into_inner_unchecked(#var)), + true => quote_spanned!(span=> ::std::pin::Pin::into_inner_unchecked(#var)), }; match ty.mutable { false => { - quote!(#var as *const #inner as *const ::std::ffi::c_void) + quote_spanned!(span=> #var as *const #inner as *const ::std::ffi::c_void) } - true => quote!(#var as *mut #inner as *mut ::std::ffi::c_void), + true => quote_spanned!(span=> #var as *mut #inner as *mut ::std::ffi::c_void), } } _ => quote!(#var), }, Type::Ptr(ty) => { if types.is_considered_improper_ctype(&ty.inner) { - quote!(#var.cast()) + quote_spanned!(span=> #var.cast()) } else { quote!(#var) } } - Type::Str(_) => quote!(::cxx::private::RustStr::from(#var)), + Type::Str(_) => quote_spanned!(span=> ::cxx::private::RustStr::from(#var)), Type::SliceRef(ty) => match ty.mutable { - false => quote!(::cxx::private::RustSlice::from_ref(#var)), - true => quote!(::cxx::private::RustSlice::from_mut(#var)), + false => quote_spanned!(span=> ::cxx::private::RustSlice::from_ref(#var)), + true => quote_spanned!(span=> ::cxx::private::RustSlice::from_mut(#var)), }, - ty if types.needs_indirect_abi(ty) => quote!(#var.as_mut_ptr()), + ty if types.needs_indirect_abi(ty) => quote_spanned!(span=> #var.as_mut_ptr()), _ => quote!(#var), } }); @@ -555,35 +575,37 @@ fn expand_cxx_function_shim(efn: &ExternFn, types: &Types) -> TokenStream { .filter(|arg| types.needs_indirect_abi(&arg.ty)) .map(|arg| { let var = &arg.name.rust; + let span = var.span(); // These are arguments for which C++ has taken ownership of the data // behind the mut reference it received. - quote! { + quote_spanned! {span=> let mut #var = ::std::mem::MaybeUninit::new(#var); } }) .collect::<TokenStream>(); let local_name = format_ident!("__{}", efn.name.rust); + let span = efn.semi_token.span; let call = if indirect_return { let ret = expand_extern_type(efn.ret.as_ref().unwrap(), types, true); - setup.extend(quote! { + setup.extend(quote_spanned! {span=> let mut __return = ::std::mem::MaybeUninit::<#ret>::uninit(); }); setup.extend(if efn.throws { - quote! { + quote_spanned! {span=> #local_name(#(#vars,)* __return.as_mut_ptr()).exception()?; } } else { - quote! { + quote_spanned! {span=> #local_name(#(#vars,)* __return.as_mut_ptr()); } }); - quote!(__return.assume_init()) + quote_spanned!(span=> __return.assume_init()) } else if efn.throws { - quote! { + quote_spanned! {span=> #local_name(#(#vars),*).exception() } } else { - quote! { + quote_spanned! {span=> #local_name(#(#vars),*) } }; @@ -594,72 +616,88 @@ fn expand_cxx_function_shim(efn: &ExternFn, types: &Types) -> TokenStream { expr = match &efn.ret { None => call, Some(ret) => match ret { - Type::Ident(ident) if ident.rust == RustString => quote!(#call.into_string()), - Type::RustBox(_) => quote!(::std::boxed::Box::from_raw(#call)), + Type::Ident(ident) if ident.rust == RustString => { + quote_spanned!(span=> #call.into_string()) + } + Type::RustBox(ty) => { + if types.is_considered_improper_ctype(&ty.inner) { + quote_spanned!(span=> ::std::boxed::Box::from_raw(#call.cast())) + } else { + quote_spanned!(span=> ::std::boxed::Box::from_raw(#call)) + } + } Type::RustVec(vec) => { if vec.inner == RustString { - quote!(#call.into_vec_string()) + quote_spanned!(span=> #call.into_vec_string()) + } else { + quote_spanned!(span=> #call.into_vec()) + } + } + Type::UniquePtr(ty) => { + if types.is_considered_improper_ctype(&ty.inner) { + quote_spanned!(span=> ::cxx::UniquePtr::from_raw(#call.cast())) } else { - quote!(#call.into_vec()) + quote_spanned!(span=> ::cxx::UniquePtr::from_raw(#call)) } } - Type::UniquePtr(_) => quote!(::cxx::UniquePtr::from_raw(#call)), Type::Ref(ty) => match &ty.inner { Type::Ident(ident) if ident.rust == RustString => match ty.mutable { - false => quote!(#call.as_string()), - true => quote!(#call.as_mut_string()), + false => quote_spanned!(span=> #call.as_string()), + true => quote_spanned!(span=> #call.as_mut_string()), }, Type::RustVec(vec) if vec.inner == RustString => match ty.mutable { - false => quote!(#call.as_vec_string()), - true => quote!(#call.as_mut_vec_string()), + false => quote_spanned!(span=> #call.as_vec_string()), + true => quote_spanned!(span=> #call.as_mut_vec_string()), }, Type::RustVec(_) => match ty.mutable { - false => quote!(#call.as_vec()), - true => quote!(#call.as_mut_vec()), + false => quote_spanned!(span=> #call.as_vec()), + true => quote_spanned!(span=> #call.as_mut_vec()), }, inner if types.is_considered_improper_ctype(inner) => { let mutability = ty.mutability; - let deref_mut = quote!(&#mutability *#call.cast()); + let deref_mut = quote_spanned!(span=> &#mutability *#call.cast()); match ty.pinned { false => deref_mut, - true => quote!(::std::pin::Pin::new_unchecked(#deref_mut)), + true => { + quote_spanned!(span=> ::std::pin::Pin::new_unchecked(#deref_mut)) + } } } _ => call, }, Type::Ptr(ty) => { if types.is_considered_improper_ctype(&ty.inner) { - quote!(#call.cast()) + quote_spanned!(span=> #call.cast()) } else { call } } - Type::Str(_) => quote!(#call.as_str()), + Type::Str(_) => quote_spanned!(span=> #call.as_str()), Type::SliceRef(slice) => { let inner = &slice.inner; match slice.mutable { - false => quote!(#call.as_slice::<#inner>()), - true => quote!(#call.as_mut_slice::<#inner>()), + false => quote_spanned!(span=> #call.as_slice::<#inner>()), + true => quote_spanned!(span=> #call.as_mut_slice::<#inner>()), } } _ => call, }, }; if efn.throws { - expr = quote!(::std::result::Result::Ok(#expr)); + expr = quote_spanned!(span=> ::std::result::Result::Ok(#expr)); } }; let mut dispatch = quote!(#setup #expr); let visibility = efn.visibility; let unsafety = &efn.sig.unsafety; if unsafety.is_none() { - dispatch = quote!(unsafe { #dispatch }); + dispatch = quote_spanned!(span=> unsafe { #dispatch }); } let fn_token = efn.sig.fn_token; let ident = &efn.name.rust; let generics = &efn.generics; let arg_list = quote_spanned!(efn.sig.paren_token.span=> (#(#all_args,)*)); - let fn_body = quote_spanned!(efn.semi_token.span=> { + let fn_body = quote_spanned!(span=> { extern "C" { #decl } @@ -697,7 +735,7 @@ fn expand_cxx_function_shim(efn: &ExternFn, types: &Types) -> TokenStream { }; &elided_generics }; - quote! { + quote_spanned! {ident.span()=> impl #generics #receiver_ident #receiver_generics { #doc #attrs @@ -718,6 +756,7 @@ fn expand_function_pointer_trampoline( let r_trampoline = mangle::r_trampoline(efn, var, types); let local_name = parse_quote!(__); let catch_unwind_label = format!("::{}::{}", efn.name.rust, var.rust); + let body_span = efn.semi_token.span; let shim = expand_rust_function_shim_impl( sig, types, @@ -725,6 +764,8 @@ fn expand_function_pointer_trampoline( local_name, catch_unwind_label, None, + Some(&efn.generics), + body_span, ); let var = &var.rust; @@ -736,9 +777,9 @@ fn expand_function_pointer_trampoline( fn trampoline(); } #shim - trampoline as usize as *const () + trampoline as usize as *const ::std::ffi::c_void }, - ptr: #var as usize as *const (), + ptr: #var as usize as *const ::std::ffi::c_void, }; } } @@ -857,6 +898,7 @@ fn expand_rust_function_shim(efn: &ExternFn, types: &Types) -> TokenStream { Some(receiver) => format!("::{}::{}", receiver.ty.rust, efn.name.rust), }; let invoke = Some(&efn.name.rust); + let body_span = efn.semi_token.span; expand_rust_function_shim_impl( efn, types, @@ -864,6 +906,8 @@ fn expand_rust_function_shim(efn: &ExternFn, types: &Types) -> TokenStream { local_name, catch_unwind_label, invoke, + None, + body_span, ) } @@ -874,138 +918,166 @@ fn expand_rust_function_shim_impl( local_name: Ident, catch_unwind_label: String, invoke: Option<&Ident>, + outer_generics: Option<&Generics>, + body_span: Span, ) -> TokenStream { - let generics = &sig.generics; + let generics = outer_generics.unwrap_or(&sig.generics); let receiver_var = sig .receiver .as_ref() .map(|receiver| quote_spanned!(receiver.var.span=> __self)); let receiver = sig.receiver.as_ref().map(|receiver| { + let colon = receiver.colon_token; let receiver_type = receiver.ty(); - quote!(#receiver_var: #receiver_type) + quote!(#receiver_var #colon #receiver_type) }); let args = sig.args.iter().map(|arg| { - let ident = &arg.name.rust; + let var = &arg.name.rust; + let colon = arg.colon_token; let ty = expand_extern_type(&arg.ty, types, false); if types.needs_indirect_abi(&arg.ty) { - quote!(#ident: *mut #ty) + quote!(#var #colon *mut #ty) } else { - quote!(#ident: #ty) + quote!(#var #colon #ty) } }); let all_args = receiver.into_iter().chain(args); let arg_vars = sig.args.iter().map(|arg| { - let ident = &arg.name.rust; + let var = &arg.name.rust; + let span = var.span(); match &arg.ty { Type::Ident(i) if i.rust == RustString => { - quote!(::std::mem::take((*#ident).as_mut_string())) + quote_spanned!(span=> ::std::mem::take((*#var).as_mut_string())) } - Type::RustBox(_) => quote!(::std::boxed::Box::from_raw(#ident)), + Type::RustBox(_) => quote_spanned!(span=> ::std::boxed::Box::from_raw(#var)), Type::RustVec(vec) => { if vec.inner == RustString { - quote!(::std::mem::take((*#ident).as_mut_vec_string())) + quote_spanned!(span=> ::std::mem::take((*#var).as_mut_vec_string())) } else { - quote!(::std::mem::take((*#ident).as_mut_vec())) + quote_spanned!(span=> ::std::mem::take((*#var).as_mut_vec())) } } - Type::UniquePtr(_) => quote!(::cxx::UniquePtr::from_raw(#ident)), + Type::UniquePtr(_) => quote_spanned!(span=> ::cxx::UniquePtr::from_raw(#var)), Type::Ref(ty) => match &ty.inner { Type::Ident(i) if i.rust == RustString => match ty.mutable { - false => quote!(#ident.as_string()), - true => quote!(#ident.as_mut_string()), + false => quote_spanned!(span=> #var.as_string()), + true => quote_spanned!(span=> #var.as_mut_string()), }, Type::RustVec(vec) if vec.inner == RustString => match ty.mutable { - false => quote!(#ident.as_vec_string()), - true => quote!(#ident.as_mut_vec_string()), + false => quote_spanned!(span=> #var.as_vec_string()), + true => quote_spanned!(span=> #var.as_mut_vec_string()), }, Type::RustVec(_) => match ty.mutable { - false => quote!(#ident.as_vec()), - true => quote!(#ident.as_mut_vec()), + false => quote_spanned!(span=> #var.as_vec()), + true => quote_spanned!(span=> #var.as_mut_vec()), }, - _ => quote!(#ident), + _ => quote!(#var), }, - Type::Str(_) => quote!(#ident.as_str()), + Type::Str(_) => quote_spanned!(span=> #var.as_str()), Type::SliceRef(slice) => { let inner = &slice.inner; match slice.mutable { - false => quote!(#ident.as_slice::<#inner>()), - true => quote!(#ident.as_mut_slice::<#inner>()), + false => quote_spanned!(span=> #var.as_slice::<#inner>()), + true => quote_spanned!(span=> #var.as_mut_slice::<#inner>()), } } - ty if types.needs_indirect_abi(ty) => quote!(::std::ptr::read(#ident)), - _ => quote!(#ident), + ty if types.needs_indirect_abi(ty) => quote_spanned!(span=> ::std::ptr::read(#var)), + _ => quote!(#var), } }); - let vars = receiver_var.into_iter().chain(arg_vars); + let vars: Vec<_> = receiver_var.into_iter().chain(arg_vars).collect(); let wrap_super = invoke.map(|invoke| expand_rust_function_shim_super(sig, &local_name, invoke)); + let mut requires_closure; let mut call = match invoke { - Some(_) => quote!(#local_name), - None => quote!(::std::mem::transmute::<*const (), #sig>(__extern)), + Some(_) => { + requires_closure = false; + quote!(#local_name) + } + None => { + requires_closure = true; + quote!(::std::mem::transmute::<*const (), #sig>(__extern)) + } }; + requires_closure |= !vars.is_empty(); call.extend(quote! { (#(#vars),*) }); + let span = body_span; let conversion = sig.ret.as_ref().and_then(|ret| match ret { Type::Ident(ident) if ident.rust == RustString => { - Some(quote!(::cxx::private::RustString::from)) + Some(quote_spanned!(span=> ::cxx::private::RustString::from)) } - Type::RustBox(_) => Some(quote!(::std::boxed::Box::into_raw)), + Type::RustBox(_) => Some(quote_spanned!(span=> ::std::boxed::Box::into_raw)), Type::RustVec(vec) => { if vec.inner == RustString { - Some(quote!(::cxx::private::RustVec::from_vec_string)) + Some(quote_spanned!(span=> ::cxx::private::RustVec::from_vec_string)) } else { - Some(quote!(::cxx::private::RustVec::from)) + Some(quote_spanned!(span=> ::cxx::private::RustVec::from)) } } - Type::UniquePtr(_) => Some(quote!(::cxx::UniquePtr::into_raw)), + Type::UniquePtr(_) => Some(quote_spanned!(span=> ::cxx::UniquePtr::into_raw)), Type::Ref(ty) => match &ty.inner { Type::Ident(ident) if ident.rust == RustString => match ty.mutable { - false => Some(quote!(::cxx::private::RustString::from_ref)), - true => Some(quote!(::cxx::private::RustString::from_mut)), + false => Some(quote_spanned!(span=> ::cxx::private::RustString::from_ref)), + true => Some(quote_spanned!(span=> ::cxx::private::RustString::from_mut)), }, Type::RustVec(vec) if vec.inner == RustString => match ty.mutable { - false => Some(quote!(::cxx::private::RustVec::from_ref_vec_string)), - true => Some(quote!(::cxx::private::RustVec::from_mut_vec_string)), + false => Some(quote_spanned!(span=> ::cxx::private::RustVec::from_ref_vec_string)), + true => Some(quote_spanned!(span=> ::cxx::private::RustVec::from_mut_vec_string)), }, Type::RustVec(_) => match ty.mutable { - false => Some(quote!(::cxx::private::RustVec::from_ref)), - true => Some(quote!(::cxx::private::RustVec::from_mut)), + false => Some(quote_spanned!(span=> ::cxx::private::RustVec::from_ref)), + true => Some(quote_spanned!(span=> ::cxx::private::RustVec::from_mut)), }, _ => None, }, - Type::Str(_) => Some(quote!(::cxx::private::RustStr::from)), + Type::Str(_) => Some(quote_spanned!(span=> ::cxx::private::RustStr::from)), Type::SliceRef(ty) => match ty.mutable { - false => Some(quote!(::cxx::private::RustSlice::from_ref)), - true => Some(quote!(::cxx::private::RustSlice::from_mut)), + false => Some(quote_spanned!(span=> ::cxx::private::RustSlice::from_ref)), + true => Some(quote_spanned!(span=> ::cxx::private::RustSlice::from_mut)), }, _ => None, }); let mut expr = match conversion { None => call, - Some(conversion) if !sig.throws => quote!(#conversion(#call)), - Some(conversion) => quote!(::std::result::Result::map(#call, #conversion)), + Some(conversion) if !sig.throws => { + requires_closure = true; + quote_spanned!(span=> #conversion(#call)) + } + Some(conversion) => { + requires_closure = true; + quote_spanned!(span=> ::std::result::Result::map(#call, #conversion)) + } }; let mut outparam = None; let indirect_return = indirect_return(sig, types); if indirect_return { let ret = expand_extern_type(sig.ret.as_ref().unwrap(), types, false); - outparam = Some(quote!(__return: *mut #ret,)); + outparam = Some(quote_spanned!(span=> __return: *mut #ret,)); } if sig.throws { let out = match sig.ret { - Some(_) => quote!(__return), - None => quote!(&mut ()), + Some(_) => quote_spanned!(span=> __return), + None => quote_spanned!(span=> &mut ()), }; - expr = quote!(::cxx::private::r#try(#out, #expr)); + requires_closure = true; + expr = quote_spanned!(span=> ::cxx::private::r#try(#out, #expr)); } else if indirect_return { - expr = quote!(::std::ptr::write(__return, #expr)); + requires_closure = true; + expr = quote_spanned!(span=> ::std::ptr::write(__return, #expr)); } - expr = quote!(::cxx::private::catch_unwind(__fn, move || #expr)); + let closure = if requires_closure { + quote_spanned!(span=> move || #expr) + } else { + quote!(#local_name) + }; + + expr = quote_spanned!(span=> ::cxx::private::catch_unwind(__fn, #closure)); let ret = if sig.throws { quote!(-> ::cxx::private::Result) @@ -1014,11 +1086,11 @@ fn expand_rust_function_shim_impl( }; let pointer = match invoke { - None => Some(quote!(__extern: *const ())), + None => Some(quote_spanned!(span=> __extern: *const ())), Some(_) => None, }; - quote! { + quote_spanned! {span=> #[doc(hidden)] #[export_name = #link_name] unsafe extern "C" fn #local_name #generics(#(#all_args,)* #outparam #pointer) #ret { @@ -1055,9 +1127,11 @@ fn expand_rust_function_shim_super( Some(ret) => quote!(#ret), None => quote!(()), }; - let impl_trait = quote_spanned!(result.span=> impl); - let display = quote_spanned!(rangle.span=> ::std::fmt::Display); - quote!(-> ::std::result::Result<#ok, #impl_trait #display>) + // Set spans that result in the `Result<...>` written by the user being + // highlighted as the cause if their error type has no Display impl. + let result_begin = quote_spanned!(result.span=> ::std::result::Result<#ok, impl); + let result_end = quote_spanned!(rangle.span=> ::std::fmt::Display>); + quote!(-> #result_begin #result_end) } else { expand_return_type(&sig.ret) }; @@ -1227,8 +1301,8 @@ fn expand_rust_vec(key: NamedImplKey, types: &Types, explicit_impl: Option<&Impl } #[doc(hidden)] #[export_name = #link_reserve_total] - unsafe extern "C" fn #local_reserve_total #impl_generics(this: *mut ::cxx::private::RustVec<#elem #ty_generics>, cap: usize) { - (*this).reserve_total(cap); + unsafe extern "C" fn #local_reserve_total #impl_generics(this: *mut ::cxx::private::RustVec<#elem #ty_generics>, new_cap: usize) { + (*this).reserve_total(new_cap); } #[doc(hidden)] #[export_name = #link_set_len] @@ -1256,18 +1330,16 @@ fn expand_unique_ptr( let (impl_generics, ty_generics) = generics::split_for_impl(key, explicit_impl, resolve); - let can_construct_from_value = types.structs.contains_key(ident) - || types.enums.contains_key(ident) - || types.aliases.contains_key(ident); + let can_construct_from_value = types.is_maybe_trivial(ident); let new_method = if can_construct_from_value { Some(quote! { #[doc(hidden)] - fn __new(value: Self) -> *mut ::std::ffi::c_void { + fn __new(value: Self) -> ::std::mem::MaybeUninit<*mut ::std::ffi::c_void> { extern "C" { #[link_name = #link_uninit] - fn __uninit(this: *mut *mut ::std::ffi::c_void) -> *mut ::std::ffi::c_void; + fn __uninit(this: *mut ::std::mem::MaybeUninit<*mut ::std::ffi::c_void>) -> *mut ::std::ffi::c_void; } - let mut repr = ::std::ptr::null_mut::<::std::ffi::c_void>(); + let mut repr = ::std::mem::MaybeUninit::uninit(); unsafe { __uninit(&mut repr).cast::<#ident #ty_generics>().write(value) } repr } @@ -1287,47 +1359,47 @@ fn expand_unique_ptr( f.write_str(#name) } #[doc(hidden)] - fn __null() -> *mut ::std::ffi::c_void { + fn __null() -> ::std::mem::MaybeUninit<*mut ::std::ffi::c_void> { extern "C" { #[link_name = #link_null] - fn __null(this: *mut *mut ::std::ffi::c_void); + fn __null(this: *mut ::std::mem::MaybeUninit<*mut ::std::ffi::c_void>); } - let mut repr = ::std::ptr::null_mut::<::std::ffi::c_void>(); + let mut repr = ::std::mem::MaybeUninit::uninit(); unsafe { __null(&mut repr) } repr } #new_method #[doc(hidden)] - unsafe fn __raw(raw: *mut Self) -> *mut ::std::ffi::c_void { + unsafe fn __raw(raw: *mut Self) -> ::std::mem::MaybeUninit<*mut ::std::ffi::c_void> { extern "C" { #[link_name = #link_raw] - fn __raw(this: *mut *mut ::std::ffi::c_void, raw: *mut ::std::ffi::c_void); + fn __raw(this: *mut ::std::mem::MaybeUninit<*mut ::std::ffi::c_void>, raw: *mut ::std::ffi::c_void); } - let mut repr = ::std::ptr::null_mut::<::std::ffi::c_void>(); + let mut repr = ::std::mem::MaybeUninit::uninit(); __raw(&mut repr, raw.cast()); repr } #[doc(hidden)] - unsafe fn __get(repr: *mut ::std::ffi::c_void) -> *const Self { + unsafe fn __get(repr: ::std::mem::MaybeUninit<*mut ::std::ffi::c_void>) -> *const Self { extern "C" { #[link_name = #link_get] - fn __get(this: *const *mut ::std::ffi::c_void) -> *const ::std::ffi::c_void; + fn __get(this: *const ::std::mem::MaybeUninit<*mut ::std::ffi::c_void>) -> *const ::std::ffi::c_void; } __get(&repr).cast() } #[doc(hidden)] - unsafe fn __release(mut repr: *mut ::std::ffi::c_void) -> *mut Self { + unsafe fn __release(mut repr: ::std::mem::MaybeUninit<*mut ::std::ffi::c_void>) -> *mut Self { extern "C" { #[link_name = #link_release] - fn __release(this: *mut *mut ::std::ffi::c_void) -> *mut ::std::ffi::c_void; + fn __release(this: *mut ::std::mem::MaybeUninit<*mut ::std::ffi::c_void>) -> *mut ::std::ffi::c_void; } __release(&mut repr).cast() } #[doc(hidden)] - unsafe fn __drop(mut repr: *mut ::std::ffi::c_void) { + unsafe fn __drop(mut repr: ::std::mem::MaybeUninit<*mut ::std::ffi::c_void>) { extern "C" { #[link_name = #link_drop] - fn __drop(this: *mut *mut ::std::ffi::c_void); + fn __drop(this: *mut ::std::mem::MaybeUninit<*mut ::std::ffi::c_void>); } __drop(&mut repr); } @@ -1352,9 +1424,7 @@ fn expand_shared_ptr( let (impl_generics, ty_generics) = generics::split_for_impl(key, explicit_impl, resolve); - let can_construct_from_value = types.structs.contains_key(ident) - || types.enums.contains_key(ident) - || types.aliases.contains_key(ident); + let can_construct_from_value = types.is_maybe_trivial(ident); let new_method = if can_construct_from_value { Some(quote! { #[doc(hidden)] @@ -1495,6 +1565,8 @@ fn expand_cxx_vector( let prefix = format!("cxxbridge1$std$vector${}$", resolve.name.to_symbol()); let link_size = format!("{}size", prefix); let link_get_unchecked = format!("{}get_unchecked", prefix); + let link_push_back = format!("{}push_back", prefix); + let link_pop_back = format!("{}pop_back", prefix); let unique_ptr_prefix = format!( "cxxbridge1$unique_ptr$std$vector${}$", resolve.name.to_symbol(), @@ -1511,6 +1583,42 @@ fn expand_cxx_vector( let end_span = explicit_impl.map_or(key.end_span, |explicit| explicit.brace_token.span); let unsafe_token = format_ident!("unsafe", span = begin_span); + let can_pass_element_by_value = types.is_maybe_trivial(elem); + let by_value_methods = if can_pass_element_by_value { + Some(quote_spanned! {end_span=> + #[doc(hidden)] + unsafe fn __push_back( + this: ::std::pin::Pin<&mut ::cxx::CxxVector<Self>>, + value: &mut ::std::mem::ManuallyDrop<Self>, + ) { + extern "C" { + #[link_name = #link_push_back] + fn __push_back #impl_generics( + this: ::std::pin::Pin<&mut ::cxx::CxxVector<#elem #ty_generics>>, + value: *mut ::std::ffi::c_void, + ); + } + __push_back(this, value as *mut ::std::mem::ManuallyDrop<Self> as *mut ::std::ffi::c_void); + } + #[doc(hidden)] + unsafe fn __pop_back( + this: ::std::pin::Pin<&mut ::cxx::CxxVector<Self>>, + out: &mut ::std::mem::MaybeUninit<Self>, + ) { + extern "C" { + #[link_name = #link_pop_back] + fn __pop_back #impl_generics( + this: ::std::pin::Pin<&mut ::cxx::CxxVector<#elem #ty_generics>>, + out: *mut ::std::ffi::c_void, + ); + } + __pop_back(this, out as *mut ::std::mem::MaybeUninit<Self> as *mut ::std::ffi::c_void); + } + }) + } else { + None + }; + quote_spanned! {end_span=> #unsafe_token impl #impl_generics ::cxx::private::VectorElement for #elem #ty_generics { #[doc(hidden)] @@ -1529,51 +1637,55 @@ fn expand_cxx_vector( unsafe fn __get_unchecked(v: *mut ::cxx::CxxVector<Self>, pos: usize) -> *mut Self { extern "C" { #[link_name = #link_get_unchecked] - fn __get_unchecked #impl_generics(_: *mut ::cxx::CxxVector<#elem #ty_generics>, _: usize) -> *mut #elem #ty_generics; + fn __get_unchecked #impl_generics( + v: *mut ::cxx::CxxVector<#elem #ty_generics>, + pos: usize, + ) -> *mut ::std::ffi::c_void; } - __get_unchecked(v, pos) + __get_unchecked(v, pos) as *mut Self } + #by_value_methods #[doc(hidden)] - fn __unique_ptr_null() -> *mut ::std::ffi::c_void { + fn __unique_ptr_null() -> ::std::mem::MaybeUninit<*mut ::std::ffi::c_void> { extern "C" { #[link_name = #link_unique_ptr_null] - fn __unique_ptr_null(this: *mut *mut ::std::ffi::c_void); + fn __unique_ptr_null(this: *mut ::std::mem::MaybeUninit<*mut ::std::ffi::c_void>); } - let mut repr = ::std::ptr::null_mut::<::std::ffi::c_void>(); + let mut repr = ::std::mem::MaybeUninit::uninit(); unsafe { __unique_ptr_null(&mut repr) } repr } #[doc(hidden)] - unsafe fn __unique_ptr_raw(raw: *mut ::cxx::CxxVector<Self>) -> *mut ::std::ffi::c_void { + unsafe fn __unique_ptr_raw(raw: *mut ::cxx::CxxVector<Self>) -> ::std::mem::MaybeUninit<*mut ::std::ffi::c_void> { extern "C" { #[link_name = #link_unique_ptr_raw] - fn __unique_ptr_raw #impl_generics(this: *mut *mut ::std::ffi::c_void, raw: *mut ::cxx::CxxVector<#elem #ty_generics>); + fn __unique_ptr_raw #impl_generics(this: *mut ::std::mem::MaybeUninit<*mut ::std::ffi::c_void>, raw: *mut ::cxx::CxxVector<#elem #ty_generics>); } - let mut repr = ::std::ptr::null_mut::<::std::ffi::c_void>(); + let mut repr = ::std::mem::MaybeUninit::uninit(); __unique_ptr_raw(&mut repr, raw); repr } #[doc(hidden)] - unsafe fn __unique_ptr_get(repr: *mut ::std::ffi::c_void) -> *const ::cxx::CxxVector<Self> { + unsafe fn __unique_ptr_get(repr: ::std::mem::MaybeUninit<*mut ::std::ffi::c_void>) -> *const ::cxx::CxxVector<Self> { extern "C" { #[link_name = #link_unique_ptr_get] - fn __unique_ptr_get #impl_generics(this: *const *mut ::std::ffi::c_void) -> *const ::cxx::CxxVector<#elem #ty_generics>; + fn __unique_ptr_get #impl_generics(this: *const ::std::mem::MaybeUninit<*mut ::std::ffi::c_void>) -> *const ::cxx::CxxVector<#elem #ty_generics>; } __unique_ptr_get(&repr) } #[doc(hidden)] - unsafe fn __unique_ptr_release(mut repr: *mut ::std::ffi::c_void) -> *mut ::cxx::CxxVector<Self> { + unsafe fn __unique_ptr_release(mut repr: ::std::mem::MaybeUninit<*mut ::std::ffi::c_void>) -> *mut ::cxx::CxxVector<Self> { extern "C" { #[link_name = #link_unique_ptr_release] - fn __unique_ptr_release #impl_generics(this: *mut *mut ::std::ffi::c_void) -> *mut ::cxx::CxxVector<#elem #ty_generics>; + fn __unique_ptr_release #impl_generics(this: *mut ::std::mem::MaybeUninit<*mut ::std::ffi::c_void>) -> *mut ::cxx::CxxVector<#elem #ty_generics>; } __unique_ptr_release(&mut repr) } #[doc(hidden)] - unsafe fn __unique_ptr_drop(mut repr: *mut ::std::ffi::c_void) { + unsafe fn __unique_ptr_drop(mut repr: ::std::mem::MaybeUninit<*mut ::std::ffi::c_void>) { extern "C" { #[link_name = #link_unique_ptr_drop] - fn __unique_ptr_drop(this: *mut *mut ::std::ffi::c_void); + fn __unique_ptr_drop(this: *mut ::std::mem::MaybeUninit<*mut ::std::ffi::c_void>); } __unique_ptr_drop(&mut repr); } @@ -1596,43 +1708,72 @@ fn indirect_return(sig: &Signature, types: &Types) -> bool { fn expand_extern_type(ty: &Type, types: &Types, proper: bool) -> TokenStream { match ty { - Type::Ident(ident) if ident.rust == RustString => quote!(::cxx::private::RustString), + Type::Ident(ident) if ident.rust == RustString => { + let span = ident.rust.span(); + quote_spanned!(span=> ::cxx::private::RustString) + } Type::RustBox(ty) | Type::UniquePtr(ty) => { - let inner = expand_extern_type(&ty.inner, types, proper); - quote!(*mut #inner) + let span = ty.name.span(); + if proper && types.is_considered_improper_ctype(&ty.inner) { + quote_spanned!(span=> *mut ::std::ffi::c_void) + } else { + let inner = expand_extern_type(&ty.inner, types, proper); + quote_spanned!(span=> *mut #inner) + } } Type::RustVec(ty) => { + let span = ty.name.span(); + let langle = ty.langle; let elem = expand_extern_type(&ty.inner, types, proper); - quote!(::cxx::private::RustVec<#elem>) + let rangle = ty.rangle; + quote_spanned!(span=> ::cxx::private::RustVec #langle #elem #rangle) } Type::Ref(ty) => { + let ampersand = ty.ampersand; + let lifetime = &ty.lifetime; let mutability = ty.mutability; match &ty.inner { Type::Ident(ident) if ident.rust == RustString => { - quote!(&#mutability ::cxx::private::RustString) + let span = ident.rust.span(); + quote_spanned!(span=> #ampersand #lifetime #mutability ::cxx::private::RustString) } Type::RustVec(ty) => { + let span = ty.name.span(); + let langle = ty.langle; let inner = expand_extern_type(&ty.inner, types, proper); - quote!(&#mutability ::cxx::private::RustVec<#inner>) + let rangle = ty.rangle; + quote_spanned!(span=> #ampersand #lifetime #mutability ::cxx::private::RustVec #langle #inner #rangle) + } + inner if proper && types.is_considered_improper_ctype(inner) => { + let star = Token![*](ampersand.span); + match ty.mutable { + false => quote!(#star const ::std::ffi::c_void), + true => quote!(#star #mutability ::std::ffi::c_void), + } } - inner if proper && types.is_considered_improper_ctype(inner) => match ty.mutable { - false => quote!(*const ::std::ffi::c_void), - true => quote!(*#mutability ::std::ffi::c_void), - }, _ => quote!(#ty), } } Type::Ptr(ty) => { if proper && types.is_considered_improper_ctype(&ty.inner) { + let star = ty.star; let mutability = ty.mutability; let constness = ty.constness; - quote!(*#mutability #constness ::std::ffi::c_void) + quote!(#star #mutability #constness ::std::ffi::c_void) } else { quote!(#ty) } } - Type::Str(_) => quote!(::cxx::private::RustStr), - Type::SliceRef(_) => quote!(::cxx::private::RustSlice), + Type::Str(ty) => { + let span = ty.ampersand.span; + let rust_str = Ident::new("RustStr", syn::spanned::Spanned::span(&ty.inner)); + quote_spanned!(span=> ::cxx::private::#rust_str) + } + Type::SliceRef(ty) => { + let span = ty.ampersand.span; + let rust_slice = Ident::new("RustSlice", ty.bracket.span); + quote_spanned!(span=> ::cxx::private::#rust_slice) + } _ => quote!(#ty), } } diff --git a/macro/src/lib.rs b/macro/src/lib.rs index f8e79034..324881d8 100644 --- a/macro/src/lib.rs +++ b/macro/src/lib.rs @@ -3,7 +3,7 @@ clippy::default_trait_access, clippy::doc_markdown, clippy::enum_glob_use, - clippy::filter_map, + clippy::if_same_then_else, clippy::inherent_to_string, clippy::items_after_statements, clippy::large_enum_variant, @@ -36,6 +36,11 @@ mod generics; mod syntax; mod type_id; +#[cfg(feature = "experimental")] +mod clang; +#[cfg(feature = "experimental")] +mod load; + use crate::syntax::file::Module; use crate::syntax::namespace::Namespace; use crate::syntax::qualified::QualifiedName; diff --git a/macro/src/load.rs b/macro/src/load.rs new file mode 100644 index 00000000..d769ebf6 --- /dev/null +++ b/macro/src/load.rs @@ -0,0 +1,315 @@ +use crate::clang::{Clang, Node}; +use crate::syntax::attrs::OtherAttrs; +use crate::syntax::namespace::Namespace; +use crate::syntax::report::Errors; +use crate::syntax::{Api, Discriminant, Doc, Enum, EnumRepr, ForeignName, Pair, Variant}; +use flate2::write::GzDecoder; +use memmap::Mmap; +use proc_macro2::{Delimiter, Group, Ident, TokenStream}; +use quote::{format_ident, quote, quote_spanned}; +use std::env; +use std::fmt::{self, Display}; +use std::fs::File; +use std::io::Write; +use std::path::PathBuf; +use std::str::FromStr; +use syn::{parse_quote, Path}; + +const CXX_CLANG_AST: &str = "CXX_CLANG_AST"; + +pub fn load(cx: &mut Errors, apis: &mut [Api]) { + let ref mut variants_from_header = Vec::new(); + for api in apis { + if let Api::Enum(enm) = api { + if enm.variants_from_header { + if enm.variants.is_empty() { + variants_from_header.push(enm); + } else { + let span = span_for_enum_error(enm); + cx.error( + span, + "enum with #![variants_from_header] must be written with no explicit variants", + ); + } + } + } + } + + let span = match variants_from_header.get(0) { + None => return, + Some(enm) => enm.variants_from_header_attr.clone().unwrap(), + }; + + let ast_dump_path = match env::var_os(CXX_CLANG_AST) { + Some(ast_dump_path) => PathBuf::from(ast_dump_path), + None => { + let msg = format!( + "environment variable ${} has not been provided", + CXX_CLANG_AST, + ); + return cx.error(span, msg); + } + }; + + let memmap = File::open(&ast_dump_path).and_then(|file| unsafe { Mmap::map(&file) }); + let mut gunzipped; + let ast_dump_bytes = match match memmap { + Ok(ref memmap) => { + let is_gzipped = memmap.get(..2) == Some(b"\x1f\x8b"); + if is_gzipped { + gunzipped = Vec::new(); + let decode_result = GzDecoder::new(&mut gunzipped).write_all(&memmap); + decode_result.map(|_| gunzipped.as_slice()) + } else { + Ok(&memmap as &[u8]) + } + } + Err(error) => Err(error), + } { + Ok(bytes) => bytes, + Err(error) => { + let msg = format!("failed to read {}: {}", ast_dump_path.display(), error); + return cx.error(span, msg); + } + }; + + let ref root: Node = match serde_json::from_slice(ast_dump_bytes) { + Ok(root) => root, + Err(error) => { + let msg = format!("failed to read {}: {}", ast_dump_path.display(), error); + return cx.error(span, msg); + } + }; + + let ref mut namespace = Vec::new(); + traverse(cx, root, namespace, variants_from_header, None); + + for enm in variants_from_header { + if enm.variants.is_empty() { + let span = &enm.variants_from_header_attr; + let name = CxxName(&enm.name); + let msg = format!("failed to find any C++ definition of enum {}", name); + cx.error(span, msg); + } + } +} + +fn traverse<'a>( + cx: &mut Errors, + node: &'a Node, + namespace: &mut Vec<&'a str>, + variants_from_header: &mut [&mut Enum], + mut idx: Option<usize>, +) { + match &node.kind { + Clang::NamespaceDecl(decl) => { + let name = match &decl.name { + Some(name) => name, + // Can ignore enums inside an anonymous namespace. + None => return, + }; + namespace.push(name); + idx = None; + } + Clang::EnumDecl(decl) => { + let name = match &decl.name { + Some(name) => name, + None => return, + }; + idx = None; + for (i, enm) in variants_from_header.iter_mut().enumerate() { + if enm.name.cxx == **name && enm.name.namespace.iter().eq(&*namespace) { + if !enm.variants.is_empty() { + let span = &enm.variants_from_header_attr; + let qual_name = CxxName(&enm.name); + let msg = format!("found multiple C++ definitions of enum {}", qual_name); + cx.error(span, msg); + return; + } + let fixed_underlying_type = match &decl.fixed_underlying_type { + Some(fixed_underlying_type) => fixed_underlying_type, + None => { + let span = &enm.variants_from_header_attr; + let name = &enm.name.cxx; + let qual_name = CxxName(&enm.name); + let msg = format!( + "implicit implementation-defined repr for enum {} is not supported yet; consider changing its C++ definition to `enum {}: int {{...}}", + qual_name, name, + ); + cx.error(span, msg); + return; + } + }; + let repr = translate_qual_type( + cx, + &enm, + fixed_underlying_type + .desugared_qual_type + .as_ref() + .unwrap_or(&fixed_underlying_type.qual_type), + ); + enm.repr = EnumRepr::Foreign { rust_type: repr }; + idx = Some(i); + break; + } + } + if idx.is_none() { + return; + } + } + Clang::EnumConstantDecl(decl) => { + if let Some(idx) = idx { + let enm = &mut *variants_from_header[idx]; + let span = enm + .variants_from_header_attr + .as_ref() + .unwrap() + .path + .get_ident() + .unwrap() + .span(); + let cxx_name = match ForeignName::parse(&decl.name, span) { + Ok(foreign_name) => foreign_name, + Err(_) => { + let span = &enm.variants_from_header_attr; + let msg = format!("unsupported C++ variant name: {}", decl.name); + return cx.error(span, msg); + } + }; + let rust_name: Ident = match syn::parse_str(&decl.name) { + Ok(ident) => ident, + Err(_) => format_ident!("__Variant{}", enm.variants.len()), + }; + let discriminant = match discriminant_value(&node.inner) { + ParsedDiscriminant::Constant(discriminant) => discriminant, + ParsedDiscriminant::Successor => match enm.variants.last() { + None => Discriminant::zero(), + Some(last) => match last.discriminant.checked_succ() { + Some(discriminant) => discriminant, + None => { + let span = &enm.variants_from_header_attr; + let msg = format!( + "overflow processing discriminant value for variant: {}", + decl.name, + ); + return cx.error(span, msg); + } + }, + }, + ParsedDiscriminant::Fail => { + let span = &enm.variants_from_header_attr; + let msg = format!( + "failed to obtain discriminant value for variant: {}", + decl.name, + ); + cx.error(span, msg); + Discriminant::zero() + } + }; + enm.variants.push(Variant { + doc: Doc::new(), + attrs: OtherAttrs::none(), + name: Pair { + namespace: Namespace::ROOT, + cxx: cxx_name, + rust: rust_name, + }, + discriminant, + expr: None, + }); + } + } + _ => {} + } + for inner in &node.inner { + traverse(cx, inner, namespace, variants_from_header, idx); + } + if let Clang::NamespaceDecl(_) = &node.kind { + let _ = namespace.pop().unwrap(); + } +} + +fn translate_qual_type(cx: &mut Errors, enm: &Enum, qual_type: &str) -> Path { + let rust_std_name = match qual_type { + "char" => "c_char", + "int" => "c_int", + "long" => "c_long", + "long long" => "c_longlong", + "signed char" => "c_schar", + "short" => "c_short", + "unsigned char" => "c_uchar", + "unsigned int" => "c_uint", + "unsigned long" => "c_ulong", + "unsigned long long" => "c_ulonglong", + "unsigned short" => "c_ushort", + unsupported => { + let span = &enm.variants_from_header_attr; + let qual_name = CxxName(&enm.name); + let msg = format!( + "unsupported underlying type for {}: {}", + qual_name, unsupported, + ); + cx.error(span, msg); + "c_int" + } + }; + let span = enm + .variants_from_header_attr + .as_ref() + .unwrap() + .path + .get_ident() + .unwrap() + .span(); + let ident = Ident::new(rust_std_name, span); + let path = quote_spanned!(span=> ::std::os::raw::#ident); + parse_quote!(#path) +} + +enum ParsedDiscriminant { + Constant(Discriminant), + Successor, + Fail, +} + +fn discriminant_value(mut clang: &[Node]) -> ParsedDiscriminant { + if clang.is_empty() { + // No discriminant expression provided; use successor of previous + // descriminant. + return ParsedDiscriminant::Successor; + } + + loop { + if clang.len() != 1 { + return ParsedDiscriminant::Fail; + } + + let node = &clang[0]; + match &node.kind { + Clang::ImplicitCastExpr => clang = &node.inner, + Clang::ConstantExpr(expr) => match Discriminant::from_str(&expr.value) { + Ok(discriminant) => return ParsedDiscriminant::Constant(discriminant), + Err(_) => return ParsedDiscriminant::Fail, + }, + _ => return ParsedDiscriminant::Fail, + } + } +} + +fn span_for_enum_error(enm: &Enum) -> TokenStream { + let enum_token = enm.enum_token; + let mut brace_token = Group::new(Delimiter::Brace, TokenStream::new()); + brace_token.set_span(enm.brace_token.span); + quote!(#enum_token #brace_token) +} + +struct CxxName<'a>(&'a Pair); + +impl<'a> Display for CxxName<'a> { + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + for namespace in &self.0.namespace { + write!(formatter, "{}::", namespace)?; + } + write!(formatter, "{}", self.0.cxx) + } +} @@ -22,6 +22,13 @@ std::size_t cxxbridge1$cxx_string$length(const std::string &s) noexcept { return s.length(); } +void cxxbridge1$cxx_string$clear(std::string &s) noexcept { s.clear(); } + +void cxxbridge1$cxx_string$reserve_total(std::string &s, + size_t new_cap) noexcept { + s.reserve(new_cap); +} + void cxxbridge1$cxx_string$push(std::string &s, const std::uint8_t *ptr, std::size_t len) noexcept { s.append(reinterpret_cast<const char *>(ptr), len); @@ -31,12 +38,18 @@ void cxxbridge1$cxx_string$push(std::string &s, const std::uint8_t *ptr, void cxxbridge1$string$new(rust::String *self) noexcept; void cxxbridge1$string$clone(rust::String *self, const rust::String &other) noexcept; -bool cxxbridge1$string$from(rust::String *self, const char *ptr, - std::size_t len) noexcept; +bool cxxbridge1$string$from_utf8(rust::String *self, const char *ptr, + std::size_t len) noexcept; +bool cxxbridge1$string$from_utf16(rust::String *self, const char16_t *ptr, + std::size_t len) noexcept; void cxxbridge1$string$drop(rust::String *self) noexcept; const char *cxxbridge1$string$ptr(const rust::String *self) noexcept; std::size_t cxxbridge1$string$len(const rust::String *self) noexcept; -void cxxbridge1$string$reserve_total(rust::String *self, size_t cap) noexcept; +std::size_t cxxbridge1$string$capacity(const rust::String *self) noexcept; +void cxxbridge1$string$reserve_additional(rust::String *self, + size_t additional) noexcept; +void cxxbridge1$string$reserve_total(rust::String *self, + size_t new_cap) noexcept; // rust::Str void cxxbridge1$str$new(rust::Str *self) noexcept; @@ -81,11 +94,17 @@ String::String(String &&other) noexcept : repr(other.repr) { String::~String() noexcept { cxxbridge1$string$drop(this); } static void initString(String *self, const char *s, std::size_t len) { - if (!cxxbridge1$string$from(self, s, len)) { + if (!cxxbridge1$string$from_utf8(self, s, len)) { panic<std::invalid_argument>("data for rust::String is not utf-8"); } } +static void initString(String *self, const char16_t *s, std::size_t len) { + if (!cxxbridge1$string$from_utf16(self, s, len)) { + panic<std::invalid_argument>("data for rust::String is not utf-16"); + } +} + String::String(const std::string &s) { initString(this, s.data(), s.length()); } String::String(const char *s) { @@ -100,6 +119,19 @@ String::String(const char *s, std::size_t len) { len); } +String::String(const char16_t *s) { + assert(s != nullptr); + initString(this, s, std::char_traits<char16_t>::length(s)); +} + +String::String(const char16_t *s, std::size_t len) { + assert(s != nullptr || len == 0); + initString(this, + s == nullptr && len == 0 ? reinterpret_cast<const char16_t *>(2) + : s, + len); +} + String &String::operator=(const String &other) &noexcept { if (this != &other) { cxxbridge1$string$drop(this); @@ -131,14 +163,24 @@ std::size_t String::length() const noexcept { return cxxbridge1$string$len(this); } +bool String::empty() const noexcept { return this->size() == 0; } + const char *String::c_str() noexcept { auto len = this->length(); - cxxbridge1$string$reserve_total(this, len + 1); + cxxbridge1$string$reserve_additional(this, 1); auto ptr = this->data(); const_cast<char *>(ptr)[len] = '\0'; return ptr; } +std::size_t String::capacity() const noexcept { + return cxxbridge1$string$capacity(this); +} + +void String::reserve(std::size_t new_cap) noexcept { + cxxbridge1$string$reserve_total(this, new_cap); +} + String::iterator String::begin() noexcept { return const_cast<char *>(this->data()); } @@ -228,6 +270,8 @@ std::size_t Str::size() const noexcept { return cxxbridge1$str$len(this); } std::size_t Str::length() const noexcept { return this->size(); } +bool Str::empty() const noexcept { return this->size() == 0; } + Str::const_iterator Str::begin() const noexcept { return this->cbegin(); } Str::const_iterator Str::end() const noexcept { return this->cend(); } @@ -427,6 +471,13 @@ using isize_if_unique = } // namespace cxxbridge1 } // namespace rust +namespace { +template <typename T> +void destroy(T *ptr) { + ptr->~T(); +} +} // namespace + extern "C" { void cxxbridge1$unique_ptr$std$string$null( std::unique_ptr<std::string> *ptr) noexcept { @@ -491,6 +542,18 @@ static_assert(sizeof(std::string) <= kMaxExpectedWordsInString * sizeof(void *), ptr->~unique_ptr(); \ } +#define STD_VECTOR_TRIVIAL_OPS(RUST_TYPE, CXX_TYPE) \ + void cxxbridge1$std$vector$##RUST_TYPE##$push_back( \ + std::vector<CXX_TYPE> *v, CXX_TYPE *value) noexcept { \ + v->push_back(std::move(*value)); \ + destroy(value); \ + } \ + void cxxbridge1$std$vector$##RUST_TYPE##$pop_back(std::vector<CXX_TYPE> *v, \ + CXX_TYPE *out) noexcept { \ + new (out) CXX_TYPE(std::move(v->back())); \ + v->pop_back(); \ + } + #define RUST_VEC_EXTERNS(RUST_TYPE, CXX_TYPE) \ void cxxbridge1$rust_vec$##RUST_TYPE##$new( \ rust::Vec<CXX_TYPE> *ptr) noexcept; \ @@ -503,7 +566,7 @@ static_assert(sizeof(std::string) <= kMaxExpectedWordsInString * sizeof(void *), const CXX_TYPE *cxxbridge1$rust_vec$##RUST_TYPE##$data( \ const rust::Vec<CXX_TYPE> *ptr) noexcept; \ void cxxbridge1$rust_vec$##RUST_TYPE##$reserve_total( \ - rust::Vec<CXX_TYPE> *ptr, std::size_t cap) noexcept; \ + rust::Vec<CXX_TYPE> *ptr, std::size_t new_cap) noexcept; \ void cxxbridge1$rust_vec$##RUST_TYPE##$set_len(rust::Vec<CXX_TYPE> *ptr, \ std::size_t len) noexcept; @@ -529,8 +592,8 @@ static_assert(sizeof(std::string) <= kMaxExpectedWordsInString * sizeof(void *), return cxxbridge1$rust_vec$##RUST_TYPE##$data(this); \ } \ template <> \ - void Vec<CXX_TYPE>::reserve_total(std::size_t cap) noexcept { \ - cxxbridge1$rust_vec$##RUST_TYPE##$reserve_total(this, cap); \ + void Vec<CXX_TYPE>::reserve_total(std::size_t new_cap) noexcept { \ + cxxbridge1$rust_vec$##RUST_TYPE##$reserve_total(this, new_cap); \ } \ template <> \ void Vec<CXX_TYPE>::set_len(std::size_t len) noexcept { \ @@ -603,10 +666,13 @@ static_assert(sizeof(std::string) <= kMaxExpectedWordsInString * sizeof(void *), MACRO(f32, float) \ MACRO(f64, double) -#define FOR_EACH_STD_VECTOR(MACRO) \ +#define FOR_EACH_TRIVIAL_STD_VECTOR(MACRO) \ FOR_EACH_NUMERIC(MACRO) \ MACRO(usize, std::size_t) \ - MACRO(isize, rust::isize) \ + MACRO(isize, rust::isize) + +#define FOR_EACH_STD_VECTOR(MACRO) \ + FOR_EACH_TRIVIAL_STD_VECTOR(MACRO) \ MACRO(string, std::string) #define FOR_EACH_RUST_VEC(MACRO) \ @@ -627,6 +693,7 @@ static_assert(sizeof(std::string) <= kMaxExpectedWordsInString * sizeof(void *), extern "C" { FOR_EACH_STD_VECTOR(STD_VECTOR_OPS) +FOR_EACH_TRIVIAL_STD_VECTOR(STD_VECTOR_TRIVIAL_OPS) FOR_EACH_RUST_VEC(RUST_VEC_EXTERNS) FOR_EACH_SHARED_PTR(SHARED_PTR_OPS) } // extern "C" diff --git a/src/cxx_string.rs b/src/cxx_string.rs index dce7053d..626c2c85 100644 --- a/src/cxx_string.rs +++ b/src/cxx_string.rs @@ -19,6 +19,10 @@ extern "C" { fn string_data(this: &CxxString) -> *const u8; #[link_name = "cxxbridge1$cxx_string$length"] fn string_length(this: &CxxString) -> usize; + #[link_name = "cxxbridge1$cxx_string$clear"] + fn string_clear(this: Pin<&mut CxxString>); + #[link_name = "cxxbridge1$cxx_string$reserve_total"] + fn string_reserve_total(this: Pin<&mut CxxString>, new_cap: usize); #[link_name = "cxxbridge1$cxx_string$push"] fn string_push(this: Pin<&mut CxxString>, ptr: *const u8, len: usize); } @@ -144,6 +148,49 @@ impl CxxString { String::from_utf8_lossy(self.as_bytes()) } + /// Removes all characters from the string. + /// + /// Matches the behavior of C++ [std::string::clear][clear]. + /// + /// Note: **unlike** the guarantee of Rust's `std::string::String::clear`, + /// the C++ standard does not require that capacity is unchanged by this + /// operation. In practice existing implementations do not change the + /// capacity but all pointers, references, and iterators into the string + /// contents are nevertheless invalidated. + /// + /// [clear]: https://en.cppreference.com/w/cpp/string/basic_string/clear + pub fn clear(self: Pin<&mut Self>) { + unsafe { string_clear(self) } + } + + /// Ensures that this string's capacity is at least `additional` bytes + /// larger than its length. + /// + /// The capacity may be increased by more than `additional` bytes if it + /// chooses, to amortize the cost of frequent reallocations. + /// + /// **The meaning of the argument is not the same as + /// [std::string::reserve][reserve] in C++.** The C++ standard library and + /// Rust standard library both have a `reserve` method on strings, but in + /// C++ code the argument always refers to total capacity, whereas in Rust + /// code it always refers to additional capacity. This API on `CxxString` + /// follows the Rust convention, the same way that for the length accessor + /// we use the Rust conventional `len()` naming and not C++ `size()` or + /// `length()`. + /// + /// # Panics + /// + /// Panics if the new capacity overflows usize. + /// + /// [reserve]: https://en.cppreference.com/w/cpp/string/basic_string/reserve + pub fn reserve(self: Pin<&mut Self>, additional: usize) { + let new_cap = self + .len() + .checked_add(additional) + .expect("CxxString capacity overflow"); + unsafe { string_reserve_total(self, new_cap) } + } + /// Appends a given string slice onto the end of this C++ string. pub fn push_str(self: Pin<&mut Self>, s: &str) { self.push_bytes(s.as_bytes()); @@ -213,6 +260,7 @@ pub struct StackString { space: MaybeUninit<[usize; 8]>, } +#[allow(missing_docs)] impl StackString { pub fn new() -> Self { StackString { @@ -222,9 +270,11 @@ impl StackString { pub unsafe fn init(&mut self, value: impl AsRef<[u8]>) -> Pin<&mut CxxString> { let value = value.as_ref(); - let this = &mut *self.space.as_mut_ptr().cast::<MaybeUninit<CxxString>>(); - string_init(this, value.as_ptr(), value.len()); - Pin::new_unchecked(&mut *this.as_mut_ptr()) + unsafe { + let this = &mut *self.space.as_mut_ptr().cast::<MaybeUninit<CxxString>>(); + string_init(this, value.as_ptr(), value.len()); + Pin::new_unchecked(&mut *this.as_mut_ptr()) + } } } diff --git a/src/cxx_vector.rs b/src/cxx_vector.rs index 16433417..d1fa23a2 100644 --- a/src/cxx_vector.rs +++ b/src/cxx_vector.rs @@ -8,9 +8,8 @@ use core::ffi::c_void; use core::fmt::{self, Debug}; use core::iter::FusedIterator; use core::marker::{PhantomData, PhantomPinned}; -use core::mem; +use core::mem::{self, ManuallyDrop, MaybeUninit}; use core::pin::Pin; -use core::ptr; use core::slice; /// Binding to C++ `std::vector<T, std::allocator<T>>`. @@ -23,7 +22,13 @@ use core::slice; /// pointer, as in `&CxxVector<T>` or `UniquePtr<CxxVector<T>>`. #[repr(C, packed)] pub struct CxxVector<T> { - _private: [T; 0], + // A thing, because repr(C) structs are not allowed to consist exclusively + // of PhantomData fields. + _void: [c_void; 0], + // The conceptual vector elements to ensure that autotraits are propagated + // correctly, e.g. CxxVector is UnwindSafe iff T is. + _elements: PhantomData<[T]>, + // Prevent unpin operation from Pin<&mut CxxVector<T>> to &mut CxxVector<T>. _pinned: PhantomData<PhantomPinned>, } @@ -81,8 +86,10 @@ where /// [operator_at]: https://en.cppreference.com/w/cpp/container/vector/operator_at pub unsafe fn get_unchecked(&self, pos: usize) -> &T { let this = self as *const CxxVector<T> as *mut CxxVector<T>; - let ptr = T::__get_unchecked(this, pos) as *const T; - &*ptr + unsafe { + let ptr = T::__get_unchecked(this, pos) as *const T; + &*ptr + } } /// Returns a pinned mutable reference to an element without doing bounds @@ -97,8 +104,10 @@ where /// /// [operator_at]: https://en.cppreference.com/w/cpp/container/vector/operator_at pub unsafe fn index_unchecked_mut(self: Pin<&mut Self>, pos: usize) -> Pin<&mut T> { - let ptr = T::__get_unchecked(self.get_unchecked_mut(), pos); - Pin::new_unchecked(&mut *ptr) + unsafe { + let ptr = T::__get_unchecked(self.get_unchecked_mut(), pos); + Pin::new_unchecked(&mut *ptr) + } } /// Returns a slice to the underlying contiguous array of elements. @@ -146,6 +155,39 @@ where pub fn iter_mut(self: Pin<&mut Self>) -> IterMut<T> { IterMut { v: self, index: 0 } } + + /// Appends an element to the back of the vector. + /// + /// Matches the behavior of C++ [std::vector\<T\>::push_back][push_back]. + /// + /// [push_back]: https://en.cppreference.com/w/cpp/container/vector/push_back + pub fn push(self: Pin<&mut Self>, value: T) + where + T: ExternType<Kind = Trivial>, + { + let mut value = ManuallyDrop::new(value); + unsafe { + // C++ calls move constructor followed by destructor on `value`. + T::__push_back(self, &mut value); + } + } + + /// Removes the last element from a vector and returns it, or `None` if the + /// vector is empty. + pub fn pop(self: Pin<&mut Self>) -> Option<T> + where + T: ExternType<Kind = Trivial>, + { + if self.is_empty() { + None + } else { + let mut value = MaybeUninit::uninit(); + Some(unsafe { + T::__pop_back(self, &mut value); + value.assume_init() + }) + } + } } /// Iterator over elements of a `CxxVector` by shared reference. @@ -296,19 +338,62 @@ pub unsafe trait VectorElement: Sized { #[doc(hidden)] unsafe fn __get_unchecked(v: *mut CxxVector<Self>, pos: usize) -> *mut Self; #[doc(hidden)] - fn __unique_ptr_null() -> *mut c_void; + unsafe fn __push_back(v: Pin<&mut CxxVector<Self>>, value: &mut ManuallyDrop<Self>) { + // Opaque C type vector elements do not get this method because they can + // never exist by value on the Rust side of the bridge. + let _ = v; + let _ = value; + unreachable!() + } #[doc(hidden)] - unsafe fn __unique_ptr_raw(raw: *mut CxxVector<Self>) -> *mut c_void; + unsafe fn __pop_back(v: Pin<&mut CxxVector<Self>>, out: &mut MaybeUninit<Self>) { + // Opaque C type vector elements do not get this method because they can + // never exist by value on the Rust side of the bridge. + let _ = v; + let _ = out; + unreachable!() + } + #[doc(hidden)] + fn __unique_ptr_null() -> MaybeUninit<*mut c_void>; #[doc(hidden)] - unsafe fn __unique_ptr_get(repr: *mut c_void) -> *const CxxVector<Self>; + unsafe fn __unique_ptr_raw(raw: *mut CxxVector<Self>) -> MaybeUninit<*mut c_void>; #[doc(hidden)] - unsafe fn __unique_ptr_release(repr: *mut c_void) -> *mut CxxVector<Self>; + unsafe fn __unique_ptr_get(repr: MaybeUninit<*mut c_void>) -> *const CxxVector<Self>; #[doc(hidden)] - unsafe fn __unique_ptr_drop(repr: *mut c_void); + unsafe fn __unique_ptr_release(repr: MaybeUninit<*mut c_void>) -> *mut CxxVector<Self>; + #[doc(hidden)] + unsafe fn __unique_ptr_drop(repr: MaybeUninit<*mut c_void>); +} + +macro_rules! vector_element_by_value_methods { + (opaque, $segment:expr, $ty:ty) => {}; + (trivial, $segment:expr, $ty:ty) => { + #[doc(hidden)] + unsafe fn __push_back(v: Pin<&mut CxxVector<$ty>>, value: &mut ManuallyDrop<$ty>) { + extern "C" { + attr! { + #[link_name = concat!("cxxbridge1$std$vector$", $segment, "$push_back")] + fn __push_back(_: Pin<&mut CxxVector<$ty>>, _: &mut ManuallyDrop<$ty>); + } + } + unsafe { __push_back(v, value) } + } + #[doc(hidden)] + unsafe fn __pop_back(v: Pin<&mut CxxVector<$ty>>, out: &mut MaybeUninit<$ty>) { + extern "C" { + attr! { + #[link_name = concat!("cxxbridge1$std$vector$", $segment, "$pop_back")] + fn __pop_back(_: Pin<&mut CxxVector<$ty>>, _: &mut MaybeUninit<$ty>); + } + } + unsafe { __pop_back(v, out) } + } + }; } macro_rules! impl_vector_element { - ($segment:expr, $name:expr, $ty:ty) => { + ($kind:ident, $segment:expr, $name:expr, $ty:ty) => { + const_assert_eq!(0, mem::size_of::<CxxVector<$ty>>()); const_assert_eq!(1, mem::align_of::<CxxVector<$ty>>()); unsafe impl VectorElement for $ty { @@ -334,61 +419,62 @@ macro_rules! impl_vector_element { fn __get_unchecked(_: *mut CxxVector<$ty>, _: usize) -> *mut $ty; } } - __get_unchecked(v, pos) + unsafe { __get_unchecked(v, pos) } } + vector_element_by_value_methods!($kind, $segment, $ty); #[doc(hidden)] - fn __unique_ptr_null() -> *mut c_void { + fn __unique_ptr_null() -> MaybeUninit<*mut c_void> { extern "C" { attr! { #[link_name = concat!("cxxbridge1$unique_ptr$std$vector$", $segment, "$null")] - fn __unique_ptr_null(this: *mut *mut c_void); + fn __unique_ptr_null(this: *mut MaybeUninit<*mut c_void>); } } - let mut repr = ptr::null_mut::<c_void>(); + let mut repr = MaybeUninit::uninit(); unsafe { __unique_ptr_null(&mut repr) } repr } #[doc(hidden)] - unsafe fn __unique_ptr_raw(raw: *mut CxxVector<Self>) -> *mut c_void { + unsafe fn __unique_ptr_raw(raw: *mut CxxVector<Self>) -> MaybeUninit<*mut c_void> { extern "C" { attr! { #[link_name = concat!("cxxbridge1$unique_ptr$std$vector$", $segment, "$raw")] - fn __unique_ptr_raw(this: *mut *mut c_void, raw: *mut CxxVector<$ty>); + fn __unique_ptr_raw(this: *mut MaybeUninit<*mut c_void>, raw: *mut CxxVector<$ty>); } } - let mut repr = ptr::null_mut::<c_void>(); - __unique_ptr_raw(&mut repr, raw); + let mut repr = MaybeUninit::uninit(); + unsafe { __unique_ptr_raw(&mut repr, raw) } repr } #[doc(hidden)] - unsafe fn __unique_ptr_get(repr: *mut c_void) -> *const CxxVector<Self> { + unsafe fn __unique_ptr_get(repr: MaybeUninit<*mut c_void>) -> *const CxxVector<Self> { extern "C" { attr! { #[link_name = concat!("cxxbridge1$unique_ptr$std$vector$", $segment, "$get")] - fn __unique_ptr_get(this: *const *mut c_void) -> *const CxxVector<$ty>; + fn __unique_ptr_get(this: *const MaybeUninit<*mut c_void>) -> *const CxxVector<$ty>; } } - __unique_ptr_get(&repr) + unsafe { __unique_ptr_get(&repr) } } #[doc(hidden)] - unsafe fn __unique_ptr_release(mut repr: *mut c_void) -> *mut CxxVector<Self> { + unsafe fn __unique_ptr_release(mut repr: MaybeUninit<*mut c_void>) -> *mut CxxVector<Self> { extern "C" { attr! { #[link_name = concat!("cxxbridge1$unique_ptr$std$vector$", $segment, "$release")] - fn __unique_ptr_release(this: *mut *mut c_void) -> *mut CxxVector<$ty>; + fn __unique_ptr_release(this: *mut MaybeUninit<*mut c_void>) -> *mut CxxVector<$ty>; } } - __unique_ptr_release(&mut repr) + unsafe { __unique_ptr_release(&mut repr) } } #[doc(hidden)] - unsafe fn __unique_ptr_drop(mut repr: *mut c_void) { + unsafe fn __unique_ptr_drop(mut repr: MaybeUninit<*mut c_void>) { extern "C" { attr! { #[link_name = concat!("cxxbridge1$unique_ptr$std$vector$", $segment, "$drop")] - fn __unique_ptr_drop(this: *mut *mut c_void); + fn __unique_ptr_drop(this: *mut MaybeUninit<*mut c_void>); } } - __unique_ptr_drop(&mut repr); + unsafe { __unique_ptr_drop(&mut repr) } } } }; @@ -396,7 +482,7 @@ macro_rules! impl_vector_element { macro_rules! impl_vector_element_for_primitive { ($ty:ident) => { - impl_vector_element!(stringify!($ty), stringify!($ty), $ty); + impl_vector_element!(trivial, stringify!($ty), stringify!($ty), $ty); }; } @@ -413,4 +499,4 @@ impl_vector_element_for_primitive!(isize); impl_vector_element_for_primitive!(f32); impl_vector_element_for_primitive!(f64); -impl_vector_element!("string", "CxxString", CxxString); +impl_vector_element!(opaque, "string", "CxxString", CxxString); diff --git a/src/exception.rs b/src/exception.rs index f61e8fa9..2ae470e7 100644 --- a/src/exception.rs +++ b/src/exception.rs @@ -1,5 +1,5 @@ use alloc::boxed::Box; -use core::fmt::{self, Debug, Display}; +use core::fmt::{self, Display}; /// Exception thrown from an `extern "C++"` function. #[derive(Debug)] @@ -16,6 +16,7 @@ impl Display for Exception { impl std::error::Error for Exception {} impl Exception { + #[allow(missing_docs)] pub fn what(&self) -> &str { &self.what } diff --git a/src/extern_type.rs b/src/extern_type.rs index 35057acb..8c9c2860 100644 --- a/src/extern_type.rs +++ b/src/extern_type.rs @@ -167,6 +167,7 @@ pub mod kind { /// indirection. pub enum Trivial {} + #[allow(missing_docs)] pub trait Kind: private::Sealed {} impl Kind for Opaque {} impl Kind for Trivial {} diff --git a/src/function.rs b/src/function.rs index 1166b3d4..f0a20100 100644 --- a/src/function.rs +++ b/src/function.rs @@ -1,5 +1,9 @@ +#![allow(missing_docs)] + +use core::ffi::c_void; + #[repr(C)] pub struct FatFunction { - pub trampoline: *const (), - pub ptr: *const (), + pub trampoline: *const c_void, + pub ptr: *const c_void, } @@ -364,8 +364,10 @@ //! </table> #![no_std] -#![doc(html_root_url = "https://docs.rs/cxx/1.0.42")] -#![deny(improper_ctypes)] +#![doc(html_root_url = "https://docs.rs/cxx/1.0.54")] +#![deny(improper_ctypes, improper_ctypes_definitions, missing_docs)] +#![cfg_attr(not(no_unsafe_op_in_unsafe_fn_lint), deny(unsafe_op_in_unsafe_fn))] +#![cfg_attr(no_unsafe_op_in_unsafe_fn_lint, allow(unused_unsafe))] #![allow(non_camel_case_types)] #![allow( clippy::cognitive_complexity, @@ -486,3 +488,6 @@ chars! { a b c d e f g h i j k l m n o p q r s t u v w x y z __ // underscore } + +#[repr(transparent)] +struct void(core::ffi::c_void); diff --git a/src/memory.rs b/src/memory.rs index 441d3d89..fd8df12a 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -4,5 +4,6 @@ pub use crate::shared_ptr::SharedPtrTarget; pub use crate::unique_ptr::UniquePtrTarget; +pub use crate::weak_ptr::WeakPtrTarget; #[doc(no_inline)] pub use cxx::{SharedPtr, UniquePtr}; diff --git a/src/opaque.rs b/src/opaque.rs index 3c8f5362..e0f8ce2c 100644 --- a/src/opaque.rs +++ b/src/opaque.rs @@ -1,3 +1,6 @@ +#![allow(missing_docs)] + +use crate::void; use core::marker::{PhantomData, PhantomPinned}; use core::mem; @@ -9,7 +12,7 @@ use core::mem; // . !Unpin #[repr(C, packed)] pub struct Opaque { - _private: [*const u8; 0], + _private: [*const void; 0], _pinned: PhantomData<PhantomPinned>, } diff --git a/src/result.rs b/src/result.rs index f41639a1..d7a31f02 100644 --- a/src/result.rs +++ b/src/result.rs @@ -1,3 +1,5 @@ +#![allow(missing_docs)] + use crate::exception::Exception; use alloc::boxed::Box; use alloc::string::{String, ToString}; @@ -26,16 +28,16 @@ where { match result { Ok(ok) => { - ptr::write(ret, ok); + unsafe { ptr::write(ret, ok) } Result { ok: ptr::null() } } - Err(err) => to_c_error(err.to_string()), + Err(err) => unsafe { to_c_error(err.to_string()) }, } } unsafe fn to_c_error(msg: String) -> Result { let mut msg = msg; - msg.as_mut_vec().push(b'\0'); + unsafe { msg.as_mut_vec() }.push(b'\0'); let ptr = msg.as_ptr(); let len = msg.len(); @@ -44,22 +46,24 @@ unsafe fn to_c_error(msg: String) -> Result { fn error(ptr: *const u8, len: usize) -> NonNull<u8>; } - let copy = error(ptr, len); + let copy = unsafe { error(ptr, len) }; let err = PtrLen { ptr: copy, len }; Result { err } } impl Result { pub unsafe fn exception(self) -> StdResult<(), Exception> { - if self.ok.is_null() { - Ok(()) - } else { - let err = self.err; - let slice = slice::from_raw_parts_mut(err.ptr.as_ptr(), err.len); - let s = str::from_utf8_unchecked_mut(slice); - Err(Exception { - what: Box::from_raw(s), - }) + unsafe { + if self.ok.is_null() { + Ok(()) + } else { + let err = self.err; + let slice = slice::from_raw_parts_mut(err.ptr.as_ptr(), err.len); + let s = str::from_utf8_unchecked_mut(slice); + Err(Exception { + what: Box::from_raw(s), + }) + } } } } diff --git a/src/rust_slice.rs b/src/rust_slice.rs index b4b5f2cb..06963117 100644 --- a/src/rust_slice.rs +++ b/src/rust_slice.rs @@ -1,41 +1,66 @@ -use core::mem; +#![allow(missing_docs)] + +use core::mem::{self, MaybeUninit}; use core::ptr::{self, NonNull}; use core::slice; +// ABI compatible with C++ rust::Slice<T> (not necessarily &[T]). #[repr(C)] pub struct RustSlice { - pub(crate) repr: NonNull<[()]>, + repr: [MaybeUninit<usize>; mem::size_of::<NonNull<[()]>>() / mem::size_of::<usize>()], } impl RustSlice { pub fn from_ref<T>(slice: &[T]) -> Self { - let ptr = ptr::slice_from_raw_parts::<()>(slice.as_ptr().cast(), slice.len()); - RustSlice { - repr: unsafe { NonNull::new_unchecked(ptr as *mut _) }, - } + let ptr = NonNull::from(slice).cast::<T>(); + let len = slice.len(); + Self::from_raw_parts(ptr, len) } pub fn from_mut<T>(slice: &mut [T]) -> Self { - let ptr = ptr::slice_from_raw_parts_mut(slice.as_mut_ptr().cast(), slice.len()); - RustSlice { - repr: unsafe { NonNull::new_unchecked(ptr) }, - } + let ptr = NonNull::from(&mut *slice).cast::<T>(); + let len = slice.len(); + Self::from_raw_parts(ptr, len) } pub unsafe fn as_slice<'a, T>(self) -> &'a [T] { - let ptr = self.repr.as_ptr(); - let len = self.repr.as_ref().len(); - slice::from_raw_parts(ptr.cast(), len) + let ptr = self.as_non_null_ptr().as_ptr(); + let len = self.len(); + unsafe { slice::from_raw_parts(ptr, len) } } pub unsafe fn as_mut_slice<'a, T>(self) -> &'a mut [T] { - let ptr = self.repr.as_ptr(); - let len = self.repr.as_ref().len(); - slice::from_raw_parts_mut(ptr.cast(), len) + let ptr = self.as_non_null_ptr().as_ptr(); + let len = self.len(); + unsafe { slice::from_raw_parts_mut(ptr, len) } + } + + pub(crate) fn from_raw_parts<T>(ptr: NonNull<T>, len: usize) -> Self { + // TODO: use NonNull::from_raw_parts(ptr.cast(), len) when stable. + // https://doc.rust-lang.org/nightly/std/ptr/struct.NonNull.html#method.from_raw_parts + // https://github.com/rust-lang/rust/issues/81513 + let ptr = ptr::slice_from_raw_parts_mut(ptr.as_ptr().cast(), len); + unsafe { mem::transmute::<NonNull<[()]>, RustSlice>(NonNull::new_unchecked(ptr)) } + } + + pub(crate) fn as_non_null_ptr<T>(&self) -> NonNull<T> { + let rust_slice = RustSlice { repr: self.repr }; + let repr = unsafe { mem::transmute::<RustSlice, NonNull<[()]>>(rust_slice) }; + repr.cast() + } + + pub(crate) fn len(&self) -> usize { + let rust_slice = RustSlice { repr: self.repr }; + let repr = unsafe { mem::transmute::<RustSlice, NonNull<[()]>>(rust_slice) }; + // TODO: use repr.len() when stable. + // https://doc.rust-lang.org/nightly/std/ptr/struct.NonNull.html#method.len + // https://github.com/rust-lang/rust/issues/71146 + unsafe { repr.as_ref() }.len() } } +const_assert_eq!(mem::size_of::<NonNull<[()]>>(), mem::size_of::<RustSlice>()); const_assert_eq!( - mem::size_of::<Option<RustSlice>>(), - mem::size_of::<RustSlice>(), + mem::align_of::<NonNull<[()]>>(), + mem::align_of::<RustSlice>(), ); diff --git a/src/rust_str.rs b/src/rust_str.rs index 9bc26145..b1b46e3e 100644 --- a/src/rust_str.rs +++ b/src/rust_str.rs @@ -1,21 +1,28 @@ -use core::mem; +#![allow(missing_docs)] + +use core::mem::{self, MaybeUninit}; use core::ptr::NonNull; use core::str; +// ABI compatible with C++ rust::Str (not necessarily &str). #[repr(C)] pub struct RustStr { - repr: NonNull<str>, + repr: [MaybeUninit<usize>; mem::size_of::<NonNull<str>>() / mem::size_of::<usize>()], } impl RustStr { pub fn from(repr: &str) -> Self { let repr = NonNull::from(repr); - RustStr { repr } + unsafe { mem::transmute::<NonNull<str>, RustStr>(repr) } } pub unsafe fn as_str<'a>(self) -> &'a str { - &*self.repr.as_ptr() + unsafe { + let repr = mem::transmute::<RustStr, NonNull<str>>(self); + &*repr.as_ptr() + } } } -const_assert_eq!(mem::size_of::<Option<RustStr>>(), mem::size_of::<RustStr>()); +const_assert_eq!(mem::size_of::<NonNull<str>>(), mem::size_of::<RustStr>()); +const_assert_eq!(mem::align_of::<NonNull<str>>(), mem::align_of::<RustStr>()); diff --git a/src/rust_string.rs b/src/rust_string.rs index a5fa3f49..051e35c3 100644 --- a/src/rust_string.rs +++ b/src/rust_string.rs @@ -1,14 +1,18 @@ +#![allow(missing_docs)] + use alloc::string::String; -use core::mem; +use core::mem::{self, MaybeUninit}; +use core::ptr; +// ABI compatible with C++ rust::String (not necessarily alloc::string::String). #[repr(C)] pub struct RustString { - repr: String, + repr: [MaybeUninit<usize>; mem::size_of::<String>() / mem::size_of::<usize>()], } impl RustString { pub fn from(s: String) -> Self { - RustString { repr: s } + unsafe { mem::transmute::<String, RustString>(s) } } pub fn from_ref(s: &String) -> &Self { @@ -20,17 +24,24 @@ impl RustString { } pub fn into_string(self) -> String { - self.repr + unsafe { mem::transmute::<RustString, String>(self) } } pub fn as_string(&self) -> &String { - &self.repr + unsafe { &*(self as *const RustString as *const String) } } pub fn as_mut_string(&mut self) -> &mut String { - &mut self.repr + unsafe { &mut *(self as *mut RustString as *mut String) } + } +} + +impl Drop for RustString { + fn drop(&mut self) { + unsafe { ptr::drop_in_place(self.as_mut_string()) } } } -const_assert_eq!(mem::size_of::<[usize; 3]>(), mem::size_of::<String>()); -const_assert_eq!(mem::align_of::<usize>(), mem::align_of::<String>()); +const_assert_eq!(mem::size_of::<[usize; 3]>(), mem::size_of::<RustString>()); +const_assert_eq!(mem::size_of::<String>(), mem::size_of::<RustString>()); +const_assert_eq!(mem::align_of::<String>(), mem::align_of::<RustString>()); diff --git a/src/rust_type.rs b/src/rust_type.rs index 7bcf440d..eacb5309 100644 --- a/src/rust_type.rs +++ b/src/rust_type.rs @@ -1,3 +1,5 @@ +#![allow(missing_docs)] + pub unsafe trait RustType {} pub unsafe trait ImplBox {} pub unsafe trait ImplVec {} diff --git a/src/rust_vec.rs b/src/rust_vec.rs index 126fdbf9..9f4484db 100644 --- a/src/rust_vec.rs +++ b/src/rust_vec.rs @@ -1,20 +1,27 @@ +#![allow(missing_docs)] + use crate::rust_string::RustString; use alloc::string::String; use alloc::vec::Vec; -use core::mem::ManuallyDrop; +use core::ffi::c_void; +use core::marker::PhantomData; +use core::mem::{self, ManuallyDrop, MaybeUninit}; +use core::ptr; +// ABI compatible with C++ rust::Vec<T> (not necessarily alloc::vec::Vec<T>). #[repr(C)] pub struct RustVec<T> { - pub(crate) repr: Vec<T>, + repr: [MaybeUninit<usize>; mem::size_of::<Vec<c_void>>() / mem::size_of::<usize>()], + marker: PhantomData<Vec<T>>, } impl<T> RustVec<T> { pub fn new() -> Self { - RustVec { repr: Vec::new() } + Self::from(Vec::new()) } pub fn from(v: Vec<T>) -> Self { - RustVec { repr: v } + unsafe { mem::transmute::<Vec<T>, RustVec<T>>(v) } } pub fn from_ref(v: &Vec<T>) -> &Self { @@ -26,38 +33,39 @@ impl<T> RustVec<T> { } pub fn into_vec(self) -> Vec<T> { - self.repr + unsafe { mem::transmute::<RustVec<T>, Vec<T>>(self) } } pub fn as_vec(&self) -> &Vec<T> { - &self.repr + unsafe { &*(self as *const RustVec<T> as *const Vec<T>) } } pub fn as_mut_vec(&mut self) -> &mut Vec<T> { - &mut self.repr + unsafe { &mut *(self as *mut RustVec<T> as *mut Vec<T>) } } pub fn len(&self) -> usize { - self.repr.len() + self.as_vec().len() } pub fn capacity(&self) -> usize { - self.repr.capacity() + self.as_vec().capacity() } pub fn as_ptr(&self) -> *const T { - self.repr.as_ptr() + self.as_vec().as_ptr() } - pub fn reserve_total(&mut self, cap: usize) { - let len = self.repr.len(); - if cap > len { - self.repr.reserve(cap - len); + pub fn reserve_total(&mut self, new_cap: usize) { + let vec = self.as_mut_vec(); + if new_cap > vec.capacity() { + let additional = new_cap - vec.len(); + vec.reserve(additional); } } pub unsafe fn set_len(&mut self, len: usize) { - self.repr.set_len(len); + unsafe { self.as_mut_vec().set_len(len) } } } @@ -79,7 +87,7 @@ impl RustVec<RustString> { } pub fn into_vec_string(self) -> Vec<String> { - let mut v = ManuallyDrop::new(self.repr); + let mut v = ManuallyDrop::new(self.into_vec()); let ptr = v.as_mut_ptr().cast::<String>(); let len = v.len(); let cap = v.capacity(); @@ -87,10 +95,16 @@ impl RustVec<RustString> { } pub fn as_vec_string(&self) -> &Vec<String> { - unsafe { &*(&self.repr as *const Vec<RustString> as *const Vec<String>) } + unsafe { &*(self as *const RustVec<RustString> as *const Vec<String>) } } pub fn as_mut_vec_string(&mut self) -> &mut Vec<String> { - unsafe { &mut *(&mut self.repr as *mut Vec<RustString> as *mut Vec<String>) } + unsafe { &mut *(self as *mut RustVec<RustString> as *mut Vec<String>) } + } +} + +impl<T> Drop for RustVec<T> { + fn drop(&mut self) { + unsafe { ptr::drop_in_place(self.as_mut_vec()) } } } diff --git a/src/shared_ptr.rs b/src/shared_ptr.rs index 66b988ba..317773d4 100644 --- a/src/shared_ptr.rs +++ b/src/shared_ptr.rs @@ -15,7 +15,7 @@ pub struct SharedPtr<T> where T: SharedPtrTarget, { - repr: [*mut c_void; 2], + repr: [MaybeUninit<*mut c_void>; 2], ty: PhantomData<T>, } @@ -216,7 +216,7 @@ macro_rules! impl_shared_ptr_target { fn __null(new: *mut c_void); } } - __null(new); + unsafe { __null(new) } } #[doc(hidden)] unsafe fn __new(value: Self, new: *mut c_void) { @@ -226,7 +226,7 @@ macro_rules! impl_shared_ptr_target { fn __uninit(new: *mut c_void) -> *mut c_void; } } - __uninit(new).cast::<$ty>().write(value); + unsafe { __uninit(new).cast::<$ty>().write(value) } } #[doc(hidden)] unsafe fn __clone(this: *const c_void, new: *mut c_void) { @@ -236,7 +236,7 @@ macro_rules! impl_shared_ptr_target { fn __clone(this: *const c_void, new: *mut c_void); } } - __clone(this, new); + unsafe { __clone(this, new) } } #[doc(hidden)] unsafe fn __get(this: *const c_void) -> *const Self { @@ -246,7 +246,7 @@ macro_rules! impl_shared_ptr_target { fn __get(this: *const c_void) -> *const c_void; } } - __get(this).cast() + unsafe { __get(this) }.cast() } #[doc(hidden)] unsafe fn __drop(this: *mut c_void) { @@ -256,7 +256,7 @@ macro_rules! impl_shared_ptr_target { fn __drop(this: *mut c_void); } } - __drop(this); + unsafe { __drop(this) } } } }; diff --git a/src/symbols/exception.rs b/src/symbols/exception.rs index 0c1bb876..cf0701ba 100644 --- a/src/symbols/exception.rs +++ b/src/symbols/exception.rs @@ -4,7 +4,7 @@ use core::slice; #[export_name = "cxxbridge1$exception"] unsafe extern "C" fn exception(ptr: *const u8, len: usize) -> *const u8 { - let slice = slice::from_raw_parts(ptr, len); + let slice = unsafe { slice::from_raw_parts(ptr, len) }; let boxed = String::from_utf8_lossy(slice).into_owned().into_boxed_str(); Box::leak(boxed).as_ptr() } diff --git a/src/symbols/rust_slice.rs b/src/symbols/rust_slice.rs index 055b4dee..df215acf 100644 --- a/src/symbols/rust_slice.rs +++ b/src/symbols/rust_slice.rs @@ -3,20 +3,18 @@ use core::mem::MaybeUninit; use core::ptr::{self, NonNull}; #[export_name = "cxxbridge1$slice$new"] -unsafe extern "C" fn slice_new(this: &mut MaybeUninit<RustSlice>, ptr: *const (), len: usize) { - let ptr = ptr::slice_from_raw_parts(ptr, len); - let rust_slice = RustSlice { - repr: NonNull::new_unchecked(ptr as *mut _), - }; - ptr::write(this.as_mut_ptr(), rust_slice); +unsafe extern "C" fn slice_new(this: &mut MaybeUninit<RustSlice>, ptr: NonNull<()>, len: usize) { + let this = this.as_mut_ptr(); + let rust_slice = RustSlice::from_raw_parts(ptr, len); + unsafe { ptr::write(this, rust_slice) } } #[export_name = "cxxbridge1$slice$ptr"] -unsafe extern "C" fn slice_ptr(this: &RustSlice) -> *const () { - this.repr.as_ptr().cast() +unsafe extern "C" fn slice_ptr(this: &RustSlice) -> NonNull<()> { + this.as_non_null_ptr() } #[export_name = "cxxbridge1$slice$len"] unsafe extern "C" fn slice_len(this: &RustSlice) -> usize { - this.repr.as_ref().len() + this.len() } diff --git a/src/symbols/rust_str.rs b/src/symbols/rust_str.rs index a9e84efb..3d5ec344 100644 --- a/src/symbols/rust_str.rs +++ b/src/symbols/rust_str.rs @@ -6,20 +6,24 @@ use core::str; #[export_name = "cxxbridge1$str$new"] unsafe extern "C" fn str_new(this: &mut MaybeUninit<&str>) { - ptr::write(this.as_mut_ptr(), ""); + let this = this.as_mut_ptr(); + unsafe { ptr::write(this, "") } } #[export_name = "cxxbridge1$str$ref"] unsafe extern "C" fn str_ref<'a>(this: &mut MaybeUninit<&'a str>, string: &'a String) { - ptr::write(this.as_mut_ptr(), string.as_str()); + let this = this.as_mut_ptr(); + let s = string.as_str(); + unsafe { ptr::write(this, s) } } #[export_name = "cxxbridge1$str$from"] unsafe extern "C" fn str_from(this: &mut MaybeUninit<&str>, ptr: *const u8, len: usize) -> bool { - let slice = slice::from_raw_parts(ptr, len); + let slice = unsafe { slice::from_raw_parts(ptr, len) }; match str::from_utf8(slice) { Ok(s) => { - ptr::write(this.as_mut_ptr(), s); + let this = this.as_mut_ptr(); + unsafe { ptr::write(this, s) } true } Err(_) => false, diff --git a/src/symbols/rust_string.rs b/src/symbols/rust_string.rs index 91fd78a3..49d40697 100644 --- a/src/symbols/rust_string.rs +++ b/src/symbols/rust_string.rs @@ -7,24 +7,47 @@ use core::str; #[export_name = "cxxbridge1$string$new"] unsafe extern "C" fn string_new(this: &mut MaybeUninit<String>) { - ptr::write(this.as_mut_ptr(), String::new()); + let this = this.as_mut_ptr(); + let new = String::new(); + unsafe { ptr::write(this, new) } } #[export_name = "cxxbridge1$string$clone"] unsafe extern "C" fn string_clone(this: &mut MaybeUninit<String>, other: &String) { - ptr::write(this.as_mut_ptr(), other.clone()); + let this = this.as_mut_ptr(); + let clone = other.clone(); + unsafe { ptr::write(this, clone) } } -#[export_name = "cxxbridge1$string$from"] -unsafe extern "C" fn string_from( +#[export_name = "cxxbridge1$string$from_utf8"] +unsafe extern "C" fn string_from_utf8( this: &mut MaybeUninit<String>, ptr: *const u8, len: usize, ) -> bool { - let slice = slice::from_raw_parts(ptr, len); + let slice = unsafe { slice::from_raw_parts(ptr, len) }; match str::from_utf8(slice) { Ok(s) => { - ptr::write(this.as_mut_ptr(), s.to_owned()); + let this = this.as_mut_ptr(); + let owned = s.to_owned(); + unsafe { ptr::write(this, owned) } + true + } + Err(_) => false, + } +} + +#[export_name = "cxxbridge1$string$from_utf16"] +unsafe extern "C" fn string_from_utf16( + this: &mut MaybeUninit<String>, + ptr: *const u16, + len: usize, +) -> bool { + let slice = unsafe { slice::from_raw_parts(ptr, len) }; + match String::from_utf16(slice) { + Ok(s) => { + let this = this.as_mut_ptr(); + unsafe { ptr::write(this, s) } true } Err(_) => false, @@ -33,7 +56,7 @@ unsafe extern "C" fn string_from( #[export_name = "cxxbridge1$string$drop"] unsafe extern "C" fn string_drop(this: &mut ManuallyDrop<String>) { - ManuallyDrop::drop(this); + unsafe { ManuallyDrop::drop(this) } } #[export_name = "cxxbridge1$string$ptr"] @@ -46,7 +69,20 @@ unsafe extern "C" fn string_len(this: &String) -> usize { this.len() } +#[export_name = "cxxbridge1$string$capacity"] +unsafe extern "C" fn string_capacity(this: &String) -> usize { + this.capacity() +} + +#[export_name = "cxxbridge1$string$reserve_additional"] +unsafe extern "C" fn string_reserve_additional(this: &mut String, additional: usize) { + this.reserve(additional); +} + #[export_name = "cxxbridge1$string$reserve_total"] -unsafe extern "C" fn string_reserve_total(this: &mut String, cap: usize) { - this.reserve(cap); +unsafe extern "C" fn string_reserve_total(this: &mut String, new_cap: usize) { + if new_cap > this.capacity() { + let additional = new_cap - this.len(); + this.reserve(additional); + } } diff --git a/src/symbols/rust_vec.rs b/src/symbols/rust_vec.rs index a26a1569..6f2dab9d 100644 --- a/src/symbols/rust_vec.rs +++ b/src/symbols/rust_vec.rs @@ -7,50 +7,51 @@ use std::os::raw::c_char; macro_rules! rust_vec_shims { ($segment:expr, $ty:ty) => { - const_assert_eq!(mem::size_of::<[usize; 3]>(), mem::size_of::<Vec<$ty>>()); - const_assert_eq!(mem::align_of::<usize>(), mem::align_of::<Vec<$ty>>()); + const_assert_eq!(mem::size_of::<[usize; 3]>(), mem::size_of::<RustVec<$ty>>()); + const_assert_eq!(mem::size_of::<Vec<$ty>>(), mem::size_of::<RustVec<$ty>>()); + const_assert_eq!(mem::align_of::<Vec<$ty>>(), mem::align_of::<RustVec<$ty>>()); const _: () = { attr! { #[export_name = concat!("cxxbridge1$rust_vec$", $segment, "$new")] unsafe extern "C" fn __new(this: *mut RustVec<$ty>) { - ptr::write(this, RustVec { repr: Vec::new() }); + unsafe { ptr::write(this, RustVec::new()) } } } attr! { #[export_name = concat!("cxxbridge1$rust_vec$", $segment, "$drop")] unsafe extern "C" fn __drop(this: *mut RustVec<$ty>) { - ptr::drop_in_place(this); + unsafe { ptr::drop_in_place(this) } } } attr! { #[export_name = concat!("cxxbridge1$rust_vec$", $segment, "$len")] unsafe extern "C" fn __len(this: *const RustVec<$ty>) -> usize { - (*this).repr.len() + unsafe { &*this }.len() } } attr! { #[export_name = concat!("cxxbridge1$rust_vec$", $segment, "$capacity")] unsafe extern "C" fn __capacity(this: *const RustVec<$ty>) -> usize { - (*this).repr.capacity() + unsafe { &*this }.capacity() } } attr! { #[export_name = concat!("cxxbridge1$rust_vec$", $segment, "$data")] unsafe extern "C" fn __data(this: *const RustVec<$ty>) -> *const $ty { - (*this).repr.as_ptr() + unsafe { &*this }.as_ptr() } } attr! { #[export_name = concat!("cxxbridge1$rust_vec$", $segment, "$reserve_total")] - unsafe extern "C" fn __reserve_total(this: *mut RustVec<$ty>, cap: usize) { - (*this).reserve_total(cap); + unsafe extern "C" fn __reserve_total(this: *mut RustVec<$ty>, new_cap: usize) { + unsafe { &mut *this }.reserve_total(new_cap); } } attr! { #[export_name = concat!("cxxbridge1$rust_vec$", $segment, "$set_len")] unsafe extern "C" fn __set_len(this: *mut RustVec<$ty>, len: usize) { - (*this).repr.set_len(len); + unsafe { (*this).set_len(len) } } } }; diff --git a/src/unique_ptr.rs b/src/unique_ptr.rs index 836f4677..63a1ca78 100644 --- a/src/unique_ptr.rs +++ b/src/unique_ptr.rs @@ -6,10 +6,9 @@ use crate::ExternType; use core::ffi::c_void; use core::fmt::{self, Debug, Display}; use core::marker::PhantomData; -use core::mem; +use core::mem::{self, MaybeUninit}; use core::ops::{Deref, DerefMut}; use core::pin::Pin; -use core::ptr; /// Binding to C++ `std::unique_ptr<T, std::default_delete<T>>`. #[repr(C)] @@ -17,7 +16,7 @@ pub struct UniquePtr<T> where T: UniquePtrTarget, { - repr: *mut c_void, + repr: MaybeUninit<*mut c_void>, ty: PhantomData<T>, } @@ -104,7 +103,7 @@ where /// twice on the same raw pointer. pub unsafe fn from_raw(raw: *mut T) -> Self { UniquePtr { - repr: T::__raw(raw), + repr: unsafe { T::__raw(raw) }, ty: PhantomData, } } @@ -207,9 +206,9 @@ pub unsafe trait UniquePtrTarget { #[doc(hidden)] fn __typename(f: &mut fmt::Formatter) -> fmt::Result; #[doc(hidden)] - fn __null() -> *mut c_void; + fn __null() -> MaybeUninit<*mut c_void>; #[doc(hidden)] - fn __new(value: Self) -> *mut c_void + fn __new(value: Self) -> MaybeUninit<*mut c_void> where Self: Sized, { @@ -219,26 +218,26 @@ pub unsafe trait UniquePtrTarget { unreachable!() } #[doc(hidden)] - unsafe fn __raw(raw: *mut Self) -> *mut c_void; + unsafe fn __raw(raw: *mut Self) -> MaybeUninit<*mut c_void>; #[doc(hidden)] - unsafe fn __get(repr: *mut c_void) -> *const Self; + unsafe fn __get(repr: MaybeUninit<*mut c_void>) -> *const Self; #[doc(hidden)] - unsafe fn __release(repr: *mut c_void) -> *mut Self; + unsafe fn __release(repr: MaybeUninit<*mut c_void>) -> *mut Self; #[doc(hidden)] - unsafe fn __drop(repr: *mut c_void); + unsafe fn __drop(repr: MaybeUninit<*mut c_void>); } extern "C" { #[link_name = "cxxbridge1$unique_ptr$std$string$null"] - fn unique_ptr_std_string_null(this: *mut *mut c_void); + fn unique_ptr_std_string_null(this: *mut MaybeUninit<*mut c_void>); #[link_name = "cxxbridge1$unique_ptr$std$string$raw"] - fn unique_ptr_std_string_raw(this: *mut *mut c_void, raw: *mut CxxString); + fn unique_ptr_std_string_raw(this: *mut MaybeUninit<*mut c_void>, raw: *mut CxxString); #[link_name = "cxxbridge1$unique_ptr$std$string$get"] - fn unique_ptr_std_string_get(this: *const *mut c_void) -> *const CxxString; + fn unique_ptr_std_string_get(this: *const MaybeUninit<*mut c_void>) -> *const CxxString; #[link_name = "cxxbridge1$unique_ptr$std$string$release"] - fn unique_ptr_std_string_release(this: *mut *mut c_void) -> *mut CxxString; + fn unique_ptr_std_string_release(this: *mut MaybeUninit<*mut c_void>) -> *mut CxxString; #[link_name = "cxxbridge1$unique_ptr$std$string$drop"] - fn unique_ptr_std_string_drop(this: *mut *mut c_void); + fn unique_ptr_std_string_drop(this: *mut MaybeUninit<*mut c_void>); } unsafe impl UniquePtrTarget for CxxString { @@ -247,30 +246,30 @@ unsafe impl UniquePtrTarget for CxxString { f.write_str("CxxString") } #[doc(hidden)] - fn __null() -> *mut c_void { - let mut repr = ptr::null_mut::<c_void>(); + fn __null() -> MaybeUninit<*mut c_void> { + let mut repr = MaybeUninit::uninit(); unsafe { unique_ptr_std_string_null(&mut repr); } repr } #[doc(hidden)] - unsafe fn __raw(raw: *mut Self) -> *mut c_void { - let mut repr = ptr::null_mut::<c_void>(); - unique_ptr_std_string_raw(&mut repr, raw); + unsafe fn __raw(raw: *mut Self) -> MaybeUninit<*mut c_void> { + let mut repr = MaybeUninit::uninit(); + unsafe { unique_ptr_std_string_raw(&mut repr, raw) } repr } #[doc(hidden)] - unsafe fn __get(repr: *mut c_void) -> *const Self { - unique_ptr_std_string_get(&repr) + unsafe fn __get(repr: MaybeUninit<*mut c_void>) -> *const Self { + unsafe { unique_ptr_std_string_get(&repr) } } #[doc(hidden)] - unsafe fn __release(mut repr: *mut c_void) -> *mut Self { - unique_ptr_std_string_release(&mut repr) + unsafe fn __release(mut repr: MaybeUninit<*mut c_void>) -> *mut Self { + unsafe { unique_ptr_std_string_release(&mut repr) } } #[doc(hidden)] - unsafe fn __drop(mut repr: *mut c_void) { - unique_ptr_std_string_drop(&mut repr); + unsafe fn __drop(mut repr: MaybeUninit<*mut c_void>) { + unsafe { unique_ptr_std_string_drop(&mut repr) } } } @@ -283,23 +282,23 @@ where write!(f, "CxxVector<{}>", display(T::__typename)) } #[doc(hidden)] - fn __null() -> *mut c_void { + fn __null() -> MaybeUninit<*mut c_void> { T::__unique_ptr_null() } #[doc(hidden)] - unsafe fn __raw(raw: *mut Self) -> *mut c_void { - T::__unique_ptr_raw(raw) + unsafe fn __raw(raw: *mut Self) -> MaybeUninit<*mut c_void> { + unsafe { T::__unique_ptr_raw(raw) } } #[doc(hidden)] - unsafe fn __get(repr: *mut c_void) -> *const Self { - T::__unique_ptr_get(repr) + unsafe fn __get(repr: MaybeUninit<*mut c_void>) -> *const Self { + unsafe { T::__unique_ptr_get(repr) } } #[doc(hidden)] - unsafe fn __release(repr: *mut c_void) -> *mut Self { - T::__unique_ptr_release(repr) + unsafe fn __release(repr: MaybeUninit<*mut c_void>) -> *mut Self { + unsafe { T::__unique_ptr_release(repr) } } #[doc(hidden)] - unsafe fn __drop(repr: *mut c_void) { - T::__unique_ptr_drop(repr); + unsafe fn __drop(repr: MaybeUninit<*mut c_void>) { + unsafe { T::__unique_ptr_drop(repr) } } } diff --git a/src/unwind.rs b/src/unwind.rs index 36f6ae3d..4967a219 100644 --- a/src/unwind.rs +++ b/src/unwind.rs @@ -1,3 +1,5 @@ +#![allow(missing_docs)] + use std::io::{self, Write}; use std::panic::{self, AssertUnwindSafe}; use std::process; diff --git a/src/weak_ptr.rs b/src/weak_ptr.rs index 8291d59b..8a9f1a68 100644 --- a/src/weak_ptr.rs +++ b/src/weak_ptr.rs @@ -16,7 +16,7 @@ pub struct WeakPtr<T> where T: WeakPtrTarget, { - repr: [*mut c_void; 2], + repr: [MaybeUninit<*mut c_void>; 2], ty: PhantomData<T>, } @@ -91,8 +91,11 @@ where } } -// Methods are private; not intended to be implemented outside of cxxbridge -// codebase. +/// Trait bound for types which may be used as the `T` inside of a `WeakPtr<T>` +/// in generic code. +/// +/// This trait has no publicly callable or implementable methods. Implementing +/// it outside of the CXX codebase is not supported. pub unsafe trait WeakPtrTarget { #[doc(hidden)] fn __typename(f: &mut fmt::Formatter) -> fmt::Result; @@ -123,7 +126,7 @@ macro_rules! impl_weak_ptr_target { fn __null(new: *mut c_void); } } - __null(new); + unsafe { __null(new) } } #[doc(hidden)] unsafe fn __clone(this: *const c_void, new: *mut c_void) { @@ -133,7 +136,7 @@ macro_rules! impl_weak_ptr_target { fn __clone(this: *const c_void, new: *mut c_void); } } - __clone(this, new); + unsafe { __clone(this, new) } } #[doc(hidden)] unsafe fn __downgrade(shared: *const c_void, weak: *mut c_void) { @@ -143,7 +146,7 @@ macro_rules! impl_weak_ptr_target { fn __downgrade(shared: *const c_void, weak: *mut c_void); } } - __downgrade(shared, weak); + unsafe { __downgrade(shared, weak) } } #[doc(hidden)] unsafe fn __upgrade(weak: *const c_void, shared: *mut c_void) { @@ -153,7 +156,7 @@ macro_rules! impl_weak_ptr_target { fn __upgrade(weak: *const c_void, shared: *mut c_void); } } - __upgrade(weak, shared); + unsafe { __upgrade(weak, shared) } } #[doc(hidden)] unsafe fn __drop(this: *mut c_void) { @@ -163,7 +166,7 @@ macro_rules! impl_weak_ptr_target { fn __drop(this: *mut c_void); } } - __drop(this); + unsafe { __drop(this) } } } }; diff --git a/syntax/attrs.rs b/syntax/attrs.rs index 4808f2e1..fa6c8097 100644 --- a/syntax/attrs.rs +++ b/syntax/attrs.rs @@ -4,7 +4,7 @@ use crate::syntax::Atom::{self, *}; use crate::syntax::{Derive, Doc, ForeignName}; use proc_macro2::{Ident, TokenStream}; use quote::ToTokens; -use syn::parse::{ParseStream, Parser as _}; +use syn::parse::{Nothing, Parse, ParseStream, Parser as _}; use syn::{Attribute, Error, LitStr, Path, Result, Token}; // Intended usage: @@ -33,6 +33,7 @@ pub struct Parser<'a> { pub namespace: Option<&'a mut Namespace>, pub cxx_name: Option<&'a mut Option<ForeignName>>, pub rust_name: Option<&'a mut Option<Ident>>, + pub variants_from_header: Option<&'a mut Option<Attribute>>, // Suppress clippy needless_update lint ("struct update has no effect, all // the fields in the struct have already been specified") when preemptively @@ -121,6 +122,14 @@ pub fn parse(cx: &mut Errors, attrs: Vec<Attribute>, mut parser: Parser) -> Othe break; } } + } else if attr.path.is_ident("variants_from_header") && cfg!(feature = "experimental") { + if let Err(err) = Nothing::parse.parse2(attr.tokens.clone()) { + cx.push(err); + } + if let Some(variants_from_header) = &mut parser.variants_from_header { + **variants_from_header = Some(attr); + continue; + } } else if attr.path.is_ident("allow") || attr.path.is_ident("warn") || attr.path.is_ident("deny") @@ -224,7 +233,19 @@ impl OtherAttrs { impl ToTokens for OtherAttrs { fn to_tokens(&self, tokens: &mut TokenStream) { for attr in &self.0 { - attr.to_tokens(tokens); + let Attribute { + pound_token, + style, + bracket_token, + path, + tokens: attr_tokens, + } = attr; + pound_token.to_tokens(tokens); + let _ = style; // ignore; render outer and inner attrs both as outer + bracket_token.surround(tokens, |tokens| { + path.to_tokens(tokens); + attr_tokens.to_tokens(tokens); + }); } } } diff --git a/syntax/check.rs b/syntax/check.rs index bab2dec5..698782f9 100644 --- a/syntax/check.rs +++ b/syntax/check.rs @@ -2,24 +2,40 @@ use crate::syntax::atom::Atom::{self, *}; use crate::syntax::report::Errors; use crate::syntax::visit::{self, Visit}; use crate::syntax::{ - error, ident, trivial, Api, Array, Enum, ExternFn, ExternType, Impl, Lang, NamedType, Ptr, - Receiver, Ref, Signature, SliceRef, Struct, Trait, Ty1, Type, TypeAlias, Types, + error, ident, trivial, Api, Array, Enum, ExternFn, ExternType, Impl, Lang, Lifetimes, + NamedType, Ptr, Receiver, Ref, Signature, SliceRef, Struct, Trait, Ty1, Type, TypeAlias, Types, }; use proc_macro2::{Delimiter, Group, Ident, TokenStream}; use quote::{quote, ToTokens}; use std::fmt::Display; +use syn::{GenericParam, Generics, Lifetime}; pub(crate) struct Check<'a> { apis: &'a [Api], types: &'a Types<'a>, errors: &'a mut Errors, + generator: Generator, } -pub(crate) fn typecheck(cx: &mut Errors, apis: &[Api], types: &Types) { +pub(crate) enum Generator { + // cxx-build crate, cxxbridge cli, cxx-gen. + #[allow(dead_code)] + Build, + // cxxbridge-macro. This is relevant in that the macro output is going to + // get fed straight to rustc, so for errors that rustc already contains + // logic to catch (probably with a better diagnostic than what the proc + // macro API is able to produce), we avoid duplicating them in our own + // diagnostics. + #[allow(dead_code)] + Macro, +} + +pub(crate) fn typecheck(cx: &mut Errors, apis: &[Api], types: &Types, generator: Generator) { do_typecheck(&mut Check { apis, types, errors: cx, + generator, }); } @@ -250,7 +266,9 @@ fn check_type_ptr(cx: &mut Check, ty: &Ptr) { fn check_type_slice_ref(cx: &mut Check, ty: &SliceRef) { let supported = !is_unsized(cx, &ty.inner) || match &ty.inner { - Type::Ident(ident) => cx.types.rust.contains(&ident.rust), + Type::Ident(ident) => { + cx.types.rust.contains(&ident.rust) || cx.types.aliases.contains_key(&ident.rust) + } _ => false, }; @@ -294,6 +312,7 @@ fn check_type_fn(cx: &mut Check, ty: &Signature) { fn check_api_struct(cx: &mut Check, strct: &Struct) { let name = &strct.name; check_reserved_name(cx, &name.rust); + check_lifetimes(cx, &strct.generics); if strct.fields.is_empty() { let span = span_for_struct_error(strct); @@ -330,8 +349,9 @@ fn check_api_struct(cx: &mut Check, strct: &Struct) { fn check_api_enum(cx: &mut Check, enm: &Enum) { check_reserved_name(cx, &enm.name.rust); + check_lifetimes(cx, &enm.generics); - if enm.variants.is_empty() && !enm.explicit_repr { + if enm.variants.is_empty() && !enm.explicit_repr && !enm.variants_from_header { let span = span_for_enum_error(enm); cx.error( span, @@ -349,6 +369,7 @@ fn check_api_enum(cx: &mut Check, enm: &Enum) { fn check_api_type(cx: &mut Check, ety: &ExternType) { check_reserved_name(cx, &ety.name.rust); + check_lifetimes(cx, &ety.generics); for derive in &ety.derives { if derive.what == Trait::ExternType && ety.lang == Lang::Rust { @@ -400,6 +421,8 @@ fn check_api_fn(cx: &mut Check, efn: &ExternFn) { } } + check_generics(cx, &efn.sig.generics); + if let Some(receiver) = &efn.receiver { let ref span = span_for_receiver_error(receiver); @@ -472,6 +495,8 @@ fn check_api_fn(cx: &mut Check, efn: &ExternFn) { } fn check_api_type_alias(cx: &mut Check, alias: &TypeAlias) { + check_lifetimes(cx, &alias.generics); + for derive in &alias.derives { let msg = format!("derive({}) on extern type alias is not supported", derive); cx.error(derive, msg); @@ -481,6 +506,8 @@ fn check_api_type_alias(cx: &mut Check, alias: &TypeAlias) { fn check_api_impl(cx: &mut Check, imp: &Impl) { let ty = &imp.ty; + check_lifetimes(cx, &imp.impl_generics); + if let Some(negative) = imp.negative_token { let span = quote!(#negative #ty); cx.error(span, "negative impl is not supported yet"); @@ -583,6 +610,31 @@ fn check_reserved_name(cx: &mut Check, ident: &Ident) { } } +fn check_reserved_lifetime(cx: &mut Check, lifetime: &Lifetime) { + if lifetime.ident == "static" { + match cx.generator { + Generator::Macro => { /* rustc already reports this */ } + Generator::Build => { + cx.error(lifetime, error::RESERVED_LIFETIME); + } + } + } +} + +fn check_lifetimes(cx: &mut Check, generics: &Lifetimes) { + for lifetime in &generics.lifetimes { + check_reserved_lifetime(cx, lifetime); + } +} + +fn check_generics(cx: &mut Check, generics: &Generics) { + for generic_param in &generics.params { + if let GenericParam::Lifetime(def) = generic_param { + check_reserved_lifetime(cx, &def.lifetime); + } + } +} + fn is_unsized(cx: &mut Check, ty: &Type) -> bool { match ty { Type::Ident(ident) => { diff --git a/syntax/discriminant.rs b/syntax/discriminant.rs index 80f7c0be..fff8f758 100644 --- a/syntax/discriminant.rs +++ b/syntax/discriminant.rs @@ -150,7 +150,7 @@ fn insert(set: &mut DiscriminantSet, discriminant: Discriminant) -> Result<Discr } impl Discriminant { - const fn zero() -> Self { + pub const fn zero() -> Self { Discriminant { sign: Sign::Positive, magnitude: 0, @@ -179,6 +179,28 @@ impl Discriminant { magnitude: i.wrapping_abs() as u64, } } + + pub const fn checked_succ(self) -> Option<Self> { + match self.sign { + Sign::Negative => { + if self.magnitude == 1 { + Some(Discriminant::zero()) + } else { + Some(Discriminant { + sign: Sign::Negative, + magnitude: self.magnitude - 1, + }) + } + } + Sign::Positive => match self.magnitude.checked_add(1) { + Some(magnitude) => Some(Discriminant { + sign: Sign::Positive, + magnitude, + }), + None => None, + }, + } + } } impl Display for Discriminant { @@ -186,7 +208,7 @@ impl Display for Discriminant { if self.sign == Sign::Negative { f.write_str("-")?; } - Display::fmt(&self.magnitude, f) + write!(f, "{}", self.magnitude) } } diff --git a/syntax/doc.rs b/syntax/doc.rs index 60bb4da1..cd764fac 100644 --- a/syntax/doc.rs +++ b/syntax/doc.rs @@ -17,6 +17,10 @@ impl Doc { self.fragments.push(lit); } + pub fn is_empty(&self) -> bool { + self.fragments.is_empty() + } + pub fn to_string(&self) -> String { let mut doc = String::new(); for lit in &self.fragments { diff --git a/syntax/error.rs b/syntax/error.rs index a6723293..f40c4a8e 100644 --- a/syntax/error.rs +++ b/syntax/error.rs @@ -21,6 +21,7 @@ pub static ERRORS: &[Error] = &[ DISCRIMINANT_OVERFLOW, DOT_INCLUDE, DOUBLE_UNDERSCORE, + RESERVED_LIFETIME, RUST_TYPE_BY_VALUE, UNSUPPORTED_TYPE, USE_NOT_ALLOWED, @@ -68,6 +69,12 @@ pub static DOUBLE_UNDERSCORE: Error = Error { note: Some("identifiers containing double underscore are reserved in C++"), }; +pub static RESERVED_LIFETIME: Error = Error { + msg: "invalid lifetime parameter name: `'static`", + label: Some("'static is a reserved lifetime name"), + note: None, +}; + pub static RUST_TYPE_BY_VALUE: Error = Error { msg: "opaque Rust type by value is not supported", label: None, diff --git a/syntax/file.rs b/syntax/file.rs index fd6dd98b..99466b8e 100644 --- a/syntax/file.rs +++ b/syntax/file.rs @@ -91,17 +91,32 @@ impl Parse for Item { let item = input.parse()?; match item { - RustItem::Struct(item) => Ok(Item::Struct(ItemStruct { attrs, ..item })), - RustItem::Enum(item) => Ok(Item::Enum(ItemEnum { attrs, ..item })), - RustItem::ForeignMod(item) => Ok(Item::ForeignMod(ItemForeignMod { - attrs, - unsafety, - abi: item.abi, - brace_token: item.brace_token, - items: item.items, - })), - RustItem::Impl(item) => Ok(Item::Impl(ItemImpl { attrs, ..item })), - RustItem::Use(item) => Ok(Item::Use(ItemUse { attrs, ..item })), + RustItem::Struct(mut item) => { + item.attrs.splice(..0, attrs); + Ok(Item::Struct(item)) + } + RustItem::Enum(mut item) => { + item.attrs.splice(..0, attrs); + Ok(Item::Enum(item)) + } + RustItem::ForeignMod(mut item) => { + item.attrs.splice(..0, attrs); + Ok(Item::ForeignMod(ItemForeignMod { + attrs: item.attrs, + unsafety, + abi: item.abi, + brace_token: item.brace_token, + items: item.items, + })) + } + RustItem::Impl(mut item) => { + item.attrs.splice(..0, attrs); + Ok(Item::Impl(item)) + } + RustItem::Use(mut item) => { + item.attrs.splice(..0, attrs); + Ok(Item::Use(item)) + } other => Ok(Item::Other(other)), } } diff --git a/syntax/impls.rs b/syntax/impls.rs index 8b0743b6..06d68dc2 100644 --- a/syntax/impls.rs +++ b/syntax/impls.rs @@ -339,6 +339,7 @@ impl PartialEq for Signature { attrs: _, visibility: _, name: _, + colon_token: _, ty, } = arg; let Var { @@ -346,6 +347,7 @@ impl PartialEq for Signature { attrs: _, visibility: _, name: _, + colon_token: _, ty: ty2, } = arg2; ty == ty2 @@ -374,6 +376,7 @@ impl Hash for Signature { attrs: _, visibility: _, name: _, + colon_token: _, ty, } = arg; ty.hash(state); @@ -393,6 +396,7 @@ impl PartialEq for Receiver { lifetime, mutable, var: _, + colon_token: _, ty, shorthand: _, pin_tokens: _, @@ -404,6 +408,7 @@ impl PartialEq for Receiver { lifetime: lifetime2, mutable: mutable2, var: _, + colon_token: _, ty: ty2, shorthand: _, pin_tokens: _, @@ -421,6 +426,7 @@ impl Hash for Receiver { lifetime, mutable, var: _, + colon_token: _, ty, shorthand: _, pin_tokens: _, diff --git a/syntax/mod.rs b/syntax/mod.rs index 3d562931..1d986345 100644 --- a/syntax/mod.rs +++ b/syntax/mod.rs @@ -30,17 +30,17 @@ pub mod types; mod visit; use self::attrs::OtherAttrs; -use self::discriminant::Discriminant; use self::namespace::Namespace; use self::parse::kw; use self::symbol::Symbol; use proc_macro2::{Ident, Span}; use syn::punctuated::Punctuated; use syn::token::{Brace, Bracket, Paren}; -use syn::{Expr, Generics, Lifetime, LitInt, Token, Type as RustType}; +use syn::{Attribute, Expr, Generics, Lifetime, LitInt, Path, Token, Type as RustType}; pub use self::atom::Atom; pub use self::derive::{Derive, Trait}; +pub use self::discriminant::Discriminant; pub use self::doc::Doc; pub use self::names::ForeignName; pub use self::parse::parse_items; @@ -111,11 +111,17 @@ pub struct Enum { pub generics: Lifetimes, pub brace_token: Brace, pub variants: Vec<Variant>, - pub repr: Atom, - pub repr_type: Type, + pub variants_from_header: bool, + pub variants_from_header_attr: Option<Attribute>, + pub repr: EnumRepr, pub explicit_repr: bool, } +pub enum EnumRepr { + Native { atom: Atom, repr_type: Type }, + Foreign { rust_type: Path }, +} + pub struct ExternFn { pub lang: Lang, pub doc: Doc, @@ -174,6 +180,7 @@ pub struct Var { pub attrs: OtherAttrs, pub visibility: Token![pub], pub name: Pair, + pub colon_token: Token![:], pub ty: Type, } @@ -184,6 +191,7 @@ pub struct Receiver { pub mutable: bool, pub var: Token![self], pub ty: NamedType, + pub colon_token: Token![:], pub shorthand: bool, pub pin_tokens: Option<(kw::Pin, Token![<], Token![>])>, pub mutability: Option<Token![mut]>, diff --git a/syntax/names.rs b/syntax/names.rs index cb6ace53..7a125ae7 100644 --- a/syntax/names.rs +++ b/syntax/names.rs @@ -9,7 +9,6 @@ use syn::punctuated::Punctuated; #[derive(Clone)] pub struct ForeignName { text: String, - span: Span, } impl Pair { @@ -56,7 +55,7 @@ impl ForeignName { match syn::parse_str::<Ident>(text) { Ok(ident) => { let text = ident.to_string(); - Ok(ForeignName { text, span }) + Ok(ForeignName { text }) } Err(err) => Err(Error::new(span, err)), } @@ -68,3 +67,9 @@ impl Display for ForeignName { formatter.write_str(&self.text) } } + +impl PartialEq<str> for ForeignName { + fn eq(&self, rhs: &str) -> bool { + self.text == rhs + } +} diff --git a/syntax/parse.rs b/syntax/parse.rs index d7920761..32d36eb3 100644 --- a/syntax/parse.rs +++ b/syntax/parse.rs @@ -4,9 +4,9 @@ use crate::syntax::file::{Item, ItemForeignMod}; use crate::syntax::report::Errors; use crate::syntax::Atom::*; use crate::syntax::{ - attrs, error, Api, Array, Derive, Doc, Enum, ExternFn, ExternType, ForeignName, Impl, Include, - IncludeKind, Lang, Lifetimes, NamedType, Namespace, Pair, Ptr, Receiver, Ref, Signature, - SliceRef, Struct, Ty1, Type, TypeAlias, Var, Variant, + attrs, error, Api, Array, Derive, Doc, Enum, EnumRepr, ExternFn, ExternType, ForeignName, Impl, + Include, IncludeKind, Lang, Lifetimes, NamedType, Namespace, Pair, Ptr, Receiver, Ref, + Signature, SliceRef, Struct, Ty1, Type, TypeAlias, Var, Variant, }; use proc_macro2::{Delimiter, Group, Span, TokenStream, TokenTree}; use quote::{format_ident, quote, quote_spanned}; @@ -146,11 +146,13 @@ fn parse_struct(cx: &mut Errors, mut item: ItemStruct, namespace: &Namespace) -> }; let visibility = visibility_pub(&field.vis, ident.span()); let name = pair(Namespace::default(), &ident, cxx_name, rust_name); + let colon_token = field.colon_token.unwrap(); fields.push(Var { doc, attrs, visibility, name, + colon_token, ty, }); } @@ -185,6 +187,7 @@ fn parse_enum(cx: &mut Errors, item: ItemEnum, namespace: &Namespace) -> Api { let mut namespace = namespace.clone(); let mut cxx_name = None; let mut rust_name = None; + let mut variants_from_header = None; let attrs = attrs::parse( cx, item.attrs, @@ -195,6 +198,7 @@ fn parse_enum(cx: &mut Errors, item: ItemEnum, namespace: &Namespace) -> Api { namespace: Some(&mut namespace), cxx_name: Some(&mut cxx_name), rust_name: Some(&mut rust_name), + variants_from_header: Some(&mut variants_from_header), ..Default::default() }, ); @@ -237,11 +241,17 @@ fn parse_enum(cx: &mut Errors, item: ItemEnum, namespace: &Namespace) -> Api { let name = pair(namespace, &item.ident, cxx_name, rust_name); let repr_ident = Ident::new(repr.as_ref(), Span::call_site()); let repr_type = Type::Ident(NamedType::new(repr_ident)); + let repr = EnumRepr::Native { + atom: repr, + repr_type, + }; let generics = Lifetimes { lt_token: None, lifetimes: Punctuated::new(), gt_token: None, }; + let variants_from_header_attr = variants_from_header; + let variants_from_header = variants_from_header_attr.is_some(); Api::Enum(Enum { doc, @@ -253,8 +263,9 @@ fn parse_enum(cx: &mut Errors, item: ItemEnum, namespace: &Namespace) -> Api { generics, brace_token, variants, + variants_from_header, + variants_from_header_attr, repr, - repr_type, explicit_repr, }) } @@ -560,6 +571,7 @@ fn parse_extern_fn( lifetime: lifetime.clone(), mutable: arg.mutability.is_some(), var: arg.self_token, + colon_token: Token![:](arg.self_token.span), ty: NamedType::new(Ident::new("Self", arg.self_token.span)), shorthand: true, pin_tokens: None, @@ -583,11 +595,13 @@ fn parse_extern_fn( let attrs = OtherAttrs::none(); let visibility = Token![pub](ident.span()); let name = pair(Namespace::default(), &ident, None, None); + let colon_token = arg.colon_token; args.push_value(Var { doc, attrs, visibility, name, + colon_token, ty, }); if let Some(comma) = comma { @@ -603,6 +617,7 @@ fn parse_extern_fn( lifetime: reference.lifetime, mutable: reference.mutable, var: Token![self](ident.rust.span()), + colon_token: arg.colon_token, ty: ident, shorthand: false, pin_tokens: reference.pin_tokens, @@ -1272,11 +1287,16 @@ fn parse_type_fn(ty: &TypeBareFn) -> Result<Type> { .iter() .enumerate() .map(|(i, arg)| { - let ty = parse_type(&arg.ty)?; - let ident = match &arg.name { - Some(ident) => ident.0.clone(), - None => format_ident!("arg{}", i), + let (ident, colon_token) = match &arg.name { + Some((ident, colon_token)) => (ident.clone(), *colon_token), + None => { + let fn_span = ty.paren_token.span; + let ident = format_ident!("arg{}", i, span = fn_span); + let colon_token = Token![:](fn_span); + (ident, colon_token) + } }; + let ty = parse_type(&arg.ty)?; let doc = Doc::new(); let attrs = OtherAttrs::none(); let visibility = Token![pub](ident.span()); @@ -1286,6 +1306,7 @@ fn parse_type_fn(ty: &TypeBareFn) -> Result<Type> { attrs, visibility, name, + colon_token, ty, }) }) diff --git a/syntax/tokens.rs b/syntax/tokens.rs index c1a06a21..33f20fa3 100644 --- a/syntax/tokens.rs +++ b/syntax/tokens.rs @@ -1,7 +1,7 @@ use crate::syntax::atom::Atom::*; use crate::syntax::{ - Array, Atom, Derive, Enum, ExternFn, ExternType, Impl, Lifetimes, NamedType, Ptr, Receiver, - Ref, Signature, SliceRef, Struct, Ty1, Type, TypeAlias, Var, + Array, Atom, Derive, Enum, EnumRepr, ExternFn, ExternType, Impl, Lifetimes, NamedType, Ptr, + Receiver, Ref, Signature, SliceRef, Struct, Ty1, Type, TypeAlias, Var, }; use proc_macro2::{Ident, Span, TokenStream}; use quote::{quote_spanned, ToTokens}; @@ -43,6 +43,7 @@ impl ToTokens for Var { attrs: _, visibility: _, name, + colon_token: _, ty, } = self; name.rust.to_tokens(tokens); @@ -279,6 +280,15 @@ impl ToTokens for Signature { } } +impl ToTokens for EnumRepr { + fn to_tokens(&self, tokens: &mut TokenStream) { + match self { + EnumRepr::Native { atom, repr_type: _ } => atom.to_tokens(tokens), + EnumRepr::Foreign { rust_type } => rust_type.to_tokens(tokens), + } + } +} + impl ToTokens for NamedType { fn to_tokens(&self, tokens: &mut TokenStream) { let NamedType { rust, generics } = self; @@ -310,6 +320,7 @@ impl ToTokens for ReceiverType<'_> { lifetime, mutable: _, var: _, + colon_token: _, ty, shorthand: _, pin_tokens, @@ -337,6 +348,7 @@ impl ToTokens for ReceiverTypeSelf<'_> { lifetime, mutable: _, var: _, + colon_token: _, ty, shorthand: _, pin_tokens, diff --git a/syntax/trivial.rs b/syntax/trivial.rs index fe95e2b7..067e2d75 100644 --- a/syntax/trivial.rs +++ b/syntax/trivial.rs @@ -11,6 +11,7 @@ pub enum TrivialReason<'a> { FunctionReturn(&'a ExternFn), BoxTarget, VecElement, + SliceElement { mutable: bool }, UnpinnedMut(&'a ExternFn), } @@ -105,6 +106,14 @@ pub fn required_trivial_reasons<'a>( insist_extern_types_are_trivial(ident, reason); } } + Type::SliceRef(ty) => { + if let Type::Ident(ident) = &ty.inner { + let reason = TrivialReason::SliceElement { + mutable: ty.mutable, + }; + insist_extern_types_are_trivial(ident, reason); + } + } _ => {} } } @@ -128,6 +137,8 @@ pub fn as_what<'a>(name: &'a Pair, reasons: &'a [TrivialReason]) -> impl Display let mut return_of = Set::new(); let mut box_target = false; let mut vec_element = false; + let mut slice_shared_element = false; + let mut slice_mut_element = false; let mut unpinned_mut = Set::new(); for reason in self.reasons { @@ -143,6 +154,13 @@ pub fn as_what<'a>(name: &'a Pair, reasons: &'a [TrivialReason]) -> impl Display } TrivialReason::BoxTarget => box_target = true, TrivialReason::VecElement => vec_element = true, + TrivialReason::SliceElement { mutable } => { + if *mutable { + slice_mut_element = true; + } else { + slice_shared_element = true; + } + } TrivialReason::UnpinnedMut(efn) => { unpinned_mut.insert(&efn.name.rust); } @@ -185,6 +203,15 @@ pub fn as_what<'a>(name: &'a Pair, reasons: &'a [TrivialReason]) -> impl Display param: self.name, }); } + if slice_shared_element || slice_mut_element { + clauses.push(Clause::Slice { + article: "a", + desc: "slice element in", + shared: slice_shared_element, + mutable: slice_mut_element, + param: self.name, + }); + } if !unpinned_mut.is_empty() { clauses.push(Clause::Set { article: "a", @@ -219,12 +246,21 @@ pub fn as_what<'a>(name: &'a Pair, reasons: &'a [TrivialReason]) -> impl Display desc: &'a str, param: &'a Pair, }, + Slice { + article: &'a str, + desc: &'a str, + shared: bool, + mutable: bool, + param: &'a Pair, + }, } impl<'a> Clause<'a> { fn article(&self) -> &'a str { match self { - Clause::Set { article, .. } | Clause::Ty1 { article, .. } => article, + Clause::Set { article, .. } + | Clause::Ty1 { article, .. } + | Clause::Slice { article, .. } => article, } } @@ -249,6 +285,25 @@ pub fn as_what<'a>(name: &'a Pair, reasons: &'a [TrivialReason]) -> impl Display desc, param, } => write!(f, "{}<{}>", desc, param.rust), + Clause::Slice { + article: _, + desc, + shared, + mutable, + param, + } => { + write!(f, "{} ", desc)?; + if *shared { + write!(f, "&[{}]", param.rust)?; + } + if *shared && *mutable { + write!(f, " and ")?; + } + if *mutable { + write!(f, "&mut [{}]", param.rust)?; + } + Ok(()) + } } } } diff --git a/syntax/types.rs b/syntax/types.rs index af7916d7..c54682bd 100644 --- a/syntax/types.rs +++ b/syntax/types.rs @@ -7,7 +7,7 @@ use crate::syntax::set::{OrderedSet, UnorderedSet}; use crate::syntax::trivial::{self, TrivialReason}; use crate::syntax::visit::{self, Visit}; use crate::syntax::{ - toposort, Api, Atom, Enum, ExternType, Impl, Lifetimes, Pair, Struct, Type, TypeAlias, + toposort, Api, Atom, Enum, EnumRepr, ExternType, Impl, Lifetimes, Pair, Struct, Type, TypeAlias, }; use proc_macro2::Ident; use quote::ToTokens; @@ -88,7 +88,12 @@ impl<'a> Types<'a> { add_resolution(&strct.name, &strct.generics); } Api::Enum(enm) => { - all.insert(&enm.repr_type); + match &enm.repr { + EnumRepr::Native { atom: _, repr_type } => { + all.insert(repr_type); + } + EnumRepr::Foreign { rust_type: _ } => {} + } let ident = &enm.name.rust; if !type_names.insert(ident) && (!cxx.contains(ident) @@ -101,6 +106,11 @@ impl<'a> Types<'a> { duplicate_name(cx, enm, ident); } enums.insert(ident, enm); + if enm.variants_from_header { + // #![variants_from_header] enums are implicitly extern + // C++ type. + cxx.insert(&enm.name.rust); + } add_resolution(&enm.name, &enm.generics); } Api::CxxType(ety) => { @@ -250,6 +260,14 @@ impl<'a> Types<'a> { ImproperCtype::Depends(ident) => self.struct_improper_ctypes.contains(ident), } } + + // Types which we need to assume could possibly exist by value on the Rust + // side. + pub fn is_maybe_trivial(&self, ty: &Ident) -> bool { + self.structs.contains_key(ty) + || self.enums.contains_key(ty) + || self.aliases.contains_key(ty) + } } impl<'t, 'a> IntoIterator for &'t Types<'a> { diff --git a/tests/BUILD b/tests/BUILD index 7886e4fa..d4af3aff 100644 --- a/tests/BUILD +++ b/tests/BUILD @@ -1,5 +1,5 @@ load("@rules_cc//cc:defs.bzl", "cc_library") -load("//tools/bazel:rust.bzl", "rust_library", "rust_test") +load("@rules_rust//rust:rust.bzl", "rust_library", "rust_test") load("//tools/bazel:rust_cxx_bridge.bzl", "rust_cxx_bridge") rust_test( diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs index fcbe1530..25b4ccdc 100644 --- a/tests/ffi/lib.rs +++ b/tests/ffi/lib.rs @@ -157,6 +157,10 @@ pub mod ffi { fn c_take_ref_rust_vec_copy(v: &Vec<u8>); fn c_take_ref_shared_string(s: &SharedString) -> &SharedString; fn c_take_callback(callback: fn(String) -> usize); + fn c_take_callback_ref(callback: fn(&String)); + #[cxx_name = "c_take_callback_ref"] + fn c_take_callback_ref_lifetime<'a>(callback: fn(&'a String)); + fn c_take_callback_mut(callback: fn(&mut String)); fn c_take_enum(e: Enum); fn c_take_ns_enum(e: AEnum); fn c_take_nested_ns_enum(e: ABEnum); @@ -322,6 +326,7 @@ pub mod ffi { } impl Box<Shared> {} + impl CxxVector<SharedString> {} } mod other { diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc index ba12ed45..df7ded07 100644 --- a/tests/ffi/tests.cc +++ b/tests/ffi/tests.cc @@ -336,13 +336,13 @@ void c_take_unique_ptr_string(std::unique_ptr<std::string> s) { } void c_take_unique_ptr_vector_u8(std::unique_ptr<std::vector<uint8_t>> v) { - if (v->size() == 4) { + if (v->size() == 3) { cxx_test_suite_set_correct(); } } void c_take_unique_ptr_vector_f64(std::unique_ptr<std::vector<double>> v) { - if (v->size() == 4) { + if (v->size() == 5) { cxx_test_suite_set_correct(); } } @@ -354,7 +354,7 @@ void c_take_unique_ptr_vector_string( } void c_take_unique_ptr_vector_shared(std::unique_ptr<std::vector<Shared>> v) { - if (v->size() == 2) { + if (v->size() == 3) { cxx_test_suite_set_correct(); } } @@ -494,6 +494,16 @@ void c_take_callback(rust::Fn<size_t(rust::String)> callback) { callback("2020"); } +void c_take_callback_ref(rust::Fn<void(const rust::String &)> callback) { + const rust::String string = "2020"; + callback(string); +} + +void c_take_callback_mut(rust::Fn<void(rust::String &)> callback) { + rust::String string = "2020"; + callback(string); +} + void c_take_enum(Enum e) { if (e == Enum::AVal) { cxx_test_suite_set_correct(); @@ -825,12 +835,24 @@ extern "C" const char *cxx_run_test() noexcept { ASSERT(cstring == "foo"); ASSERT(other_cstring == "test"); + ASSERT(cstring.capacity() == 3); + cstring.reserve(2); + ASSERT(cstring.capacity() == 3); + cstring.reserve(5); + ASSERT(cstring.capacity() >= 5); + rust::Str cstr = "test"; rust::Str other_cstr = "foo"; swap(cstr, other_cstr); ASSERT(cstr == "foo"); ASSERT(other_cstr == "test"); + const char *utf8_literal = u8"Test string"; + const char16_t *utf16_literal = u"Test string"; + rust::String utf8_rstring = utf8_literal; + rust::String utf16_rstring = utf16_literal; + ASSERT(utf8_rstring == utf16_rstring); + rust::Vec<int> vec1{1, 2}; rust::Vec<int> vec2{3, 4}; swap(vec1, vec2); diff --git a/tests/ffi/tests.h b/tests/ffi/tests.h index 74acbf4f..b624d393 100644 --- a/tests/ffi/tests.h +++ b/tests/ffi/tests.h @@ -160,6 +160,8 @@ void c_take_ref_rust_vec_index(const rust::Vec<uint8_t> &v); void c_take_ref_rust_vec_copy(const rust::Vec<uint8_t> &v); const SharedString &c_take_ref_shared_string(const SharedString &s); void c_take_callback(rust::Fn<size_t(rust::String)> callback); +void c_take_callback_ref(rust::Fn<void(const rust::String &)> callback); +void c_take_callback_mut(rust::Fn<void(rust::String &)> callback); void c_take_enum(Enum e); void c_take_ns_enum(::A::AEnum e); void c_take_nested_ns_enum(::A::B::ABEnum e); diff --git a/tests/test.rs b/tests/test.rs index 32b8e832..1f0b1660 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -150,15 +150,15 @@ fn test_c_take() { check!(ffi::c_take_unique_ptr_string( ffi::c_return_unique_ptr_string() )); - check!(ffi::c_take_unique_ptr_vector_u8( - ffi::c_return_unique_ptr_vector_u8() - )); - check!(ffi::c_take_unique_ptr_vector_f64( - ffi::c_return_unique_ptr_vector_f64() - )); - check!(ffi::c_take_unique_ptr_vector_shared( - ffi::c_return_unique_ptr_vector_shared() - )); + let mut vector = ffi::c_return_unique_ptr_vector_u8(); + assert_eq!(vector.pin_mut().pop(), Some(9)); + check!(ffi::c_take_unique_ptr_vector_u8(vector)); + let mut vector = ffi::c_return_unique_ptr_vector_f64(); + vector.pin_mut().push(9.0); + check!(ffi::c_take_unique_ptr_vector_f64(vector)); + let mut vector = ffi::c_return_unique_ptr_vector_shared(); + vector.pin_mut().push(ffi::Shared { z: 9 }); + check!(ffi::c_take_unique_ptr_vector_shared(vector)); check!(ffi::c_take_ref_vector(&ffi::c_return_unique_ptr_vector_u8())); let test_vec = [86_u8, 75_u8, 30_u8, 9_u8].to_vec(); check!(ffi::c_take_rust_vec(test_vec.clone())); @@ -204,7 +204,23 @@ fn test_c_callback() { 0 } + #[allow(clippy::ptr_arg)] + fn callback_ref(s: &String) { + if s == "2020" { + cxx_test_suite_set_correct(); + } + } + + fn callback_mut(s: &mut String) { + if s == "2020" { + cxx_test_suite_set_correct(); + } + } + check!(ffi::c_take_callback(callback)); + check!(ffi::c_take_callback_ref(callback_ref)); + check!(ffi::c_take_callback_ref_lifetime(callback_ref)); + check!(ffi::c_take_callback_mut(callback_mut)); } #[test] diff --git a/tests/ui/array_len_suffix.stderr b/tests/ui/array_len_suffix.stderr index b72fc022..143bcb04 100644 --- a/tests/ui/array_len_suffix.stderr +++ b/tests/ui/array_len_suffix.stderr @@ -7,4 +7,4 @@ error[E0308]: mismatched types help: change the type of the numeric literal from `u16` to `usize` | 4 | fn array() -> [String; 12usize]; - | ^^^^^^^ + | ~~~~~ diff --git a/tests/ui/derive_nonclone.rs b/tests/ui/derive_nonclone.rs deleted file mode 100644 index 58110289..00000000 --- a/tests/ui/derive_nonclone.rs +++ /dev/null @@ -1,13 +0,0 @@ -#[cxx::bridge] -mod ffi { - #[derive(Clone)] - struct TryClone { - other: Other, - } - - struct Other { - x: usize, - } -} - -fn main() {} diff --git a/tests/ui/derive_nonclone.stderr b/tests/ui/derive_nonclone.stderr deleted file mode 100644 index 04eae4fd..00000000 --- a/tests/ui/derive_nonclone.stderr +++ /dev/null @@ -1,7 +0,0 @@ -error[E0277]: the trait bound `ffi::Other: Clone` is not satisfied - --> $DIR/derive_nonclone.rs:5:9 - | -5 | other: Other, - | ^^^^^^^^^^^^ the trait `Clone` is not implemented for `ffi::Other` - | - = note: required by `clone` diff --git a/tests/ui/expected_named.stderr b/tests/ui/expected_named.stderr index 46764014..dab3b5a3 100644 --- a/tests/ui/expected_named.stderr +++ b/tests/ui/expected_named.stderr @@ -8,4 +8,4 @@ error[E0106]: missing lifetime specifier help: consider using the `'static` lifetime | 5 | fn borrowed() -> UniquePtr<Borrowed<'static>>; - | ^^^^^^^^^^^^^^^^^ + | ~~~~~~~~~~~~~~~~~ diff --git a/tests/ui/opaque_autotraits.stderr b/tests/ui/opaque_autotraits.stderr index 4f469eff..15a2b64c 100644 --- a/tests/ui/opaque_autotraits.stderr +++ b/tests/ui/opaque_autotraits.stderr @@ -1,40 +1,59 @@ -error[E0277]: `*const u8` cannot be sent between threads safely +error[E0277]: `*const cxx::void` cannot be sent between threads safely --> $DIR/opaque_autotraits.rs:13:5 | -8 | fn assert_send<T: Send>() {} - | ---- required by this bound in `assert_send` -... 13 | assert_send::<ffi::Opaque>(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ `*const u8` cannot be sent between threads safely + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ `*const cxx::void` cannot be sent between threads safely | - = help: within `ffi::Opaque`, the trait `Send` is not implemented for `*const u8` - = note: required because it appears within the type `[*const u8; 0]` + = help: within `ffi::Opaque`, the trait `Send` is not implemented for `*const cxx::void` + = note: required because it appears within the type `[*const cxx::void; 0]` = note: required because it appears within the type `cxx::private::Opaque` - = note: required because it appears within the type `ffi::Opaque` +note: required because it appears within the type `ffi::Opaque` + --> $DIR/opaque_autotraits.rs:4:14 + | +4 | type Opaque; + | ^^^^^^ +note: required by a bound in `assert_send` + --> $DIR/opaque_autotraits.rs:8:19 + | +8 | fn assert_send<T: Send>() {} + | ^^^^ required by this bound in `assert_send` -error[E0277]: `*const u8` cannot be shared between threads safely +error[E0277]: `*const cxx::void` cannot be shared between threads safely --> $DIR/opaque_autotraits.rs:14:5 | -9 | fn assert_sync<T: Sync>() {} - | ---- required by this bound in `assert_sync` -... 14 | assert_sync::<ffi::Opaque>(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ `*const u8` cannot be shared between threads safely + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ `*const cxx::void` cannot be shared between threads safely | - = help: within `ffi::Opaque`, the trait `Sync` is not implemented for `*const u8` - = note: required because it appears within the type `[*const u8; 0]` + = help: within `ffi::Opaque`, the trait `Sync` is not implemented for `*const cxx::void` + = note: required because it appears within the type `[*const cxx::void; 0]` = note: required because it appears within the type `cxx::private::Opaque` - = note: required because it appears within the type `ffi::Opaque` +note: required because it appears within the type `ffi::Opaque` + --> $DIR/opaque_autotraits.rs:4:14 + | +4 | type Opaque; + | ^^^^^^ +note: required by a bound in `assert_sync` + --> $DIR/opaque_autotraits.rs:9:19 + | +9 | fn assert_sync<T: Sync>() {} + | ^^^^ required by this bound in `assert_sync` error[E0277]: `PhantomPinned` cannot be unpinned --> $DIR/opaque_autotraits.rs:15:5 | -10 | fn assert_unpin<T: Unpin>() {} - | ----- required by this bound in `assert_unpin` -... 15 | assert_unpin::<ffi::Opaque>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ within `ffi::Opaque`, the trait `Unpin` is not implemented for `PhantomPinned` | + = note: consider using `Box::pin` = note: required because it appears within the type `PhantomData<PhantomPinned>` = note: required because it appears within the type `cxx::private::Opaque` - = note: required because it appears within the type `ffi::Opaque` +note: required because it appears within the type `ffi::Opaque` + --> $DIR/opaque_autotraits.rs:4:14 + | +4 | type Opaque; + | ^^^^^^ +note: required by a bound in `assert_unpin` + --> $DIR/opaque_autotraits.rs:10:20 + | +10 | fn assert_unpin<T: Unpin>() {} + | ^^^^^ required by this bound in `assert_unpin` diff --git a/tests/ui/opaque_not_sized.stderr b/tests/ui/opaque_not_sized.stderr index 9818e444..b50a2699 100644 --- a/tests/ui/opaque_not_sized.stderr +++ b/tests/ui/opaque_not_sized.stderr @@ -2,10 +2,16 @@ error[E0277]: the size for values of type `str` cannot be known at compilation t --> $DIR/opaque_not_sized.rs:4:14 | 4 | type TypeR; - | -----^^^^^- - | | | - | | doesn't have a size known at compile-time - | required by this bound in `__AssertSized` + | ^^^^^ doesn't have a size known at compile-time | = help: within `TypeR`, the trait `Sized` is not implemented for `str` - = note: required because it appears within the type `TypeR` +note: required because it appears within the type `TypeR` + --> $DIR/opaque_not_sized.rs:8:8 + | +8 | struct TypeR(str); + | ^^^^^ +note: required by a bound in `__AssertSized` + --> $DIR/opaque_not_sized.rs:4:9 + | +4 | type TypeR; + | ^^^^^^^^^^^ required by this bound in `__AssertSized` diff --git a/tests/ui/reserved_lifetime.rs b/tests/ui/reserved_lifetime.rs new file mode 100644 index 00000000..179a4db5 --- /dev/null +++ b/tests/ui/reserved_lifetime.rs @@ -0,0 +1,10 @@ +#[cxx::bridge] +mod ffi { + unsafe extern "C++" { + type Logger; + + fn logger<'static>() -> Pin<&'static Logger>; + } +} + +fn main() {} diff --git a/tests/ui/reserved_lifetime.stderr b/tests/ui/reserved_lifetime.stderr new file mode 100644 index 00000000..723572d9 --- /dev/null +++ b/tests/ui/reserved_lifetime.stderr @@ -0,0 +1,5 @@ +error[E0262]: invalid lifetime parameter name: `'static` + --> $DIR/reserved_lifetime.rs:6:19 + | +6 | fn logger<'static>() -> Pin<&'static Logger>; + | ^^^^^^^ 'static is a reserved lifetime name diff --git a/tests/ui/rust_pinned.stderr b/tests/ui/rust_pinned.stderr index f16f9d55..8857681c 100644 --- a/tests/ui/rust_pinned.stderr +++ b/tests/ui/rust_pinned.stderr @@ -1,10 +1,17 @@ error[E0277]: `PhantomPinned` cannot be unpinned - --> $DIR/rust_pinned.rs:6:14 - | -6 | type Pinned; - | -----^^^^^^- - | | | - | | within `Pinned`, the trait `Unpin` is not implemented for `PhantomPinned` - | required by this bound in `__AssertUnpin` - | - = note: required because it appears within the type `Pinned` + --> $DIR/rust_pinned.rs:6:14 + | +6 | type Pinned; + | ^^^^^^ within `Pinned`, the trait `Unpin` is not implemented for `PhantomPinned` + | + = note: consider using `Box::pin` +note: required because it appears within the type `Pinned` + --> $DIR/rust_pinned.rs:10:12 + | +10 | pub struct Pinned { + | ^^^^^^ +note: required by a bound in `__AssertUnpin` + --> $DIR/rust_pinned.rs:6:9 + | +6 | type Pinned; + | ^^^^^^^^^^^^ required by this bound in `__AssertUnpin` diff --git a/tests/ui/slice_of_type_alias.rs b/tests/ui/slice_of_type_alias.rs new file mode 100644 index 00000000..a7bbc114 --- /dev/null +++ b/tests/ui/slice_of_type_alias.rs @@ -0,0 +1,30 @@ +use cxx::{type_id, ExternType}; + +#[repr(C)] +struct ElementTrivial(usize); + +#[repr(C)] +struct ElementOpaque(usize); + +#[cxx::bridge] +mod ffi { + unsafe extern "C++" { + type ElementTrivial = crate::ElementTrivial; + type ElementOpaque = crate::ElementOpaque; + + fn f(slice: &mut [ElementTrivial]); + fn g(slice: &[ElementOpaque]); + } +} + +unsafe impl ExternType for ElementTrivial { + type Id = type_id!("ElementTrivial"); + type Kind = cxx::kind::Trivial; +} + +unsafe impl ExternType for ElementOpaque { + type Id = type_id!("ElementOpaque"); + type Kind = cxx::kind::Opaque; +} + +fn main() {} diff --git a/tests/ui/slice_of_type_alias.stderr b/tests/ui/slice_of_type_alias.stderr new file mode 100644 index 00000000..aff06f8d --- /dev/null +++ b/tests/ui/slice_of_type_alias.stderr @@ -0,0 +1,11 @@ +error[E0271]: type mismatch resolving `<ElementOpaque as ExternType>::Kind == Trivial` + --> $DIR/slice_of_type_alias.rs:13:9 + | +13 | type ElementOpaque = crate::ElementOpaque; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected enum `Trivial`, found enum `cxx::kind::Opaque` + | +note: required by a bound in `verify_extern_kind` + --> $DIR/extern_type.rs:186:41 + | +186 | pub fn verify_extern_kind<T: ExternType<Kind = Kind>, Kind: self::Kind>() {} + | ^^^^^^^^^^^ required by this bound in `verify_extern_kind` diff --git a/tests/ui/slice_unsupported.stderr b/tests/ui/slice_unsupported.stderr index 787076fb..2cbd26da 100644 --- a/tests/ui/slice_unsupported.stderr +++ b/tests/ui/slice_unsupported.stderr @@ -3,3 +3,9 @@ error: unsupported &mut [T] element type: opaque C++ type is not supported yet | 6 | fn f(_: &mut [Opaque]); | ^^^^^^^^^^^^^ + +error: needs a cxx::ExternType impl in order to be used as a slice element in &mut [Opaque] + --> $DIR/slice_unsupported.rs:4:9 + | +4 | type Opaque; + | ^^^^^^^^^^^ diff --git a/tests/ui/unique_ptr_to_opaque.stderr b/tests/ui/unique_ptr_to_opaque.stderr index 19d76a3b..28e45cf5 100644 --- a/tests/ui/unique_ptr_to_opaque.stderr +++ b/tests/ui/unique_ptr_to_opaque.stderr @@ -4,4 +4,10 @@ error[E0271]: type mismatch resolving `<outside::C as ExternType>::Kind == Trivi 22 | cxx::UniquePtr::new(outside::C { a: 4 }); | ^^^^^^^^^^^^^^^^^^^ expected enum `Trivial`, found enum `cxx::kind::Opaque` | - = note: required by `UniquePtr::<T>::new` +note: required by `UniquePtr::<T>::new` + --> $DIR/unique_ptr.rs:38:5 + | +38 | / pub fn new(value: T) -> Self +39 | | where +40 | | T: ExternType<Kind = Trivial>, + | |______________________________________^ diff --git a/tests/ui/unsupported_elided.stderr b/tests/ui/unsupported_elided.stderr index 932076d9..8e8a986b 100644 --- a/tests/ui/unsupported_elided.stderr +++ b/tests/ui/unsupported_elided.stderr @@ -3,6 +3,8 @@ error[E0726]: implicit elided lifetime not allowed here | 6 | type T; | ^- help: indicate the anonymous lifetime: `<'_>` + | + = note: assuming a `'static` lifetime... error[E0106]: missing lifetime specifier --> $DIR/unsupported_elided.rs:8:24 @@ -14,4 +16,4 @@ error[E0106]: missing lifetime specifier help: consider introducing a named lifetime parameter | 8 | fn f<'a>(t: &'a T) -> &'a str; - | ^^^^ ^^^^^ ^^^ + | ++++ ++ ++ diff --git a/tests/ui/vec_opaque.stderr b/tests/ui/vec_opaque.stderr index b8dad916..3f208fec 100644 --- a/tests/ui/vec_opaque.stderr +++ b/tests/ui/vec_opaque.stderr @@ -16,7 +16,8 @@ error[E0271]: type mismatch resolving `<handle::Job as ExternType>::Kind == Triv 22 | type Job = crate::handle::Job; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected enum `Trivial`, found enum `cxx::kind::Opaque` | - ::: $WORKSPACE/src/extern_type.rs +note: required by a bound in `verify_extern_kind` + --> $DIR/extern_type.rs:186:41 | - | pub fn verify_extern_kind<T: ExternType<Kind = Kind>, Kind: self::Kind>() {} - | ----------- required by this bound in `verify_extern_kind` +186 | pub fn verify_extern_kind<T: ExternType<Kind = Kind>, Kind: self::Kind>() {} + | ^^^^^^^^^^^ required by this bound in `verify_extern_kind` diff --git a/tests/ui/vector_autotraits.rs b/tests/ui/vector_autotraits.rs new file mode 100644 index 00000000..cc918d58 --- /dev/null +++ b/tests/ui/vector_autotraits.rs @@ -0,0 +1,21 @@ +use cxx::CxxVector; + +#[cxx::bridge] +mod ffi { + extern "C++" { + type ThreadSafe; + type NotThreadSafe; + } + + impl CxxVector<ThreadSafe> {} + impl CxxVector<NotThreadSafe> {} +} + +unsafe impl Send for ffi::ThreadSafe {} + +fn assert_send<T: Send>() {} + +fn main() { + assert_send::<CxxVector<ffi::ThreadSafe>>(); + assert_send::<CxxVector<ffi::NotThreadSafe>>(); +} diff --git a/tests/ui/vector_autotraits.stderr b/tests/ui/vector_autotraits.stderr new file mode 100644 index 00000000..52fd2a41 --- /dev/null +++ b/tests/ui/vector_autotraits.stderr @@ -0,0 +1,22 @@ +error[E0277]: `*const cxx::void` cannot be sent between threads safely + --> $DIR/vector_autotraits.rs:20:5 + | +20 | assert_send::<CxxVector<ffi::NotThreadSafe>>(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `*const cxx::void` cannot be sent between threads safely + | + = help: within `CxxVector<NotThreadSafe>`, the trait `Send` is not implemented for `*const cxx::void` + = note: required because it appears within the type `[*const cxx::void; 0]` + = note: required because it appears within the type `cxx::private::Opaque` +note: required because it appears within the type `NotThreadSafe` + --> $DIR/vector_autotraits.rs:7:14 + | +7 | type NotThreadSafe; + | ^^^^^^^^^^^^^ + = note: required because it appears within the type `[NotThreadSafe]` + = note: required because it appears within the type `PhantomData<[NotThreadSafe]>` + = note: required because it appears within the type `CxxVector<NotThreadSafe>` +note: required by a bound in `assert_send` + --> $DIR/vector_autotraits.rs:16:19 + | +16 | fn assert_send<T: Send>() {} + | ^^^^ required by this bound in `assert_send` diff --git a/tests/ui/wrong_type_id.stderr b/tests/ui/wrong_type_id.stderr index 2d8e50aa..b66bb266 100644 --- a/tests/ui/wrong_type_id.stderr +++ b/tests/ui/wrong_type_id.stderr @@ -4,10 +4,10 @@ error[E0271]: type mismatch resolving `<StringPiece as ExternType>::Id == (f, o, 11 | type ByteRange = crate::here::StringPiece; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected a tuple with 15 elements, found one with 17 elements | - ::: $WORKSPACE/src/extern_type.rs - | - | pub fn verify_extern_type<T: ExternType<Id = Id>, Id>() {} - | ------- required by this bound in `verify_extern_type` - | = note: expected tuple `(f, o, l, l, y, (), B, y, t, e, R, a, n, g, e)` found tuple `(f, o, l, l, y, (), S, t, r, i, n, g, P, i, e, c, e)` +note: required by a bound in `verify_extern_type` + --> $DIR/extern_type.rs:183:41 + | +183 | pub fn verify_extern_type<T: ExternType<Id = Id>, Id>() {} + | ^^^^^^^ required by this bound in `verify_extern_type` diff --git a/third-party/BUCK b/third-party/BUCK index 84a5e18b..2633da87 100644 --- a/third-party/BUCK +++ b/third-party/BUCK @@ -1,13 +1,15 @@ # To be generated by Facebook's `reindeer` tool once that is open source. +load("//tools/buck:rust_library.bzl", "rust_library") + rust_library( name = "bitflags", - srcs = glob(["vendor/bitflags-1.2.1/src/**"]), + srcs = glob(["vendor/bitflags-1.3.2/src/**"]), ) rust_library( name = "cc", - srcs = glob(["vendor/cc-1.0.67/src/**"]), + srcs = glob(["vendor/cc-1.0.69/src/**"]), visibility = ["PUBLIC"], ) @@ -41,25 +43,21 @@ rust_library( rust_library( name = "proc-macro2", - srcs = glob(["vendor/proc-macro2-1.0.26/src/**"]), - visibility = ["PUBLIC"], + srcs = glob(["vendor/proc-macro2-1.0.28/src/**"]), + build_script = "vendor/proc-macro2-1.0.28/build.rs", features = [ "proc-macro", "span-locations", ], - rustc_flags = [ - "--cfg=span_locations", - "--cfg=use_proc_macro", - "--cfg=wrap_proc_macro", - ], + visibility = ["PUBLIC"], deps = [":unicode-xid"], ) rust_library( name = "quote", srcs = glob(["vendor/quote-1.0.9/src/**"]), - visibility = ["PUBLIC"], features = ["proc-macro"], + visibility = ["PUBLIC"], deps = [":proc-macro2"], ) @@ -72,8 +70,8 @@ rust_library( rust_library( name = "syn", - srcs = glob(["vendor/syn-1.0.68/src/**"]), - visibility = ["PUBLIC"], + srcs = glob(["vendor/syn-1.0.75/src/**"]), + build_script = "vendor/syn-1.0.75/build.rs", features = [ "clone-impls", "derive", @@ -82,6 +80,7 @@ rust_library( "printing", "proc-macro", ], + visibility = ["PUBLIC"], deps = [ ":proc-macro2", ":quote", @@ -107,5 +106,5 @@ rust_library( rust_library( name = "unicode-xid", - srcs = glob(["vendor/unicode-xid-0.2.1/src/**"]), + srcs = glob(["vendor/unicode-xid-0.2.2/src/**"]), ) diff --git a/third-party/BUILD b/third-party/BUILD index cf1a2883..1760b707 100644 --- a/third-party/BUILD +++ b/third-party/BUILD @@ -1,17 +1,18 @@ load( - "//tools/bazel:rust.bzl", + "//tools/bazel:third_party.bzl", + cargo_build_script = "third_party_cargo_build_script", glob = "third_party_glob", rust_library = "third_party_rust_library", ) rust_library( name = "bitflags", - srcs = glob(["vendor/bitflags-1.2.1/src/**"]), + srcs = glob(["vendor/bitflags-1.3.2/src/**"]), ) rust_library( name = "cc", - srcs = glob(["vendor/cc-1.0.67/src/**"]), + srcs = glob(["vendor/cc-1.0.69/src/**"]), visibility = ["//visibility:public"], ) @@ -45,18 +46,26 @@ rust_library( rust_library( name = "proc-macro2", - srcs = glob(["vendor/proc-macro2-1.0.26/src/**"]), + srcs = glob(["vendor/proc-macro2-1.0.28/src/**"]), crate_features = [ "proc-macro", "span-locations", ], - rustc_flags = [ - "--cfg=span_locations", - "--cfg=use_proc_macro", - "--cfg=wrap_proc_macro", - ], visibility = ["//visibility:public"], - deps = [":unicode-xid"], + deps = [ + ":proc-macro2@build", + ":unicode-xid", + ], +) + +cargo_build_script( + name = "proc-macro2@build", + srcs = ["vendor/proc-macro2-1.0.28/build.rs"], + crate_features = [ + "proc-macro", + "span-locations", + ], + crate_name = "build", ) rust_library( @@ -76,7 +85,7 @@ rust_library( rust_library( name = "syn", - srcs = glob(["vendor/syn-1.0.68/src/**"]), + srcs = glob(["vendor/syn-1.0.75/src/**"]), crate_features = [ "clone-impls", "derive", @@ -89,10 +98,25 @@ rust_library( deps = [ ":proc-macro2", ":quote", + ":syn@build", ":unicode-xid", ], ) +cargo_build_script( + name = "syn@build", + srcs = ["vendor/syn-1.0.75/build.rs"], + crate_features = [ + "clone-impls", + "derive", + "full", + "parsing", + "printing", + "proc-macro", + ], + crate_name = "build", +) + rust_library( name = "termcolor", srcs = glob(["vendor/termcolor-1.1.2/src/**"]), @@ -111,5 +135,5 @@ rust_library( rust_library( name = "unicode-xid", - srcs = glob(["vendor/unicode-xid-0.2.1/src/**"]), + srcs = glob(["vendor/unicode-xid-0.2.2/src/**"]), ) diff --git a/third-party/Cargo.lock b/third-party/Cargo.lock index a95e737b..0a3d543d 100644 --- a/third-party/Cargo.lock +++ b/third-party/Cargo.lock @@ -3,6 +3,12 @@ version = 3 [[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] name = "ansi_term" version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -23,21 +29,42 @@ dependencies = [ ] [[package]] +name = "autocfg" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" + +[[package]] name = "bitflags" -version = "1.2.1" +version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "cc" -version = "1.0.67" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3c69b077ad434294d3ce9f1f6143a2a4b89a8a2d54ef813d85003a4fd1137fd" +checksum = "e70cc2f62c6ce1868963827bd677764c62d07c3d9a3e1fb1177ee1a9ab199eb2" dependencies = [ "jobserver", ] [[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "clang-ast" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eed6681036a96f9855a75b08a9f14199e212017508a967d1d1e868f575f7d8e9" +dependencies = [ + "serde", +] + +[[package]] name = "clap" version = "2.33.3" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -63,8 +90,17 @@ dependencies = [ ] [[package]] +name = "crc32fast" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81156fece84ab6a9f2afdb109ce3ae577e42b1228441eded99bd77f627953b1a" +dependencies = [ + "cfg-if", +] + +[[package]] name = "cxx" -version = "1.0.42" +version = "1.0.54" dependencies = [ "cc", "cxx-build", @@ -79,7 +115,7 @@ dependencies = [ [[package]] name = "cxx-build" -version = "1.0.42" +version = "1.0.54" dependencies = [ "cc", "codespan-reporting", @@ -94,7 +130,7 @@ dependencies = [ [[package]] name = "cxx-gen" -version = "0.7.42" +version = "0.7.54" dependencies = [ "cc", "codespan-reporting", @@ -114,7 +150,7 @@ dependencies = [ [[package]] name = "cxxbridge-cmd" -version = "1.0.42" +version = "1.0.54" dependencies = [ "clap", "codespan-reporting", @@ -125,15 +161,20 @@ dependencies = [ [[package]] name = "cxxbridge-flags" -version = "1.0.42" +version = "1.0.54" [[package]] name = "cxxbridge-macro" -version = "1.0.42" +version = "1.0.54" dependencies = [ + "clang-ast", "cxx", + "flate2", + "memmap", "proc-macro2", "quote", + "serde", + "serde_json", "syn", ] @@ -152,6 +193,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc4b29f4b9bb94bf267d57269fd0706d343a160937108e9619fe380645428abb" [[package]] +name = "flate2" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd3aec53de10fe96d7d8c565eb17f2c687bb5518a2ec453b5b1252964526abe0" +dependencies = [ + "cfg-if", + "crc32fast", + "libc", + "miniz_oxide", +] + +[[package]] name = "glob" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -159,24 +212,24 @@ checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" [[package]] name = "hermit-abi" -version = "0.1.18" +version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" dependencies = [ "libc", ] [[package]] name = "itoa" -version = "0.4.7" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" +checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" [[package]] name = "jobserver" -version = "0.1.21" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c71313ebb9439f74b00d9d2dcec36440beaf57a6aa0623068441dd7cd81a7f2" +checksum = "af25a77299a7f711a01975c35a6a424eb6862092cc2d6c72c4ed6cbc56dfc1fa" dependencies = [ "libc", ] @@ -189,9 +242,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.92" +version = "0.2.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56d855069fafbb9b344c0f962150cd2c1187975cb1c22c1522c240d8c4986714" +checksum = "3cb00336871be5ed2c8ed44b60ae9959dc5b9f08539422ed43f09e34ecaeba21" [[package]] name = "link-cplusplus" @@ -203,6 +256,26 @@ dependencies = [ ] [[package]] +name = "memmap" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6585fd95e7bb50d6cc31e20d4cf9afb4e2ba16c5846fc76793f11218da9c475b" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "miniz_oxide" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b" +dependencies = [ + "adler", + "autocfg", +] + +[[package]] name = "pkg-config" version = "0.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -210,9 +283,9 @@ checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c" [[package]] name = "proc-macro2" -version = "1.0.26" +version = "1.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a152013215dca273577e18d2bf00fa862b89b24169fb78c4c95aeb07992c9cec" +checksum = "5c7ed8b8c7b886ea3ed7dde405212185f423ab44682667c8c6dd14aa1d9f6612" dependencies = [ "unicode-xid", ] @@ -228,9 +301,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.4" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb5d2a036dc6d2d8fd16fde3498b04306e29bd193bf306a57427019b823d5acd" +checksum = "61b3909d758bb75c79f23d4736fac9433868679d3ad2ea7a61e3c25cfda9a088" [[package]] name = "ryu" @@ -246,18 +319,18 @@ checksum = "7e114536316b51a5aa7a0e59fc49661fd263c5507dd08bd28de052e57626ce69" [[package]] name = "serde" -version = "1.0.125" +version = "1.0.129" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "558dc50e1a5a5fa7112ca2ce4effcb321b0300c0d4ccf0776a9f60cd89031171" +checksum = "d1f72836d2aa753853178eda473a3b9d8e4eefdaf20523b919677e6de489f8f1" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.125" +version = "1.0.129" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b093b7a2bb58203b5da3056c05b4ec1fed827dcfdb37347a8841695263b3d06d" +checksum = "e57ae87ad533d9a56427558b516d0adac283614e347abf85b0dc0cbbf0a249f3" dependencies = [ "proc-macro2", "quote", @@ -266,9 +339,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.64" +version = "1.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "799e97dc9fdae36a5c8b8f2cae9ce2ee9fdce2058c57a93e6099d919fd982f79" +checksum = "336b10da19a12ad094b59d870ebde26a45402e5b470add4b5fd03c5048a32127" dependencies = [ "itoa", "ryu", @@ -283,9 +356,9 @@ checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" [[package]] name = "syn" -version = "1.0.68" +version = "1.0.75" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ce15dd3ed8aa2f8eeac4716d6ef5ab58b6b9256db41d7e1a0224c2788e8fd87" +checksum = "b7f58f7e8eaa0009c5fec437aabf511bd9933e4b2d7407bd05273c01a8906ea7" dependencies = [ "proc-macro2", "quote", @@ -321,9 +394,9 @@ dependencies = [ [[package]] name = "trybuild" -version = "1.0.41" +version = "1.0.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99471a206425fba51842a9186315f32d91c56eadc21ea4c21f847b59cf778f8b" +checksum = "5bdaf2a1d317f3d58b44b31c7f6436b9b9acafe7bddfeace50897c2b804d7792" dependencies = [ "dissimilar", "glob", @@ -342,9 +415,9 @@ checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" [[package]] name = "unicode-xid" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" +checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" [[package]] name = "vec_map" diff --git a/tools/bazel/rust.bzl b/tools/bazel/rust.bzl deleted file mode 100644 index 9f71923a..00000000 --- a/tools/bazel/rust.bzl +++ /dev/null @@ -1,29 +0,0 @@ -"""A module wrapping the core rules of `rules_rust`""" - -load( - "@rules_rust//rust:rust.bzl", - _rust_binary = "rust_binary", - _rust_library = "rust_library", - _rust_test = "rust_test", -) -load("@third-party//:vendor.bzl", "vendored") - -def third_party_glob(include): - return vendored and native.glob(include) - -def rust_binary(edition = "2018", **kwargs): - _rust_binary(edition = edition, **kwargs) - -def third_party_rust_binary(rustc_flags = [], **kwargs): - rustc_flags = rustc_flags + ["--cap-lints=allow"] - rust_binary(rustc_flags = rustc_flags, **kwargs) - -def rust_library(edition = "2018", **kwargs): - _rust_library(edition = edition, **kwargs) - -def third_party_rust_library(rustc_flags = [], **kwargs): - rustc_flags = rustc_flags + ["--cap-lints=allow"] - rust_library(rustc_flags = rustc_flags, **kwargs) - -def rust_test(edition = "2018", **kwargs): - _rust_test(edition = edition, **kwargs) diff --git a/tools/bazel/third_party.bzl b/tools/bazel/third_party.bzl new file mode 100644 index 00000000..7f51c466 --- /dev/null +++ b/tools/bazel/third_party.bzl @@ -0,0 +1,18 @@ +load("@rules_rust//cargo:cargo_build_script.bzl", "cargo_build_script") +load("@rules_rust//rust:rust.bzl", "rust_binary", "rust_library") +load("@third-party//:vendor.bzl", "vendored") + +def third_party_glob(include): + return vendored and native.glob(include) + +def third_party_cargo_build_script(rustc_flags = [], **kwargs): + rustc_flags = rustc_flags + ["--cap-lints=allow"] + cargo_build_script(rustc_flags = rustc_flags, **kwargs) + +def third_party_rust_binary(rustc_flags = [], **kwargs): + rustc_flags = rustc_flags + ["--cap-lints=allow"] + rust_binary(rustc_flags = rustc_flags, **kwargs) + +def third_party_rust_library(rustc_flags = [], **kwargs): + rustc_flags = rustc_flags + ["--cap-lints=allow"] + rust_library(rustc_flags = rustc_flags, **kwargs) diff --git a/tools/bazel/vendor.bzl b/tools/bazel/vendor.bzl index e404ad99..e51adb7b 100644 --- a/tools/bazel/vendor.bzl +++ b/tools/bazel/vendor.bzl @@ -2,6 +2,8 @@ of a crate in the current workspace. """ +load("@rules_rust//rust:repositories.bzl", "DEFAULT_RUST_VERSION", "load_arbitrary_tool") + def _impl(repository_ctx): # Link cxx repository into @third-party. lockfile = repository_ctx.path(repository_ctx.attr.lockfile) @@ -14,8 +16,27 @@ def _impl(repository_ctx): root_lockfile = repository_ctx.path("workspace/Cargo.lock") _copy_file(repository_ctx, src = vendor_lockfile, dst = root_lockfile) - # Execute cargo vendor. - cmd = ["cargo", "vendor", "--versioned-dirs", "third-party/vendor"] + # Figure out which version of cargo to use. + if repository_ctx.attr.target_triple: + target_triple = repository_ctx.attr.target_triple + elif "mac" in repository_ctx.os.name: + target_triple = "x86_64-apple-darwin" + elif "windows" in repository_ctx.os.name: + target_triple = "x86_64-pc-windows-msvc" + else: + target_triple = "x86_64-unknown-linux-gnu" + + # Download cargo. + load_arbitrary_tool( + ctx = repository_ctx, + tool_name = "cargo", + tool_subdirectories = ["cargo"], + version = repository_ctx.attr.cargo_version, + iso_date = repository_ctx.attr.cargo_iso_date, + target_triple = target_triple, + ) + + cmd = ["{}/bin/cargo".format(repository_ctx.path(".")), "vendor", "--versioned-dirs", "third-party/vendor"] result = repository_ctx.execute( cmd, quiet = True, @@ -54,6 +75,16 @@ def _log_cargo_vendor(repository_ctx, result): vendor = repository_rule( doc = "A rule used to vendor the dependencies of a crate in the current workspace", attrs = { + "cargo_version": attr.string( + doc = "The version of cargo to use", + default = DEFAULT_RUST_VERSION, + ), + "cargo_iso_date": attr.string( + doc = "The date of the tool (or None, if the version is a specific version)", + ), + "target_triple": attr.string( + doc = "The target triple of the cargo binary to download", + ), "lockfile": attr.label( doc = "A lockfile providing the set of crates to vendor", ), diff --git a/tools/buck/rust_library.bzl b/tools/buck/rust_library.bzl new file mode 100644 index 00000000..67ec2ac7 --- /dev/null +++ b/tools/buck/rust_library.bzl @@ -0,0 +1,34 @@ +load("//tools/buck:genrule.bzl", "genrule") + +def rust_library( + name, + srcs, + features = [], + rustc_flags = [], + build_script = None, + **kwargs): + if build_script: + rust_binary( + name = "%s@build" % name, + srcs = srcs + [build_script], + crate = "build", + crate_root = build_script, + features = features, + rustc_flags = rustc_flags, + ) + + genrule( + name = "%s@cfg" % name, + out = "output", + cmd = "env RUSTC=rustc TARGET= $(exe :%s@build) | sed -n s/^cargo:rustc-cfg=/--cfg=/p > ${OUT}" % name, + ) + + rustc_flags = rustc_flags + ["@$(location :%s@cfg)" % name] + + native.rust_library( + name = name, + srcs = srcs, + features = features, + rustc_flags = rustc_flags, + **kwargs + ) |