aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorandroid-build-team Robot <android-build-team-robot@google.com>2021-04-10 03:06:10 +0000
committerandroid-build-team Robot <android-build-team-robot@google.com>2021-04-10 03:06:10 +0000
commit862a23082a087566776280a5b1539d3b62701bcb (patch)
tree9a2047c0d2430d59acc4534f49b6de28012d7551
parent597064f368565e63b8bb96d9cbe79f1664c4c93a (diff)
parentf89b6a0193e78a7cf95fa1dc9c4df7bfd3b50dcd (diff)
downloadcxx-android12-s2-release.tar.gz
Snap for 7272808 from f89b6a0193e78a7cf95fa1dc9c4df7bfd3b50dcd to sc-releaseandroid-vts-12.0_r9android-vts-12.0_r8android-vts-12.0_r7android-vts-12.0_r6android-vts-12.0_r5android-vts-12.0_r4android-vts-12.0_r3android-vts-12.0_r2android-vts-12.0_r12android-vts-12.0_r11android-vts-12.0_r10android-vts-12.0_r1android-security-12.0.0_r59android-security-12.0.0_r58android-security-12.0.0_r57android-security-12.0.0_r56android-security-12.0.0_r55android-security-12.0.0_r54android-security-12.0.0_r53android-security-12.0.0_r52android-security-12.0.0_r51android-security-12.0.0_r50android-security-12.0.0_r49android-security-12.0.0_r48android-security-12.0.0_r47android-security-12.0.0_r46android-security-12.0.0_r45android-security-12.0.0_r44android-security-12.0.0_r43android-security-12.0.0_r42android-security-12.0.0_r41android-security-12.0.0_r40android-security-12.0.0_r39android-security-12.0.0_r38android-security-12.0.0_r37android-security-12.0.0_r36android-security-12.0.0_r35android-security-12.0.0_r34android-security-11.0.0_r71android-platform-12.0.0_r9android-platform-12.0.0_r8android-platform-12.0.0_r7android-platform-12.0.0_r6android-platform-12.0.0_r5android-platform-12.0.0_r4android-platform-12.0.0_r31android-platform-12.0.0_r30android-platform-12.0.0_r3android-platform-12.0.0_r29android-platform-12.0.0_r28android-platform-12.0.0_r27android-platform-12.0.0_r26android-platform-12.0.0_r25android-platform-12.0.0_r24android-platform-12.0.0_r23android-platform-12.0.0_r22android-platform-12.0.0_r21android-platform-12.0.0_r20android-platform-12.0.0_r2android-platform-12.0.0_r19android-platform-12.0.0_r18android-platform-12.0.0_r17android-platform-12.0.0_r16android-platform-12.0.0_r15android-platform-12.0.0_r14android-platform-12.0.0_r13android-platform-12.0.0_r12android-platform-12.0.0_r11android-platform-12.0.0_r10android-platform-12.0.0_r1android-cts-12.0_r9android-cts-12.0_r8android-cts-12.0_r7android-cts-12.0_r6android-cts-12.0_r5android-cts-12.0_r4android-cts-12.0_r3android-cts-12.0_r2android-cts-12.0_r12android-cts-12.0_r11android-cts-12.0_r10android-cts-12.0_r1android-12.0.0_r9android-12.0.0_r8android-12.0.0_r34android-12.0.0_r33android-12.0.0_r31android-12.0.0_r30android-12.0.0_r3android-12.0.0_r25android-12.0.0_r2android-12.0.0_r11android-12.0.0_r10android-12.0.0_r1android12-tests-releaseandroid12-security-releaseandroid12-s5-releaseandroid12-s4-releaseandroid12-s3-releaseandroid12-s2-releaseandroid12-s1-releaseandroid12-releaseandroid12-platform-release
Change-Id: I86b45f909ebb18df00bc36c38418161ed5a27319
-rw-r--r--.clang-tidy18
-rw-r--r--.clippy.toml1
-rw-r--r--.devcontainer/build.Dockerfile2
-rw-r--r--.github/workflows/ci.yml25
-rw-r--r--.github/workflows/site.yml12
-rw-r--r--.vscode/launch.json12
-rw-r--r--Cargo.toml17
-rw-r--r--README.md54
-rw-r--r--WORKSPACE38
-rw-r--r--book/.gitignore1
-rw-r--r--book/book.toml12
-rwxr-xr-xbook/build.js104
-rwxr-xr-xbook/build.sh17
-rw-r--r--book/css/cxx.css44
-rw-r--r--book/diagram/.gitignore2
-rw-r--r--book/diagram/Makefile12
-rw-r--r--book/diagram/overview.ascii13
-rw-r--r--book/diagram/overview.svg132
-rw-r--r--book/package-lock.json207
-rw-r--r--book/package.json12
-rw-r--r--book/src/404.md5
-rw-r--r--book/src/SUMMARY.md36
-rw-r--r--book/src/about.md1
-rw-r--r--book/src/async.md86
-rw-r--r--book/src/attributes.md75
-rw-r--r--book/src/binding/box.md120
-rw-r--r--book/src/binding/cxxstring.md139
-rw-r--r--book/src/binding/cxxvector.md62
-rw-r--r--book/src/binding/fn.md34
-rw-r--r--book/src/binding/rawptr.md100
-rw-r--r--book/src/binding/result.md148
-rw-r--r--book/src/binding/sharedptr.md80
-rw-r--r--book/src/binding/slice.md171
-rw-r--r--book/src/binding/str.md117
-rw-r--r--book/src/binding/string.md115
-rw-r--r--book/src/binding/uniqueptr.md63
-rw-r--r--book/src/binding/vec.md190
-rw-r--r--book/src/bindings.md56
-rw-r--r--book/src/build/bazel.md106
-rw-r--r--book/src/build/cargo.md306
-rw-r--r--book/src/build/cmake.md24
-rw-r--r--book/src/build/other.md75
-rw-r--r--book/src/building.md20
-rw-r--r--book/src/concepts.md85
-rw-r--r--book/src/context.md118
-rw-r--r--book/src/cxx.pngbin0 -> 64483 bytes
-rw-r--r--book/src/extern-c++.md352
-rw-r--r--book/src/extern-rust.md165
-rw-r--r--book/src/index.md83
-rw-r--r--book/src/overview.svg99
-rw-r--r--book/src/reference.md29
-rw-r--r--book/src/shared.md246
-rw-r--r--book/src/tutorial.md687
-rw-r--r--build.rs33
-rw-r--r--compile_flags.txt1
-rw-r--r--demo/Cargo.toml7
-rw-r--r--demo/include/blobstore.h4
-rw-r--r--demo/src/blobstore.cc6
-rw-r--r--demo/src/main.rs4
-rw-r--r--flags/Cargo.toml2
l---------flags/LICENSE-APACHE1
l---------flags/LICENSE-MIT1
-rw-r--r--gen/build/Cargo.toml14
-rw-r--r--gen/build/src/cfg.rs4
-rw-r--r--gen/build/src/intern.rs4
-rw-r--r--gen/build/src/lib.rs108
-rw-r--r--gen/build/src/out.rs55
-rw-r--r--gen/build/src/paths.rs33
-rw-r--r--gen/build/src/target.rs9
-rw-r--r--gen/cmd/Cargo.toml9
-rw-r--r--gen/cmd/src/app.rs2
-rw-r--r--gen/cmd/src/main.rs28
-rw-r--r--gen/lib/Cargo.toml8
-rw-r--r--gen/lib/src/lib.rs23
-rw-r--r--gen/lib/tests/test.rs2
-rw-r--r--gen/src/builtin.rs229
-rw-r--r--gen/src/error.rs7
-rw-r--r--gen/src/fs.rs46
-rw-r--r--gen/src/include.rs92
-rw-r--r--gen/src/nested.rs25
-rw-r--r--gen/src/out.rs2
-rw-r--r--gen/src/write.rs1139
-rw-r--r--include/cxx.h857
-rw-r--r--macro/Cargo.toml7
-rw-r--r--macro/src/derive.rs285
-rw-r--r--macro/src/expand.rs1066
-rw-r--r--macro/src/generics.rs63
-rw-r--r--macro/src/lib.rs39
-rw-r--r--macro/src/type_id.rs24
-rw-r--r--src/cxx.cc529
-rw-r--r--src/cxx_string.rs133
-rw-r--r--src/cxx_vector.rs235
-rw-r--r--src/exception.rs2
-rw-r--r--src/extern_type.rs41
-rw-r--r--src/fmt.rs16
-rw-r--r--src/lib.rs92
-rw-r--r--src/memory.rs8
-rw-r--r--src/opaque.rs3
-rw-r--r--src/result.rs20
-rw-r--r--src/rust_slice.rs41
-rw-r--r--src/rust_sliceu8.rs29
-rw-r--r--src/rust_str.rs17
-rw-r--r--src/rust_type.rs3
-rw-r--r--src/rust_vec.rs4
-rw-r--r--src/shared_ptr.rs285
-rw-r--r--src/symbols/exception.rs2
-rw-r--r--src/symbols/mod.rs1
-rw-r--r--src/symbols/rust_slice.rs22
-rw-r--r--src/symbols/rust_str.rs35
-rw-r--r--src/symbols/rust_string.rs17
-rw-r--r--src/symbols/rust_vec.rs29
-rw-r--r--src/type_id.rs9
-rw-r--r--src/unique_ptr.rs108
-rw-r--r--src/vector.rs9
-rw-r--r--src/weak_ptr.rs192
-rw-r--r--syntax/atom.rs3
-rw-r--r--syntax/attrs.rs172
-rw-r--r--syntax/check.rs466
-rw-r--r--syntax/derive.rs69
-rw-r--r--syntax/discriminant.rs89
-rw-r--r--syntax/error.rs4
-rw-r--r--syntax/ident.rs45
-rw-r--r--syntax/impls.rs274
-rw-r--r--syntax/improper.rs11
-rw-r--r--syntax/instantiate.rs80
-rw-r--r--syntax/mangle.rs106
-rw-r--r--syntax/map.rs179
-rw-r--r--syntax/mod.rs109
-rw-r--r--syntax/names.rs103
-rw-r--r--syntax/parse.rs1076
-rw-r--r--syntax/pod.rs36
-rw-r--r--syntax/qualified.rs11
-rw-r--r--syntax/resolve.rs46
-rw-r--r--syntax/set.rs21
-rw-r--r--syntax/symbol.rs17
-rw-r--r--syntax/tokens.rs277
-rw-r--r--syntax/toposort.rs2
-rw-r--r--syntax/trivial.rs257
-rw-r--r--syntax/types.rs184
-rw-r--r--syntax/visit.rs34
-rw-r--r--tests/BUCK9
-rw-r--r--tests/BUILD10
-rw-r--r--tests/cxx_gen.rs6
-rw-r--r--tests/cxx_string.rs15
-rw-r--r--tests/ffi/build.rs14
-rw-r--r--tests/ffi/cast.rs14
-rw-r--r--tests/ffi/extra.rs63
-rw-r--r--tests/ffi/lib.rs277
-rw-r--r--tests/ffi/module.rs73
-rw-r--r--tests/ffi/tests.cc277
-rw-r--r--tests/ffi/tests.h43
-rw-r--r--tests/test.rs137
-rw-r--r--tests/ui/array_len_expr.rs10
-rw-r--r--tests/ui/array_len_expr.stderr17
-rw-r--r--tests/ui/array_len_suffix.rs8
-rw-r--r--tests/ui/array_len_suffix.stderr10
-rw-r--r--tests/ui/async_fn.rs10
-rw-r--r--tests/ui/async_fn.stderr5
-rw-r--r--tests/ui/by_value_not_supported.rs2
-rw-r--r--tests/ui/by_value_not_supported.stderr2
-rw-r--r--tests/ui/const_fn.rs10
-rw-r--r--tests/ui/const_fn.stderr5
-rw-r--r--tests/ui/deny_missing_docs.rs94
-rw-r--r--tests/ui/deny_missing_docs.stderr47
-rw-r--r--tests/ui/derive_duplicate.rs9
-rw-r--r--tests/ui/derive_duplicate.stderr7
-rw-r--r--tests/ui/derive_nonclone.rs13
-rw-r--r--tests/ui/derive_nonclone.stderr7
-rw-r--r--tests/ui/derive_noncopy.rs13
-rw-r--r--tests/ui/derive_noncopy.stderr8
-rw-r--r--tests/ui/disallow_lifetime.rs13
-rw-r--r--tests/ui/disallow_lifetime.stderr11
-rw-r--r--tests/ui/drop_shared.rs14
-rw-r--r--tests/ui/drop_shared.stderr8
-rw-r--r--tests/ui/duplicate_enum_discriminants.rs15
-rw-r--r--tests/ui/duplicate_enum_discriminants.stderr11
-rw-r--r--tests/ui/empty_enum.stderr2
-rw-r--r--tests/ui/enum_match_without_wildcard.stderr4
-rw-r--r--tests/ui/enum_receiver.rs11
-rw-r--r--tests/ui/enum_receiver.stderr5
-rw-r--r--tests/ui/expected_named.rs9
-rw-r--r--tests/ui/expected_named.stderr11
-rw-r--r--tests/ui/extern_fn_abi.rs8
-rw-r--r--tests/ui/extern_fn_abi.stderr5
-rw-r--r--tests/ui/extern_type_bound.rs15
-rw-r--r--tests/ui/extern_type_bound.stderr11
-rw-r--r--tests/ui/extern_type_generic.rs8
-rw-r--r--tests/ui/extern_type_generic.stderr5
-rw-r--r--tests/ui/extern_type_lifetime_bound.rs8
-rw-r--r--tests/ui/extern_type_lifetime_bound.stderr5
-rw-r--r--tests/ui/fallible_fnptr.rs8
-rw-r--r--tests/ui/fallible_fnptr.stderr5
-rw-r--r--tests/ui/function_with_body.rs8
-rw-r--r--tests/ui/function_with_body.stderr5
-rw-r--r--tests/ui/generic_enum.rs8
-rw-r--r--tests/ui/generic_enum.stderr14
-rw-r--r--tests/ui/include.rs2
-rw-r--r--tests/ui/lifetime_extern_cxx.rs9
-rw-r--r--tests/ui/lifetime_extern_cxx.stderr5
-rw-r--r--tests/ui/lifetime_extern_rust.rs17
-rw-r--r--tests/ui/lifetime_extern_rust.stderr5
-rw-r--r--tests/ui/multiple_parse_error.stderr6
-rw-r--r--tests/ui/mut_return.rs18
-rw-r--r--tests/ui/mut_return.stderr11
-rw-r--r--tests/ui/nonlocal_rust_type.rs18
-rw-r--r--tests/ui/nonlocal_rust_type.stderr21
-rw-r--r--tests/ui/opaque_autotraits.rs16
-rw-r--r--tests/ui/opaque_autotraits.stderr40
-rw-r--r--tests/ui/pin_mut_opaque.rs14
-rw-r--r--tests/ui/pin_mut_opaque.stderr35
-rw-r--r--tests/ui/ptr_in_fnptr.rs8
-rw-r--r--tests/ui/ptr_in_fnptr.stderr5
-rw-r--r--tests/ui/ptr_missing_unsafe.rs10
-rw-r--r--tests/ui/ptr_missing_unsafe.stderr5
-rw-r--r--tests/ui/ptr_no_const_mut.rs10
-rw-r--r--tests/ui/ptr_no_const_mut.stderr13
-rw-r--r--tests/ui/ptr_unsupported.rs12
-rw-r--r--tests/ui/ptr_unsupported.stderr17
-rw-r--r--tests/ui/reference_to_reference.rs2
-rw-r--r--tests/ui/reserved_name.rs2
-rw-r--r--tests/ui/root_namespace.rs13
-rw-r--r--tests/ui/root_namespace.stderr5
-rw-r--r--tests/ui/rust_pinned.rs14
-rw-r--r--tests/ui/rust_pinned.stderr10
-rw-r--r--tests/ui/slice_unsupported.rs10
-rw-r--r--tests/ui/slice_unsupported.stderr5
-rw-r--r--tests/ui/unique_ptr_as_mut.rs23
-rw-r--r--tests/ui/unique_ptr_as_mut.stderr7
-rw-r--r--tests/ui/unique_ptr_to_opaque.rs2
-rw-r--r--tests/ui/unique_ptr_twice.rs4
-rw-r--r--tests/ui/unique_ptr_twice.stderr2
-rw-r--r--tests/ui/unnamed_receiver.rs2
-rw-r--r--tests/ui/unpin_impl.rs10
-rw-r--r--tests/ui/unpin_impl.stderr8
-rw-r--r--tests/ui/unrecognized_receiver.rs2
-rw-r--r--tests/ui/unsupported_elided.stderr6
-rw-r--r--tests/ui/vec_opaque.rs34
-rw-r--r--tests/ui/vec_opaque.stderr22
-rw-r--r--tests/ui/wrong_type_id.rs4
-rw-r--r--tests/unique_ptr.rs2
-rw-r--r--third-party/BUCK12
-rw-r--r--third-party/BUILD14
-rw-r--r--third-party/Cargo.lock86
-rw-r--r--tools/bazel/BUILD7
-rw-r--r--tools/bazel/rust.bzl4
-rw-r--r--tools/bazel/rust_cxx_bridge.bzl10
-rw-r--r--tools/bazel/vendor.bzl11
-rw-r--r--tools/cargo/build.rs10
248 files changed, 14193 insertions, 2409 deletions
diff --git a/.clang-tidy b/.clang-tidy
new file mode 100644
index 00000000..b0a6da98
--- /dev/null
+++ b/.clang-tidy
@@ -0,0 +1,18 @@
+Checks:
+ clang-analyzer-*,
+ clang-diagnostic-*,
+ cppcoreguidelines-*,
+ modernize-*,
+ -cppcoreguidelines-macro-usage,
+ -cppcoreguidelines-owning-memory,
+ -cppcoreguidelines-pro-bounds-array-to-pointer-decay,
+ -cppcoreguidelines-pro-bounds-pointer-arithmetic,
+ -cppcoreguidelines-pro-type-const-cast,
+ -cppcoreguidelines-pro-type-member-init,
+ -cppcoreguidelines-pro-type-reinterpret-cast,
+ -cppcoreguidelines-pro-type-vararg,
+ -cppcoreguidelines-special-member-functions,
+ -modernize-use-default-member-init,
+ -modernize-use-equals-default,
+ -modernize-use-trailing-return-type,
+HeaderFilterRegex: cxx\.h
diff --git a/.clippy.toml b/.clippy.toml
new file mode 100644
index 00000000..11d46a73
--- /dev/null
+++ b/.clippy.toml
@@ -0,0 +1 @@
+msrv = "1.48.0"
diff --git a/.devcontainer/build.Dockerfile b/.devcontainer/build.Dockerfile
index 6085459d..f2763884 100644
--- a/.devcontainer/build.Dockerfile
+++ b/.devcontainer/build.Dockerfile
@@ -5,7 +5,7 @@ RUN apt-get update \
&& apt-get -y install --no-install-recommends openjdk-11-jdk lld \
&& rustup default nightly 2>&1 \
&& rustup component add rust-analyzer-preview rustfmt clippy 2>&1 \
- && wget -q -O bin/install-bazel https://github.com/bazelbuild/bazel/releases/download/2.1.1/bazel-2.1.1-installer-linux-x86_64.sh \
+ && wget -q -O bin/install-bazel https://github.com/bazelbuild/bazel/releases/download/4.0.0/bazel-4.0.0-installer-linux-x86_64.sh \
&& wget -q -O bin/buck https://jitpack.io/com/github/facebook/buck/a5f0342ae3/buck-a5f0342ae3-java11.pex \
&& wget -q -O bin/buildifier https://github.com/bazelbuild/buildtools/releases/latest/download/buildifier \
&& wget -q -O tmp/watchman.zip https://github.com/facebook/watchman/releases/download/v2020.09.21.00/watchman-v2020.09.21.00-linux.zip \
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 26b2722a..3fafd9ba 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -16,7 +16,7 @@ jobs:
- rust: nightly
- rust: beta
- rust: stable
- - rust: 1.43.0
+ - rust: 1.48.0
- name: macOS
rust: nightly
os: macos
@@ -26,6 +26,10 @@ jobs:
- name: Windows (msvc)
rust: nightly-x86_64-pc-windows-msvc
os: windows
+ flags: /EHsc
+ env:
+ CXXFLAGS: ${{matrix.flags}}
+ RUSTFLAGS: --cfg deny_warnings
steps:
- name: Enable symlinks (windows)
if: matrix.os == 'windows'
@@ -42,7 +46,8 @@ jobs:
# still ensure the test is kept passing on the basis of the scheduled
# builds.
if: matrix.os && github.event_name != 'schedule'
- run: echo '::set-env name=RUSTFLAGS::--cfg skip_ui_tests'
+ run: echo "RUSTFLAGS=--cfg skip_ui_tests $RUSTFLAGS" >> $GITHUB_ENV
+ shell: bash
- run: cargo run --manifest-path demo/Cargo.toml
- run: cargo test --workspace --exclude cxx-test-suite
@@ -63,7 +68,7 @@ jobs:
chmod +x bin/buck
echo bin >> $GITHUB_PATH
- name: Install lld
- run: sudo apt install lld
+ run: sudo apt-get install lld
- name: Vendor dependencies
run: |
cp third-party/Cargo.lock .
@@ -79,7 +84,7 @@ jobs:
- uses: actions/checkout@v2
- name: Install Bazel
run: |
- wget -q -O install.sh https://github.com/bazelbuild/bazel/releases/download/2.1.1/bazel-2.1.1-installer-linux-x86_64.sh
+ wget -q -O install.sh https://github.com/bazelbuild/bazel/releases/download/4.0.0/bazel-4.0.0-installer-linux-x86_64.sh
chmod +x install.sh
./install.sh --user
echo $HOME/bin >> $GITHUB_PATH
@@ -89,7 +94,19 @@ jobs:
clippy:
name: Clippy
runs-on: ubuntu-latest
+ if: github.event_name != 'pull_request'
steps:
- uses: actions/checkout@v2
- uses: dtolnay/rust-toolchain@clippy
- run: cargo clippy --workspace --tests -- -Dclippy::all
+
+ clang-tidy:
+ name: Clang Tidy
+ runs-on: ubuntu-latest
+ if: github.event_name != 'pull_request'
+ steps:
+ - uses: actions/checkout@v2
+ - name: Install clang-tidy
+ run: sudo apt-get install clang-tidy-11
+ - name: Run clang-tidy
+ run: clang-tidy-11 src/cxx.cc --warnings-as-errors=*
diff --git a/.github/workflows/site.yml b/.github/workflows/site.yml
index 73b8186e..d6b0ca6d 100644
--- a/.github/workflows/site.yml
+++ b/.github/workflows/site.yml
@@ -6,6 +6,7 @@ on:
- master
paths:
- book/**
+ - .github/workflows/site.yml
jobs:
deploy:
@@ -15,16 +16,15 @@ jobs:
- uses: actions/checkout@v2
- name: Get mdBook
- working-directory: book
run: |
- export MDBOOK_VERSION="v0.4.4"
+ export MDBOOK_VERSION="dtolnay"
export MDBOOK_TARBALL="mdbook-${MDBOOK_VERSION}-x86_64-unknown-linux-gnu.tar.gz"
- export MDBOOK_URL="https://github.com/rust-lang/mdBook/releases/download/${MDBOOK_VERSION}/${MDBOOK_TARBALL}"
- curl -Lf "${MDBOOK_URL}" | tar -xz
+ export MDBOOK_URL="https://github.com/dtolnay/mdBook/releases/download/cxx/${MDBOOK_TARBALL}"
+ curl -Lf "${MDBOOK_URL}" | tar -xzC book
+ book/mdbook --version
- name: Build
- working-directory: book
- run: ./mdbook build
+ run: book/build.sh
- name: Push to gh-pages
working-directory: book/build
diff --git a/.vscode/launch.json b/.vscode/launch.json
index 244f5c4a..0218f47f 100644
--- a/.vscode/launch.json
+++ b/.vscode/launch.json
@@ -12,6 +12,18 @@
"kind": "bin"
}
}
+ },
+ {
+ "name": "Debug cargo tests",
+ "type": "lldb",
+ "request": "launch",
+ "cargo": {
+ "args": ["test", "--no-run"],
+ "filter": {
+ "name": "test",
+ "kind": "test"
+ }
+ }
}
]
}
diff --git a/Cargo.toml b/Cargo.toml
index f0692aee..e2b189ec 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,13 +1,14 @@
[package]
name = "cxx"
-version = "0.5.9" # remember to update html_root_url
+version = "1.0.42" # remember to update html_root_url
authors = ["David Tolnay <dtolnay@gmail.com>"]
edition = "2018"
-links = "cxxbridge05"
+links = "cxxbridge1"
license = "MIT OR Apache-2.0"
description = "Safe interop between Rust and C++"
repository = "https://github.com/dtolnay/cxx"
documentation = "https://docs.rs/cxx"
+homepage = "https://cxx.rs"
readme = "README.md"
exclude = ["/demo", "/gen", "/syntax", "/third-party"]
keywords = ["ffi"]
@@ -20,16 +21,16 @@ default = ["cxxbridge-flags/default"] # c++11
"c++20" = ["cxxbridge-flags/c++20"]
[dependencies]
-cxxbridge-macro = { version = "=0.5.9", path = "macro" }
+cxxbridge-macro = { version = "=1.0.42", path = "macro" }
link-cplusplus = "1.0"
[build-dependencies]
cc = "1.0.49"
-cxxbridge-flags = { version = "=0.5.9", path = "flags", default-features = false }
+cxxbridge-flags = { version = "=1.0.42", path = "flags", default-features = false }
[dev-dependencies]
-cxx-build = { version = "=0.5.9", path = "gen/build" }
-cxx-gen = { version = "0.6", path = "gen/lib" }
+cxx-build = { version = "=1.0.42", path = "gen/build" }
+cxx-gen = { version = "0.7", path = "gen/lib" }
cxx-test-suite = { version = "0", path = "tests/ffi" }
rustversion = "1.0"
trybuild = { version = "1.0.33", features = ["diff"] }
@@ -39,3 +40,7 @@ members = ["demo", "flags", "gen/build", "gen/cmd", "gen/lib", "macro", "tests/f
[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]
+
+[patch.crates-io]
+cxx = { path = "." }
+cxx-build = { path = "gen/build" }
diff --git a/README.md b/README.md
index 9c0289b2..7dd4ded9 100644
--- a/README.md
+++ b/README.md
@@ -18,17 +18,24 @@ can be 100% safe.
```toml
[dependencies]
-cxx = "0.5"
+cxx = "1.0"
[build-dependencies]
-cxx-build = "0.5"
+cxx-build = "1.0"
```
-*Compiler support: requires rustc 1.43+ and c++11 or newer*<br>
+*Compiler support: requires rustc 1.48+ and c++11 or newer*<br>
*[Release notes](https://github.com/dtolnay/cxx/releases)*
<br>
+## Guide
+
+Please see **<https://cxx.rs>** for a tutorial, reference material, and example
+code.
+
+<br>
+
## Overview
The idea is that we define the signatures of both sides of our FFI boundary
@@ -90,7 +97,7 @@ mod ffi {
fn next_chunk(buf: &mut MultiBuf) -> &[u8];
}
- extern "C++" {
+ unsafe extern "C++" {
// One or more headers with the matching C++ declarations. Our code
// generators don't read it but it gets #include'd and used in static
// assertions to ensure our picture of the FFI boundary is accurate.
@@ -151,19 +158,19 @@ items:
- **Functions** &mdash; implemented in either language, callable from the other
language.
-Within the `extern "C"` part of the CXX bridge we list the types and functions
-for which C++ is the source of truth, as well as the header(s) that declare
-those APIs. In the future it's possible that this section could be generated
-bindgen-style from the headers but for now we need the signatures written out;
-static assertions will verify that they are accurate.
+Within the `extern "Rust"` part of the CXX bridge we list the types and
+functions for which Rust is the source of truth. These all implicitly refer to
+the `super` module, the parent module of the CXX bridge. You can think of the
+two items listed in the example above as being like `use super::MultiBuf` and
+`use super::next_chunk` except re-exported to C++. The parent module will either
+contain the definitions directly for simple things, or contain the relevant
+`use` statements to bring them into scope from elsewhere.
-Within the `extern "Rust"` part, we list types and functions for which Rust is
-the source of truth. These all implicitly refer to the `super` module, the
-parent module of the CXX bridge. You can think of the two items listed in the
-example above as being like `use super::ThingR` and `use super::print_r` except
-re-exported to C++. The parent module will either contain the definitions
-directly for simple things, or contain the relevant `use` statements to bring
-them into scope from elsewhere.
+Within the `extern "C++"` part, we list types and functions for which C++ is the
+source of truth, as well as the header(s) that declare those APIs. In the future
+it's possible that this section could be generated bindgen-style from the
+headers but for now we need the signatures written out; static assertions will
+verify that they are accurate.
Your function implementations themselves, whether in C++ or Rust, *do not* need
to be defined as `extern "C"` ABI or no\_mangle. CXX will put in the right shims
@@ -228,7 +235,7 @@ set up any additional source files and compiler flags as normal.
# Cargo.toml
[build-dependencies]
-cxx-build = "0.5"
+cxx-build = "1.0"
```
```rust
@@ -315,12 +322,16 @@ returns of functions.
<tr><th>name in Rust</th><th>name in C++</th><th>restrictions</th></tr>
<tr><td>String</td><td>rust::String</td><td></td></tr>
<tr><td>&amp;str</td><td>rust::Str</td><td></td></tr>
-<tr><td>&amp;[u8]</td><td>rust::Slice&lt;uint8_t&gt;</td><td><sup><i>arbitrary &amp;[T] not implemented yet</i></sup></td></tr>
-<tr><td><a href="https://docs.rs/cxx/0.5/cxx/struct.CxxString.html">CxxString</a></td><td>std::string</td><td><sup><i>cannot be passed by value</i></sup></td></tr>
+<tr><td>&amp;[T]</td><td>rust::Slice&lt;const T&gt;</td><td><sup><i>cannot hold opaque C++ type</i></sup></td></tr>
+<tr><td>&amp;mut [T]</td><td>rust::Slice&lt;T&gt;</td><td><sup><i>cannot hold opaque C++ type</i></sup></td></tr>
+<tr><td><a href="https://docs.rs/cxx/1.0/cxx/struct.CxxString.html">CxxString</a></td><td>std::string</td><td><sup><i>cannot be passed by value</i></sup></td></tr>
<tr><td>Box&lt;T&gt;</td><td>rust::Box&lt;T&gt;</td><td><sup><i>cannot hold opaque C++ type</i></sup></td></tr>
-<tr><td><a href="https://docs.rs/cxx/0.5/cxx/struct.UniquePtr.html">UniquePtr&lt;T&gt;</a></td><td>std::unique_ptr&lt;T&gt;</td><td><sup><i>cannot hold opaque Rust type</i></sup></td></tr>
+<tr><td><a href="https://docs.rs/cxx/1.0/cxx/struct.UniquePtr.html">UniquePtr&lt;T&gt;</a></td><td>std::unique_ptr&lt;T&gt;</td><td><sup><i>cannot hold opaque Rust type</i></sup></td></tr>
+<tr><td><a href="https://docs.rs/cxx/1.0/cxx/struct.SharedPtr.html">SharedPtr&lt;T&gt;</a></td><td>std::shared_ptr&lt;T&gt;</td><td><sup><i>cannot hold opaque Rust type</i></sup></td></tr>
+<tr><td>[T; N]</td><td>std::array&lt;T, N&gt;</td><td><sup><i>cannot hold opaque C++ type</i></sup></td></tr>
<tr><td>Vec&lt;T&gt;</td><td>rust::Vec&lt;T&gt;</td><td><sup><i>cannot hold opaque C++ type</i></sup></td></tr>
-<tr><td><a href="https://docs.rs/cxx/0.5/cxx/struct.CxxVector.html">CxxVector&lt;T&gt;</a></td><td>std::vector&lt;T&gt;</td><td><sup><i>cannot be passed by value, cannot hold opaque Rust type</i></sup></td></tr>
+<tr><td><a href="https://docs.rs/cxx/1.0/cxx/struct.CxxVector.html">CxxVector&lt;T&gt;</a></td><td>std::vector&lt;T&gt;</td><td><sup><i>cannot be passed by value, cannot hold opaque Rust type</i></sup></td></tr>
+<tr><td>*mut T, *const T</td><td>T*, const T*</td><td><sup><i>fn with a raw pointer argument must be declared unsafe to call</i></sup></td></tr>
<tr><td>fn(T, U) -&gt; V</td><td>rust::Fn&lt;V(T, U)&gt;</td><td><sup><i>only passing from Rust to C++ is implemented so far</i></sup></td></tr>
<tr><td>Result&lt;T&gt;</td><td>throw/catch</td><td><sup><i>allowed as return type only</i></sup></td></tr>
</table>
@@ -341,7 +352,6 @@ matter of designing a nice API for each in its non-native language.
<tr><td>Option&lt;T&gt;</td><td><sup><i>tbd</i></sup></td></tr>
<tr><td><sup><i>tbd</i></sup></td><td>std::map&lt;K, V&gt;</td></tr>
<tr><td><sup><i>tbd</i></sup></td><td>std::unordered_map&lt;K, V&gt;</td></tr>
-<tr><td><sup><i>tbd</i></sup></td><td>std::shared_ptr&lt;T&gt;</td></tr>
</table>
<br>
diff --git a/WORKSPACE b/WORKSPACE
index 6093eeef..17a0af54 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -1,39 +1,23 @@
+workspace(name = "cxx.rs")
+
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
load("//tools/bazel:vendor.bzl", "vendor")
http_archive(
- name = "io_bazel_rules_rust",
- sha256 = "5ed804fcd10a506a5b8e9e59bc6b3b7f43bc30c87ce4670e6f78df43604894fd",
- strip_prefix = "rules_rust-fdf9655ba95616e0314b4e0ebab40bb0c5fe005c",
- # Master branch as of 2020-07-30
- url = "https://github.com/bazelbuild/rules_rust/archive/fdf9655ba95616e0314b4e0ebab40bb0c5fe005c.tar.gz",
-)
-
-http_archive(
- name = "bazel_skylib",
- sha256 = "97e70364e9249702246c0e9444bccdc4b847bed1eb03c5a3ece4f83dfe6abc44",
+ name = "rules_rust",
+ sha256 = "e6d835ee673f388aa5b62dc23d82db8fc76497e93fa47d8a4afe97abaf09b10d",
+ strip_prefix = "rules_rust-f37b9d6a552e9412285e627f30cb124e709f4f7a",
urls = [
- "https://mirror.bazel.build/github.com/bazelbuild/bazel-skylib/releases/download/1.0.2/bazel-skylib-1.0.2.tar.gz",
- "https://github.com/bazelbuild/bazel-skylib/releases/download/1.0.2/bazel-skylib-1.0.2.tar.gz",
+ # Master branch as of 2021-01-27
+ "https://github.com/bazelbuild/rules_rust/archive/f37b9d6a552e9412285e627f30cb124e709f4f7a.tar.gz",
],
)
-load("@io_bazel_rules_rust//:workspace.bzl", "bazel_version")
-
-bazel_version(name = "bazel_version")
-
-load("@io_bazel_rules_rust//rust:repositories.bzl", "rust_repository_set")
-
-rust_repository_set(
- name = "rust_1_47_linux",
- exec_triple = "x86_64-unknown-linux-gnu",
- version = "1.47.0",
-)
+load("@rules_rust//rust:repositories.bzl", "rust_repositories")
-rust_repository_set(
- name = "rust_1_47_darwin",
- exec_triple = "x86_64-apple-darwin",
- version = "1.47.0",
+rust_repositories(
+ edition = "2018",
+ version = "1.50.0",
)
vendor(
diff --git a/book/.gitignore b/book/.gitignore
index 690b5b80..72775071 100644
--- a/book/.gitignore
+++ b/book/.gitignore
@@ -1,2 +1,3 @@
/build
/mdbook
+/node_modules
diff --git a/book/book.toml b/book/book.toml
index d3187e0a..066f3a62 100644
--- a/book/book.toml
+++ b/book/book.toml
@@ -1,14 +1,22 @@
[book]
-title = "CXX"
+#title = "Rust ♡ C++"
authors = ["David Tolnay"]
-description = "Guide for the `cxx` crate, a safe approach to FFI between Rust and C++."
+description = "CXX — safe interop between Rust and C++"
[rust]
edition = "2018"
[build]
build-dir = "build"
+create-missing = false
[output.html]
+additional-css = ["css/cxx.css"]
cname = "cxx.rs"
git-repository-url = "https://github.com/dtolnay/cxx"
+playground = { copyable = false }
+print = { enable = false }
+
+[output.html.redirect]
+"binding/index.html" = "../bindings.html"
+"build/index.html" = "../building.html"
diff --git a/book/build.js b/book/build.js
new file mode 100755
index 00000000..2cda5860
--- /dev/null
+++ b/book/build.js
@@ -0,0 +1,104 @@
+#!/usr/bin/env node
+
+const fs = require('fs');
+const cheerio = require('cheerio');
+const hljs = require('./build/highlight.js');
+const Entities = require('html-entities').AllHtmlEntities;
+const entities = new Entities();
+
+const githublink = `\
+<li class="part-title">\
+<a href="https://github.com/dtolnay/cxx">\
+<i class="fa fa-github"></i>\
+https://github.com/dtolnay/cxx\
+</a>\
+</li>`;
+
+const opengraph = `\
+<meta property="og:image" content="https://cxx.rs/cxx.png" />\
+<meta property="og:site_name" content="CXX" />\
+<meta property="og:title" content="CXX — safe interop between Rust and C++" />\
+<meta name="twitter:image:src" content="https://cxx.rs/cxx.png" />\
+<meta name="twitter:site" content="@davidtolnay" />\
+<meta name="twitter:card" content="summary" />\
+<meta name="twitter:title" content="CXX — safe interop between Rust and C++" />`;
+
+const htmljs = `\
+var html = document.querySelector('html');
+html.classList.remove('no-js');
+html.classList.add('js');`;
+
+const dirs = ['build'];
+while (dirs.length) {
+ const dir = dirs.pop();
+ fs.readdirSync(dir).forEach((entry) => {
+ path = dir + '/' + entry;
+ const stat = fs.statSync(path);
+ if (stat.isDirectory()) {
+ dirs.push(path);
+ return;
+ }
+
+ if (!path.endsWith('.html')) {
+ return;
+ }
+
+ const index = fs.readFileSync(path, 'utf8');
+ const $ = cheerio.load(index, { decodeEntities: false });
+
+ $('head').append(opengraph);
+ $('script:nth-of-type(3)').text(htmljs);
+ $('nav#sidebar ol.chapter').append(githublink);
+ $('head link[href="tomorrow-night.css"]').attr('disabled', true);
+ $('head link[href="ayu-highlight.css"]').attr('disabled', true);
+ $('button#theme-toggle').attr('style', 'display:none');
+ $('pre code').each(function () {
+ const node = $(this);
+ const langClass = node.attr('class').split(' ', 2)[0];
+ if (!langClass.startsWith('language-')) {
+ return;
+ }
+ const lang = langClass.replace('language-', '');
+ const lines = node.html().split('\n');
+ const boring = lines.map((line) =>
+ line.includes('<span class="boring">')
+ );
+ const ellipsis = lines.map((line) => line.includes('// ...'));
+ const target = entities.decode(node.text());
+ const highlighted = hljs.highlight(lang, target).value;
+ const result = highlighted
+ .split('\n')
+ .map(function (line, i) {
+ if (boring[i]) {
+ line = '<span class="boring">' + line;
+ } else if (ellipsis[i]) {
+ line = '<span class="ellipsis">' + line;
+ }
+ if (i > 0 && (boring[i - 1] || ellipsis[i - 1])) {
+ line = '</span>' + line;
+ }
+ return line;
+ })
+ .join('\n');
+ node.text(result);
+ node.removeClass(langClass);
+ if (!node.hasClass('focuscomment')) {
+ node.addClass('hidelines');
+ node.addClass('hide-boring');
+ }
+ });
+ $('code').each(function () {
+ $(this).addClass('hljs');
+ });
+
+ const out = $.html();
+ fs.writeFileSync(path, out);
+ });
+}
+
+fs.copyFileSync('build/highlight.css', 'build/tomorrow-night.css');
+fs.copyFileSync('build/highlight.css', 'build/ayu-highlight.css');
+
+var bookjs = fs.readFileSync('build/book.js', 'utf8');
+bookjs = bookjs.replace('set_theme(theme, false);', '');
+fs.writeFileSync('build/book.js', bookjs);
diff --git a/book/build.sh b/book/build.sh
new file mode 100755
index 00000000..783d304b
--- /dev/null
+++ b/book/build.sh
@@ -0,0 +1,17 @@
+#!/bin/bash
+
+set -e
+
+cd "$(dirname "$0")"
+
+if [ -f ./mdbook ]; then
+ ./mdbook build
+else
+ mdbook build
+fi
+
+if [ ! -d node_modules ]; then
+ npm install
+fi
+
+./build.js
diff --git a/book/css/cxx.css b/book/css/cxx.css
new file mode 100644
index 00000000..647f4f71
--- /dev/null
+++ b/book/css/cxx.css
@@ -0,0 +1,44 @@
+:root {
+ --sidebar-width: 310px;
+}
+
+.badges img {
+ margin: 0 7px 7px 0;
+}
+
+.badges {
+ margin: 16px 0 120px;
+}
+
+.boring {
+ opacity: 0.5;
+}
+
+.no-js code:not(.focuscomment) .boring {
+ display: none;
+}
+
+.js code:not(.hide-boring) .ellipsis {
+ display: none;
+}
+
+.focuscomment .hljs-comment {
+ font-weight: bold;
+ color: black;
+}
+
+.focuscomment .boring {
+ opacity: 0.5;
+}
+
+nav.sidebar li.part-title i.fa-github {
+ font-size: 20px;
+ padding-right: 5px;
+ padding-top: 12px;
+ position: relative;
+ top: 1px;
+}
+
+.sidebar .sidebar-scrollbox {
+ padding: 10px 0 10px 10px;
+}
diff --git a/book/diagram/.gitignore b/book/diagram/.gitignore
new file mode 100644
index 00000000..81631c69
--- /dev/null
+++ b/book/diagram/.gitignore
@@ -0,0 +1,2 @@
+/bin
+/build
diff --git a/book/diagram/Makefile b/book/diagram/Makefile
new file mode 100644
index 00000000..161da349
--- /dev/null
+++ b/book/diagram/Makefile
@@ -0,0 +1,12 @@
+all: overview.svg
+
+build/bin/svgbob:
+ cargo install --git https://github.com/ivanceras/svgbob --rev df01674c47350665158ececa476e63f51c58a9c7 --root build
+
+%.svg: %.ascii build/bin/svgbob
+ build/bin/svgbob $< > $@
+
+clean:
+ rm -f *.svg
+
+.PHONY: all clean
diff --git a/book/diagram/overview.ascii b/book/diagram/overview.ascii
new file mode 100644
index 00000000..dd1c4b93
--- /dev/null
+++ b/book/diagram/overview.ascii
@@ -0,0 +1,13 @@
+ .-----------------------------.
+ | #[cxx::bridge] mod |
+ | description of boundary |
+ '--------------+--------------'
+ |
+ |
+ "Macro expansion" | "Code generation"
+ +---------------+---------------+
+ Safe | |
+ straightforward v v Straightforward
+ "Rust APIs".----------------. "Hidden C ABI".---------------. "C++ APIs"
+Rust <----------->| "Rust bindings"|<~~~~~~~~~~~~~>| "C++ bindings"|<-----------> "C++"
+code '----------------' '---------------' code
diff --git a/book/diagram/overview.svg b/book/diagram/overview.svg
new file mode 100644
index 00000000..e5154668
--- /dev/null
+++ b/book/diagram/overview.svg
@@ -0,0 +1,132 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="696" height="224">
+ <style>line, path, circle,rect,polygon {
+ stroke: black;
+ stroke-width: 2;
+ stroke-opacity: 1;
+ fill-opacity: 1;
+ stroke-linecap: round;
+ stroke-linejoin: miter;
+ }
+
+ text {
+ fill: black;
+ }
+ rect.backdrop{
+ stroke: none;
+ fill: white;
+ }
+ .broken{
+ stroke-dasharray: 8;
+ }
+ .filled{
+ fill: black;
+ }
+ .bg_filled{
+ fill: white;
+ }
+ .nofill{
+ fill: white;
+ }
+
+ text {
+ font-family: monospace;
+ font-size: 14px;
+ }
+
+ .end_marked_arrow{
+ marker-end: url(#arrow);
+ }
+ .start_marked_arrow{
+ marker-start: url(#arrow);
+ }
+
+ .end_marked_diamond{
+ marker-end: url(#diamond);
+ }
+ .start_marked_diamond{
+ marker-start: url(#diamond);
+ }
+
+ .end_marked_circle{
+ marker-end: url(#circle);
+ }
+ .start_marked_circle{
+ marker-start: url(#circle);
+ }
+
+ .end_marked_open_circle{
+ marker-end: url(#open_circle);
+ }
+ .start_marked_open_circle{
+ marker-start: url(#open_circle);
+ }
+
+ .end_marked_big_open_circle{
+ marker-end: url(#big_open_circle);
+ }
+ .start_marked_big_open_circle{
+ marker-start: url(#big_open_circle);
+ }
+
+
+ </style>
+ <defs>
+ <marker id="arrow" viewBox="-2 -2 8 8" refX="4" refY="2" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
+ <polygon points="0,0 0,4 4,2 0,0"></polygon>
+ </marker>
+ <marker id="diamond" viewBox="-2 -2 8 8" refX="4" refY="2" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
+ <polygon points="0,2 2,0 4,2 2,4 0,2"></polygon>
+ </marker>
+ <marker id="circle" viewBox="0 0 8 8" refX="4" refY="4" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
+ <circle cx="4" cy="4" r="2" class="filled"></circle>
+ </marker>
+ <marker id="open_circle" viewBox="0 0 8 8" refX="4" refY="4" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
+ <circle cx="4" cy="4" r="2" class="bg_filled"></circle>
+ </marker>
+ <marker id="big_open_circle" viewBox="0 0 8 8" refX="4" refY="4" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
+ <circle cx="4" cy="4" r="3" class="bg_filled"></circle>
+ </marker>
+ </defs>
+ <rect class="backdrop" x="0" y="0" width="696" height="224"></rect>
+ <rect x="148" y="168" width="136" height="32" class="solid nofill" rx="4"></rect>
+ <text x="162" y="188" >Rust bindings</text>
+ <rect x="412" y="168" width="128" height="32" class="solid nofill" rx="4"></rect>
+ <text x="426" y="188" >C++ bindings</text>
+ <text x="266" y="28" >#[cxx::bridge]</text>
+ <text x="394" y="28" >mod</text>
+ <text x="250" y="44" >description</text>
+ <text x="346" y="44" >of</text>
+ <text x="370" y="44" >boundary</text>
+ <line x1="220" y1="120" x2="220" y2="160" class="solid end_marked_arrow"></line>
+ <line x1="476" y1="120" x2="476" y2="160" class="solid end_marked_arrow"></line>
+ <text x="74" y="140" >Safe</text>
+ <text x="34" y="156" >straightforward</text>
+ <text x="530" y="156" >Straightforward</text>
+ <polygon points="48,180 40,184 48,188" class="filled"></polygon>
+ <line x1="48" y1="184" x2="144" y2="184" class="solid end_marked_arrow"></line>
+ <polygon points="296,180 288,184 296,188" class="filled"></polygon>
+ <line x1="296" y1="184" x2="408" y2="184" class="broken end_marked_arrow"></line>
+ <polygon points="552,180 544,184 552,188" class="filled"></polygon>
+ <line x1="552" y1="184" x2="648" y2="184" class="solid end_marked_arrow"></line>
+ <text x="2" y="188" >Rust</text>
+ <text x="2" y="204" >code</text>
+ <text x="658" y="204" >code</text>
+ <text x="202" y="108" >Macro expansion</text>
+ <text x="370" y="108" >Code generation</text>
+ <text x="58" y="172" >Rust APIs</text>
+ <text x="298" y="172" >Hidden C ABI</text>
+ <text x="562" y="172" >C++ APIs</text>
+ <text x="658" y="188" >C++</text>
+ <g>
+ <path d="M 228,8 A 8,8 0,0,0 220,16" class="nofill"></path>
+ <line x1="228" y1="8" x2="468" y2="8" class="solid"></line>
+ <path d="M 468,8 A 8,8 0,0,1 476,16" class="nofill"></path>
+ <line x1="220" y1="16" x2="220" y2="48" class="solid"></line>
+ <line x1="476" y1="16" x2="476" y2="48" class="solid"></line>
+ <path d="M 220,48 A 8,8 0,0,0 228,56" class="nofill"></path>
+ <line x1="228" y1="56" x2="468" y2="56" class="solid"></line>
+ <line x1="348" y1="56" x2="348" y2="120" class="solid"></line>
+ <path d="M 476,48 A 8,8 0,0,1 468,56" class="nofill"></path>
+ <line x1="220" y1="120" x2="476" y2="120" class="solid"></line>
+ </g>
+</svg>
diff --git a/book/package-lock.json b/book/package-lock.json
new file mode 100644
index 00000000..dec26ad1
--- /dev/null
+++ b/book/package-lock.json
@@ -0,0 +1,207 @@
+{
+ "name": "cxx-book-build",
+ "version": "0.0.0",
+ "lockfileVersion": 1,
+ "requires": true,
+ "dependencies": {
+ "boolbase": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
+ "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24="
+ },
+ "cheerio": {
+ "version": "0.22.0",
+ "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-0.22.0.tgz",
+ "integrity": "sha1-qbqoYKP5tZWmuBsahocxIe06Jp4=",
+ "requires": {
+ "css-select": "~1.2.0",
+ "dom-serializer": "~0.1.0",
+ "entities": "~1.1.1",
+ "htmlparser2": "^3.9.1",
+ "lodash.assignin": "^4.0.9",
+ "lodash.bind": "^4.1.4",
+ "lodash.defaults": "^4.0.1",
+ "lodash.filter": "^4.4.0",
+ "lodash.flatten": "^4.2.0",
+ "lodash.foreach": "^4.3.0",
+ "lodash.map": "^4.4.0",
+ "lodash.merge": "^4.4.0",
+ "lodash.pick": "^4.2.1",
+ "lodash.reduce": "^4.4.0",
+ "lodash.reject": "^4.4.0",
+ "lodash.some": "^4.4.0"
+ }
+ },
+ "css-select": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz",
+ "integrity": "sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg=",
+ "requires": {
+ "boolbase": "~1.0.0",
+ "css-what": "2.1",
+ "domutils": "1.5.1",
+ "nth-check": "~1.0.1"
+ }
+ },
+ "css-what": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.3.tgz",
+ "integrity": "sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg=="
+ },
+ "dom-serializer": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.1.tgz",
+ "integrity": "sha512-l0IU0pPzLWSHBcieZbpOKgkIn3ts3vAh7ZuFyXNwJxJXk/c4Gwj9xaTJwIDVQCXawWD0qb3IzMGH5rglQaO0XA==",
+ "requires": {
+ "domelementtype": "^1.3.0",
+ "entities": "^1.1.1"
+ }
+ },
+ "domelementtype": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz",
+ "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w=="
+ },
+ "domhandler": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.2.tgz",
+ "integrity": "sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==",
+ "requires": {
+ "domelementtype": "1"
+ }
+ },
+ "domutils": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz",
+ "integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=",
+ "requires": {
+ "dom-serializer": "0",
+ "domelementtype": "1"
+ }
+ },
+ "entities": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz",
+ "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w=="
+ },
+ "html-entities": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-1.3.1.tgz",
+ "integrity": "sha512-rhE/4Z3hIhzHAUKbW8jVcCyuT5oJCXXqhN/6mXXVCpzTmvJnoH2HL/bt3EZ6p55jbFJBeAe1ZNpL5BugLujxNA=="
+ },
+ "htmlparser2": {
+ "version": "3.10.1",
+ "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz",
+ "integrity": "sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==",
+ "requires": {
+ "domelementtype": "^1.3.1",
+ "domhandler": "^2.3.0",
+ "domutils": "^1.5.1",
+ "entities": "^1.1.1",
+ "inherits": "^2.0.1",
+ "readable-stream": "^3.1.1"
+ }
+ },
+ "inherits": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
+ },
+ "lodash.assignin": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/lodash.assignin/-/lodash.assignin-4.2.0.tgz",
+ "integrity": "sha1-uo31+4QesKPoBEIysOJjqNxqKKI="
+ },
+ "lodash.bind": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/lodash.bind/-/lodash.bind-4.2.1.tgz",
+ "integrity": "sha1-euMBfpOWIqwxt9fX3LGzTbFpDTU="
+ },
+ "lodash.defaults": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz",
+ "integrity": "sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw="
+ },
+ "lodash.filter": {
+ "version": "4.6.0",
+ "resolved": "https://registry.npmjs.org/lodash.filter/-/lodash.filter-4.6.0.tgz",
+ "integrity": "sha1-ZosdSYFgOuHMWm+nYBQ+SAtMSs4="
+ },
+ "lodash.flatten": {
+ "version": "4.4.0",
+ "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz",
+ "integrity": "sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8="
+ },
+ "lodash.foreach": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/lodash.foreach/-/lodash.foreach-4.5.0.tgz",
+ "integrity": "sha1-Gmo16s5AEoDH8G3d7DUWWrJ+PlM="
+ },
+ "lodash.map": {
+ "version": "4.6.0",
+ "resolved": "https://registry.npmjs.org/lodash.map/-/lodash.map-4.6.0.tgz",
+ "integrity": "sha1-dx7Hg540c9nEzeKLGTlMNWL09tM="
+ },
+ "lodash.merge": {
+ "version": "4.6.2",
+ "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
+ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="
+ },
+ "lodash.pick": {
+ "version": "4.4.0",
+ "resolved": "https://registry.npmjs.org/lodash.pick/-/lodash.pick-4.4.0.tgz",
+ "integrity": "sha1-UvBWEP/53tQiYRRB7R/BI6AwAbM="
+ },
+ "lodash.reduce": {
+ "version": "4.6.0",
+ "resolved": "https://registry.npmjs.org/lodash.reduce/-/lodash.reduce-4.6.0.tgz",
+ "integrity": "sha1-8atrg5KZrUj3hKu/R2WW8DuRTTs="
+ },
+ "lodash.reject": {
+ "version": "4.6.0",
+ "resolved": "https://registry.npmjs.org/lodash.reject/-/lodash.reject-4.6.0.tgz",
+ "integrity": "sha1-gNZJLcFHCGS79YNTO2UfQqn1JBU="
+ },
+ "lodash.some": {
+ "version": "4.6.0",
+ "resolved": "https://registry.npmjs.org/lodash.some/-/lodash.some-4.6.0.tgz",
+ "integrity": "sha1-G7nzFO9ri63tE7VJFpsqlF62jk0="
+ },
+ "nth-check": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz",
+ "integrity": "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==",
+ "requires": {
+ "boolbase": "~1.0.0"
+ }
+ },
+ "readable-stream": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
+ "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
+ "requires": {
+ "inherits": "^2.0.3",
+ "string_decoder": "^1.1.1",
+ "util-deprecate": "^1.0.1"
+ }
+ },
+ "safe-buffer": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="
+ },
+ "string_decoder": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
+ "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
+ "requires": {
+ "safe-buffer": "~5.2.0"
+ }
+ },
+ "util-deprecate": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+ "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
+ }
+ }
+}
diff --git a/book/package.json b/book/package.json
new file mode 100644
index 00000000..092cea21
--- /dev/null
+++ b/book/package.json
@@ -0,0 +1,12 @@
+{
+ "name": "cxx-book-build",
+ "version": "0.0.0",
+ "main": "build.js",
+ "dependencies": {
+ "cheerio": "^0.22.0",
+ "html-entities": "^1.3.1"
+ },
+ "prettier": {
+ "singleQuote": true
+ }
+}
diff --git a/book/src/404.md b/book/src/404.md
new file mode 100644
index 00000000..c5b71f28
--- /dev/null
+++ b/book/src/404.md
@@ -0,0 +1,5 @@
+### Whoops, this page doesn’t exist :-(
+
+<br>
+
+<img src="https://www.rust-lang.org/static/images/ferris-error.png" alt="ferris" width="325">
diff --git a/book/src/SUMMARY.md b/book/src/SUMMARY.md
index f3fa924f..a8f89bfc 100644
--- a/book/src/SUMMARY.md
+++ b/book/src/SUMMARY.md
@@ -1,3 +1,37 @@
# Summary
-[Rust ❤️ C++](about.md)
+- [Rust ❤️ C++](index.md)
+
+- [Core concepts](concepts.md)
+
+- [Tutorial](tutorial.md)
+
+- [Other Rust&ndash;C++ interop tools](context.md)
+
+- [Multi-language build system options](building.md)
+ - [Cargo](build/cargo.md)
+ - [Bazel](build/bazel.md)
+ - [CMake](build/cmake.md)
+ - [More...](build/other.md)
+
+- [Reference: the bridge module](reference.md)
+ - [extern "Rust"](extern-rust.md)
+ - [extern "C++"](extern-c++.md)
+ - [Shared types](shared.md)
+ - [Attributes](attributes.md)
+ - [Async functions](async.md)
+ - [Error handling](binding/result.md)
+
+- [Reference: built-in bindings](bindings.md)
+ - [String &mdash; rust::String](binding/string.md)
+ - [&str &mdash; rust::Str](binding/str.md)
+ - [&&#91;T&#93;, &mut &#91;T&#93; &mdash; rust::Slice\<T\>](binding/slice.md)
+ - [CxxString &mdash; std::string](binding/cxxstring.md)
+ - [Box\<T\> &mdash; rust::Box\<T\>](binding/box.md)
+ - [UniquePtr\<T\> &mdash; std::unique\_ptr\<T\>](binding/uniqueptr.md)
+ - [SharedPtr\<T\> &mdash; std::shared\_ptr\<T\>](binding/sharedptr.md)
+ - [Vec\<T\> &mdash; rust::Vec\<T\>](binding/vec.md)
+ - [CxxVector\<T\> &mdash; std::vector\<T\>](binding/cxxvector.md)
+ - [*mut T, *const T raw pointers](binding/rawptr.md)
+ - [Function pointers](binding/fn.md)
+ - [Result\<T\>](binding/result.md)
diff --git a/book/src/about.md b/book/src/about.md
deleted file mode 100644
index 6e63bf5e..00000000
--- a/book/src/about.md
+++ /dev/null
@@ -1 +0,0 @@
-### Coming soon
diff --git a/book/src/async.md b/book/src/async.md
new file mode 100644
index 00000000..b4c696a3
--- /dev/null
+++ b/book/src/async.md
@@ -0,0 +1,86 @@
+{{#title Async functions — Rust ♡ C++}}
+# Async functions
+
+Direct FFI of async functions is absolutely in scope for CXX (on C++20 and up)
+but is not implemented yet in the current release. We are aiming for an
+implementation that is as easy as:
+
+```rust,noplayground
+#[cxx::bridge]
+mod ffi {
+ unsafe extern "C++" {
+ async fn doThing(arg: Arg) -> Ret;
+ }
+}
+```
+
+```cpp,hidelines
+rust::Future<Ret> doThing(Arg arg) {
+ auto v1 = co_await f();
+ auto v2 = co_await g(arg);
+ co_return v1 + v2;
+}
+```
+
+## Workaround
+
+For now the recommended approach is to handle the return codepath over a oneshot
+channel (such as [`futures::channel::oneshot`]) represented in an opaque Rust
+type on the FFI.
+
+[`futures::channel::oneshot`]: https://docs.rs/futures/0.3.8/futures/channel/oneshot/index.html
+
+```rust,noplayground
+// bridge.rs
+
+use futures::channel::oneshot;
+
+#[cxx::bridge]
+mod ffi {
+ extern "Rust" {
+ type DoThingContext;
+ }
+
+ unsafe extern "C++" {
+ include!("path/to/bridge_shim.h");
+
+ fn shim_doThing(
+ arg: Arg,
+ done: fn(Box<DoThingContext>, ret: Ret),
+ ctx: Box<DoThingContext>,
+ );
+ }
+}
+
+struct DoThingContext(oneshot::Sender<Ret>);
+
+pub async fn do_thing(arg: Arg) -> Ret {
+ let (tx, rx) = oneshot::channel();
+ let context = Box::new(DoThingContext(tx));
+
+ ffi::shim_doThing(
+ arg,
+ |context, ret| { let _ = context.0.send(ret); },
+ context,
+ );
+
+ rx.await.unwrap()
+}
+```
+
+```cpp
+// bridge_shim.cc
+
+#include "path/to/bridge.rs.h"
+#include "rust/cxx.h"
+
+void shim_doThing(
+ Arg arg,
+ rust::Fn<void(rust::Box<DoThingContext> ctx, Ret ret)> done,
+ rust::Box<DoThingContext> ctx) noexcept {
+ doThing(arg)
+ .then([done, ctx(std::move(ctx))](auto &&res) mutable {
+ (*done)(std::move(ctx), std::move(res));
+ });
+}
+```
diff --git a/book/src/attributes.md b/book/src/attributes.md
new file mode 100644
index 00000000..9c33b777
--- /dev/null
+++ b/book/src/attributes.md
@@ -0,0 +1,75 @@
+{{#title Attributes — Rust ♡ C++}}
+# Attributes
+
+## namespace
+
+The top-level cxx::bridge attribute macro takes an optional `namespace` argument
+to control the C++ namespace into which to emit extern Rust items and the
+namespace in which to expect to find the extern C++ items.
+
+```rust,noplayground
+#[cxx::bridge(namespace = "path::of::my::company")]
+mod ffi {
+ extern "Rust" {
+ type MyType; // emitted to path::of::my::company::MyType
+ }
+
+ extern "C++" {
+ type TheirType; // refers to path::of::my::company::TheirType
+ }
+}
+```
+
+Additionally, a `#[namespace = "..."]` attribute may be used inside the bridge
+module on any extern block or individual item. An item will inherit the
+namespace specified on its surrounding extern block if any, otherwise the
+namespace specified with the top level cxx::bridge attribute if any, otherwise
+the global namespace.
+
+```rust,noplayground
+#[cxx::bridge(namespace = "third_priority")]
+mod ffi {
+ #[namespace = "second_priority"]
+ extern "Rust" {
+ fn f();
+
+ #[namespace = "first_priority"]
+ fn g();
+ }
+
+ extern "Rust" {
+ fn h();
+ }
+}
+```
+
+The above would result in functions `::second_priority::f`,
+`::first_priority::g`, `::third_priority::h`.
+
+## rust\_name, cxx\_name
+
+Sometimes you want the Rust name of a function or type to differ from its C++
+name. Importantly, this enables binding multiple overloads of the same C++
+function name using distinct Rust names.
+
+```rust,noplayground
+#[cxx::bridge]
+mod ffi {
+ unsafe extern "C++" {
+ #[rust_name = "i32_overloaded_function"]
+ fn cOverloadedFunction(x: i32) -> String;
+ #[rust_name = "str_overloaded_function"]
+ fn cOverloadedFunction(x: &str) -> String;
+ }
+}
+```
+
+The `#[rust_name = "..."]` attribute replaces the name that Rust should use for
+this function, and an analogous `#[cxx_name = "..."]` attribute replaces the
+name that C++ should use.
+
+Either of the two attributes may be used on extern "Rust" as well as extern
+"C++" functions, according to which one you find clearer in context.
+
+The same attribute works for renaming functions, opaque types, shared
+structs and enums, and enum variants.
diff --git a/book/src/binding/box.md b/book/src/binding/box.md
new file mode 100644
index 00000000..7df19597
--- /dev/null
+++ b/book/src/binding/box.md
@@ -0,0 +1,120 @@
+{{#title rust::Box<T> — Rust ♡ C++}}
+# rust::Box\<T\>
+
+### Public API:
+
+```cpp,hidelines
+// rust/cxx.h
+#
+# #include <type_traits>
+#
+# namespace rust {
+
+template <typename T>
+class Box final {
+public:
+ using element_type = T;
+ using const_pointer =
+ typename std::add_pointer<typename std::add_const<T>::type>::type;
+ using pointer = typename std::add_pointer<T>::type;
+
+ Box(Box &&) noexcept;
+ ~Box() noexcept;
+
+ explicit Box(const T &);
+ explicit Box(T &&);
+
+ Box &operator=(Box &&) noexcept;
+
+ const T *operator->() const noexcept;
+ const T &operator*() const noexcept;
+ T *operator->() noexcept;
+ T &operator*() noexcept;
+
+ template <typename... Fields>
+ static Box in_place(Fields &&...);
+
+ void swap(Box &) noexcept;
+
+ // Important: requires that `raw` came from an into_raw call. Do not
+ // pass a pointer from `new` or any other source.
+ static Box from_raw(T *) noexcept;
+
+ T *into_raw() noexcept;
+};
+#
+# } // namespace rust
+```
+
+### Restrictions:
+
+Box\<T\> does not support T being an opaque C++ type. You should use
+[UniquePtr\<T\>](uniqueptr.md) or [SharedPtr\<T\>](sharedptr.md) instead for
+transferring ownership of opaque C++ types on the language boundary.
+
+If T is an opaque Rust type, the Rust type is required to be [Sized] i.e. size
+known at compile time. In the future we may introduce support for dynamically
+sized opaque Rust types.
+
+[Sized]: https://doc.rust-lang.org/std/marker/trait.Sized.html
+
+## Example
+
+This program uses a Box to pass ownership of some opaque piece of Rust state
+over to C++ and then back to a Rust callback, which is a useful pattern for
+implementing [async functions over FFI](../async.md).
+
+```rust,noplayground
+// src/main.rs
+
+use std::io::Write;
+
+#[cxx::bridge]
+mod ffi {
+ extern "Rust" {
+ type File;
+ }
+
+ unsafe extern "C++" {
+ include!("example/include/example.h");
+
+ fn f(
+ callback: fn(Box<File>, fst: &str, snd: &str),
+ out: Box<File>,
+ );
+ }
+}
+
+pub struct File(std::fs::File);
+
+fn main() {
+ let out = std::fs::File::create("example.log").unwrap();
+
+ ffi::f(
+ |mut out, fst, snd| { let _ = write!(out.0, "{}{}\n", fst, snd); },
+ Box::new(File(out)),
+ );
+}
+```
+
+```cpp
+// include/example.h
+
+#pragma once
+#include "example/src/main.rs.h"
+#include "rust/cxx.h"
+
+void f(rust::Fn<void(rust::Box<File>, rust::Str, rust::Str)> callback,
+ rust::Box<File> out);
+```
+
+```cpp
+// include/example.cc
+
+#include "example/include/example.h"
+
+void f(rust::Fn<void(rust::Box<File>, rust::Str, rust::Str)> callback,
+ rust::Box<File> out) {
+ callback(std::move(out), "fearless", "concurrency");
+}
+```
diff --git a/book/src/binding/cxxstring.md b/book/src/binding/cxxstring.md
new file mode 100644
index 00000000..0b1d7bbd
--- /dev/null
+++ b/book/src/binding/cxxstring.md
@@ -0,0 +1,139 @@
+{{#title std::string — Rust ♡ C++}}
+# std::string
+
+The Rust binding of std::string is called **[`CxxString`]**. See the link for
+documentation of the Rust API.
+
+[`CxxString`]: https://docs.rs/cxx/*/cxx/struct.CxxString.html
+
+### Restrictions:
+
+Rust code can never obtain a CxxString by value. C++'s string requires a move
+constructor and may hold internal pointers, which is not compatible with Rust's
+move behavior. Instead in Rust code we will only ever look at a CxxString
+through a reference or smart pointer, as in &CxxString or Pin\<&mut CxxString\>
+or UniquePtr\<CxxString\>.
+
+In order to construct a CxxString on the stack from Rust, you must use the
+[`let_cxx_string!`] macro which will pin the string properly. The code below
+uses this in one place, and the link covers the syntax.
+
+[`let_cxx_string!`]: https://docs.rs/cxx/*/cxx/macro.let_cxx_string.html
+
+## Example
+
+This example uses C++17's std::variant to build a toy JSON type. JSON can hold
+various types including strings, and JSON's object type is a map with string
+keys. The example demonstrates Rust indexing into one of those maps.
+
+```rust,noplayground
+// src/main.rs
+
+use cxx::let_cxx_string;
+
+#[cxx::bridge]
+mod ffi {
+ unsafe extern "C++" {
+ include!("example/include/json.h");
+
+ #[cxx_name = "json"]
+ type Json;
+ #[cxx_name = "object"]
+ type Object;
+
+ fn isNull(self: &Json) -> bool;
+ fn isNumber(self: &Json) -> bool;
+ fn isString(self: &Json) -> bool;
+ fn isArray(self: &Json) -> bool;
+ fn isObject(self: &Json) -> bool;
+
+ fn getNumber(self: &Json) -> f64;
+ fn getString(self: &Json) -> &CxxString;
+ fn getArray(self: &Json) -> &CxxVector<Json>;
+ fn getObject(self: &Json) -> &Object;
+
+ #[cxx_name = "at"]
+ fn get<'a>(self: &'a Object, key: &CxxString) -> &'a Json;
+
+ fn load_config() -> UniquePtr<Json>;
+ }
+}
+
+fn main() {
+ let config = ffi::load_config();
+
+ let_cxx_string!(key = "name");
+ println!("{}", config.getObject().get(&key).getString());
+}
+```
+
+```cpp
+// include/json.h
+
+#pragma once
+#include <map>
+#include <memory>
+#include <variant>
+#include <vector>
+
+class json final {
+public:
+ static const json null;
+ using number = double;
+ using string = std::string;
+ using array = std::vector<json>;
+ using object = std::map<string, json>;
+
+ json() noexcept = default;
+ json(const json &) = default;
+ json(json &&) = default;
+ template <typename... T>
+ json(T &&...value) : value(std::forward<T>(value)...) {}
+
+ bool isNull() const;
+ bool isNumber() const;
+ bool isString() const;
+ bool isArray() const;
+ bool isObject() const;
+
+ number getNumber() const;
+ const string &getString() const;
+ const array &getArray() const;
+ const object &getObject() const;
+
+private:
+ std::variant<std::monostate, number, string, array, object> value;
+};
+
+using object = json::object;
+
+std::unique_ptr<json> load_config();
+```
+
+```cpp
+// include/json.cc
+
+#include "example/include/json.h"
+#include <initializer_list>
+#include <utility>
+
+const json json::null{};
+bool json::isNull() const { return std::holds_alternative<std::monostate>(value); }
+bool json::isNumber() const { return std::holds_alternative<number>(value); }
+bool json::isString() const { return std::holds_alternative<string>(value); }
+bool json::isArray() const { return std::holds_alternative<array>(value); }
+bool json::isObject() const { return std::holds_alternative<object>(value); }
+json::number json::getNumber() const { return std::get<number>(value); }
+const json::string &json::getString() const { return std::get<string>(value); }
+const json::array &json::getArray() const { return std::get<array>(value); }
+const json::object &json::getObject() const { return std::get<object>(value); }
+
+std::unique_ptr<json> load_config() {
+ return std::make_unique<json>(
+ std::in_place_type<json::object>,
+ std::initializer_list<std::pair<const std::string, json>>{
+ {"name", "cxx-example"},
+ {"edition", 2018.},
+ {"repository", json::null}});
+}
+```
diff --git a/book/src/binding/cxxvector.md b/book/src/binding/cxxvector.md
new file mode 100644
index 00000000..fd95a2db
--- /dev/null
+++ b/book/src/binding/cxxvector.md
@@ -0,0 +1,62 @@
+{{#title std::vector<T> — Rust ♡ C++}}
+# std::vector\<T\>
+
+The Rust binding of std::vector\<T\> is called **[`CxxVector<T>`]**. See the
+link for documentation of the Rust API.
+
+[`CxxVector<T>`]: https://docs.rs/cxx/*/cxx/struct.CxxVector.html
+
+### Restrictions:
+
+Rust code can never obtain a CxxVector by value. Instead in Rust code we will
+only ever look at a vector behind a reference or smart pointer, as in
+&CxxVector\<T\> or UniquePtr\<CxxVector\<T\>\>.
+
+CxxVector\<T\> does not support T being an opaque Rust type. You should use a
+Vec\<T\> (C++ rust::Vec\<T\>) instead for collections of opaque Rust types on
+the language boundary.
+
+## Example
+
+This program involves Rust code converting a `CxxVector<CxxString>` (i.e.
+`std::vector<std::string>`) into a Rust `Vec<String>`.
+
+```rust,noplayground
+// src/main.rs
+
+#![no_main] // main defined in C++ by main.cc
+
+use cxx::{CxxString, CxxVector};
+
+#[cxx::bridge]
+mod ffi {
+ extern "Rust" {
+ fn f(vec: &CxxVector<CxxString>);
+ }
+}
+
+fn f(vec: &CxxVector<CxxString>) {
+ let vec: Vec<String> = vec
+ .iter()
+ .map(|s| s.to_string_lossy().into_owned())
+ .collect();
+ g(&vec);
+}
+
+fn g(vec: &[String]) {
+ println!("{:?}", vec);
+}
+```
+
+```cpp
+// src/main.cc
+
+#include "example/src/main.rs.h"
+#include <string>
+#include <vector>
+
+int main() {
+ std::vector<std::string> vec{"fearless", "concurrency"};
+ f(vec);
+}
+```
diff --git a/book/src/binding/fn.md b/book/src/binding/fn.md
new file mode 100644
index 00000000..2934b069
--- /dev/null
+++ b/book/src/binding/fn.md
@@ -0,0 +1,34 @@
+{{#title Function pointers — Rust ♡ C++}}
+# Function pointers
+
+### Public API:
+
+```cpp,hidelines
+// rust/cxx.h
+#
+# namespace rust {
+
+template <typename Signature>
+class Fn;
+
+template <typename Ret, typename... Args>
+class Fn<Ret(Args...)> final {
+public:
+ Ret operator()(Args... args) const noexcept;
+ Fn operator*() const noexcept;
+};
+#
+# } // namespace rust
+```
+
+### Restrictions:
+
+Function pointers with a Result return type are not implemented yet.
+
+Passing a function pointer from C++ to Rust is not implemented yet, only from
+Rust to an `extern "C++"` function is implemented.
+
+## Example
+
+Function pointers are commonly useful for implementing [async functions over
+FFI](../async.md). See the example code on that page.
diff --git a/book/src/binding/rawptr.md b/book/src/binding/rawptr.md
new file mode 100644
index 00000000..17942112
--- /dev/null
+++ b/book/src/binding/rawptr.md
@@ -0,0 +1,100 @@
+{{#title *mut T, *const T — Rust ♡ C++}}
+# *mut T,&ensp;*const T
+
+Generally you should use references (`&mut T`, `&T`) or [std::unique_ptr\<T\>]
+where possible over raw pointers, but raw pointers are available too as an
+unsafe fallback option.
+
+[std::unique_ptr\<T\>]: uniqueptr.md
+
+### Restrictions:
+
+Extern functions and function pointers taking a raw pointer as an argument must
+be declared `unsafe fn` i.e. unsafe to call. The same does not apply to
+functions which only *return* a raw pointer, though presumably doing anything
+useful with the returned pointer is going to involve unsafe code elsewhere
+anyway.
+
+## Example
+
+This example illustrates making a Rust call to a canonical C-style `main`
+signature involving `char *argv[]`.
+
+```cpp
+// include/args.h
+
+#pragma once
+
+void parseArgs(int argc, char *argv[]);
+```
+
+```cpp
+// src/args.cc
+
+#include "example/include/args.h"
+#include <iostream>
+
+void parseArgs(int argc, char *argv[]) {
+ std::cout << argc << std::endl;
+ for (int i = 0; i < argc; i++) {
+ std::cout << '"' << argv[i] << '"' << std::endl;
+ }
+}
+```
+
+```rust,noplayground
+// src/main.rs
+
+use std::env;
+use std::ffi::CString;
+use std::os::raw::c_char;
+use std::os::unix::ffi::OsStrExt;
+use std::ptr;
+
+#[cxx::bridge]
+mod ffi {
+ extern "C++" {
+ include!("example/include/args.h");
+
+ unsafe fn parseArgs(argc: i32, argv: *mut *mut c_char);
+ }
+}
+
+fn main() {
+ // Convert from OsString to nul-terminated CString, truncating each argument
+ // at the first inner nul byte if present.
+ let args: Vec<CString> = env::args_os()
+ .map(|os_str| {
+ let bytes = os_str.as_bytes();
+ CString::new(bytes).unwrap_or_else(|nul_error| {
+ let nul_position = nul_error.nul_position();
+ let mut bytes = nul_error.into_vec();
+ bytes.truncate(nul_position);
+ CString::new(bytes).unwrap()
+ })
+ })
+ .collect();
+
+ // Convert from Vec<CString> of owned strings to Vec<*mut c_char> of
+ // borrowed string pointers.
+ //
+ // Once extern type stabilizes (https://github.com/rust-lang/rust/issues/43467)
+ // and https://internals.rust-lang.org/t/pre-rfc-make-cstr-a-thin-pointer/6258
+ // is implemented, and CStr pointers become thin, we can sidestep this step
+ // by accumulating the args as Vec<Box<CStr>> up front, then simply casting
+ // from *mut [Box<CStr>] to *mut [*mut CStr] to *mut *mut c_char.
+ let argc = args.len();
+ let mut argv: Vec<*mut c_char> = Vec::with_capacity(argc + 1);
+ for arg in &args {
+ argv.push(arg.as_ptr() as *mut c_char);
+ }
+ argv.push(ptr::null_mut()); // Nul terminator.
+
+ unsafe {
+ ffi::parseArgs(argc as i32, argv.as_mut_ptr());
+ }
+
+ // The CStrings go out of scope here. C function must not have held on to
+ // the pointers beyond this point.
+}
+```
diff --git a/book/src/binding/result.md b/book/src/binding/result.md
new file mode 100644
index 00000000..e49dcf4d
--- /dev/null
+++ b/book/src/binding/result.md
@@ -0,0 +1,148 @@
+{{#title Result<T> — Rust ♡ C++}}
+# Result\<T\>
+
+Result\<T\> is allowed as the return type of an extern function in either
+direction. Its behavior is to translate to/from C++ exceptions. If your codebase
+does not use C++ exceptions, or prefers to represent fallibility using something
+like outcome\<T\>, leaf::result\<T\>, StatusOr\<T\>, etc then you'll need to
+handle the translation of those to Rust Result\<T\> using your own shims for
+now. Better support for this is planned.
+
+If an exception is thrown from an `extern "C++"` function that is *not* declared
+by the CXX bridge to return Result, the program calls C++'s `std::terminate`.
+The behavior is equivalent to the same exception being thrown through a
+`noexcept` C++ function.
+
+If a panic occurs in *any* `extern "Rust"` function, regardless of whether it is
+declared by the CXX bridge to return Result, a message is logged and the program
+calls Rust's `std::process::abort`.
+
+## Returning Result from Rust to C++
+
+An `extern "Rust"` function returning a Result turns into a `throw` in C++ if
+the Rust side produces an error.
+
+Note that the return type written inside of cxx::bridge must be written without
+a second type parameter. Only the Ok type is specified for the purpose of the
+FFI. The Rust *implementation* (outside of the bridge module) may pick any error
+type as long as it has a std::fmt::Display impl.
+
+```rust,noplayground
+# use std::io;
+#
+#[cxx::bridge]
+mod ffi {
+ extern "Rust" {
+ fn fallible1(depth: usize) -> Result<String>;
+ fn fallible2() -> Result<()>;
+ }
+}
+
+fn fallible1(depth: usize) -> anyhow::Result<String> {
+ if depth == 0 {
+ return Err(anyhow::Error::msg("fallible1 requires depth > 0"));
+ }
+ ...
+}
+
+fn fallible2() -> Result<(), io::Error> {
+ ...
+ Ok(())
+}
+```
+
+The exception that gets thrown by CXX on the C++ side is always of type
+`rust::Error` and has the following C++ public API. The `what()` member function
+gives the error message according to the Rust error's std::fmt::Display impl.
+
+```cpp,hidelines
+// rust/cxx.h
+#
+# namespace rust {
+
+class Error final : public std::exception {
+public:
+ Error(const Error &);
+ Error(Error &&) noexcept;
+ ~Error() noexcept;
+
+ Error &operator=(const Error &);
+ Error &operator=(Error &&) noexcept;
+
+ const char *what() const noexcept override;
+};
+#
+# } // namespace rust
+```
+
+## Returning Result from C++ to Rust
+
+An `extern "C++"` function returning a Result turns into a `catch` in C++ that
+converts the exception into an Err for Rust.
+
+Note that the return type written inside of cxx::bridge must be written without
+a second type parameter. Only the Ok type is specified for the purpose of the
+FFI. The resulting error type created by CXX when an `extern "C++"` function
+throws will always be of type **[`cxx::Exception`]**.
+
+[`cxx::Exception`]: https://docs.rs/cxx/*/cxx/struct.Exception.html
+
+```rust,noplayground
+# use std::process;
+#
+#[cxx::bridge]
+mod ffi {
+ unsafe extern "C++" {
+ include!("example/include/example.h");
+ fn fallible1(depth: usize) -> Result<String>;
+ fn fallible2() -> Result<()>;
+ }
+}
+
+fn main() {
+ if let Err(err) = ffi::fallible1(99) {
+ eprintln!("Error: {}", err);
+ process::exit(1);
+ }
+}
+```
+
+The specific set of caught exceptions and the conversion to error message are
+both customizable. The way you do this is by defining a template function
+`rust::behavior::trycatch` with a suitable signature inside any one of the
+headers `include!`'d by your cxx::bridge.
+
+The template signature is required to be:
+
+```cpp,hidelines
+namespace rust {
+namespace behavior {
+
+template <typename Try, typename Fail>
+static void trycatch(Try &&func, Fail &&fail) noexcept;
+
+} // namespace behavior
+} // namespace rust
+```
+
+The default `trycatch` used by CXX if you have not provided your own is the
+following. You must follow the same pattern: invoke `func` with no arguments,
+catch whatever exception(s) you want, and invoke `fail` with the error message
+you'd like for the Rust error to have.
+
+```cpp,hidelines
+# #include <exception>
+#
+# namespace rust {
+# namespace behavior {
+#
+template <typename Try, typename Fail>
+static void trycatch(Try &&func, Fail &&fail) noexcept try {
+ func();
+} catch (const std::exception &e) {
+ fail(e.what());
+}
+#
+# } // namespace behavior
+# } // namespace rust
+```
diff --git a/book/src/binding/sharedptr.md b/book/src/binding/sharedptr.md
new file mode 100644
index 00000000..a3b70700
--- /dev/null
+++ b/book/src/binding/sharedptr.md
@@ -0,0 +1,80 @@
+{{#title std::shared_ptr<T> — Rust ♡ C++}}
+# std::shared\_ptr\<T\>
+
+The Rust binding of std::shared\_ptr\<T\> is called **[`SharedPtr<T>`]**. See
+the link for documentation of the Rust API.
+
+[`SharedPtr<T>`]: https://docs.rs/cxx/*/cxx/struct.SharedPtr.html
+
+### Restrictions:
+
+SharedPtr\<T\> does not support T being an opaque Rust type. You should use a
+Box\<T\> (C++ [rust::Box\<T\>](box.md)) instead for transferring ownership of
+opaque Rust types on the language boundary.
+
+## Example
+
+```rust,noplayground
+// src/main.rs
+
+use std::ops::Deref;
+use std::ptr;
+
+#[cxx::bridge]
+mod ffi {
+ unsafe extern "C++" {
+ include!("example/include/example.h");
+
+ type Object;
+
+ fn create_shared_ptr() -> SharedPtr<Object>;
+ }
+}
+
+fn main() {
+ let ptr1 = ffi::create_shared_ptr();
+
+ {
+ // Create a second shared_ptr holding shared ownership of the same
+ // object. There is still only one Object but two SharedPtr<Object>.
+ // Both pointers point to the same object on the heap.
+ let ptr2 = ptr1.clone();
+ assert!(ptr::eq(ptr1.deref(), ptr2.deref()));
+
+ // ptr2 goes out of scope, but Object is not destroyed yet.
+ }
+
+ println!("say goodbye to Object");
+
+ // ptr1 goes out of scope and Object is destroyed.
+}
+```
+
+```cpp
+// include/example.h
+
+#pragma once
+#include <memory>
+
+class Object {
+public:
+ Object();
+ ~Object();
+};
+
+std::shared_ptr<Object> create_shared_ptr();
+```
+
+```cpp
+// src/example.cc
+
+#include "example/include/example.h"
+#include <iostream>
+
+Object::Object() { std::cout << "construct Object" << std::endl; }
+Object::~Object() { std::cout << "~Object" << std::endl; }
+
+std::shared_ptr<Object> create_shared_ptr() {
+ return std::make_shared<Object>();
+}
+```
diff --git a/book/src/binding/slice.md b/book/src/binding/slice.md
new file mode 100644
index 00000000..803277ba
--- /dev/null
+++ b/book/src/binding/slice.md
@@ -0,0 +1,171 @@
+{{#title rust::Slice<T> — Rust ♡ C++}}
+# rust::Slice\<const T\>,&ensp;rust::Slice\<T\>
+
+- Rust `&[T]` is written `rust::Slice<const T>` in C++
+- Rust `&mut [T]` is written `rust::Slice<T>` in C++
+
+### Public API:
+
+```cpp,hidelines
+// rust/cxx.h
+#
+# #include <iterator>
+# #include <type_traits>
+#
+# namespace rust {
+
+template <typename T>
+class Slice final {
+public:
+ using value_type = T;
+
+ Slice() noexcept;
+ Slice(const Slice<T> &) noexcept;
+ Slice(T *, size_t count) noexcept;
+
+ Slice &operator=(Slice<T> &&) noexcept;
+ Slice &operator=(const Slice<T> &) noexcept
+ requires std::is_const_v<T>;
+
+ T *data() const noexcept;
+ size_t size() const noexcept;
+ size_t length() const noexcept;
+ bool empty() const noexcept;
+
+ T &operator[](size_t n) const noexcept;
+ T &at(size_t n) const;
+ T &front() const noexcept;
+ T &back() const noexcept;
+
+ class iterator;
+ iterator begin() const noexcept;
+ iterator end() const noexcept;
+
+ void swap(Slice &) noexcept;
+};
+#
+# template <typename T>
+# class Slice<T>::iterator final {
+# public:
+# using iterator_category = std::random_access_iterator_tag;
+# using value_type = T;
+# using pointer = T *;
+# using reference = T &;
+#
+# T &operator*() const noexcept;
+# T *operator->() const noexcept;
+# T &operator[](ptrdiff_t) const noexcept;
+#
+# iterator &operator++() noexcept;
+# iterator operator++(int) noexcept;
+# iterator &operator--() noexcept;
+# iterator operator--(int) noexcept;
+#
+# iterator &operator+=(ptrdiff_t) noexcept;
+# iterator &operator-=(ptrdiff_t) noexcept;
+# iterator operator+(ptrdiff_t) const noexcept;
+# iterator operator-(ptrdiff_t) const noexcept;
+# ptrdiff_t operator-(const iterator &) const noexcept;
+#
+# bool operator==(const iterator &) const noexcept;
+# bool operator!=(const iterator &) const noexcept;
+# bool operator<(const iterator &) const noexcept;
+# bool operator>(const iterator &) const noexcept;
+# bool operator<=(const iterator &) const noexcept;
+# bool operator>=(const iterator &) const noexcept;
+# };
+#
+# } // namespace rust
+```
+
+### Restrictions:
+
+T must not be an opaque Rust type or opaque C++ type. Support for opaque Rust
+types in slices is coming.
+
+Allowed as function argument or return value. Not supported in shared structs.
+
+Only rust::Slice\<const T\> is copy-assignable, not rust::Slice\<T\>. (Both are
+move-assignable.) You'll need to write std::move occasionally as a reminder that
+accidentally exposing overlapping &amp;mut \[T\] to Rust is UB.
+
+## Example
+
+This example is a C++ program that constructs a slice containing JSON data (by
+reading from stdin, but it could be from anywhere), then calls into Rust to
+pretty-print that JSON data into a std::string via the [serde_json] and
+[serde_transcode] crates.
+
+[serde_json]: https://github.com/serde-rs/json
+[serde_transcode]: https://github.com/sfackler/serde-transcode
+
+```rust,noplayground
+// src/main.rs
+
+#![no_main] // main defined in C++ by main.cc
+
+use cxx::CxxString;
+use std::io::{self, Write};
+use std::pin::Pin;
+
+#[cxx::bridge]
+mod ffi {
+ extern "Rust" {
+ fn prettify_json(input: &[u8], output: Pin<&mut CxxString>) -> Result<()>;
+ }
+}
+
+struct WriteToCxxString<'a>(Pin<&'a mut CxxString>);
+
+impl<'a> Write for WriteToCxxString<'a> {
+ fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+ self.0.as_mut().push_bytes(buf);
+ Ok(buf.len())
+ }
+ fn flush(&mut self) -> io::Result<()> {
+ Ok(())
+ }
+}
+
+fn prettify_json(input: &[u8], output: Pin<&mut CxxString>) -> serde_json::Result<()> {
+ let writer = WriteToCxxString(output);
+ let mut deserializer = serde_json::Deserializer::from_slice(input);
+ let mut serializer = serde_json::Serializer::pretty(writer);
+ serde_transcode::transcode(&mut deserializer, &mut serializer)
+}
+```
+
+```cpp
+// src/main.cc
+
+#include "example/src/main.rs.h"
+#include <iostream>
+#include <iterator>
+#include <string>
+#include <vector>
+
+int main() {
+ // Read json from stdin.
+ std::istreambuf_iterator<char> begin{std::cin}, end;
+ std::vector<unsigned char> input{begin, end};
+ rust::Slice<const uint8_t> slice{input.data(), input.size()};
+
+ // Prettify using serde_json and serde_transcode.
+ std::string output;
+ prettify_json(slice, output);
+
+ // Write to stdout.
+ std::cout << output << std::endl;
+}
+```
+
+Testing the example:
+
+```console
+$ echo '{"fearless":"concurrency"}' | cargo run
+ Finished dev [unoptimized + debuginfo] target(s) in 0.02s
+ Running `target/debug/example`
+{
+ "fearless": "concurrency"
+}
+```
diff --git a/book/src/binding/str.md b/book/src/binding/str.md
new file mode 100644
index 00000000..a4820aaf
--- /dev/null
+++ b/book/src/binding/str.md
@@ -0,0 +1,117 @@
+{{#title rust::Str — Rust ♡ C++}}
+# rust::Str
+
+### Public API:
+
+```cpp,hidelines
+// rust/cxx.h
+#
+# #include <iosfwd>
+# #include <string>
+#
+# namespace rust {
+
+class Str final {
+public:
+ Str() noexcept;
+ Str(const Str &) noexcept;
+ Str(const String &) noexcept;
+
+ // Throws std::invalid_argument if not utf-8.
+ Str(const std::string &);
+ Str(const char *);
+ Str(const char *, size_t);
+
+ Str &operator=(const Str &) noexcept;
+
+ explicit operator std::string() const;
+
+ // Note: no null terminator.
+ const char *data() const noexcept;
+ size_t size() const noexcept;
+ size_t length() const noexcept;
+
+ using iterator = const char *;
+ using const_iterator = const char *;
+ const_iterator begin() const noexcept;
+ const_iterator end() const noexcept;
+ const_iterator cbegin() const noexcept;
+ const_iterator cend() const noexcept;
+
+ bool operator==(const Str &) const noexcept;
+ bool operator!=(const Str &) const noexcept;
+ bool operator<(const Str &) const noexcept;
+ bool operator<=(const Str &) const noexcept;
+ bool operator>(const Str &) const noexcept;
+ bool operator>=(const Str &) const noexcept;
+
+ void swap(Str &) noexcept;
+};
+
+std::ostream &operator<<(std::ostream &, const Str &);
+#
+# } // namespace rust
+```
+
+### Notes:
+
+**Be aware that rust::Str behaves like &amp;str i.e. it is a borrow!**&ensp;C++
+needs to be mindful of the lifetimes at play.
+
+Just to reiterate: &amp;str is rust::Str. Do not try to write &amp;str as `const
+rust::Str &`. A language-level C++ reference is not able to capture the fat
+pointer nature of &amp;str.
+
+### Restrictions:
+
+Allowed as function argument or return value. Not supported in shared structs
+yet. `&mut str` is not supported yet, but is also extremely obscure so this is
+fine.
+
+## Example
+
+```rust,noplayground
+// src/main.rs
+
+#[cxx::bridge]
+mod ffi {
+ extern "Rust" {
+ fn r(greeting: &str);
+ }
+
+ unsafe extern "C++" {
+ include!("example/include/greeting.h");
+ fn c(greeting: &str);
+ }
+}
+
+fn r(greeting: &str) {
+ println!("{}", greeting);
+}
+
+fn main() {
+ ffi::c("hello from Rust");
+}
+```
+
+```cpp
+// include/greeting.h
+
+#pragma once
+#include "example/src/main.rs.h"
+#include "rust/cxx.h"
+
+void c(rust::Str greeting);
+```
+
+```cpp
+// src/greeting.cc
+
+#include "example/include/greeting.h"
+#include <iostream>
+
+void c(rust::Str greeting) {
+ std::cout << greeting << std::endl;
+ r("hello from C++");
+}
+```
diff --git a/book/src/binding/string.md b/book/src/binding/string.md
new file mode 100644
index 00000000..a7d0790b
--- /dev/null
+++ b/book/src/binding/string.md
@@ -0,0 +1,115 @@
+{{#title rust::String — Rust ♡ C++}}
+# rust::String
+
+### Public API:
+
+```cpp,hidelines
+// rust/cxx.h
+#
+# #include <iosfwd>
+# #include <string>
+#
+# namespace rust {
+
+class String final {
+public:
+ String() noexcept;
+ String(const String &) noexcept;
+ String(String &&) noexcept;
+ ~String() noexcept;
+
+ // Throws std::invalid_argument if not utf-8.
+ String(const std::string &);
+ String(const char *);
+ String(const char *, size_t);
+
+ String &operator=(const String &) noexcept;
+ String &operator=(String &&) noexcept;
+
+ explicit operator std::string() const;
+
+ // Note: no null terminator.
+ const char *data() const noexcept;
+ size_t size() const noexcept;
+ size_t length() const noexcept;
+
+ const char *c_str() noexcept;
+
+ using iterator = char *;
+ iterator begin() noexcept;
+ iterator end() noexcept;
+
+ using const_iterator = const char *;
+ const_iterator begin() const noexcept;
+ const_iterator end() const noexcept;
+ const_iterator cbegin() const noexcept;
+ const_iterator cend() const noexcept;
+
+ bool operator==(const String &) const noexcept;
+ bool operator!=(const String &) const noexcept;
+ bool operator<(const String &) const noexcept;
+ bool operator<=(const String &) const noexcept;
+ bool operator>(const String &) const noexcept;
+ bool operator>=(const String &) const noexcept;
+
+ void swap(String &) noexcept;
+};
+
+std::ostream &operator<<(std::ostream &, const String &);
+#
+# } // namespace rust
+```
+
+### Restrictions:
+
+None. Strings may be used as function arguments and function return values, by
+value or by reference, as well as fields of shared structs.
+
+## Example
+
+```rust,noplayground
+// src/main.rs
+
+#[cxx::bridge]
+mod ffi {
+ struct ConcatRequest {
+ fst: String,
+ snd: String,
+ }
+
+ unsafe extern "C++" {
+ include!("example/include/concat.h");
+ fn concat(r: ConcatRequest) -> String;
+ }
+}
+
+fn main() {
+ let concatenated = ffi::concat(ffi::ConcatRequest {
+ fst: "fearless".to_owned(),
+ snd: "concurrency".to_owned(),
+ });
+ println!("concatenated: {:?}", concatenated);
+}
+```
+
+```cpp
+// include/concat.h
+
+#pragma once
+#include "example/src/main.rs.h"
+#include "rust/cxx.h"
+
+rust::String concat(ConcatRequest r);
+```
+
+```cpp
+// src/concat.cc
+
+#include "example/include/concat.h"
+
+rust::String concat(ConcatRequest r) {
+ // The full suite of operator overloads hasn't been added
+ // yet on rust::String, but we can get it done like this:
+ return std::string(r.fst) + std::string(r.snd);
+}
+```
diff --git a/book/src/binding/uniqueptr.md b/book/src/binding/uniqueptr.md
new file mode 100644
index 00000000..eefbc34a
--- /dev/null
+++ b/book/src/binding/uniqueptr.md
@@ -0,0 +1,63 @@
+{{#title std::unique_ptr<T> — Rust ♡ C++}}
+# std::unique\_ptr\<T\>
+
+The Rust binding of std::unique\_ptr\<T\> is called **[`UniquePtr<T>`]**. See
+the link for documentation of the Rust API.
+
+[`UniquePtr<T>`]: https://docs.rs/cxx/*/cxx/struct.UniquePtr.html
+
+### Restrictions:
+
+Only `std::unique_ptr<T, std::default_delete<T>>` is currently supported. Custom
+deleters may be supported in the future.
+
+UniquePtr\<T\> does not support T being an opaque Rust type. You should use a
+Box\<T\> (C++ [rust::Box\<T\>](box.md)) instead for transferring ownership of
+opaque Rust types on the language boundary.
+
+## Example
+
+UniquePtr is commonly useful for returning opaque C++ objects to Rust. This use
+case was featured in the [*blobstore tutorial*](../tutorial.md).
+
+```rust,noplayground
+// src/main.rs
+
+#[cxx::bridge]
+mod ffi {
+ unsafe extern "C++" {
+ include!("example/include/blobstore.h");
+
+ type BlobstoreClient;
+
+ fn new_blobstore_client() -> UniquePtr<BlobstoreClient>;
+ // ...
+ }
+}
+
+fn main() {
+ let client = ffi::new_blobstore_client();
+ // ...
+}
+```
+
+```cpp
+// include/blobstore.h
+
+#pragma once
+#include <memory>
+
+class BlobstoreClient;
+
+std::unique_ptr<BlobstoreClient> new_blobstore_client();
+```
+
+```cpp
+// src/blobstore.cc
+
+#include "example/include/blobstore.h"
+
+std::unique_ptr<BlobstoreClient> new_blobstore_client() {
+ return std::make_unique<BlobstoreClient>();
+}
+```
diff --git a/book/src/binding/vec.md b/book/src/binding/vec.md
new file mode 100644
index 00000000..948fa671
--- /dev/null
+++ b/book/src/binding/vec.md
@@ -0,0 +1,190 @@
+{{#title rust::Vec<T> — Rust ♡ C++}}
+# rust::Vec\<T\>
+
+### Public API:
+
+```cpp,hidelines
+// rust/cxx.h
+#
+# #include <initializer_list>
+# #include <iterator>
+# #include <type_traits>
+#
+# namespace rust {
+
+template <typename T>
+class Vec final {
+public:
+ using value_type = T;
+
+ Vec() noexcept;
+ Vec(std::initializer_list<T>);
+ Vec(const Vec &);
+ Vec(Vec &&) noexcept;
+ ~Vec() noexcept;
+
+ Vec &operator=(Vec &&) noexcept;
+ Vec &operator=(const Vec &);
+
+ size_t size() const noexcept;
+ bool empty() const noexcept;
+ const T *data() const noexcept;
+ T *data() noexcept;
+ size_t capacity() const noexcept;
+
+ const T &operator[](size_t n) const noexcept;
+ const T &at(size_t n) const;
+ const T &front() const;
+ const T &back() const;
+
+ T &operator[](size_t n) noexcept;
+ T &at(size_t n);
+ T &front();
+ T &back();
+
+ void reserve(size_t new_cap);
+ void push_back(const T &value);
+ void push_back(T &&value);
+ template <typename... Args>
+ void emplace_back(Args &&...args);
+
+ class iterator;
+ iterator begin() noexcept;
+ iterator end() noexcept;
+
+ class const_iterator;
+ const_iterator begin() const noexcept;
+ const_iterator end() const noexcept;
+ const_iterator cbegin() const noexcept;
+ const_iterator cend() const noexcept;
+
+ void swap(Vec &) noexcept;
+};
+#
+# template <typename T>
+# class Vec<T>::iterator final {
+# public:
+# using iterator_category = std::random_access_iterator_tag;
+# using value_type = T;
+# using pointer = T *;
+# using reference = T &;
+#
+# T &operator*() const noexcept;
+# T *operator->() const noexcept;
+# T &operator[](ptrdiff_t) const noexcept;
+#
+# iterator &operator++() noexcept;
+# iterator operator++(int) noexcept;
+# iterator &operator--() noexcept;
+# iterator operator--(int) noexcept;
+#
+# iterator &operator+=(ptrdiff_t) noexcept;
+# iterator &operator-=(ptrdiff_t) noexcept;
+# iterator operator+(ptrdiff_t) const noexcept;
+# iterator operator-(ptrdiff_t) const noexcept;
+# ptrdiff_t operator-(const iterator &) const noexcept;
+#
+# bool operator==(const iterator &) const noexcept;
+# bool operator!=(const iterator &) const noexcept;
+# bool operator<(const iterator &) const noexcept;
+# bool operator<=(const iterator &) const noexcept;
+# bool operator>(const iterator &) const noexcept;
+# bool operator>=(const iterator &) const noexcept;
+# };
+#
+# template <typename T>
+# class Vec<T>::const_iterator final {
+# public:
+# using iterator_category = std::random_access_iterator_tag;
+# using value_type = const T;
+# using pointer = const T *;
+# using reference = const T &;
+#
+# const T &operator*() const noexcept;
+# const T *operator->() const noexcept;
+# const T &operator[](ptrdiff_t) const noexcept;
+#
+# const_iterator &operator++() noexcept;
+# const_iterator operator++(int) noexcept;
+# const_iterator &operator--() noexcept;
+# const_iterator operator--(int) noexcept;
+#
+# const_iterator &operator+=(ptrdiff_t) noexcept;
+# const_iterator &operator-=(ptrdiff_t) noexcept;
+# const_iterator operator+(ptrdiff_t) const noexcept;
+# const_iterator operator-(ptrdiff_t) const noexcept;
+# ptrdiff_t operator-(const const_iterator &) const noexcept;
+#
+# bool operator==(const const_iterator &) const noexcept;
+# bool operator!=(const const_iterator &) const noexcept;
+# bool operator<(const const_iterator &) const noexcept;
+# bool operator<=(const const_iterator &) const noexcept;
+# bool operator>(const const_iterator &) const noexcept;
+# bool operator>=(const const_iterator &) const noexcept;
+# };
+#
+# } // namespace rust
+```
+
+### Restrictions:
+
+Vec\<T\> does not support T being an opaque C++ type. You should use
+CxxVector\<T\> (C++ std::vector\<T\>) instead for collections of opaque C++
+types on the language boundary.
+
+## Example
+
+```rust,noplayground
+// src/main.rs
+
+#[cxx::bridge]
+mod ffi {
+ struct Shared {
+ v: u32,
+ }
+
+ unsafe extern "C++" {
+ include!("example/include/example.h");
+
+ fn f(elements: Vec<Shared>);
+ }
+}
+
+fn main() {
+ let shared = |v| ffi::Shared { v };
+ let elements = vec![shared(3), shared(2), shared(1)];
+ ffi::f(elements);
+}
+```
+
+```cpp
+// include/example.h
+
+#pragma once
+#include "example/src/main.rs.h"
+#include "rust/cxx.h"
+
+void f(rust::Vec<Shared> elements);
+```
+
+```cpp
+// src/example.cc
+
+#include "example/include/example.h"
+#include <algorithm>
+#include <cassert>
+#include <iostream>
+#include <iterator>
+#include <vector>
+
+void f(rust::Vec<Shared> v) {
+ for (auto shared : v) {
+ std::cout << shared.v << std::endl;
+ }
+
+ // Copy the elements to a C++ std::vector using STL algorithm.
+ std::vector<Shared> stdv;
+ std::copy(v.begin(), v.end(), std::back_inserter(stdv));
+ assert(v.size() == stdv.size());
+}
+```
diff --git a/book/src/bindings.md b/book/src/bindings.md
new file mode 100644
index 00000000..bcb51d8e
--- /dev/null
+++ b/book/src/bindings.md
@@ -0,0 +1,56 @@
+{{#title Built-in bindings — Rust ♡ C++}}
+# Built-in bindings reference
+
+In addition to all the primitive types (i32 &lt;=&gt; int32_t), the following
+common types may be used in the fields of shared structs and the arguments and
+returns of extern functions.
+
+<br>
+
+<table>
+<tr><th>name in Rust</th><th>name in C++</th><th>restrictions</th></tr>
+<tr><td style="padding:3px 6px">String</td><td style="padding:3px 6px"><b><a href="binding/string.md">rust::String</a></b></td><td style="padding:3px 6px"></td></tr>
+<tr><td style="padding:3px 6px">&amp;str</td><td style="padding:3px 6px"><b><a href="binding/str.md">rust::Str</a></b></td><td style="padding:3px 6px"></td></tr>
+<tr><td style="padding:3px 6px">&amp;[T]</td><td style="padding:3px 6px"><b><a href="binding/slice.md">rust::Slice&lt;const&nbsp;T&gt;</a></b></td><td style="padding:3px 6px"><sup><i>cannot hold opaque C++ type</i></sup></td></tr>
+<tr><td style="padding:3px 6px">&amp;mut [T]</td><td style="padding:3px 6px"><b><a href="binding/slice.md">rust::Slice&lt;T&gt;</a></b></td><td style="padding:3px 6px"><sup><i>cannot hold opaque C++ type</i></sup></td></tr>
+<tr><td style="padding:3px 6px"><b><a href="binding/cxxstring.md">CxxString</a></b></td><td style="padding:3px 6px">std::string</td><td style="padding:3px 6px"><sup><i>cannot be passed by value</i></sup></td></tr>
+<tr><td style="padding:3px 6px">Box&lt;T&gt;</td><td style="padding:3px 6px"><b><a href="binding/box.md">rust::Box&lt;T&gt;</a></b></td><td style="padding:3px 6px"><sup><i>cannot hold opaque C++ type</i></sup></td></tr>
+<tr><td style="padding:3px 6px"><b><a href="binding/uniqueptr.md">UniquePtr&lt;T&gt;</a></b></td><td style="padding:3px 6px">std::unique_ptr&lt;T&gt;</td><td style="padding:3px 6px"><sup><i>cannot hold opaque Rust type</i></sup></td></tr>
+<tr><td style="padding:3px 6px"><b><a href="binding/sharedptr.md">SharedPtr&lt;T&gt;</a></b></td><td style="padding:3px 6px">std::shared_ptr&lt;T&gt;</td><td style="padding:3px 6px"><sup><i>cannot hold opaque Rust type</i></sup></td></tr>
+<tr><td style="padding:3px 6px">[T; N]</td><td style="padding:3px 6px">std::array&lt;T, N&gt;</td><td style="padding:3px 6px"><sup><i>cannot hold opaque C++ type</i></sup></td></tr>
+<tr><td style="padding:3px 6px">Vec&lt;T&gt;</td><td style="padding:3px 6px"><b><a href="binding/vec.md">rust::Vec&lt;T&gt;</a></b></td><td style="padding:3px 6px"><sup><i>cannot hold opaque C++ type</i></sup></td></tr>
+<tr><td style="padding:3px 6px"><b><a href="binding/cxxvector.md">CxxVector&lt;T&gt;</a></b></td><td style="padding:3px 6px">std::vector&lt;T&gt;</td><td style="padding:3px 6px"><sup><i>cannot be passed by value, cannot hold opaque Rust type</i></sup></td></tr>
+<tr><td style="padding:3px 6px"><b><a href="binding/rawptr.md">*mut T, *const T</a></b></td><td style="padding:3px 6px">T*, const T*</td><td style="padding:3px 6px"><sup><i>fn with a raw pointer argument must be declared unsafe to call</i></sup></td></tr>
+<tr><td style="padding:3px 6px">fn(T, U) -&gt; V</td><td style="padding:3px 6px"><b><a href="binding/fn.md">rust::Fn&lt;V(T, U)&gt;</a></b></td><td style="padding:3px 6px"><sup><i>only passing from Rust to C++ is implemented so far</i></sup></td></tr>
+<tr><td style="padding:3px 6px"><b><a href="binding/result.md">Result&lt;T&gt;</a></b></td><td style="padding:3px 6px">throw/catch</td><td style="padding:3px 6px"><sup><i>allowed as return type only</i></sup></td></tr>
+</table>
+
+<br>
+
+The C++ API of the `rust` namespace is defined by the *include/cxx.h* file in
+the CXX GitHub repo. You will need to include this header in your C++ code when
+working with those types. **When using Cargo and the cxx-build crate, the header
+is made available to you at `#include "rust/cxx.h"`.**
+
+The `rust` namespace additionally provides lowercase type aliases of all the
+types mentioned in the table, for use in codebases preferring that style. For
+example `rust::String`, `rust::Vec` may alternatively be written `rust::string`,
+`rust::vec` etc.
+
+## Pending bindings
+
+The following types are intended to be supported "soon" but are just not
+implemented yet. I don't expect any of these to be hard to make work but it's a
+matter of designing a nice API for each in its non-native language.
+
+<br>
+
+<table>
+<tr><th>name in Rust</th><th>name in C++</th></tr>
+<tr><td>BTreeMap&lt;K, V&gt;</td><td><sup><i>tbd</i></sup></td></tr>
+<tr><td>HashMap&lt;K, V&gt;</td><td><sup><i>tbd</i></sup></td></tr>
+<tr><td>Arc&lt;T&gt;</td><td><sup><i>tbd</i></sup></td></tr>
+<tr><td>Option&lt;T&gt;</td><td><sup><i>tbd</i></sup></td></tr>
+<tr><td><sup><i>tbd</i></sup></td><td>std::map&lt;K, V&gt;</td></tr>
+<tr><td><sup><i>tbd</i></sup></td><td>std::unordered_map&lt;K, V&gt;</td></tr>
+</table>
diff --git a/book/src/build/bazel.md b/book/src/build/bazel.md
new file mode 100644
index 00000000..08edb19f
--- /dev/null
+++ b/book/src/build/bazel.md
@@ -0,0 +1,106 @@
+{{#title Bazel, Buck — Rust ♡ C++}}
+## Bazel, Buck, potentially other similar environments
+
+Starlark-based build systems with the ability to compile a code generator and
+invoke it as a `genrule` will run CXX's C++ code generator via its `cxxbridge`
+command line interface.
+
+The tool is packaged as the `cxxbridge-cmd` crate on crates.io or can be built
+from the *gen/cmd/* directory of the CXX GitHub repo.
+
+```console
+$ cargo install cxxbridge-cmd
+
+$ cxxbridge src/bridge.rs --header > path/to/bridge.rs.h
+$ cxxbridge src/bridge.rs > path/to/bridge.rs.cc
+```
+
+The CXX repo maintains working Bazel `BUILD` and Buck `BUCK` targets for the
+complete blobstore tutorial (chapter 3) for your reference, tested in CI. These
+aren't meant to be directly what you use in your codebase, but serve as an
+illustration of one possible working pattern.
+
+```python
+# tools/bazel/rust_cxx_bridge.bzl
+
+load("@bazel_skylib//rules:run_binary.bzl", "run_binary")
+load("@rules_cc//cc:defs.bzl", "cc_library")
+
+def rust_cxx_bridge(name, src, deps = []):
+ native.alias(
+ name = "%s/header" % name,
+ actual = src + ".h",
+ )
+
+ native.alias(
+ name = "%s/source" % name,
+ actual = src + ".cc",
+ )
+
+ run_binary(
+ name = "%s/generated" % name,
+ srcs = [src],
+ outs = [
+ src + ".h",
+ src + ".cc",
+ ],
+ args = [
+ "$(location %s)" % src,
+ "-o",
+ "$(location %s.h)" % src,
+ "-o",
+ "$(location %s.cc)" % src,
+ ],
+ tool = "//:codegen",
+ )
+
+ cc_library(
+ name = name,
+ srcs = [src + ".cc"],
+ deps = deps + [":%s/include" % name],
+ )
+
+ cc_library(
+ name = "%s/include" % name,
+ hdrs = [src + ".h"],
+ )
+```
+
+```python
+# demo/BUILD
+
+load("@rules_cc//cc:defs.bzl", "cc_library")
+load("//tools/bazel:rust.bzl", "rust_binary")
+load("//tools/bazel:rust_cxx_bridge.bzl", "rust_cxx_bridge")
+
+rust_binary(
+ name = "demo",
+ srcs = glob(["src/**/*.rs"]),
+ deps = [
+ ":blobstore-sys",
+ ":bridge",
+ "//:cxx",
+ ],
+)
+
+rust_cxx_bridge(
+ name = "bridge",
+ src = "src/main.rs",
+ deps = [":blobstore-include"],
+)
+
+cc_library(
+ name = "blobstore-sys",
+ srcs = ["src/blobstore.cc"],
+ deps = [
+ ":blobstore-include",
+ ":bridge/include",
+ ],
+)
+
+cc_library(
+ name = "blobstore-include",
+ hdrs = ["include/blobstore.h"],
+ deps = ["//:core"],
+)
+```
diff --git a/book/src/build/cargo.md b/book/src/build/cargo.md
new file mode 100644
index 00000000..82ccfb50
--- /dev/null
+++ b/book/src/build/cargo.md
@@ -0,0 +1,306 @@
+{{#title Cargo-based setup — Rust ♡ C++}}
+# Cargo-based builds
+
+As one aspect of delivering a good Rust&ndash;C++ interop experience, CXX turns
+Cargo into a quite usable build system for C++ projects published as a
+collection of crates.io packages, including a consistent and frictionless
+experience `#include`-ing C++ headers across dependencies.
+
+## Canonical setup
+
+CXX's integration with Cargo is handled through the [cxx-build] crate.
+
+[cxx-build]: https://docs.rs/cxx-build
+
+```toml,hidelines
+## Cargo.toml
+# [package]
+# name = "..."
+# version = "..."
+# edition = "2018"
+
+[dependencies]
+cxx = "1.0"
+
+[build-dependencies]
+cxx-build = "1.0"
+```
+
+The canonical build script is as follows. The indicated line returns a
+[`cc::Build`] instance (from the usual widely used `cc` crate) on which you can
+set up any additional source files and compiler flags as normal.
+
+[`cc::Build`]: https://docs.rs/cc/1.0/cc/struct.Build.html
+
+```rust,noplayground
+// build.rs
+
+fn main() {
+ cxx_build::bridge("src/main.rs") // returns a cc::Build
+ .file("src/demo.cc")
+ .flag_if_supported("-std=c++11")
+ .compile("cxxbridge-demo");
+
+ println!("cargo:rerun-if-changed=src/main.rs");
+ println!("cargo:rerun-if-changed=src/demo.cc");
+ println!("cargo:rerun-if-changed=include/demo.h");
+}
+```
+
+The `rerun-if-changed` lines are optional but make it so that Cargo does not
+spend time recompiling your C++ code when only non-C++ code has changed since
+the previous Cargo build. By default without any `rerun-if-changed`, Cargo will
+re-execute the build script after *any* file changed in the project.
+
+If stuck, try comparing what you have against the *demo/* directory of the CXX
+GitHub repo, which maintains a working Cargo-based setup for the blobstore
+tutorial (chapter 3).
+
+## Header include paths
+
+With cxx-build, by default your include paths always start with the crate name.
+This applies to both `#include` within your C++ code, and `include!` in the
+`extern "C++"` section of your Rust cxx::bridge.
+
+Your crate name is determined by the `name` entry in Cargo.toml.
+
+For example if your crate is named `yourcratename` and contains a C++ header
+file `path/to/header.h` relative to Cargo.toml, that file will be includable as:
+
+```cpp
+#include "yourcratename/path/to/header.h"
+```
+
+A crate can choose a prefix for its headers that is different from the crate
+name by modifying **[`CFG.include_prefix`][CFG]** from build.rs:
+
+[CFG]: https://docs.rs/cxx-build/*/cxx_build/static.CFG.html
+
+```rust,noplayground
+// build.rs
+
+use cxx_build::CFG;
+
+fn main() {
+ CFG.include_prefix = "my/project";
+
+ cxx_build::bridge(...)...
+}
+```
+
+Subsequently the header located at `path/to/header.h` would now be includable
+as:
+
+```cpp
+#include "my/project/path/to/header.h"
+```
+
+The empty string `""` is a valid include prefix and will make it possible to
+have `#include "path/to/header.h"`. However, if your crate is a library, be
+considerate of possible name collisions that may occur in downstream crates. If
+using an empty include prefix, you'll want to make sure your headers' local path
+within the crate is sufficiently namespaced or unique.
+
+## Including generated code
+
+If your `#[cxx::bridge]` module contains an `extern "Rust"` block i.e. types or
+functions exposed from Rust to C++, or any shared data structures, the
+CXX-generated C++ header declaring those things is available using a `.rs.h`
+extension on the Rust source file's name.
+
+```cpp
+// the header generated from path/to/lib.rs
+#include "yourcratename/path/to/lib.rs.h"
+```
+
+For giggles, it's also available using just a plain `.rs` extension as if you
+were including the Rust file directly. Use whichever you find more palatable.
+
+```cpp
+#include "yourcratename/path/to/lib.rs"
+```
+
+## Including headers from dependencies
+
+You get to include headers from your dependencies, both handwritten ones
+contained as `.h` files in their Cargo package, as well as CXX-generated ones.
+
+It works the same as an include of a local header: use the crate name (or their
+include\_prefix if their crate changed it) followed by the relative path of the
+header within the crate.
+
+```cpp
+#include "dependencycratename/path/to/their/header.h`
+```
+
+Note that cross-crate imports are only made available between **direct
+dependencies**. You must directly depend on the other crate in order to #include
+its headers; a transitive dependency is not sufficient.
+
+Additionally, headers from a direct dependency are only importable if the
+dependency's Cargo.toml manifest contains a `links` key. If not, its headers
+will not be importable from outside of the same crate. See *[the `links`
+manifest key][links]* in the Cargo reference.
+
+[links]: https://doc.rust-lang.org/cargo/reference/build-scripts.html#the-links-manifest-key
+
+<br><br><br>
+
+# Advanced features
+
+The following CFG settings are only relevant to you if you are writing a library
+that needs to support downstream crates `#include`-ing its C++ public headers.
+
+## Publicly exporting header directories
+
+**[`CFG.exported_header_dirs`][CFG]** (vector of absolute paths) defines a set
+of additional directories from which the current crate, directly dependent
+crates, and further crates to which this crate's headers are exported (more
+below) will be able to `#include` headers.
+
+Adding a directory to `exported_header_dirs` is similar to adding it to the
+current build via the `cc` crate's [`Build::include`], but *also* makes the
+directory available to downstream crates that want to `#include` one of the
+headers from your crate. If the dir were added only using `Build::include`, the
+downstream crate including your header would need to manually add the same
+directory to their own build as well.
+
+[`Build::include`]: https://docs.rs/cc/1/cc/struct.Build.html#method.include
+
+When using `exported_header_dirs`, your crate must also set a `links` key for
+itself in Cargo.toml. See [*the `links` manifest key*][links]. The reason is
+that Cargo imposes no ordering on the execution of build scripts without a
+`links` key, which means the downstream crate's build script might otherwise
+execute before yours decides what to put into `exported_header_dirs`.
+
+### Example
+
+One of your crate's headers wants to include a system library, such as `#include
+"Python.h"`.
+
+```rust,noplayground
+// build.rs
+
+use cxx_build::CFG;
+use std::path::PathBuf;
+
+fn main() {
+ let python3 = pkg_config::probe_library("python3").unwrap();
+ let python_include_paths = python3.include_paths.iter().map(PathBuf::as_path);
+ CFG.exported_header_dirs.extend(python_include_paths);
+
+ cxx_build::bridge("src/bridge.rs").compile("demo");
+}
+```
+
+### Example
+
+Your crate wants to rearrange the headers that it exports vs how they're laid
+out locally inside the crate's source directory.
+
+Suppose the crate as published contains a file at `./include/myheader.h` but
+wants it available to downstream crates as `#include "foo/v1/public.h"`.
+
+```rust,noplayground
+// build.rs
+
+use cxx_build::CFG;
+use std::path::Path;
+use std::{env, fs};
+
+fn main() {
+ let out_dir = env::var_os("OUT_DIR").unwrap();
+ let headers = Path::new(&out_dir).join("headers");
+ CFG.exported_header_dirs.push(&headers);
+
+ // We contain `include/myheader.h` locally, but
+ // downstream will use `#include "foo/v1/public.h"`
+ let foo = headers.join("foo").join("v1");
+ fs::create_dir_all(&foo).unwrap();
+ fs::copy("include/myheader.h", foo.join("public.h")).unwrap();
+
+ cxx_build::bridge("src/bridge.rs").compile("demo");
+}
+```
+
+## Publicly exporting dependencies
+
+**[`CFG.exported_header_prefixes`][CFG]** (vector of strings) each refer to the
+`include_prefix` of one of your direct dependencies, or a prefix thereof. They
+describe which of your dependencies participate in your crate's C++ public API,
+as opposed to private use by your crate's implementation.
+
+As a general rule, if one of your headers `#include`s something from one of your
+dependencies, you need to put that dependency's `include_prefix` into
+`CFG.exported_header_prefixes` (*or* their `links` key into
+`CFG.exported_header_links`; see below). On the other hand if only your C++
+implementation files and *not* your headers are importing from the dependency,
+you do not export that dependency.
+
+The significance of exported headers is that if downstream code (crate **𝒜**)
+contains an `#include` of a header from your crate (**ℬ**) and your header
+contains an `#include` of something from your dependency (**𝒞**), the exported
+dependency **𝒞** becomes available during the downstream crate **𝒜**'s build.
+Otherwise the downstream crate **𝒜** doesn't know about **𝒞** and wouldn't be
+able to find what header your header is referring to, and would fail to build.
+
+When using `exported_header_prefixes`, your crate must also set a `links` key
+for itself in Cargo.toml.
+
+### Example
+
+Suppose you have a crate with 5 direct dependencies and the `include_prefix` for
+each one are:
+
+- "crate0"
+- "group/api/crate1"
+- "group/api/crate2"
+- "group/api/contrib/crate3"
+- "detail/crate4"
+
+Your header involves types from the first four so we re-export those as part of
+your public API, while crate4 is only used internally by your cc file not your
+header, so we do not export:
+
+```rust,noplayground
+// build.rs
+
+use cxx_build::CFG;
+
+fn main() {
+ CFG.exported_header_prefixes = vec!["crate0", "group/api"];
+
+ cxx_build::bridge("src/bridge.rs")
+ .file("src/impl.cc")
+ .compile("demo");
+}
+```
+
+<br>
+
+For more fine grained control, there is **[`CFG.exported_header_links`][CFG]**
+(vector of strings) which each refer to the `links` attribute ([*the `links`
+manifest key*][links]) of one of your crate's direct dependencies.
+
+This achieves an equivalent result to `CFG.exported_header_prefixes` by
+re-exporting a C++ dependency as part of your crate's public API, except with
+finer control for cases when multiple crates might be sharing the same
+`include_prefix` and you'd like to export some but not others. Links attributes
+are guaranteed to be unique identifiers by Cargo.
+
+When using `exported_header_links`, your crate must also set a `links` key for
+itself in Cargo.toml.
+
+### Example
+
+```rust,noplayground
+// build.rs
+
+use cxx_build::CFG;
+
+fn main() {
+ CFG.exported_header_links.push("git2");
+
+ cxx_build::bridge("src/bridge.rs").compile("demo");
+}
+```
diff --git a/book/src/build/cmake.md b/book/src/build/cmake.md
new file mode 100644
index 00000000..ed3cb61d
--- /dev/null
+++ b/book/src/build/cmake.md
@@ -0,0 +1,24 @@
+{{#title CMake — Rust ♡ C++}}
+# CMake
+
+There is not an officially endorsed CMake setup for CXX, but a few developers
+have shared one that they got working. You can try one of these as a starting
+point. If you feel that you have arrived at a CMake setup that is superior to
+what is available in these links, feel free to make a PR adding it to this list.
+
+<br>
+
+---
+
+- **<https://github.com/XiangpengHao/cxx-cmake-example>**
+
+ - Supports cross-language link time optimization (LTO)
+
+---
+
+- **<https://github.com/david-cattermole/cxx-demo-example>**
+
+ - Includes a cbindgen component
+ - Tested on Windows 10 with MSVC, and on Linux
+
+---
diff --git a/book/src/build/other.md b/book/src/build/other.md
new file mode 100644
index 00000000..8933c41b
--- /dev/null
+++ b/book/src/build/other.md
@@ -0,0 +1,75 @@
+{{#title Other build systems — Rust ♡ C++}}
+# Some other build system
+
+You will need to achieve at least these three things:
+
+- Produce the CXX-generated C++ bindings code.
+- Compile the generated C++ code.
+- Link the resulting objects together with your other C++ and Rust objects.
+
+### Producing the generated code
+
+CXX's Rust code generation automatically happens when the `#[cxx::bridge]`
+procedural macro is expanded during the normal Rust compilation process, so no
+special build steps are required there.
+
+But the C++ side of the bindings needs to be generated. Your options are:
+
+- Use the `cxxbridge` command, which is a standalone command line interface to
+ the CXX C++ code generator. Wire up your build system to compile and invoke
+ this tool.
+
+ ```console
+ $ cxxbridge src/bridge.rs --header > path/to/bridge.rs.h
+ $ cxxbridge src/bridge.rs > path/to/bridge.rs.cc
+ ```
+
+ It's packaged as the `cxxbridge-cmd` crate on crates.io or can be built from
+ the *gen/cmd/* directory of the CXX GitHub repo.
+
+- Or, build your own code generator frontend on top of the [cxx-gen] crate. This
+ is currently unofficial and unsupported.
+
+[cxx-gen]: https://docs.rs/cxx-gen
+
+### Compiling C++
+
+However you like. We can provide no guidance.
+
+### Linking the C++ and Rust together
+
+When linking a binary which contains mixed Rust and C++ code, you will have to
+choose between using the Rust toolchain (`rustc`) or the C++ toolchain which you
+may already have extensively tuned.
+
+Rust does not generate simple standalone `.o` files, so you can't just throw the
+Rust-generated code into your existing C++ toolchain linker. Instead you need to
+choose one of these options:
+
+* Use `rustc` as the final linker. Pass any non-Rust libraries using `-L
+ <directory>` and `-l<library>` rustc arguments, and/or `#[link]` directives in
+ your Rust code. If you need to link against C/C++ `.o` files you can use
+ `-Clink-arg=file.o`.
+
+* Use your C++ linker. In this case, you first need to use `rustc` and/or
+ `cargo` to generate a _single_ Rust `staticlib` target and pass that into your
+ foreign linker invocation.
+
+ * If you need to link multiple Rust subsystems, you will need to generate a
+ _single_ `staticlib` perhaps using lots of `extern crate` statements to
+ include multiple Rust `rlib`s. Multiple Rust `staticlib` files are likely
+ to conflict.
+
+Passing Rust `rlib`s directly into your non-Rust linker is not supported (but
+apparently sometimes works).
+
+See the [Rust reference's *Linkage*][linkage] page for some general information
+here.
+
+[linkage]: https://doc.rust-lang.org/reference/linkage.html
+
+The following open rust-lang issues might hold more recent guidance or
+inspiration: [rust-lang/rust#73632], [rust-lang/rust#73295].
+
+[rust-lang/rust#73632]: https://github.com/rust-lang/rust/issues/73632
+[rust-lang/rust#73295]: https://github.com/rust-lang/rust/issues/73295
diff --git a/book/src/building.md b/book/src/building.md
new file mode 100644
index 00000000..c75939e0
--- /dev/null
+++ b/book/src/building.md
@@ -0,0 +1,20 @@
+{{#title Multi-language build system options — Rust ♡ C++}}
+# Multi-language build system options
+
+CXX is designed to be convenient to integrate into a variety of build systems.
+
+If you are working in a project that does not already have a preferred build
+system for its C++ code *or* which will be relying heavily on open source
+libraries from the Rust package registry, you're likely to have the easiest
+experience with Cargo which is the build system commonly used by open source
+Rust projects. Refer to the ***[Cargo](build/cargo.md)*** chapter about CXX's
+Cargo support.
+
+Among build systems designed for first class multi-language support, Bazel is a
+solid choice. Refer to the ***[Bazel](build/bazel.md)*** chapter.
+
+If your codebase is already invested in CMake, refer to the
+***[CMake](build/cmake.md)*** chapter.
+
+If you have some other build system that you'd like to try to make work with
+CXX, see [this page](build/other.md) for notes.
diff --git a/book/src/concepts.md b/book/src/concepts.md
new file mode 100644
index 00000000..75daedde
--- /dev/null
+++ b/book/src/concepts.md
@@ -0,0 +1,85 @@
+{{#title Core concepts — Rust ♡ C++}}
+# Core concepts
+
+This page is a brief overview of the major concepts of CXX, enough so that you
+recognize the shape of things as you read the tutorial and following chapters.
+
+In CXX, the language of the FFI boundary involves 3 kinds of items:
+
+- **Shared structs** &mdash; data structures whose fields are made visible to
+ both languages. The definition written within cxx::bridge in Rust is usually
+ the single source of truth, though there are ways to do sharing based on a
+ bindgen-generated definition with C++ as source of truth.
+
+- **Opaque types** &mdash; their fields are secret from the other language.
+ These cannot be passed across the FFI by value but only behind an indirection,
+ such as a reference `&`, a Rust `Box`, or a C++ `unique_ptr`. Can be a type
+ alias for an arbitrarily complicated generic language-specific type depending
+ on your use case.
+
+- **Functions** &mdash; implemented in either language, callable from the other
+ language.
+
+```rust,noplayground,focuscomment
+# #[cxx::bridge]
+# mod ffi {
+ // Any shared structs, whose fields will be visible to both languages.
+# struct BlobMetadata {
+# size: usize,
+# tags: Vec<String>,
+# }
+#
+# extern "Rust" {
+ // Zero or more opaque types which both languages can pass around
+ // but only Rust can see the fields.
+# type MultiBuf;
+#
+ // Functions implemented in Rust.
+# fn next_chunk(buf: &mut MultiBuf) -> &[u8];
+# }
+#
+# unsafe extern "C++" {
+ // One or more headers with the matching C++ declarations for the
+ // enclosing extern "C++" block. Our code generators don't read it
+ // but it gets #include'd and used in static assertions to ensure
+ // our picture of the FFI boundary is accurate.
+# include!("demo/include/blobstore.h");
+#
+ // Zero or more opaque types which both languages can pass around
+ // but only C++ can see the fields.
+# type BlobstoreClient;
+#
+ // Functions implemented in C++.
+# fn new_blobstore_client() -> UniquePtr<BlobstoreClient>;
+# fn put(&self, parts: &mut MultiBuf) -> u64;
+# fn tag(&self, blobid: u64, tag: &str);
+# fn metadata(&self, blobid: u64) -> BlobMetadata;
+# }
+# }
+```
+
+Within the `extern "Rust"` part of the CXX bridge we list the types and
+functions for which Rust is the source of truth. These all implicitly refer to
+the `super` module, the parent module of the CXX bridge. You can think of the
+two items listed in the example above as being like `use super::MultiBuf` and
+`use super::next_chunk` except re-exported to C++. The parent module will either
+contain the definitions directly for simple things, or contain the relevant
+`use` statements to bring them into scope from elsewhere.
+
+Within the `extern "C++"` part, we list types and functions for which C++ is the
+source of truth, as well as the header(s) that declare those APIs. In the future
+it's possible that this section could be generated bindgen-style from the
+headers but for now we need the signatures written out; static assertions verify
+that they are accurate.
+
+<br><br>
+
+Be aware that the design of this library is intentionally restrictive and
+opinionated! It isn't a goal to be flexible enough to handle an arbitrary
+signature in either language. Instead this project is about carving out a highly
+expressive set of functionality about which we can make powerful safety
+guarantees today and extend over time. You may find that it takes some practice
+to use CXX bridge effectively as it won't work in all the ways that you may be
+used to.
+
+<br>
diff --git a/book/src/context.md b/book/src/context.md
new file mode 100644
index 00000000..516ee91f
--- /dev/null
+++ b/book/src/context.md
@@ -0,0 +1,118 @@
+{{#title Other Rust–C++ interop tools — Rust ♡ C++}}
+# Context: other Rust&ndash;C++ interop tools
+
+When it comes to interacting with an idiomatic Rust API or idiomatic C++ API
+from the other language, the generally applicable approaches outside of the CXX
+crate are:
+
+- Build a C-compatible wrapper around the code (expressed using `extern "C"`
+ signatures, primitives, C-compatible structs, raw pointers). Translate that
+ manually to equivalent `extern "C"` declarations in the other language and
+ keep them in sync. Preferably, build a safe/idiomatic wrapper around the
+ translated `extern "C"` signatures for callers to use.
+
+- Build a C wrapper around the C++ code and use **[bindgen]** to translate that
+ programmatically to `extern "C"` Rust signatures. Preferably, build a
+ safe/idiomatic Rust wrapper on top.
+
+- Build a C-compatible Rust wrapper around the Rust code and use **[cbindgen]**
+ to translate that programmatically to an `extern "C"` C++ header. Preferably,
+ build an idiomatic C++ wrapper.
+
+**If the code you are binding is already *"effectively C"*, the above has you
+covered.** You should use bindgen or cbindgen, or manually translated C
+signatures if there aren't too many and they seldom change.
+
+[bindgen]: https://github.com/rust-lang/rust-bindgen
+[cbindgen]: https://github.com/eqrion/cbindgen
+
+## C++ vs C
+
+Bindgen has some basic support for C++. It can reason about classes, member
+functions, and the layout of templated types. However, everything it does
+related to C++ is best-effort only. Bindgen starts from a point of wanting to
+generate declarations for everything, so any C++ detail that it hasn't
+implemented will cause a crash if you are lucky ([bindgen#388]) or more likely
+silently emit an incompatible signature ([bindgen#380], [bindgen#607],
+[bindgen#652], [bindgen#778], [bindgen#1194]) which will do arbitrary
+memory-unsafe things at runtime whenever called.
+
+[bindgen#388]: https://github.com/rust-lang/rust-bindgen/issues/388
+[bindgen#380]: https://github.com/rust-lang/rust-bindgen/issues/380
+[bindgen#607]: https://github.com/rust-lang/rust-bindgen/issues/607
+[bindgen#652]: https://github.com/rust-lang/rust-bindgen/issues/652
+[bindgen#778]: https://github.com/rust-lang/rust-bindgen/issues/778
+[bindgen#1194]: https://github.com/rust-lang/rust-bindgen/issues/1194
+
+Thus using bindgen correctly requires not just juggling all your pointers
+correctly at the language boundary, but also understanding ABI details and their
+workarounds and reliably applying them. For example, the programmer will
+discover that their program sometimes segfaults if they call a function that
+returns std::unique\_ptr\<T\> through bindgen. Why? Because unique\_ptr, despite
+being "just a pointer", has a different ABI than a pointer or a C struct
+containing a pointer ([bindgen#778]) and is not directly expressible in Rust.
+Bindgen emitted something that *looks* reasonable and you will have a hell of a
+time in gdb working out what went wrong. Eventually people learn to avoid
+anything involving a non-trivial copy constructor, destructor, or inheritance,
+and instead stick to raw pointers and primitives and trivial structs only
+&mdash; in other words C.
+
+## Geometric intuition for why there is so much opportunity for improvement
+
+The CXX project attempts a different approach to C++ FFI.
+
+Imagine Rust and C and C++ as three vertices of a scalene triangle, with length
+of the edges being related to similarity of the languages when it comes to
+library design.
+
+The most similar pair (the shortest edge) is Rust&ndash;C++. These languages
+have largely compatible concepts of things like ownership, vectors, strings,
+fallibility, etc that translate clearly from signatures in either language to
+signatures in the other language.
+
+When we make a binding for an idiomatic C++ API using bindgen, and we fall down
+to raw pointers and primitives and trivial structs as described above, what we
+are really doing is coding the two longest edges of the triangle: getting from
+C++ down to C, and C back up to Rust. The Rust&ndash;C edge always involves a
+great deal of `unsafe` code, and the C++&ndash;C edge similarly requires care
+just for basic memory safety. Something as basic as "how do I pass ownership of
+a string to the other language?" becomes a strap-yourself-in moment,
+particularly for someone not already an expert in one or both sides.
+
+You should think of the `cxx` crate as being the midpoint of the Rust&ndash;C++
+edge. Rather than coding the two long edges, you will code half the short edge
+in Rust and half the short edge in C++, in both cases with the library playing
+to the strengths of the Rust type system *and* the C++ type system to help
+assure correctness.
+
+If you've already been through the tutorial in the previous chapter, take a
+moment to appreciate that the C++ side *really* looks like we are just writing
+C++ and the Rust side *really* looks like we are just writing Rust. Anything you
+could do wrong in Rust, and almost anything you could reasonably do wrong in
+C++, will be caught by the compiler. This highlights that we are on the "short
+edge of the triangle".
+
+But it all still boils down to the same things: it's still FFI from one piece of
+native code to another, nothing is getting serialized or allocated or
+runtime-checked in between.
+
+## Role of CXX
+
+The role of CXX is to capture the language boundary with more fidelity than what
+`extern "C"` is able to represent. You can think of CXX as being a replacement
+for `extern "C"` in a sense.
+
+From this perspective, CXX is a lower level tool than the bindgens. Just as
+bindgen and cbindgen are built on top of `extern "C"`, it makes sense to think
+about higher level tools built on top of CXX. Such a tool might consume a C++
+header and/or Rust module (and/or IDL like Thrift) and emit the corresponding
+safe cxx::bridge language boundary, leveraging CXX's static analysis and
+underlying implementation of that boundary. We are beginning to see this space
+explored by the [autocxx] tool, though nothing yet ready for broad use in the
+way that CXX on its own is.
+
+[autocxx]: https://github.com/google/autocxx
+
+But note in other ways CXX is higher level than the bindgens, with rich support
+for common standard library types. CXX's types serve as an intuitive vocabulary
+for designing a good boundary between components in different languages.
diff --git a/book/src/cxx.png b/book/src/cxx.png
new file mode 100644
index 00000000..aeb7ca9b
--- /dev/null
+++ b/book/src/cxx.png
Binary files differ
diff --git a/book/src/extern-c++.md b/book/src/extern-c++.md
new file mode 100644
index 00000000..11ed7b54
--- /dev/null
+++ b/book/src/extern-c++.md
@@ -0,0 +1,352 @@
+{{#title extern "C++" — Rust ♡ C++}}
+# extern "C++"
+
+```rust,noplayground
+#[cxx::bridge]
+mod ffi {
+ extern "C++" {
+ include!("path/to/header.h");
+ include!("path/to/another.h");
+
+ ...
+ }
+}
+```
+
+The `extern "C++"` section of a CXX bridge declares C++ types and signatures to
+be made available to Rust, and gives the paths of the header(s) which contain
+the corresponding C++ declarations.
+
+A bridge module may contain zero or more extern "C++" blocks.
+
+## Opaque C++ types
+
+Type defined in C++ that are made available to Rust, but only behind an
+indirection.
+
+```rust,noplayground
+# #[cxx::bridge]
+# mod ffi {
+ extern "C++" {
+ # include!("path/to/header.h");
+ #
+ type MyType;
+ type MyOtherType;
+ }
+# }
+```
+
+For example in the ***[Tutorial](tutorial.md)*** we saw `BlobstoreClient`
+implemented as an opaque C++ type. The blobstore client was created in C++ and
+returned to Rust by way of a UniquePtr.
+
+**Mutability:** Unlike extern Rust types and shared types, an extern C++ type is
+not permitted to be passed by plain mutable reference `&mut MyType` across the
+FFI bridge. For mutation support, the bridge is required to use `Pin<&mut
+MyType>`. This is to safeguard against things like mem::swap-ing the contents of
+two mutable references, given that Rust doesn't have information about the size
+of the underlying object and couldn't invoke an appropriate C++ move constructor
+anyway.
+
+**Thread safety:** Be aware that CXX does not assume anything about the thread
+safety of your extern C++ types. In other words the `MyType` etc bindings which
+CXX produces for you in Rust *do not* come with `Send` and `Sync` impls. If you
+are sure that your C++ type satisfies the requirements of `Send` and/or `Sync`
+and need to leverage that fact from Rust, you must provide your own unsafe
+marker trait impls.
+
+```rust,noplayground
+# #[cxx::bridge]
+# mod ffi {
+# extern "C++" {
+# include!("path/to/header.h");
+#
+# type MyType;
+# }
+# }
+#
+/// The C++ implementation of MyType is thread safe.
+unsafe impl Send for ffi::MyType {}
+unsafe impl Sync for ffi::MyType {}
+```
+
+Take care in doing this because thread safety in C++ can be extremely tricky to
+assess if you are coming from a Rust background. For example the
+`BlobstoreClient` type in the tutorial is *not thread safe* despite doing only
+completely innocuous things in its implementation. Concurrent calls to the `tag`
+member function trigger a data race on the `blobs` map.
+
+## Functions and member functions
+
+This largely follows the same principles as ***[extern
+"Rust"](extern-rust.md)*** functions and methods. In particular, any signature
+with a `self` parameter is interpreted as a C++ non-static member function and
+exposed to Rust as a method.
+
+The programmer **does not** need to promise that the signatures they have typed
+in are accurate; that would be unreasonable. CXX performs static assertions that
+the signatures exactly correspond with what is declared in C++. Rather, the
+programmer is only on the hook for things that C++'s static information is not
+precise enough to capture, i.e. things that would only be represented at most by
+comments in the C++ code unintelligible to a static assertion: namely whether
+the C++ function is safe or unsafe to be called from Rust.
+
+**Safety:** the extern "C++" block is responsible for deciding whether to expose
+each signature inside as safe-to-call or unsafe-to-call. If an extern block
+contains at least one safe-to-call signature, it must be written as an `unsafe
+extern` block, which serves as an item level unsafe block to indicate that an
+unchecked safety claim is being made about the contents of the block.
+
+```rust,noplayground
+#[cxx::bridge]
+mod ffi {
+ unsafe extern "C++" {
+ # include!("path/to/header.h");
+ #
+ fn f(); // safe to call
+ }
+
+ extern "C++" {
+ unsafe fn g(); // unsafe to call
+ }
+}
+```
+
+## Lifetimes
+
+C++ types holding borrowed data may be described naturally in Rust by an extern
+type with a generic lifetime parameter. For example in the case of the following
+pair of types:
+
+```cpp
+// header.h
+
+class Resource;
+
+class TypeContainingBorrow {
+ TypeContainingBorrow(const Resource &res) : res(res) {}
+ const Resource &res;
+};
+
+std::shared_ptr<TypeContainingBorrow> create(const Resource &res);
+```
+
+we'd want to expose this to Rust as:
+
+```rust,noplayground
+#[cxx::bridge]
+mod ffi {
+ unsafe extern "C++" {
+ # include!("path/to/header.h");
+ #
+ type Resource;
+ type TypeContainingBorrow<'a>;
+
+ fn create<'a>(res: &'a Resource) -> SharedPtr<TypeContainingBorrow<'a>>;
+
+ // or with lifetime elision:
+ fn create(res: &Resource) -> SharedPtr<TypeContainingBorrow>;
+ }
+}
+```
+
+## Reusing existing binding types
+
+Extern C++ types support a syntax for declaring that a Rust binding of the
+correct C++ type already exists outside of the current bridge module. This
+avoids generating a fresh new binding which Rust's type system would consider
+non-interchangeable with the first.
+
+```rust,noplayground
+#[cxx::bridge(namespace = "path::to")]
+mod ffi {
+ extern "C++" {
+ type MyType = crate::existing::MyType;
+ }
+
+ extern "Rust" {
+ fn f(x: &MyType) -> usize;
+ }
+}
+```
+
+In this case rather than producing a unique new Rust type `ffi::MyType` for the
+Rust binding of C++'s `::path::to::MyType`, CXX will reuse the already existing
+binding at `crate::existing::MyType` in expressing the signature of `f` and any
+other uses of `MyType` within the bridge module.
+
+CXX safely validates that `crate::existing::MyType` is in fact a binding for the
+right C++ type `::path::to::MyType` by generating a static assertion based on
+`crate::existing::MyType`'s implementation of [`ExternType`], which is a trait
+automatically implemented by CXX for bindings that it generates but can also be
+manually implemented as described below.
+
+[`ExternType`]: https://docs.rs/cxx/*/cxx/trait.ExternType.html
+
+`ExternType` serves the following two related use cases.
+
+#### Safely unifying occurrences of an extern type across bridges
+
+In the following snippet, two #\[cxx::bridge\] invocations in different files
+(possibly different crates) both contain function signatures involving the same
+C++ type `example::Demo`. If both were written just containing `type Demo;`,
+then both macro expansions would produce their own separate Rust type called
+`Demo` and thus the compiler wouldn't allow us to take the `Demo` returned by
+`file1::ffi::create_demo` and pass it as the `Demo` argument accepted by
+`file2::ffi::take_ref_demo`. Instead, one of the two `Demo`s has been defined as
+an extern type alias of the other, making them the same type in Rust.
+
+```rust,noplayground
+// file1.rs
+#[cxx::bridge(namespace = "example")]
+pub mod ffi {
+ unsafe extern "C++" {
+ type Demo;
+
+ fn create_demo() -> UniquePtr<Demo>;
+ }
+}
+```
+
+```rust,noplayground
+// file2.rs
+#[cxx::bridge(namespace = "example")]
+pub mod ffi {
+ unsafe extern "C++" {
+ type Demo = crate::file1::ffi::Demo;
+
+ fn take_ref_demo(demo: &Demo);
+ }
+}
+```
+
+#### Integrating with bindgen-generated or handwritten unsafe bindings
+
+Handwritten `ExternType` impls make it possible to plug in a data structure
+emitted by bindgen as the definition of a C++ type emitted by CXX.
+
+By writing the unsafe `ExternType` impl, the programmer asserts that the C++
+namespace and type name given in the type id refers to a C++ type that is
+equivalent to Rust type that is the `Self` type of the impl.
+
+```rust,noplayground
+mod folly_sys; // the bindgen-generated bindings
+
+use cxx::{type_id, ExternType};
+
+unsafe impl ExternType for folly_sys::StringPiece {
+ type Id = type_id!("folly::StringPiece");
+ type Kind = cxx::kind::Opaque;
+}
+
+#[cxx::bridge(namespace = "folly")]
+pub mod ffi {
+ unsafe extern "C++" {
+ include!("rust_cxx_bindings.h");
+
+ type StringPiece = crate::folly_sys::StringPiece;
+
+ fn print_string_piece(s: &StringPiece);
+ }
+}
+
+// Now if we construct a StringPiece or obtain one through one
+// of the bindgen-generated signatures, we are able to pass it
+// along to ffi::print_string_piece.
+```
+
+The `ExternType::Id` associated type encodes a type-level representation of the
+type's C++ namespace and type name. It will always be defined using the
+`type_id!` macro exposed in the cxx crate.
+
+The `ExternType::Kind` associated type will always be either
+[`cxx::kind::Opaque`] or [`cxx::kind::Trivial`] identifying whether a C++ type
+is soundly relocatable by Rust's move semantics. A C++ type is only okay to hold
+and pass around by value in Rust if its [move constructor is trivial] and it has
+no destructor. In CXX, these are called Trivial extern C++ types, while types
+with nontrivial move behavior or a destructor must be considered Opaque and
+handled by Rust only behind an indirection, such as a reference or UniquePtr.
+
+[`cxx::kind::Opaque`]: https://docs.rs/cxx/*/cxx/kind/enum.Opaque.html
+[`cxx::kind::Trivial`]: https://docs.rs/cxx/*/cxx/kind/enum.Trivial.html
+[move constructor is trivial]: https://en.cppreference.com/w/cpp/types/is_move_constructible
+
+If you believe your C++ type reflected by the ExternType impl is indeed fine to
+hold by value and move in Rust, you can specify:
+
+```rust,noplayground
+# unsafe impl cxx::ExternType for TypeName {
+# type Id = cxx::type_id!("name::space::of::TypeName");
+ type Kind = cxx::kind::Trivial;
+# }
+```
+
+which will enable you to pass it into C++ functions by value, return it by
+value, and include it in `struct`s that you have declared to `cxx::bridge`. Your
+claim about the triviality of the C++ type will be checked by a `static_assert`
+in the generated C++ side of the binding.
+
+## Explicit shim trait impls
+
+This is a somewhat niche feature, but important when you need it.
+
+CXX's support for C++'s std::unique\_ptr and std::vector is built on a set of
+internal trait impls connecting the Rust API of UniquePtr and CxxVector to
+underlying template instantiations performed by the C++ compiler.
+
+When reusing a binding type across multiple bridge modules as described in the
+previous section, you may find that your code needs some trait impls which CXX
+hasn't decided to generate.
+
+```rust,noplayground
+#[cxx::bridge]
+mod ffi1 {
+ extern "C++" {
+ include!("path/to/header.h");
+
+ type A;
+ type B;
+
+ // Okay: CXX sees UniquePtr<B> using a type B defined within the same
+ // bridge, and automatically emits the right template instantiations
+ // corresponding to std::unique_ptr<B>.
+ fn get_b() -> UniquePtr<B>;
+ }
+}
+
+#[cxx::bridge]
+mod ffi2 {
+ extern "C++" {
+ type A = crate::ffi1::A;
+
+ // Rust trait error: CXX processing this module has no visibility into
+ // whether template instantiations corresponding to std::unique_ptr<A>
+ // have already been emitted by the upstream library, so it does not
+ // emit them here. If the upstream library does not have any signatures
+ // involving UniquePtr<A>, an explicit instantiation of the template
+ // needs to be requested in one module or the other.
+ fn get_a() -> UniquePtr<A>;
+ }
+}
+```
+
+You can request a specific template instantiation at a particular location in
+the Rust crate hierarchy by writing `impl UniquePtr<A> {}` inside of the bridge
+module which defines `A` but does not otherwise contain any use of
+`UniquePtr<A>`.
+
+```rust,noplayground
+#[cxx::bridge]
+mod ffi1 {
+ extern "C++" {
+ include!("path/to/header.h");
+
+ type A;
+ type B;
+
+ fn get_b() -> UniquePtr<B>;
+ }
+
+ impl UniquePtr<A> {} // explicit instantiation
+}
+```
diff --git a/book/src/extern-rust.md b/book/src/extern-rust.md
new file mode 100644
index 00000000..ab9dcb64
--- /dev/null
+++ b/book/src/extern-rust.md
@@ -0,0 +1,165 @@
+{{#title extern "Rust" — Rust ♡ C++}}
+# extern "Rust"
+
+```rust,noplayground
+#[cxx::bridge]
+mod ffi {
+ extern "Rust" {
+
+ }
+}
+```
+
+The `extern "Rust"` section of a CXX bridge declares Rust types and signatures
+to be made available to C++.
+
+The CXX code generator uses your extern "Rust" section(s) to produce a C++
+header file containing the corresponding C++ declarations. The generated header
+has the same path as the Rust source file containing the bridge, except with a
+`.rs.h` file extension.
+
+A bridge module may contain zero or more extern "Rust" blocks.
+
+## Opaque Rust types
+
+Types defined in Rust that are made available to C++, but only behind an
+indirection.
+
+```rust,noplayground
+# #[cxx::bridge]
+# mod ffi {
+ extern "Rust" {
+ type MyType;
+ type MyOtherType;
+ type OneMoreType<'a>;
+ }
+# }
+```
+
+For example in the ***[Tutorial](tutorial.md)*** we saw `MultiBuf` used in this
+way. Rust code created the `MultiBuf`, passed a `&mut MultiBuf` to C++, and C++
+later passed a `&mut MultiBuf` back across the bridge to Rust.
+
+Another example is the one on the ***[Box\<T\>](binding/box.md)*** page, which
+exposes the Rust standard library's `std::fs::File` to C++ as an opaque type in
+a similar way but with Box as the indirection rather than &mut.
+
+The types named as opaque types (`MyType` etc) refer to types in the `super`
+module, the parent module of the CXX bridge. You can think of an opaque type `T`
+as being like a re-export `use super::T` made available to C++ via the generated
+header.
+
+Opaque types are currently required to be [`Sized`] and [`Unpin`]. In
+particular, a trait object `dyn MyTrait` or slice `[T]` may not be used for an
+opaque Rust type. These restrictions may be lifted in the future.
+
+[`Sized`]: https://doc.rust-lang.org/std/marker/trait.Sized.html
+[`Unpin`]: https://doc.rust-lang.org/std/marker/trait.Unpin.html
+
+For now, types used as extern Rust types are required to be defined by the same
+crate that contains the bridge using them. This restriction may be lifted in the
+future.
+
+The bridge's parent module will contain the appropriate imports or definitions
+for these types.
+
+```rust,noplayground
+use path::to::MyType;
+
+pub struct MyOtherType {
+ ...
+}
+#
+# #[cxx::bridge]
+# mod ffi {
+# extern "Rust" {
+# type MyType;
+# type MyOtherType;
+# }
+# }
+```
+
+## Functions
+
+Rust functions made callable to C++.
+
+Just like for opaque types, these functions refer implicitly to something in
+scope in the `super` module, whether defined there or imported by some `use`
+statement.
+
+```rust,noplayground
+#[cxx::bridge]
+mod ffi {
+ extern "Rust" {
+ struct MyType;
+ fn f() -> Box<MyType>;
+ }
+}
+
+struct MyType(i32);
+
+fn f() -> Box<MyType> {
+ return Box::new(MyType(1));
+}
+```
+
+Extern Rust function signature may consist of types defined in the bridge,
+primitives, and [any of these additional bindings](bindings.md).
+
+## Methods
+
+Any signature with a `self` parameter is interpreted as a Rust method and
+exposed to C++ as a non-static member function.
+
+```rust,noplayground
+# #[cxx::bridge]
+# mod ffi {
+ extern "Rust" {
+ type MyType;
+ fn f(&self) -> usize;
+ }
+# }
+```
+
+The `self` parameter may be a shared reference `&self`, an exclusive reference
+`&mut self`, or a pinned reference `self: Pin<&mut Self>`. A by-value `self` is
+not currently supported.
+
+If the surrounding `extern "Rust"` block contains exactly one extern type, that
+type is implicitly the receiver for a `&self` or `&mut self` method. If the
+surrounding block contains *more than one* extern type, a receiver type must be
+provided explicitly for the self parameter, or you can consider splitting into
+multiple extern blocks.
+
+```rust,noplayground
+# #[cxx::bridge]
+# mod ffi {
+ extern "Rust" {
+ type First;
+ type Second;
+ fn bar(self: &First);
+ fn foo(self: &mut Second);
+ }
+# }
+```
+
+## Functions with explicit lifetimes
+
+An extern Rust function signature is allowed to contain explicit lifetimes but
+in this case the function must be declared unsafe-to-call. This is pretty
+meaningless given we're talking about calls from C++, but at least it draws some
+extra attention from the caller that they may be responsible for upholding some
+atypical lifetime relationship.
+
+```rust,noplayground
+#[cxx::bridge]
+mod ffi {
+ extern "Rust" {
+ type MyType;
+ unsafe fn f<'a>(&'a self, s: &str) -> &'a str;
+ }
+}
+```
+
+Bounds on a lifetime (like `<'a, 'b: 'a>`) are not currently supported. Nor are
+type parameters or where-clauses.
diff --git a/book/src/index.md b/book/src/index.md
new file mode 100644
index 00000000..7b03e7e1
--- /dev/null
+++ b/book/src/index.md
@@ -0,0 +1,83 @@
+<div class="badges">
+<a href="https://github.com/dtolnay/cxx"><img src="https://img.shields.io/badge/github-8da0cb?style=for-the-badge&labelColor=555555&logo=github" alt="github" height="28" class="badge"></a><a href="https://crates.io/crates/cxx"><img src="https://img.shields.io/badge/crates.io-fc8d62?style=for-the-badge&labelColor=555555&logo=rust" alt="crates-io" height="28" class="badge"></a><a href="https://docs.rs/cxx"><img src="https://img.shields.io/badge/docs.rs-66c2a5?style=for-the-badge&labelColor=555555&logoColor=white&logo=data:image/svg+xml;base64,PHN2ZyByb2xlPSJpbWciIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgdmlld0JveD0iMCAwIDUxMiA1MTIiPjxwYXRoIGZpbGw9IiNmNWY1ZjUiIGQ9Ik00ODguNiAyNTAuMkwzOTIgMjE0VjEwNS41YzAtMTUtOS4zLTI4LjQtMjMuNC0zMy43bC0xMDAtMzcuNWMtOC4xLTMuMS0xNy4xLTMuMS0yNS4zIDBsLTEwMCAzNy41Yy0xNC4xIDUuMy0yMy40IDE4LjctMjMuNCAzMy43VjIxNGwtOTYuNiAzNi4yQzkuMyAyNTUuNSAwIDI2OC45IDAgMjgzLjlWMzk0YzAgMTMuNiA3LjcgMjYuMSAxOS45IDMyLjJsMTAwIDUwYzEwLjEgNS4xIDIyLjEgNS4xIDMyLjIgMGwxMDMuOS01MiAxMDMuOSA1MmMxMC4xIDUuMSAyMi4xIDUuMSAzMi4yIDBsMTAwLTUwYzEyLjItNi4xIDE5LjktMTguNiAxOS45LTMyLjJWMjgzLjljMC0xNS05LjMtMjguNC0yMy40LTMzLjd6TTM1OCAyMTQuOGwtODUgMzEuOXYtNjguMmw4NS0zN3Y3My4zek0xNTQgMTA0LjFsMTAyLTM4LjIgMTAyIDM4LjJ2LjZsLTEwMiA0MS40LTEwMi00MS40di0uNnptODQgMjkxLjFsLTg1IDQyLjV2LTc5LjFsODUtMzguOHY3NS40em0wLTExMmwtMTAyIDQxLjQtMTAyLTQxLjR2LS42bDEwMi0zOC4yIDEwMiAzOC4ydi42em0yNDAgMTEybC04NSA0Mi41di03OS4xbDg1LTM4Ljh2NzUuNHptMC0xMTJsLTEwMiA0MS40LTEwMi00MS40di0uNmwxMDItMzguMiAxMDIgMzguMnYuNnoiPjwvcGF0aD48L3N2Zz4K" alt="docs-rs" height="28" class="badge"></a>
+</div>
+
+# CXX — safe interop between Rust and C++
+
+This library provides a safe mechanism for calling C++ code from Rust and Rust
+code from C++. It carves out a regime of commonality where Rust and C++ are
+semantically very similar and guides the programmer to express their language
+boundary effectively within this regime. CXX fills in the low level stuff so
+that you get a safe binding, preventing the pitfalls of doing a foreign function
+interface over unsafe C-style signatures.
+
+<div style="height:226px;padding:34px 0 24px">
+<object type="image/svg+xml" data="overview.svg"></object>
+</div>
+
+From a high level description of the language boundary, CXX uses static analysis
+of the types and function signatures to protect both Rust's and C++'s
+invariants. Then it uses a pair of code generators to implement the boundary
+efficiently on both sides together with any necessary static assertions for
+later in the build process to verify correctness.
+
+The resulting FFI bridge operates at zero or negligible overhead, i.e. no
+copying, no serialization, no memory allocation, no runtime checks needed.
+
+The FFI signatures are able to use native data structures from whichever side
+they please. In addition, CXX provides builtin bindings for key standard library
+types like strings, vectors, Box, unique\_ptr, etc to expose an idiomatic API on
+those types to the other language.
+
+## Example
+
+In this example we are writing a Rust application that calls a C++ client of a
+large-file blobstore service. The blobstore supports a `put` operation for a
+discontiguous buffer upload. For example we might be uploading snapshots of a
+circular buffer which would tend to consist of 2 pieces, or fragments of a file
+spread across memory for some other reason (like a rope data structure).
+
+```rust,noplayground
+#[cxx::bridge]
+mod ffi {
+ extern "Rust" {
+ type MultiBuf;
+
+ fn next_chunk(buf: &mut MultiBuf) -> &[u8];
+ }
+
+ unsafe extern "C++" {
+ include!("example/include/blobstore.h");
+
+ type BlobstoreClient;
+
+ fn new_blobstore_client() -> UniquePtr<BlobstoreClient>;
+ fn put(self: &BlobstoreClient, buf: &mut MultiBuf) -> Result<u64>;
+ }
+}
+```
+
+Now we simply provide Rust definitions of all the things in the `extern "Rust"`
+block and C++ definitions of all the things in the `extern "C++"` block, and get
+to call back and forth safely.
+
+The [***Tutorial***](tutorial.md) chapter walks through a fleshed out version of
+this blobstore example in full detail, including all of the Rust code and all of
+the C++ code. The code is also provided in runnable form in the *demo* directory
+of <https://github.com/dtolnay/cxx>. To try it out, run `cargo run` from that
+directory.
+
+- [demo/src/main.rs](https://github.com/dtolnay/cxx/blob/master/demo/src/main.rs)
+- [demo/include/blobstore.h](https://github.com/dtolnay/cxx/blob/master/demo/include/blobstore.h)
+- [demo/src/blobstore.cc](https://github.com/dtolnay/cxx/blob/master/demo/src/blobstore.cc)
+
+The key takeaway, which is enabled by the CXX library, is that the Rust code in
+main.rs is 100% ordinary safe Rust code working idiomatically with Rust types
+while the C++ code in blobstore.cc is 100% ordinary C++ code working
+idiomatically with C++ types. The Rust code feels like Rust and the C++ code
+feels like C++, not like C-style "FFI glue".
+
+<br>
+
+***Chapter outline:** See the hamburger menu in the top left if you are on a
+small screen and it didn't open with a sidebar by default.*
diff --git a/book/src/overview.svg b/book/src/overview.svg
new file mode 100644
index 00000000..65905471
--- /dev/null
+++ b/book/src/overview.svg
@@ -0,0 +1,99 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="696" height="224">
+ <style>line, path, circle,rect,polygon {
+ stroke: black;
+ stroke-width: 2;
+ stroke-opacity: 1;
+ fill-opacity: 1;
+ stroke-linecap: round;
+ stroke-linejoin: miter;
+ }
+
+ text {
+ fill: black;
+ }
+ rect.backdrop{
+ stroke: none;
+ fill: white;
+ fill-opacity: 0;
+ }
+ .broken{
+ stroke-dasharray: 8;
+ }
+ .filled{
+ fill: black;
+ }
+ .bg_filled{
+ fill: white;
+ }
+ .nofill{
+ fill: white;
+ fill-opacity: 0;
+ }
+
+ text {
+ font-family: monospace;
+ font-size: 14px;
+ }
+ </style>
+ <defs>
+ <marker id="arrow" viewBox="-2 -2 8 8" refX="4" refY="2" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
+ <polygon points="0,0 0,4 4,2 0,0"></polygon>
+ </marker>
+ <marker id="diamond" viewBox="-2 -2 8 8" refX="4" refY="2" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
+ <polygon points="0,2 2,0 4,2 2,4 0,2"></polygon>
+ </marker>
+ <marker id="circle" viewBox="0 0 8 8" refX="4" refY="4" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
+ <circle cx="4" cy="4" r="2" class="filled"></circle>
+ </marker>
+ <marker id="open_circle" viewBox="0 0 8 8" refX="4" refY="4" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
+ <circle cx="4" cy="4" r="2" class="bg_filled"></circle>
+ </marker>
+ <marker id="big_open_circle" viewBox="0 0 8 8" refX="4" refY="4" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
+ <circle cx="4" cy="4" r="3" class="bg_filled"></circle>
+ </marker>
+ </defs>
+ <rect class="backdrop" x="0" y="0" width="696" height="224"></rect>
+ <rect x="148" y="168" width="136" height="32" class="solid nofill" rx="4"></rect>
+ <text x="162" y="188" >Rust bindings</text>
+ <rect x="412" y="168" width="128" height="32" class="solid nofill" rx="4"></rect>
+ <text x="426" y="188" >C++ bindings</text>
+ <text x="272" y="28" >#[cxx::bridge] mod</text>
+ <text x="250" y="44" >description of boundary</text>
+ <line x1="220" y1="120" x2="220" y2="160" class="solid"></line>
+ <polygon points="216,156 224,156 220,164" class="filled"></polygon>
+ <line x1="476" y1="120" x2="476" y2="160" class="solid"></line>
+ <polygon points="472,156 480,156 476,164" class="filled"></polygon>
+ <text x="74" y="140" >Safe</text>
+ <text x="34" y="156" >straightforward</text>
+ <text x="530" y="156" >Straightforward</text>
+ <polygon points="48,180 40,184 48,188" class="filled"></polygon>
+ <line x1="48" y1="184" x2="144" y2="184" class="solid"></line>
+ <polygon points="136,180 144,184 136,188" class="filled"></polygon>
+ <polygon points="296,180 288,184 296,188" class="filled"></polygon>
+ <line x1="296" y1="184" x2="408" y2="184" class="broken"></line>
+ <polygon points="400,180 408,184 400,188" class="filled"></polygon>
+ <polygon points="552,180 544,184 552,188" class="filled"></polygon>
+ <line x1="552" y1="184" x2="648" y2="184" class="solid"></line>
+ <polygon points="644,180 652,184 644,188" class="filled"></polygon>
+ <text x="2" y="188" >Rust</text>
+ <text x="2" y="204" >code</text>
+ <text x="658" y="204" >code</text>
+ <text x="202" y="108" >Macro expansion</text>
+ <text x="370" y="108" >Code generation</text>
+ <text x="58" y="172" >Rust APIs</text>
+ <text x="298" y="172" >Hidden C ABI</text>
+ <text x="562" y="172" >C++ APIs</text>
+ <text x="658" y="188" >C++</text>
+ <g>
+ <path d="M 228,8 A 8,8 0,0,0 220,16" class="nofill"></path>
+ <line x1="228" y1="8" x2="468" y2="8" class="solid"></line>
+ <path d="M 468,8 A 8,8 0,0,1 476,16" class="nofill"></path>
+ <line x1="220" y1="16" x2="220" y2="48" class="solid"></line>
+ <line x1="476" y1="16" x2="476" y2="48" class="solid"></line>
+ <path d="M 220,48 A 8,8 0,0,0 228,56" class="nofill"></path>
+ <line x1="228" y1="56" x2="468" y2="56" class="solid"></line>
+ <line x1="348" y1="56" x2="348" y2="120" class="solid"></line>
+ <path d="M 476,48 A 8,8 0,0,1 468,56" class="nofill"></path>
+ <line x1="220" y1="120" x2="476" y2="120" class="solid"></line>
+ </g>
+</svg>
diff --git a/book/src/reference.md b/book/src/reference.md
new file mode 100644
index 00000000..62ca35c5
--- /dev/null
+++ b/book/src/reference.md
@@ -0,0 +1,29 @@
+{{#title The bridge module — Rust ♡ C++}}
+# The bridge module reference
+
+The ***[Core concepts](concepts.md)*** in chapter 2 covered the high level model
+that CXX uses to represent a language boundary. This chapter builds on that one
+to document an exhaustive reference on the syntax and functionality of
+\#\[cxx::bridge\].
+
+- ***[extern "Rust"](extern-rust.md)*** &mdash; exposing opaque Rust types, Rust
+ functions, Rust methods to C++; functions with lifetimes.
+
+- ***[extern "C++"](extern-c++.md)*** &mdash; binding opaque C++ types, C++
+ functions, C++ member functions; sharing an opaque type definition across
+ multiple bridge modules or different crates; using bindgen-generated data
+ structures across a CXX bridge; Rust orphan-rule-compatible way to request
+ that particular glue code be emitted in a specific bridge module.
+
+- ***[Shared types](shared.md)*** &mdash; shared structs; shared enums; using
+ Rust as source of truth vs C++ as source of truth.
+
+- ***[Attributes](attributes.md)*** &mdash; working with namespaces; giving
+ functions a different name in their non-native language.
+
+- ***[Async functions](async.md)*** &mdash; integrating async C++ with async
+ Rust.
+
+- ***[Error handling](binding/result.md)*** &mdash; representing fallibility on
+ the language boundary; accessing a Rust error message from C++; customizing
+ the set of caught exceptions and their conversion to a Rust error message.
diff --git a/book/src/shared.md b/book/src/shared.md
new file mode 100644
index 00000000..4043db12
--- /dev/null
+++ b/book/src/shared.md
@@ -0,0 +1,246 @@
+{{#title Shared types — Rust ♡ C++}}
+# Shared types
+
+Shared types enable *both* languages to have visibility into the internals of a
+type. This is in contrast to opaque Rust types and opaque C++ types, for which
+only one side gets to manipulate the internals.
+
+Unlike opaque types, the FFI bridge is allowed to pass and return shared types
+by value.
+
+The order in which shared types are written is not important. C++ is order
+sensitive but CXX will topologically sort and forward-declare your types as
+necessary.
+
+## Shared structs and enums
+
+For enums, only C-like a.k.a. unit variants are currently supported.
+
+```rust,noplayground
+#[cxx::bridge]
+mod ffi {
+ struct PlayingCard {
+ suit: Suit,
+ value: u8, // A=1, J=11, Q=12, K=13
+ }
+
+ enum Suit {
+ Clubs,
+ Diamonds,
+ Hearts,
+ Spades,
+ }
+
+ unsafe extern "C++" {
+ fn deck() -> Vec<PlayingCard>;
+ fn sort(cards: &mut Vec<PlayingCard>);
+ }
+}
+```
+
+## The generated data structures
+
+Shared structs compile to an aggregate-initialization compatible C++ struct.
+
+Shared enums compile to a C++ `enum class` with a sufficiently sized integral
+base type decided by CXX.
+
+```cpp
+// generated header
+
+struct PlayingCard final {
+ Suit suit;
+ uint8_t value;
+};
+
+enum class Suit : uint8_t {
+ Clubs = 0,
+ Diamonds = 1,
+ Hearts = 2,
+ Spades = 3,
+};
+```
+
+Because it is not UB in C++ for an `enum class` to hold a value different from
+all of the listed variants, we use a Rust representation for shared enums that
+is compatible with this. The API you'll get is something like:
+
+```rust,noplayground
+#[derive(Copy, Clone, PartialEq, Eq)]
+#[repr(transparent)]
+pub struct Suit {
+ pub repr: u8,
+}
+#[allow(non_upper_case_globals)]
+impl Suit {
+ pub const Clubs: Self = Suit { repr: 0 };
+ pub const Diamonds: Self = Suit { repr: 1 };
+ pub const Hearts: Self = Suit { repr: 2 };
+ pub const Spades: Self = Suit { repr: 3 };
+}
+```
+
+Notice you're free to treat the enum as an integer in Rust code via the public
+`repr` field.
+
+Pattern matching with `match` still works but will require you to write wildcard
+arms to handle the situation of an enum value that is not one of the listed
+variants.
+
+```rust,noplayground
+fn main() {
+ let suit: Suit = /*...*/;
+ match suit {
+ Suit::Clubs => ...,
+ Suit::Diamonds => ...,
+ Suit::Hearts => ...,
+ Suit::Spades => ...,
+ _ => ..., // fallback arm
+ }
+}
+```
+
+If a shared struct has generic lifetime parameters, the lifetimes are simply not
+represented on the C++ side. C++ code will need care when working with borrowed
+data (as usual in C++).
+
+```rust,noplayground
+#[cxx::bridge]
+mod ffi {
+ struct Borrowed<'a> {
+ flags: &'a [&'a str],
+ }
+}
+```
+
+```cpp
+// generated header
+
+struct Borrowed final {
+ rust::Slice<const rust::Str> flags;
+};
+```
+
+## Enum discriminants
+
+You may provide explicit discriminants for some or all of the enum variants, in
+which case those numbers will be propagated into the generated C++ `enum class`.
+
+```rust,noplayground
+#[cxx::bridge]
+mod ffi {
+ enum SmallPrime {
+ Two = 2,
+ Three = 3,
+ Five = 5,
+ Seven = 7,
+ }
+}
+```
+
+Variants without an explicit discriminant are assigned the previous discriminant
+plus 1. If the first variant has not been given an explicit discriminant, it is
+assigned discriminant 0.
+
+By default CXX represents your enum using the smallest integer type capable of
+fitting all the discriminants (whether explicit or implicit). If you need a
+different representation for reasons, provide a `repr` attribute.
+
+```rust,noplayground
+#[cxx::bridge]
+mod ffi {
+ #[repr(i32)]
+ enum Enum {
+ Zero,
+ One,
+ Five = 5,
+ Six,
+ }
+}
+```
+
+```cpp
+// generated header
+
+enum class Enum : int32_t {
+ Zero = 0,
+ One = 1,
+ Five = 5,
+ Six = 6,
+};
+```
+
+## Extern enums
+
+If you need to interoperate with an already existing enum for which an existing
+C++ definition is the source of truth, make sure that definition is provided by
+some header in the bridge and then declare your enum *additionally* as an extern
+C++ type.
+
+```rust,noplayground
+#[cxx::bridge]
+mod ffi {
+ enum Enum {
+ Yes,
+ No,
+ }
+
+ extern "C++" {
+ include!("path/to/the/header.h");
+ type Enum;
+ }
+}
+```
+
+CXX will recognize this pattern and, instead of generating a C++ definition of
+the enum, will instead generate C++ static assertions asserting that the
+variants and discriminant values and integer representation written in Rust all
+correctly match the existing C++ enum definition.
+
+Extern enums support all the same features as ordinary shared enums (explicit
+discriminants, repr). Again, CXX will static assert that all of those things you
+wrote are correct.
+
+## Derives
+
+The following standard traits are supported in `derive(...)` within the CXX
+bridge module.
+
+- `Clone`
+- `Copy`
+- `Debug`
+- `Default`
+- `Eq`
+- `Hash`
+- `Ord`
+- `PartialEq`
+- `PartialOrd`
+
+Note that shared enums automatically always come with impls of `Copy`, `Clone`,
+`Eq`, and `PartialEq`, so you're free to omit those derives on an enum.
+
+```rust,noplayground
+#[cxx::bridge]
+mod ffi {
+ #[derive(Clone, Debug, Hash)]
+ struct ExampleStruct {
+ x: u32,
+ s: String,
+ }
+
+ #[derive(Hash, Ord, PartialOrd)]
+ enum ExampleEnum {
+ Yes,
+ No,
+ }
+}
+```
+
+The derives naturally apply to *both* the Rust data type *and* the corresponding
+C++ data type:
+
+- `Hash` gives you a specialization of [`template <> struct std::hash<T>`][hash] in C++
+- `PartialEq` produces `operator==` and `operator!=`
+- `PartialOrd` produces `operator<`, `operator<=`, `operator>`, `operator>=`
+
+[hash]: https://en.cppreference.com/w/cpp/utility/hash
diff --git a/book/src/tutorial.md b/book/src/tutorial.md
new file mode 100644
index 00000000..c9fa00cb
--- /dev/null
+++ b/book/src/tutorial.md
@@ -0,0 +1,687 @@
+{{#title Tutorial — Rust ♡ C++}}
+# Tutorial: CXX blobstore client
+
+This example walks through a Rust application that calls into a C++ client of a
+blobstore service. In fact we'll see calls going in both directions: Rust to C++
+as well as C++ to Rust. For your own use case it may be that you need just one
+of these directions.
+
+All of the code involved in the example is shown on this page, but it's also
+provided in runnable form in the *demo* directory of
+<https://github.com/dtolnay/cxx>. To try it out directly, run `cargo run` from
+that directory.
+
+This tutorial assumes you've read briefly about **shared structs**, **opaque
+types**, and **functions** in the [*Core concepts*](concepts.md) page.
+
+## Creating the project
+
+We'll use Cargo, which is the build system commonly used by open source Rust
+projects. (CXX works with other build systems too; refer to chapter 5.)
+
+Create a blank Cargo project: `mkdir cxx-demo`; `cd cxx-demo`; `cargo init`.
+
+Edit the Cargo.toml to add a dependency on the `cxx` crate:
+
+```toml,hidelines
+## Cargo.toml
+# [package]
+# name = "cxx-demo"
+# version = "0.1.0"
+# edition = "2018"
+
+[dependencies]
+cxx = "1.0"
+```
+
+We'll revisit this Cargo.toml later when we get to compiling some C++ code.
+
+## Defining the language boundary
+
+CXX relies on a description of the function signatures that will be exposed from
+each language to the other. You provide this description using `extern` blocks
+in a Rust module annotated with the `#[cxx::bridge]` attribute macro.
+
+We'll open with just the following at the top of src/main.rs and walk through
+each item in detail.
+
+```rust,noplayground
+// src/main.rs
+
+#[cxx::bridge]
+mod ffi {
+
+}
+#
+# fn main() {}
+```
+
+The contents of this module will be everything that needs to be agreed upon by
+both sides of the FFI boundary.
+
+## Calling a C++ function from Rust
+
+Let's obtain an instance of the C++ blobstore client, a class `BlobstoreClient`
+defined in C++.
+
+We'll treat `BlobstoreClient` as an *opaque type* in CXX's classification so
+that Rust does not need to assume anything about its implementation, not even
+its size or alignment. In general, a C++ type might have a move-constructor
+which is incompatible with Rust's move semantics, or may hold internal
+references which cannot be modeled by Rust's borrowing system. Though there are
+alternatives, the easiest way to not care about any such thing on an FFI
+boundary is to require no knowledge about a type by treating it as opaque.
+
+Opaque types may only be manipulated behind an indirection such as a reference
+`&`, a Rust `Box`, or a `UniquePtr` (Rust binding of `std::unique_ptr`). We'll
+add a function through which C++ can return a `std::unique_ptr<BlobstoreClient>`
+to Rust.
+
+```rust,noplayground
+// src/main.rs
+
+#[cxx::bridge]
+mod ffi {
+ unsafe extern "C++" {
+ include!("cxx-demo/include/blobstore.h");
+
+ type BlobstoreClient;
+
+ fn new_blobstore_client() -> UniquePtr<BlobstoreClient>;
+ }
+}
+
+fn main() {
+ let client = ffi::new_blobstore_client();
+}
+```
+
+The nature of `unsafe` extern blocks is clarified in more detail in the
+[*extern "C++"*](extern-c++.md) chapter. In brief: the programmer is **not**
+promising that the signatures they have typed in are accurate; that would be
+unreasonable. CXX performs static assertions that the signatures exactly match
+what is declared in C++. Rather, the programmer is only on the hook for things
+that C++'s semantics are not precise enough to capture, i.e. things that would
+only be represented at most by comments in the C++ code. In this case, it's
+whether `new_blobstore_client` is safe or unsafe to call. If that function said
+something like "must be called at most once or we'll stomp yer memery", Rust
+would instead want to expose it as `unsafe fn new_blobstore_client`, this time
+inside a safe `extern "C++"` block because the programmer is no longer on the
+hook for any safety claim about the signature.
+
+If you build this file right now with `cargo build`, it won't build because we
+haven't written a C++ implementation of `new_blobstore_client` nor instructed
+Cargo about how to link it into the resulting binary. You'll see an error from
+the linker like this:
+
+```console
+error: linking with `cc` failed: exit code: 1
+ |
+ = /bin/ld: target/debug/deps/cxx-demo-7cb7fddf3d67d880.rcgu.o: in function `cxx_demo::ffi::new_blobstore_client':
+ src/main.rs:1: undefined reference to `cxxbridge1$new_blobstore_client'
+ collect2: error: ld returned 1 exit status
+```
+
+## Adding in the C++ code
+
+In CXX's integration with Cargo, all #include paths begin with a crate name by
+default (when not explicitly selected otherwise by a crate; see
+`CFG.include_prefix` in chapter 5). That's why we see
+`include!("cxx-demo/include/blobstore.h")` above &mdash; we'll be putting the
+C++ header at relative path `include/blobstore.h` within the Rust crate. If your
+crate is named something other than `cxx-demo` according to the `name` field in
+Cargo.toml, you will need to use that name everywhere in place of `cxx-demo`
+throughout this tutorial.
+
+```cpp
+// include/blobstore.h
+
+#pragma once
+#include <memory>
+
+class BlobstoreClient {
+public:
+ BlobstoreClient();
+};
+
+std::unique_ptr<BlobstoreClient> new_blobstore_client();
+```
+
+```cpp
+// src/blobstore.cc
+
+#include "cxx-demo/include/blobstore.h"
+
+BlobstoreClient::BlobstoreClient() {}
+
+std::unique_ptr<BlobstoreClient> new_blobstore_client() {
+ return std::unique_ptr<BlobstoreClient>(new BlobstoreClient());
+}
+```
+
+Using `std::make_unique` would work too, as long as you pass `-std=c++14` to the
+C++ compiler as described later on.
+
+The placement in *include/* and *src/* is not significant; you can place C++
+code anywhere else in the crate as long as you use the right paths throughout
+the tutorial.
+
+Be aware that *CXX does not look at any of these files.* You're free to put
+arbitrary C++ code in here, #include your own libraries, etc. All we do is emit
+static assertions against what you provide in the headers.
+
+## Compiling the C++ code with Cargo
+
+Cargo has a [build scripts] feature suitable for compiling non-Rust code.
+
+We need to introduce a new build-time dependency on CXX's C++ code generator in
+Cargo.toml:
+
+```toml,hidelines
+## Cargo.toml
+# [package]
+# name = "cxx-demo"
+# version = "0.1.0"
+# edition = "2018"
+
+[dependencies]
+cxx = "1.0"
+
+[build-dependencies]
+cxx-build = "1.0"
+```
+
+Then add a build.rs build script adjacent to Cargo.toml to run the cxx-build
+code generator and C++ compiler. The relevant arguments are the path to the Rust
+source file containing the cxx::bridge language boundary definition, and the
+paths to any additional C++ source files to be compiled during the Rust crate's
+build.
+
+```rust,noplayground
+// build.rs
+
+fn main() {
+ cxx_build::bridge("src/main.rs")
+ .file("src/blobstore.cc")
+ .compile("cxx-demo");
+}
+```
+
+This build.rs would also be where you set up C++ compiler flags, for example if
+you'd like to have access to `std::make_unique` from C++14. See the page on
+***[Cargo-based builds](build/cargo.md)*** for more details about CXX's Cargo
+integration.
+
+```rust,noplayground
+# // build.rs
+#
+# fn main() {
+ cxx_build::bridge("src/main.rs")
+ .file("src/blobstore.cc")
+ .flag_if_supported("-std=c++14")
+ .compile("cxx-demo");
+# }
+```
+
+[build scripts]: https://doc.rust-lang.org/cargo/reference/build-scripts.html
+
+The project should now build and run successfully, though not do anything useful
+yet.
+
+```console
+cxx-demo$ cargo run
+ Compiling cxx-demo v0.1.0
+ Finished dev [unoptimized + debuginfo] target(s) in 0.34s
+ Running `target/debug/cxx-demo`
+
+cxx-demo$
+```
+
+## Calling a Rust function from C++
+
+Our C++ blobstore supports a `put` operation for a discontiguous buffer upload.
+For example we might be uploading snapshots of a circular buffer which would
+tend to consist of 2 pieces, or fragments of a file spread across memory for
+some other reason (like a rope data structure).
+
+We'll express this by handing off an iterator over contiguous borrowed chunks.
+This loosely resembles the API of the widely used `bytes` crate's `Buf` trait.
+During a `put`, we'll make C++ call back into Rust to obtain contiguous chunks
+of the upload (all with no copying or allocation on the language boundary). In
+reality the C++ client might contain some sophisticated batching of chunks
+and/or parallel uploading that all of this ties into.
+
+```rust,noplayground
+// src/main.rs
+
+#[cxx::bridge]
+mod ffi {
+ extern "Rust" {
+ type MultiBuf;
+
+ fn next_chunk(buf: &mut MultiBuf) -> &[u8];
+ }
+
+ unsafe extern "C++" {
+ include!("cxx-demo/include/blobstore.h");
+
+ type BlobstoreClient;
+
+ fn new_blobstore_client() -> UniquePtr<BlobstoreClient>;
+ fn put(&self, parts: &mut MultiBuf) -> u64;
+ }
+}
+#
+# fn main() {
+# let client = ffi::new_blobstore_client();
+# }
+```
+
+Any signature having a `self` parameter (the Rust name for C++'s `this`) is
+considered a method / non-static member function. If there is only one `type` in
+the surrounding extern block, it'll be a method of that type. If there is more
+than one `type`, you can disambiguate which one a method belongs to by writing
+`self: &BlobstoreClient` in the argument list.
+
+As usual, now we need to provide Rust definitions of everything declared by the
+`extern "Rust"` block and a C++ definition of the new signature declared by the
+`extern "C++"` block.
+
+```rust,noplayground
+// src/main.rs
+#
+# #[cxx::bridge]
+# mod ffi {
+# extern "Rust" {
+# type MultiBuf;
+#
+# fn next_chunk(buf: &mut MultiBuf) -> &[u8];
+# }
+#
+# unsafe extern "C++" {
+# include!("cxx-demo/include/blobstore.h");
+#
+# type BlobstoreClient;
+#
+# fn new_blobstore_client() -> UniquePtr<BlobstoreClient>;
+# fn put(&self, parts: &mut MultiBuf) -> u64;
+# }
+# }
+
+// An iterator over contiguous chunks of a discontiguous file object. Toy
+// implementation uses a Vec<Vec<u8>> but in reality this might be iterating
+// over some more complex Rust data structure like a rope, or maybe loading
+// chunks lazily from somewhere.
+pub struct MultiBuf {
+ chunks: Vec<Vec<u8>>,
+ pos: usize,
+}
+
+pub fn next_chunk(buf: &mut MultiBuf) -> &[u8] {
+ let next = buf.chunks.get(buf.pos);
+ buf.pos += 1;
+ next.map_or(&[], Vec::as_slice)
+}
+#
+# fn main() {
+# let client = ffi::new_blobstore_client();
+# }
+```
+
+```cpp,hidelines
+// include/blobstore.h
+
+# #pragma once
+# #include <memory>
+#
+struct MultiBuf;
+
+class BlobstoreClient {
+public:
+ BlobstoreClient();
+ uint64_t put(MultiBuf &buf) const;
+};
+#
+#std::unique_ptr<BlobstoreClient> new_blobstore_client();
+```
+
+In blobstore.cc we're able to call the Rust `next_chunk` function, exposed to
+C++ by a header `main.rs.h` generated by the CXX code generator. In CXX's Cargo
+integration this generated header has a path containing the crate name, the
+relative path of the Rust source file within the crate, and a `.rs.h` extension.
+
+```cpp,hidelines
+// src/blobstore.cc
+
+##include "cxx-demo/include/blobstore.h"
+##include "cxx-demo/src/main.rs.h"
+##include <functional>
+#
+# BlobstoreClient::BlobstoreClient() {}
+#
+# std::unique_ptr<BlobstoreClient> new_blobstore_client() {
+# return std::make_unique<BlobstoreClient>();
+# }
+
+// Upload a new blob and return a blobid that serves as a handle to the blob.
+uint64_t BlobstoreClient::put(MultiBuf &buf) const {
+ // Traverse the caller's chunk iterator.
+ std::string contents;
+ while (true) {
+ auto chunk = next_chunk(buf);
+ if (chunk.size() == 0) {
+ break;
+ }
+ contents.append(reinterpret_cast<const char *>(chunk.data()), chunk.size());
+ }
+
+ // Pretend we did something useful to persist the data.
+ auto blobid = std::hash<std::string>{}(contents);
+ return blobid;
+}
+```
+
+This is now ready to use. :)
+
+```rust,noplayground
+// src/main.rs
+#
+# #[cxx::bridge]
+# mod ffi {
+# extern "Rust" {
+# type MultiBuf;
+#
+# fn next_chunk(buf: &mut MultiBuf) -> &[u8];
+# }
+#
+# unsafe extern "C++" {
+# include!("cxx-demo/include/blobstore.h");
+#
+# type BlobstoreClient;
+#
+# fn new_blobstore_client() -> UniquePtr<BlobstoreClient>;
+# fn put(&self, parts: &mut MultiBuf) -> u64;
+# }
+# }
+#
+# pub struct MultiBuf {
+# chunks: Vec<Vec<u8>>,
+# pos: usize,
+# }
+# pub fn next_chunk(buf: &mut MultiBuf) -> &[u8] {
+# let next = buf.chunks.get(buf.pos);
+# buf.pos += 1;
+# next.map_or(&[], Vec::as_slice)
+# }
+
+fn main() {
+ let client = ffi::new_blobstore_client();
+
+ // Upload a blob.
+ let chunks = vec![b"fearless".to_vec(), b"concurrency".to_vec()];
+ let mut buf = MultiBuf { chunks, pos: 0 };
+ let blobid = client.put(&mut buf);
+ println!("blobid = {}", blobid);
+}
+```
+
+```console
+cxx-demo$ cargo run
+ Compiling cxx-demo v0.1.0
+ Finished dev [unoptimized + debuginfo] target(s) in 0.41s
+ Running `target/debug/cxx-demo`
+
+blobid = 9851996977040795552
+```
+
+## Interlude: What gets generated?
+
+For the curious, it's easy to look behind the scenes at what CXX has done to
+make these function calls work. You shouldn't need to do this during normal
+usage of CXX, but for the purpose of this tutorial it can be educative.
+
+CXX comprises *two* code generators: a Rust one (which is the cxx::bridge
+attribute procedural macro) and a C++ one.
+
+### Rust generated code
+
+It's easiest to view the output of the procedural macro by installing
+[cargo-expand]. Then run `cargo expand ::ffi` to macro-expand the `mod ffi`
+module.
+
+[cargo-expand]: https://github.com/dtolnay/cargo-expand
+
+```console
+cxx-demo$ cargo install cargo-expand
+cxx-demo$ cargo expand ::ffi
+```
+
+You'll see some deeply unpleasant code involving `#[repr(C)]`, `#[link_name]`,
+and `#[export_name]`.
+
+### C++ generated code
+
+For debugging convenience, `cxx_build` links all generated C++ code into Cargo's
+target directory under *target/cxxbridge/*.
+
+```console
+cxx-demo$ exa -T target/cxxbridge/
+target/cxxbridge
+├── cxx-demo
+│ └── src
+│ ├── main.rs.cc -> ../../../debug/build/cxx-demo-11c6f678ce5c3437/out/cxxbridge/sources/cxx-demo/src/main.rs.cc
+│ └── main.rs.h -> ../../../debug/build/cxx-demo-11c6f678ce5c3437/out/cxxbridge/include/cxx-demo/src/main.rs.h
+└── rust
+ └── cxx.h -> ~/.cargo/registry/src/github.com-1ecc6299db9ec823/cxx-1.0.0/include/cxx.h
+```
+
+In those files you'll see declarations or templates of any CXX Rust types
+present in your language boundary (like `rust::Slice<T>` for `&[T]`) and `extern
+"C"` signatures corresponding to your extern functions.
+
+If it fits your workflow better, the CXX C++ code generator is also available as
+a standalone executable which outputs generated code to stdout.
+
+```console
+cxx-demo$ cargo install cxxbridge-cmd
+cxx-demo$ cxxbridge src/main.rs
+```
+
+## Shared data structures
+
+So far the calls in both directions above only used **opaque types**, not
+**shared structs**.
+
+Shared structs are data structures whose complete definition is visible to both
+languages, making it possible to pass them by value across the language
+boundary. Shared structs translate to a C++ aggregate-initialization compatible
+struct exactly matching the layout of the Rust one.
+
+As the last step of this demo, we'll use a shared struct `BlobMetadata` to pass
+metadata about blobs between our Rust application and C++ blobstore client.
+
+```rust,noplayground
+// src/main.rs
+
+#[cxx::bridge]
+mod ffi {
+ struct BlobMetadata {
+ size: usize,
+ tags: Vec<String>,
+ }
+
+ extern "Rust" {
+ // ...
+# type MultiBuf;
+#
+# fn next_chunk(buf: &mut MultiBuf) -> &[u8];
+ }
+
+ unsafe extern "C++" {
+ // ...
+# include!("cxx-demo/include/blobstore.h");
+#
+# type BlobstoreClient;
+#
+# fn new_blobstore_client() -> UniquePtr<BlobstoreClient>;
+# fn put(&self, parts: &mut MultiBuf) -> u64;
+ fn tag(&self, blobid: u64, tag: &str);
+ fn metadata(&self, blobid: u64) -> BlobMetadata;
+ }
+}
+#
+# pub struct MultiBuf {
+# chunks: Vec<Vec<u8>>,
+# pos: usize,
+# }
+# pub fn next_chunk(buf: &mut MultiBuf) -> &[u8] {
+# let next = buf.chunks.get(buf.pos);
+# buf.pos += 1;
+# next.map_or(&[], Vec::as_slice)
+# }
+
+fn main() {
+ let client = ffi::new_blobstore_client();
+
+ // Upload a blob.
+ let chunks = vec![b"fearless".to_vec(), b"concurrency".to_vec()];
+ let mut buf = MultiBuf { chunks, pos: 0 };
+ let blobid = client.put(&mut buf);
+ println!("blobid = {}", blobid);
+
+ // Add a tag.
+ client.tag(blobid, "rust");
+
+ // Read back the tags.
+ let metadata = client.metadata(blobid);
+ println!("tags = {:?}", metadata.tags);
+}
+```
+
+```cpp,hidelines
+// include/blobstore.h
+#
+# #pragma once
+# #include "rust/cxx.h"
+# #include <memory>
+
+struct MultiBuf;
+struct BlobMetadata;
+
+class BlobstoreClient {
+public:
+ BlobstoreClient();
+ uint64_t put(MultiBuf &buf) const;
+ void tag(uint64_t blobid, rust::Str tag) const;
+ BlobMetadata metadata(uint64_t blobid) const;
+
+private:
+ class impl;
+ std::shared_ptr<impl> impl;
+};
+#
+# std::unique_ptr<BlobstoreClient> new_blobstore_client();
+```
+
+```cpp,hidelines
+// src/blobstore.cc
+
+##include "cxx-demo/include/blobstore.h"
+##include "cxx-demo/src/main.rs.h"
+##include <algorithm>
+##include <functional>
+##include <set>
+##include <string>
+##include <unordered_map>
+
+// Toy implementation of an in-memory blobstore.
+//
+// In reality the implementation of BlobstoreClient could be a large
+// complex C++ library.
+class BlobstoreClient::impl {
+ friend BlobstoreClient;
+ using Blob = struct {
+ std::string data;
+ std::set<std::string> tags;
+ };
+ std::unordered_map<uint64_t, Blob> blobs;
+};
+
+BlobstoreClient::BlobstoreClient() : impl(new class BlobstoreClient::impl) {}
+#
+# // Upload a new blob and return a blobid that serves as a handle to the blob.
+# uint64_t BlobstoreClient::put(MultiBuf &buf) const {
+# // Traverse the caller's chunk iterator.
+# std::string contents;
+# while (true) {
+# auto chunk = next_chunk(buf);
+# if (chunk.size() == 0) {
+# break;
+# }
+# contents.append(reinterpret_cast<const char *>(chunk.data()), chunk.size());
+# }
+#
+# // Insert into map and provide caller the handle.
+# auto blobid = std::hash<std::string>{}(contents);
+# impl->blobs[blobid] = {std::move(contents), {}};
+# return blobid;
+# }
+
+// Add tag to an existing blob.
+void BlobstoreClient::tag(uint64_t blobid, rust::Str tag) const {
+ impl->blobs[blobid].tags.emplace(tag);
+}
+
+// Retrieve metadata about a blob.
+BlobMetadata BlobstoreClient::metadata(uint64_t blobid) const {
+ BlobMetadata metadata{};
+ auto blob = impl->blobs.find(blobid);
+ if (blob != impl->blobs.end()) {
+ metadata.size = blob->second.data.size();
+ std::for_each(blob->second.tags.cbegin(), blob->second.tags.cend(),
+ [&](auto &t) { metadata.tags.emplace_back(t); });
+ }
+ return metadata;
+}
+#
+# std::unique_ptr<BlobstoreClient> new_blobstore_client() {
+# return std::make_unique<BlobstoreClient>();
+# }
+```
+
+```console
+cxx-demo$ cargo run
+ Running `target/debug/cxx-demo`
+
+blobid = 9851996977040795552
+tags = ["rust"]
+```
+
+*You've now seen all the code involved in the tutorial. It's available all
+together in runnable form in the* demo *directory of
+<https://github.com/dtolnay/cxx>. You can run it directly without stepping
+through the steps above by running `cargo run` from that directory.*
+
+<br>
+
+# Takeaways
+
+The key contribution of CXX is it gives you Rust&ndash;C++ interop in which
+*all* of the Rust side of the code you write *really* looks like you are just
+writing normal Rust, and the C++ side *really* looks like you are just writing
+normal C++.
+
+You've seen in this tutorial that none of the code involved feels like C or like
+the usual perilous "FFI glue" prone to leaks or memory safety flaws.
+
+An expressive system of opaque types, shared types, and key standard library
+type bindings enables API design on the language boundary that captures the
+proper ownership and borrowing contracts of the interface.
+
+CXX plays to the strengths of the Rust type system *and* C++ type system *and*
+the programmer's intuitions. An individual working on the C++ side without a
+Rust background, or the Rust side without a C++ background, will be able to
+apply all their usual intuitions and best practices about development in their
+language to maintain a correct FFI.
+
+<br><br>
diff --git a/build.rs b/build.rs
index 57b3e52f..f7994418 100644
--- a/build.rs
+++ b/build.rs
@@ -1,5 +1,6 @@
use std::env;
use std::path::Path;
+use std::process::Command;
fn main() {
cc::Build::new()
@@ -7,12 +8,42 @@ fn main() {
.cpp(true)
.cpp_link_stdlib(None) // linked via link-cplusplus crate
.flag_if_supported(cxxbridge_flags::STD)
- .compile("cxxbridge05");
+ .warnings_into_errors(cfg!(deny_warnings))
+ .compile("cxxbridge1");
+
println!("cargo:rerun-if-changed=src/cxx.cc");
println!("cargo:rerun-if-changed=include/cxx.h");
println!("cargo:rustc-cfg=built_with_cargo");
+
if let Some(manifest_dir) = env::var_os("CARGO_MANIFEST_DIR") {
let cxx_h = Path::new(&manifest_dir).join("include").join("cxx.h");
println!("cargo:HEADER={}", cxx_h.to_string_lossy());
}
+
+ if let Some(rustc) = rustc_version() {
+ if rustc.minor < 48 {
+ println!("cargo:warning=The cxx crate requires a rustc version 1.48.0 or newer.");
+ println!(
+ "cargo:warning=You appear to be building with: {}",
+ rustc.version,
+ );
+ }
+ }
+}
+
+struct RustVersion {
+ version: String,
+ minor: u32,
+}
+
+fn rustc_version() -> Option<RustVersion> {
+ let rustc = env::var_os("RUSTC")?;
+ let output = Command::new(rustc).arg("--version").output().ok()?;
+ let version = String::from_utf8(output.stdout).ok()?;
+ let mut pieces = version.split('.');
+ if pieces.next() != Some("rustc 1") {
+ return None;
+ }
+ let minor = pieces.next()?.parse().ok()?;
+ Some(RustVersion { version, minor })
}
diff --git a/compile_flags.txt b/compile_flags.txt
new file mode 100644
index 00000000..c24e3b5e
--- /dev/null
+++ b/compile_flags.txt
@@ -0,0 +1 @@
+-std=c++11
diff --git a/demo/Cargo.toml b/demo/Cargo.toml
index dc948610..8115b1e4 100644
--- a/demo/Cargo.toml
+++ b/demo/Cargo.toml
@@ -2,11 +2,14 @@
name = "demo"
version = "0.0.0"
authors = ["David Tolnay <dtolnay@gmail.com>"]
+license = "MIT OR Apache-2.0"
+description = "Toy project from https://github.com/dtolnay/cxx"
+repository = "https://github.com/dtolnay/cxx"
edition = "2018"
publish = false
[dependencies]
-cxx = { path = ".." }
+cxx = "1.0"
[build-dependencies]
-cxx-build = { path = "../gen/build" }
+cxx-build = "1.0"
diff --git a/demo/include/blobstore.h b/demo/include/blobstore.h
index 68a7fc2b..d89583aa 100644
--- a/demo/include/blobstore.h
+++ b/demo/include/blobstore.h
@@ -16,8 +16,8 @@ public:
BlobMetadata metadata(uint64_t blobid) const;
private:
- class Impl;
- std::shared_ptr<Impl> impl;
+ class impl;
+ std::shared_ptr<impl> impl;
};
std::unique_ptr<BlobstoreClient> new_blobstore_client();
diff --git a/demo/src/blobstore.cc b/demo/src/blobstore.cc
index 0036a327..7cf40dfe 100644
--- a/demo/src/blobstore.cc
+++ b/demo/src/blobstore.cc
@@ -13,7 +13,7 @@ namespace blobstore {
//
// In reality the implementation of BlobstoreClient could be a large complex C++
// library.
-class BlobstoreClient::Impl {
+class BlobstoreClient::impl {
friend BlobstoreClient;
using Blob = struct {
std::string data;
@@ -22,7 +22,7 @@ class BlobstoreClient::Impl {
std::unordered_map<uint64_t, Blob> blobs;
};
-BlobstoreClient::BlobstoreClient() : impl(new BlobstoreClient::Impl) {}
+BlobstoreClient::BlobstoreClient() : impl(new class BlobstoreClient::impl) {}
// Upload a new blob and return a blobid that serves as a handle to the blob.
uint64_t BlobstoreClient::put(MultiBuf &buf) const {
@@ -57,7 +57,7 @@ BlobMetadata BlobstoreClient::metadata(uint64_t blobid) const {
auto blob = impl->blobs.find(blobid);
if (blob != impl->blobs.end()) {
metadata.size = blob->second.data.size();
- std::for_each(blob->second.tags.begin(), blob->second.tags.end(),
+ std::for_each(blob->second.tags.cbegin(), blob->second.tags.cend(),
[&](auto &t) { metadata.tags.emplace_back(t); });
}
return metadata;
diff --git a/demo/src/main.rs b/demo/src/main.rs
index 10f57e5b..458f1f21 100644
--- a/demo/src/main.rs
+++ b/demo/src/main.rs
@@ -14,7 +14,7 @@ mod ffi {
}
// C++ types and signatures exposed to Rust.
- extern "C++" {
+ unsafe extern "C++" {
include!("demo/include/blobstore.h");
type BlobstoreClient;
@@ -38,7 +38,7 @@ pub struct MultiBuf {
pub fn next_chunk(buf: &mut MultiBuf) -> &[u8] {
let next = buf.chunks.get(buf.pos);
buf.pos += 1;
- next.map(Vec::as_slice).unwrap_or(&[])
+ next.map_or(&[], Vec::as_slice)
}
fn main() {
diff --git a/flags/Cargo.toml b/flags/Cargo.toml
index cc5f2302..87c796cb 100644
--- a/flags/Cargo.toml
+++ b/flags/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "cxxbridge-flags"
-version = "0.5.9"
+version = "1.0.42"
authors = ["David Tolnay <dtolnay@gmail.com>"]
edition = "2018"
license = "MIT OR Apache-2.0"
diff --git a/flags/LICENSE-APACHE b/flags/LICENSE-APACHE
new file mode 120000
index 00000000..965b606f
--- /dev/null
+++ b/flags/LICENSE-APACHE
@@ -0,0 +1 @@
+../LICENSE-APACHE \ No newline at end of file
diff --git a/flags/LICENSE-MIT b/flags/LICENSE-MIT
new file mode 120000
index 00000000..76219eb7
--- /dev/null
+++ b/flags/LICENSE-MIT
@@ -0,0 +1 @@
+../LICENSE-MIT \ No newline at end of file
diff --git a/gen/build/Cargo.toml b/gen/build/Cargo.toml
index 3f7b0eac..33f17dff 100644
--- a/gen/build/Cargo.toml
+++ b/gen/build/Cargo.toml
@@ -1,26 +1,30 @@
[package]
name = "cxx-build"
-version = "0.5.9"
+version = "1.0.42"
authors = ["David Tolnay <dtolnay@gmail.com>"]
edition = "2018"
license = "MIT OR Apache-2.0"
description = "C++ code generator for integrating `cxx` crate into a Cargo build."
repository = "https://github.com/dtolnay/cxx"
+homepage = "https://cxx.rs"
exclude = ["build.rs"]
keywords = ["ffi"]
categories = ["development-tools::ffi"]
+[features]
+parallel = ["cc/parallel"]
+
[dependencies]
cc = "1.0.49"
-codespan-reporting = "0.9"
+codespan-reporting = "0.11"
lazy_static = "1.4"
-proc-macro2 = { version = "1.0.17", default-features = false, features = ["span-locations"] }
+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.20", default-features = false, features = ["parsing", "printing", "clone-impls", "full"] }
+syn = { version = "1.0.68", default-features = false, features = ["parsing", "printing", "clone-impls", "full"] }
[dev-dependencies]
-cxx-gen = { version = "0.6", path = "../lib" }
+cxx-gen = { version = "0.7", path = "../lib" }
pkg-config = "0.3"
[package.metadata.docs.rs]
diff --git a/gen/build/src/cfg.rs b/gen/build/src/cfg.rs
index c15a1737..6818bdaa 100644
--- a/gen/build/src/cfg.rs
+++ b/gen/build/src/cfg.rs
@@ -266,10 +266,10 @@ pub use self::r#impl::Cfg::CFG;
#[cfg(not(doc))]
mod r#impl {
use crate::intern::{intern, InternedString};
+ use crate::syntax::map::UnorderedMap as Map;
use crate::vec::{self, InternedVec as _};
use lazy_static::lazy_static;
use std::cell::RefCell;
- use std::collections::HashMap;
use std::fmt::{self, Debug};
use std::marker::PhantomData;
use std::ops::{Deref, DerefMut};
@@ -316,7 +316,7 @@ mod r#impl {
// cfg: AtomicPtr<super::Cfg>,
// }
//
- static CONST_DEREFS: RefCell<HashMap<Handle, Box<super::Cfg<'static>>>> = RefCell::default();
+ static CONST_DEREFS: RefCell<Map<Handle, Box<super::Cfg<'static>>>> = RefCell::default();
}
#[derive(Eq, PartialEq, Hash)]
diff --git a/gen/build/src/intern.rs b/gen/build/src/intern.rs
index 51bc07cd..25b87061 100644
--- a/gen/build/src/intern.rs
+++ b/gen/build/src/intern.rs
@@ -1,5 +1,5 @@
+use crate::syntax::set::UnorderedSet as Set;
use lazy_static::lazy_static;
-use std::collections::HashSet;
use std::sync::{Mutex, PoisonError};
#[derive(Copy, Clone, Default)]
@@ -13,7 +13,7 @@ impl InternedString {
pub fn intern(s: &str) -> InternedString {
lazy_static! {
- static ref INTERN: Mutex<HashSet<&'static str>> = Mutex::new(HashSet::new());
+ static ref INTERN: Mutex<Set<&'static str>> = Mutex::new(Set::new());
}
let mut set = INTERN.lock().unwrap_or_else(PoisonError::into_inner);
diff --git a/gen/build/src/lib.rs b/gen/build/src/lib.rs
index db15335a..63b2cf63 100644
--- a/gen/build/src/lib.rs
+++ b/gen/build/src/lib.rs
@@ -46,12 +46,35 @@
//! ```
#![allow(
+ clippy::cast_sign_loss,
+ clippy::default_trait_access,
+ clippy::doc_markdown,
clippy::drop_copy,
+ clippy::enum_glob_use,
clippy::inherent_to_string,
+ clippy::items_after_statements,
+ clippy::let_underscore_drop,
+ clippy::match_bool,
+ clippy::match_on_vec_items,
+ clippy::match_same_arms,
+ clippy::module_name_repetitions,
clippy::needless_doctest_main,
+ clippy::needless_pass_by_value,
clippy::new_without_default,
+ clippy::nonminimal_bool,
+ clippy::option_if_let_else,
clippy::or_fun_call,
- clippy::toplevel_ref_arg
+ clippy::redundant_else,
+ clippy::shadow_unrelated,
+ clippy::similar_names,
+ clippy::single_match_else,
+ clippy::struct_excessive_bools,
+ clippy::too_many_arguments,
+ clippy::too_many_lines,
+ clippy::toplevel_ref_arg,
+ clippy::upper_case_acronyms,
+ // clippy bug: https://github.com/rust-lang/rust-clippy/issues/6983
+ clippy::wrong_self_convention
)]
mod cfg;
@@ -70,10 +93,10 @@ use crate::error::{Error, Result};
use crate::gen::error::report;
use crate::gen::Opt;
use crate::paths::PathExt;
+use crate::syntax::map::{Entry, UnorderedMap};
use crate::target::TargetDir;
use cc::Build;
-use std::collections::btree_map::Entry;
-use std::collections::{BTreeMap, BTreeSet};
+use std::collections::BTreeSet;
use std::env;
use std::ffi::{OsStr, OsString};
use std::io::{self, Write};
@@ -186,7 +209,6 @@ fn build(rust_source_files: &mut dyn Iterator<Item = impl AsRef<Path>>) -> Resul
let ref prj = Project::init()?;
validate_cfg(prj)?;
let this_crate = make_this_crate(prj)?;
- this_crate.print_to_cargo();
let mut build = Build::new();
build.cpp(true);
@@ -196,6 +218,7 @@ fn build(rust_source_files: &mut dyn Iterator<Item = impl AsRef<Path>>) -> Resul
generate_bridge(prj, &mut build, path.as_ref())?;
}
+ this_crate.print_to_cargo();
eprintln!("\nCXX include path:");
for header_dir in this_crate.header_dirs {
build.include(&header_dir.path);
@@ -256,12 +279,10 @@ fn make_this_crate(prj: &Project) -> Result<Crate> {
path: include_dir,
});
- if let Some(crate_dir) = crate_dir {
- this_crate.header_dirs.push(HeaderDir {
- exported: true,
- path: crate_dir,
- });
- }
+ this_crate.header_dirs.push(HeaderDir {
+ exported: true,
+ path: crate_dir,
+ });
for exported_dir in &CFG.exported_header_dirs {
this_crate.header_dirs.push(HeaderDir {
@@ -270,7 +291,7 @@ fn make_this_crate(prj: &Project) -> Result<Crate> {
});
}
- let mut header_dirs_index = BTreeMap::new();
+ let mut header_dirs_index = UnorderedMap::new();
let mut used_header_links = BTreeSet::new();
let mut used_header_prefixes = BTreeSet::new();
for krate in deps::direct_dependencies() {
@@ -335,24 +356,30 @@ fn make_this_crate(prj: &Project) -> Result<Crate> {
Ok(this_crate)
}
-fn make_crate_dir(prj: &Project) -> Option<PathBuf> {
+fn make_crate_dir(prj: &Project) -> PathBuf {
if prj.include_prefix.as_os_str().is_empty() {
- return Some(prj.manifest_dir.clone());
+ return prj.manifest_dir.clone();
}
let crate_dir = prj.out_dir.join("cxxbridge").join("crate");
- let link = crate_dir.join(&prj.include_prefix);
- if out::symlink_dir(&prj.manifest_dir, link).is_ok() {
- Some(crate_dir)
- } else {
- None
+ let ref link = crate_dir.join(&prj.include_prefix);
+ let ref manifest_dir = prj.manifest_dir;
+ if out::symlink_dir(manifest_dir, link).is_err() && cfg!(not(unix)) {
+ let cachedir_tag = "\
+ Signature: 8a477f597d28d172789f06886806bc55\n\
+ # This file is a cache directory tag created by cxx.\n\
+ # For information about cache directory tags see https://bford.info/cachedir/\n";
+ let _ = out::write(crate_dir.join("CACHEDIR.TAG"), cachedir_tag.as_bytes());
+ let max_depth = 6;
+ best_effort_copy_headers(manifest_dir, link, max_depth);
}
+ crate_dir
}
fn make_include_dir(prj: &Project) -> Result<PathBuf> {
let include_dir = prj.out_dir.join("cxxbridge").join("include");
let cxx_h = include_dir.join("rust").join("cxx.h");
let ref shared_cxx_h = prj.shared_dir.join("rust").join("cxx.h");
- if let Some(ref original) = env::var_os("DEP_CXXBRIDGE05_HEADER") {
+ if let Some(ref original) = env::var_os("DEP_CXXBRIDGE1_HEADER") {
out::symlink_file(original, cxx_h)?;
out::symlink_file(original, shared_cxx_h)?;
} else {
@@ -393,6 +420,49 @@ fn generate_bridge(prj: &Project, build: &mut Build, rust_source_file: &Path) ->
Ok(())
}
+fn best_effort_copy_headers(src: &Path, dst: &Path, max_depth: usize) {
+ // Not using crate::gen::fs because we aren't reporting the errors.
+ use std::fs;
+
+ let mut dst_created = false;
+ let mut entries = match fs::read_dir(src) {
+ Ok(entries) => entries,
+ Err(_) => return,
+ };
+
+ while let Some(Ok(entry)) = entries.next() {
+ let file_name = entry.file_name();
+ if file_name.to_string_lossy().starts_with('.') {
+ continue;
+ }
+ match entry.file_type() {
+ Ok(file_type) if file_type.is_dir() && max_depth > 0 => {
+ let src = entry.path();
+ if src.join("Cargo.toml").exists() || src.join("CACHEDIR.TAG").exists() {
+ continue;
+ }
+ let dst = dst.join(file_name);
+ best_effort_copy_headers(&src, &dst, max_depth - 1);
+ }
+ Ok(file_type) if file_type.is_file() => {
+ let src = entry.path();
+ match src.extension().and_then(OsStr::to_str) {
+ Some("h") | Some("hh") | Some("hpp") => {}
+ _ => continue,
+ }
+ if !dst_created && fs::create_dir_all(dst).is_err() {
+ return;
+ }
+ dst_created = true;
+ let dst = dst.join(file_name);
+ let _ = fs::remove_file(&dst);
+ let _ = fs::copy(src, dst);
+ }
+ _ => {}
+ }
+ }
+}
+
fn env_os(key: impl AsRef<OsStr>) -> Result<OsString> {
let key = key.as_ref();
env::var_os(key).ok_or_else(|| Error::NoEnv(key.to_owned()))
diff --git a/gen/build/src/out.rs b/gen/build/src/out.rs
index b97e9924..e275049c 100644
--- a/gen/build/src/out.rs
+++ b/gen/build/src/out.rs
@@ -14,7 +14,7 @@ pub(crate) fn write(path: impl AsRef<Path>, content: &[u8]) -> Result<()> {
return Ok(());
}
}
- let _ = fs::remove_file(path);
+ best_effort_remove(path);
} else {
let parent = path.parent().unwrap();
create_dir_error = fs::create_dir_all(parent).err();
@@ -28,19 +28,19 @@ pub(crate) fn write(path: impl AsRef<Path>, content: &[u8]) -> Result<()> {
}
}
-pub(crate) fn symlink_file(src: impl AsRef<Path>, dst: impl AsRef<Path>) -> Result<()> {
- let src = src.as_ref();
- let dst = dst.as_ref();
+pub(crate) fn symlink_file(original: impl AsRef<Path>, link: impl AsRef<Path>) -> Result<()> {
+ let original = original.as_ref();
+ let link = link.as_ref();
let mut create_dir_error = None;
- if dst.exists() {
- let _ = fs::remove_file(dst).unwrap();
+ if link.exists() {
+ best_effort_remove(link);
} else {
- let parent = dst.parent().unwrap();
+ let parent = link.parent().unwrap();
create_dir_error = fs::create_dir_all(parent).err();
}
- match paths::symlink_or_copy(src, dst) {
+ match paths::symlink_or_copy(original, link) {
// As long as symlink_or_copy succeeded, ignore any create_dir_all error.
Ok(()) => Ok(()),
// If create_dir_all and symlink_or_copy both failed, prefer the first error.
@@ -48,22 +48,47 @@ pub(crate) fn symlink_file(src: impl AsRef<Path>, dst: impl AsRef<Path>) -> Resu
}
}
-pub(crate) fn symlink_dir(src: impl AsRef<Path>, dst: impl AsRef<Path>) -> Result<()> {
- let src = src.as_ref();
- let dst = dst.as_ref();
+pub(crate) fn symlink_dir(original: impl AsRef<Path>, link: impl AsRef<Path>) -> Result<()> {
+ let original = original.as_ref();
+ let link = link.as_ref();
let mut create_dir_error = None;
- if dst.exists() {
- let _ = paths::remove_symlink_dir(dst).unwrap();
+ if link.exists() {
+ best_effort_remove(link);
} else {
- let parent = dst.parent().unwrap();
+ let parent = link.parent().unwrap();
create_dir_error = fs::create_dir_all(parent).err();
}
- match paths::symlink_dir(src, dst) {
+ match fs::symlink_dir(original, link) {
// As long as symlink_dir succeeded, ignore any create_dir_all error.
Ok(()) => Ok(()),
// If create_dir_all and symlink_dir both failed, prefer the first error.
Err(err) => Err(Error::Fs(create_dir_error.unwrap_or(err))),
}
}
+
+fn best_effort_remove(path: &Path) {
+ use std::fs;
+
+ let file_type = match if cfg!(windows) {
+ // On Windows, the correct choice of remove_file vs remove_dir needs to
+ // be used according to what the symlink *points to*. Trying to use
+ // remove_file to remove a symlink which points to a directory fails
+ // with "Access is denied".
+ fs::metadata(path)
+ } else {
+ // On non-Windows, we check metadata not following symlinks. All
+ // symlinks are removed using remove_file.
+ fs::symlink_metadata(path)
+ } {
+ Ok(metadata) => metadata.file_type(),
+ Err(_) => return,
+ };
+
+ if file_type.is_dir() {
+ let _ = fs::remove_dir_all(path);
+ } else {
+ let _ = fs::remove_file(path);
+ }
+}
diff --git a/gen/build/src/paths.rs b/gen/build/src/paths.rs
index 4459363c..c514a570 100644
--- a/gen/build/src/paths.rs
+++ b/gen/build/src/paths.rs
@@ -43,32 +43,25 @@ impl PathExt for Path {
pub(crate) use self::fs::symlink_file as symlink_or_copy;
#[cfg(windows)]
-pub(crate) fn symlink_or_copy(src: impl AsRef<Path>, dst: impl AsRef<Path>) -> fs::Result<()> {
+pub(crate) fn symlink_or_copy(
+ original: impl AsRef<Path>,
+ link: impl AsRef<Path>,
+) -> fs::Result<()> {
// Pre-Windows 10, symlinks require admin privileges. Since Windows 10, they
// require Developer Mode. If it fails, fall back to copying the file.
- let src = src.as_ref();
- let dst = dst.as_ref();
- if fs::symlink_file(src, dst).is_err() {
- fs::copy(src, dst)?;
+ let original = original.as_ref();
+ let link = link.as_ref();
+ if fs::symlink_file(original, link).is_err() {
+ fs::copy(original, link)?;
}
Ok(())
}
#[cfg(not(any(unix, windows)))]
-pub(crate) use self::fs::copy as symlink_or_copy;
-
-#[cfg(any(unix, windows))]
-pub(crate) use self::fs::symlink_dir;
-
-#[cfg(not(any(unix, windows)))]
-pub(crate) fn symlink_dir(_src: impl AsRef<Path>, _dst: impl AsRef<Path>) -> fs::Result<()> {
+pub(crate) fn symlink_or_copy(
+ original: impl AsRef<Path>,
+ copy: impl AsRef<Path>,
+) -> fs::Result<()> {
+ fs::copy(original, copy)?;
Ok(())
}
-
-#[cfg(not(windows))]
-pub(crate) use self::fs::remove_file as remove_symlink_dir;
-
-// On Windows, trying to use remove_file to remove a symlink which points to a
-// directory fails with "Access is denied".
-#[cfg(windows)]
-pub(crate) use self::fs::remove_dir as remove_symlink_dir;
diff --git a/gen/build/src/target.rs b/gen/build/src/target.rs
index 58ada3a7..4c9a9f3d 100644
--- a/gen/build/src/target.rs
+++ b/gen/build/src/target.rs
@@ -1,4 +1,5 @@
use std::env;
+use std::ffi::OsStr;
use std::path::{Path, PathBuf};
pub(crate) enum TargetDir {
@@ -24,7 +25,13 @@ pub(crate) fn find_target_dir(out_dir: &Path) -> TargetDir {
let mut dir = out_dir.to_owned();
loop {
- if dir.join(".rustc_info.json").exists() || dir.join("CACHEDIR.TAG").exists() {
+ if dir.join(".rustc_info.json").exists()
+ || dir.join("CACHEDIR.TAG").exists()
+ || dir.file_name() == Some(OsStr::new("target"))
+ && dir
+ .parent()
+ .map_or(false, |parent| parent.join("Cargo.toml").exists())
+ {
return TargetDir::Path(dir);
}
if dir.pop() {
diff --git a/gen/cmd/Cargo.toml b/gen/cmd/Cargo.toml
index f3bdebf8..7db02062 100644
--- a/gen/cmd/Cargo.toml
+++ b/gen/cmd/Cargo.toml
@@ -1,11 +1,12 @@
[package]
name = "cxxbridge-cmd"
-version = "0.5.9"
+version = "1.0.42"
authors = ["David Tolnay <dtolnay@gmail.com>"]
edition = "2018"
license = "MIT OR Apache-2.0"
description = "C++ code generator for integrating `cxx` crate into a non-Cargo build."
repository = "https://github.com/dtolnay/cxx"
+homepage = "https://cxx.rs"
exclude = ["build.rs"]
keywords = ["ffi"]
categories = ["development-tools::ffi"]
@@ -16,10 +17,10 @@ path = "src/main.rs"
[dependencies]
clap = "2.33"
-codespan-reporting = "0.9"
-proc-macro2 = { version = "1.0.17", default-features = false, features = ["span-locations"] }
+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.20", default-features = false, features = ["parsing", "printing", "clone-impls", "full"] }
+syn = { version = "1.0.68", 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/app.rs b/gen/cmd/src/app.rs
index e2945a1d..c233ad77 100644
--- a/gen/cmd/src/app.rs
+++ b/gen/cmd/src/app.rs
@@ -94,8 +94,8 @@ pub(super) fn from_args() -> Opt {
Opt {
input,
- cxx_impl_annotations,
header,
+ cxx_impl_annotations,
include,
outputs,
}
diff --git a/gen/cmd/src/main.rs b/gen/cmd/src/main.rs
index c7230393..f419dad4 100644
--- a/gen/cmd/src/main.rs
+++ b/gen/cmd/src/main.rs
@@ -1,10 +1,30 @@
#![allow(
+ clippy::cast_sign_loss,
clippy::cognitive_complexity,
+ clippy::default_trait_access,
+ clippy::enum_glob_use,
clippy::inherent_to_string,
+ clippy::items_after_statements,
clippy::large_enum_variant,
+ clippy::match_bool,
+ clippy::match_on_vec_items,
+ clippy::match_same_arms,
+ clippy::module_name_repetitions,
+ clippy::needless_pass_by_value,
clippy::new_without_default,
+ clippy::nonminimal_bool,
+ clippy::option_if_let_else,
clippy::or_fun_call,
- clippy::toplevel_ref_arg
+ clippy::redundant_else,
+ clippy::shadow_unrelated,
+ clippy::similar_names,
+ clippy::single_match_else,
+ clippy::struct_excessive_bools,
+ clippy::too_many_arguments,
+ clippy::too_many_lines,
+ clippy::toplevel_ref_arg,
+ // clippy bug: https://github.com/rust-lang/rust-clippy/issues/6983
+ clippy::wrong_self_convention
)]
mod app;
@@ -51,7 +71,11 @@ fn try_main() -> Result<()> {
for output in opt.outputs {
let kind = if opt.input.is_none() {
Kind::Header
- } else if opt.header || output.ends_with(".h") {
+ } else if opt.header
+ || output.ends_with(".h")
+ || output.ends_with(".hh")
+ || output.ends_with(".hpp")
+ {
gen_header = true;
Kind::GeneratedHeader
} else {
diff --git a/gen/lib/Cargo.toml b/gen/lib/Cargo.toml
index 2de0f28c..ff33d979 100644
--- a/gen/lib/Cargo.toml
+++ b/gen/lib/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "cxx-gen"
-version = "0.6.6"
+version = "0.7.42"
authors = ["Adrian Taylor <adetaylor@chromium.org>"]
edition = "2018"
license = "MIT OR Apache-2.0"
@@ -12,10 +12,10 @@ categories = ["development-tools::ffi"]
[dependencies]
cc = "1.0.49"
-codespan-reporting = "0.9"
-proc-macro2 = { version = "1.0.17", default-features = false, features = ["span-locations"] }
+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.20", default-features = false, features = ["parsing", "printing", "clone-impls", "full"] }
+syn = { version = "1.0.68", 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 963e8707..e3eca5e7 100644
--- a/gen/lib/src/lib.rs
+++ b/gen/lib/src/lib.rs
@@ -9,10 +9,31 @@
#![allow(dead_code)]
#![allow(
+ clippy::cast_sign_loss,
+ clippy::default_trait_access,
+ clippy::enum_glob_use,
clippy::inherent_to_string,
+ clippy::items_after_statements,
+ clippy::match_bool,
+ clippy::match_on_vec_items,
+ clippy::match_same_arms,
+ clippy::missing_errors_doc,
+ clippy::module_name_repetitions,
+ clippy::needless_pass_by_value,
clippy::new_without_default,
+ clippy::nonminimal_bool,
+ clippy::option_if_let_else,
clippy::or_fun_call,
- clippy::toplevel_ref_arg
+ clippy::redundant_else,
+ clippy::shadow_unrelated,
+ clippy::similar_names,
+ clippy::single_match_else,
+ clippy::struct_excessive_bools,
+ clippy::too_many_arguments,
+ clippy::too_many_lines,
+ clippy::toplevel_ref_arg,
+ // clippy bug: https://github.com/rust-lang/rust-clippy/issues/6983
+ clippy::wrong_self_convention
)]
mod error;
diff --git a/gen/lib/tests/test.rs b/gen/lib/tests/test.rs
index 73a25f33..d035b522 100644
--- a/gen/lib/tests/test.rs
+++ b/gen/lib/tests/test.rs
@@ -6,7 +6,7 @@ fn test_positive() {
let rs = quote! {
#[cxx::bridge]
mod ffi {
- extern "C" {
+ unsafe extern "C++" {
fn in_C();
}
extern "Rust" {
diff --git a/gen/src/builtin.rs b/gen/src/builtin.rs
index 41d037a8..eaaa08d1 100644
--- a/gen/src/builtin.rs
+++ b/gen/src/builtin.rs
@@ -12,12 +12,16 @@ pub struct Builtins<'a> {
pub rust_vec: bool,
pub rust_fn: bool,
pub rust_isize: bool,
+ pub opaque: bool,
+ pub layout: bool,
pub unsafe_bitcopy: bool,
+ pub unsafe_bitcopy_t: bool,
pub rust_error: bool,
pub manually_drop: bool,
pub maybe_uninit: bool,
pub trycatch: bool,
pub ptr_len: bool,
+ pub repr_fat: bool,
pub rust_str_new_unchecked: bool,
pub rust_str_repr: bool,
pub rust_slice_new: bool,
@@ -25,6 +29,8 @@ pub struct Builtins<'a> {
pub exception: bool,
pub relocatable: bool,
pub friend_impl: bool,
+ pub is_complete: bool,
+ pub deleter_if: bool,
pub content: Content<'a>,
}
@@ -50,28 +56,46 @@ pub(super) fn write(out: &mut OutFile) {
}
if builtin.rust_str {
+ include.array = true;
include.cstdint = true;
include.string = true;
builtin.friend_impl = true;
}
- if builtin.rust_slice {
- builtin.friend_impl = true;
- }
-
- if builtin.rust_box {
+ if builtin.rust_vec {
+ include.algorithm = true;
+ include.array = true;
+ include.cassert = true;
+ include.cstddef = true;
+ include.cstdint = true;
+ include.initializer_list = true;
+ include.iterator = true;
include.new = true;
+ include.stdexcept = true;
include.type_traits = true;
include.utility = true;
+ builtin.panic = true;
+ builtin.rust_slice = true;
+ builtin.unsafe_bitcopy_t = true;
}
- if builtin.rust_vec {
+ if builtin.rust_slice {
include.array = true;
+ include.cassert = true;
+ include.cstddef = true;
+ include.cstdint = true;
+ include.iterator = true;
+ include.stdexcept = true;
+ include.type_traits = true;
+ builtin.friend_impl = true;
+ builtin.layout = true;
+ builtin.panic = true;
+ }
+
+ if builtin.rust_box {
include.new = true;
include.type_traits = true;
include.utility = true;
- builtin.panic = true;
- builtin.unsafe_bitcopy = true;
}
if builtin.rust_fn {
@@ -85,40 +109,122 @@ pub(super) fn write(out: &mut OutFile) {
if builtin.rust_isize {
include.basetsd = true;
+ include.sys_types = true;
}
if builtin.relocatable {
include.type_traits = true;
}
+ if builtin.layout {
+ include.type_traits = true;
+ include.cstddef = true;
+ builtin.is_complete = true;
+ }
+
+ if builtin.is_complete {
+ include.cstddef = true;
+ include.type_traits = true;
+ }
+
+ if builtin.unsafe_bitcopy {
+ builtin.unsafe_bitcopy_t = true;
+ }
+
out.begin_block(Block::Namespace("rust"));
- out.begin_block(Block::InlineNamespace("cxxbridge05"));
- writeln!(out, "// #include \"rust/cxx.h\"");
+ out.begin_block(Block::InlineNamespace("cxxbridge1"));
- ifndef::write(out, builtin.panic, "CXXBRIDGE05_PANIC");
+ let cxx_header = include.has_cxx_header();
+ if !cxx_header {
+ writeln!(out, "// #include \"rust/cxx.h\"");
+
+ ifndef::write(out, builtin.panic, "CXXBRIDGE1_PANIC");
+
+ if builtin.rust_string {
+ out.next_section();
+ writeln!(out, "struct unsafe_bitcopy_t;");
+ }
+
+ if builtin.friend_impl {
+ out.begin_block(Block::AnonymousNamespace);
+ writeln!(out, "template <typename T>");
+ writeln!(out, "class impl;");
+ out.end_block(Block::AnonymousNamespace);
+ }
- if builtin.rust_string {
out.next_section();
- writeln!(out, "struct unsafe_bitcopy_t;");
+ if builtin.rust_str && !builtin.rust_string {
+ writeln!(out, "class String;");
+ }
+ if builtin.layout && !builtin.opaque {
+ writeln!(out, "class Opaque;");
+ }
+
+ if builtin.rust_slice {
+ out.next_section();
+ writeln!(out, "template <typename T>");
+ writeln!(out, "::std::size_t size_of();");
+ writeln!(out, "template <typename T>");
+ writeln!(out, "::std::size_t align_of();");
+ }
+
+ ifndef::write(out, builtin.rust_string, "CXXBRIDGE1_RUST_STRING");
+ ifndef::write(out, builtin.rust_str, "CXXBRIDGE1_RUST_STR");
+ ifndef::write(out, builtin.rust_slice, "CXXBRIDGE1_RUST_SLICE");
+ ifndef::write(out, builtin.rust_box, "CXXBRIDGE1_RUST_BOX");
+ ifndef::write(out, builtin.unsafe_bitcopy_t, "CXXBRIDGE1_RUST_BITCOPY_T");
+ ifndef::write(out, builtin.unsafe_bitcopy, "CXXBRIDGE1_RUST_BITCOPY");
+ ifndef::write(out, builtin.rust_vec, "CXXBRIDGE1_RUST_VEC");
+ ifndef::write(out, builtin.rust_fn, "CXXBRIDGE1_RUST_FN");
+ ifndef::write(out, builtin.rust_error, "CXXBRIDGE1_RUST_ERROR");
+ ifndef::write(out, builtin.rust_isize, "CXXBRIDGE1_RUST_ISIZE");
+ ifndef::write(out, builtin.opaque, "CXXBRIDGE1_RUST_OPAQUE");
+ ifndef::write(out, builtin.is_complete, "CXXBRIDGE1_IS_COMPLETE");
+ ifndef::write(out, builtin.layout, "CXXBRIDGE1_LAYOUT");
+ ifndef::write(out, builtin.relocatable, "CXXBRIDGE1_RELOCATABLE");
+ }
+
+ if builtin.rust_str_new_unchecked {
+ out.next_section();
+ writeln!(out, "class Str::uninit {{}};");
+ writeln!(out, "inline Str::Str(uninit) noexcept {{}}");
+ }
+
+ if builtin.rust_slice_new {
+ out.next_section();
+ writeln!(out, "template <typename T>");
+ writeln!(out, "class Slice<T>::uninit {{}};");
+ writeln!(out, "template <typename T>");
+ writeln!(out, "inline Slice<T>::Slice(uninit) noexcept {{}}");
}
- if builtin.friend_impl {
- out.begin_block(Block::AnonymousNamespace);
+ out.begin_block(Block::Namespace("detail"));
+
+ if builtin.maybe_uninit {
+ include.cstddef = true;
+ include.new = true;
+ out.next_section();
+ writeln!(out, "template <typename T, typename = void *>");
+ writeln!(out, "struct operator_new {{");
+ writeln!(
+ out,
+ " void *operator()(::std::size_t sz) {{ return ::operator new(sz); }}",
+ );
+ writeln!(out, "}};");
+ out.next_section();
writeln!(out, "template <typename T>");
- writeln!(out, "class impl;");
- out.end_block(Block::AnonymousNamespace);
+ writeln!(
+ out,
+ "struct operator_new<T, decltype(T::operator new(sizeof(T)))> {{",
+ );
+ writeln!(
+ out,
+ " void *operator()(::std::size_t sz) {{ return T::operator new(sz); }}",
+ );
+ writeln!(out, "}};");
}
- ifndef::write(out, builtin.rust_string, "CXXBRIDGE05_RUST_STRING");
- ifndef::write(out, builtin.rust_str, "CXXBRIDGE05_RUST_STR");
- ifndef::write(out, builtin.rust_slice, "CXXBRIDGE05_RUST_SLICE");
- ifndef::write(out, builtin.rust_box, "CXXBRIDGE05_RUST_BOX");
- ifndef::write(out, builtin.unsafe_bitcopy, "CXXBRIDGE05_RUST_BITCOPY");
- ifndef::write(out, builtin.rust_vec, "CXXBRIDGE05_RUST_VEC");
- ifndef::write(out, builtin.rust_fn, "CXXBRIDGE05_RUST_FN");
- ifndef::write(out, builtin.rust_error, "CXXBRIDGE05_RUST_ERROR");
- ifndef::write(out, builtin.rust_isize, "CXXBRIDGE05_RUST_ISIZE");
- ifndef::write(out, builtin.relocatable, "CXXBRIDGE05_RELOCATABLE");
+ out.end_block(Block::Namespace("detail"));
if builtin.manually_drop {
out.next_section();
@@ -135,10 +241,15 @@ pub(super) fn write(out: &mut OutFile) {
}
if builtin.maybe_uninit {
+ include.cstddef = true;
out.next_section();
writeln!(out, "template <typename T>");
writeln!(out, "union MaybeUninit {{");
writeln!(out, " T value;");
+ writeln!(
+ out,
+ " void *operator new(::std::size_t sz) {{ return detail::operator_new<T>{{}}(sz); }}",
+ );
writeln!(out, " MaybeUninit() {{}}");
writeln!(out, " ~MaybeUninit() {{}}");
writeln!(out, "}};");
@@ -146,11 +257,22 @@ pub(super) fn write(out: &mut OutFile) {
out.begin_block(Block::AnonymousNamespace);
+ if builtin.repr_fat {
+ include.array = true;
+ include.cstdint = true;
+ out.next_section();
+ out.begin_block(Block::Namespace("repr"));
+ writeln!(out, "using Fat = ::std::array<::std::uintptr_t, 2>;");
+ out.end_block(Block::Namespace("repr"));
+ }
+
if builtin.ptr_len {
+ include.cstddef = true;
+ out.next_section();
out.begin_block(Block::Namespace("repr"));
writeln!(out, "struct PtrLen final {{");
- writeln!(out, " const void *ptr;");
- writeln!(out, " size_t len;");
+ writeln!(out, " void *ptr;");
+ writeln!(out, " ::std::size_t len;");
writeln!(out, "}};");
out.end_block(Block::Namespace("repr"));
}
@@ -163,17 +285,16 @@ pub(super) fn write(out: &mut OutFile) {
if builtin.rust_str_new_unchecked {
writeln!(
out,
- " static Str new_unchecked(repr::PtrLen repr) noexcept {{",
+ " static Str new_unchecked(repr::Fat repr) noexcept {{",
);
- writeln!(out, " Str str;");
- writeln!(out, " str.ptr = static_cast<const char *>(repr.ptr);");
- writeln!(out, " str.len = repr.len;");
+ writeln!(out, " Str str = Str::uninit{{}};");
+ writeln!(out, " str.repr = repr;");
writeln!(out, " return str;");
writeln!(out, " }}");
}
if builtin.rust_str_repr {
- writeln!(out, " static repr::PtrLen repr(Str str) noexcept {{");
- writeln!(out, " return repr::PtrLen{{str.ptr, str.len}};");
+ writeln!(out, " static repr::Fat repr(Str str) noexcept {{");
+ writeln!(out, " return str.repr;");
writeln!(out, " }}");
}
writeln!(out, "}};");
@@ -185,22 +306,15 @@ pub(super) fn write(out: &mut OutFile) {
writeln!(out, "class impl<Slice<T>> final {{");
writeln!(out, "public:");
if builtin.rust_slice_new {
- writeln!(
- out,
- " static Slice<T> slice(repr::PtrLen repr) noexcept {{",
- );
- writeln!(
- out,
- " return {{static_cast<const T *>(repr.ptr), repr.len}};",
- );
+ writeln!(out, " static Slice<T> slice(repr::Fat repr) noexcept {{");
+ writeln!(out, " Slice<T> slice = typename Slice<T>::uninit{{}};");
+ writeln!(out, " slice.repr = repr;");
+ writeln!(out, " return slice;");
writeln!(out, " }}");
}
if builtin.rust_slice_repr {
- writeln!(
- out,
- " static repr::PtrLen repr(Slice<T> slice) noexcept {{",
- );
- writeln!(out, " return repr::PtrLen{{slice.ptr, slice.len}};");
+ writeln!(out, " static repr::Fat repr(Slice<T> slice) noexcept {{");
+ writeln!(out, " return slice.repr;");
writeln!(out, " }}");
}
writeln!(out, "}};");
@@ -220,8 +334,22 @@ pub(super) fn write(out: &mut OutFile) {
writeln!(out, "}};");
}
+ if builtin.deleter_if {
+ out.next_section();
+ writeln!(out, "template <bool> struct deleter_if {{");
+ writeln!(out, " template <typename T> void operator()(T *) {{}}");
+ writeln!(out, "}};");
+ out.next_section();
+ writeln!(out, "template <> struct deleter_if<true> {{");
+ writeln!(
+ out,
+ " template <typename T> void operator()(T *ptr) {{ ptr->~T(); }}",
+ );
+ writeln!(out, "}};");
+ }
+
out.end_block(Block::AnonymousNamespace);
- out.end_block(Block::InlineNamespace("cxxbridge05"));
+ out.end_block(Block::InlineNamespace("cxxbridge1"));
if builtin.trycatch {
out.begin_block(Block::Namespace("behavior"));
@@ -249,10 +377,11 @@ pub(super) fn write(out: &mut OutFile) {
out.end_block(Block::Namespace("rust"));
if builtin.exception {
+ include.cstddef = true;
out.begin_block(Block::ExternC);
writeln!(
out,
- "const char *cxxbridge05$exception(const char *, size_t);",
+ "const char *cxxbridge1$exception(const char *, ::std::size_t);",
);
out.end_block(Block::ExternC);
}
diff --git a/gen/src/error.rs b/gen/src/error.rs
index 2335d43e..2c8287f3 100644
--- a/gen/src/error.rs
+++ b/gen/src/error.rs
@@ -68,6 +68,13 @@ pub(super) fn format_err(path: &Path, source: &str, error: Error) -> ! {
display_syn_error(stderr, path, source, error);
}
}
+ Error::NoBridgeMod => {
+ let _ = writeln!(
+ io::stderr(),
+ "cxxbridge: no #[cxx::bridge] module found in {}",
+ path.display(),
+ );
+ }
_ => {
let _ = writeln!(io::stderr(), "cxxbridge: {}", report(error));
}
diff --git a/gen/src/fs.rs b/gen/src/fs.rs
index 8f94f005..7053cc44 100644
--- a/gen/src/fs.rs
+++ b/gen/src/fs.rs
@@ -9,7 +9,7 @@ pub(crate) type Result<T> = std::result::Result<T, Error>;
#[derive(Debug)]
pub(crate) struct Error {
- source: io::Error,
+ source: Option<io::Error>,
message: String,
}
@@ -21,14 +21,15 @@ impl Display for Error {
impl StdError for Error {
fn source(&self) -> Option<&(dyn StdError + 'static)> {
- Some(&self.source)
+ let source = self.source.as_ref()?;
+ Some(source)
}
}
macro_rules! err {
($io_error:expr, $fmt:expr $(, $path:expr)* $(,)?) => {
Err(Error {
- source: $io_error,
+ source: Option::from($io_error),
message: format!($fmt $(, $path.display())*),
})
}
@@ -91,44 +92,57 @@ pub(crate) fn remove_dir(path: impl AsRef<Path>) -> Result<()> {
}
fn symlink<'a>(
- src: &'a Path,
- dst: &'a Path,
+ original: &'a Path,
+ link: &'a Path,
fun: fn(&'a Path, &'a Path) -> io::Result<()>,
) -> Result<()> {
- match fun(src, dst) {
+ match fun(original, link) {
Ok(()) => Ok(()),
Err(e) => err!(
e,
"Failed to create symlink `{}` pointing to `{}`",
- dst,
- src,
+ link,
+ original,
),
}
}
+pub(crate) fn symlink_fail(original: impl AsRef<Path>, link: impl AsRef<Path>) -> Result<()> {
+ err!(
+ None,
+ "Failed to create symlink `{}` pointing to `{}`",
+ link.as_ref(),
+ original.as_ref(),
+ )
+}
+
#[cfg(unix)]
#[allow(unused_imports)]
pub(crate) use self::symlink_file as symlink_dir;
+#[cfg(not(any(unix, windows)))]
+#[allow(unused_imports)]
+pub(crate) use self::symlink_fail as symlink_dir;
+
#[cfg(unix)]
-pub(crate) fn symlink_file(src: impl AsRef<Path>, dst: impl AsRef<Path>) -> Result<()> {
- symlink(src.as_ref(), dst.as_ref(), std::os::unix::fs::symlink)
+pub(crate) fn symlink_file(original: impl AsRef<Path>, link: impl AsRef<Path>) -> Result<()> {
+ symlink(original.as_ref(), link.as_ref(), std::os::unix::fs::symlink)
}
#[cfg(windows)]
-pub(crate) fn symlink_file(src: impl AsRef<Path>, dst: impl AsRef<Path>) -> Result<()> {
+pub(crate) fn symlink_file(original: impl AsRef<Path>, link: impl AsRef<Path>) -> Result<()> {
symlink(
- src.as_ref(),
- dst.as_ref(),
+ original.as_ref(),
+ link.as_ref(),
std::os::windows::fs::symlink_file,
)
}
#[cfg(windows)]
-pub(crate) fn symlink_dir(src: impl AsRef<Path>, dst: impl AsRef<Path>) -> Result<()> {
+pub(crate) fn symlink_dir(original: impl AsRef<Path>, link: impl AsRef<Path>) -> Result<()> {
symlink(
- src.as_ref(),
- dst.as_ref(),
+ original.as_ref(),
+ link.as_ref(),
std::os::windows::fs::symlink_dir,
)
}
diff --git a/gen/src/include.rs b/gen/src/include.rs
index cf718dab..62c92320 100644
--- a/gen/src/include.rs
+++ b/gen/src/include.rs
@@ -21,18 +21,25 @@ pub struct Include {
#[derive(Default, PartialEq)]
pub struct Includes<'a> {
pub custom: Vec<Include>,
+ pub algorithm: bool,
pub array: bool,
+ pub cassert: bool,
pub cstddef: bool,
pub cstdint: bool,
pub cstring: bool,
pub exception: bool,
+ pub functional: bool,
+ pub initializer_list: bool,
+ pub iterator: bool,
pub memory: bool,
pub new: bool,
+ pub stdexcept: bool,
pub string: bool,
pub type_traits: bool,
pub utility: bool,
pub vector: bool,
pub basetsd: bool,
+ pub sys_types: bool,
pub content: Content<'a>,
}
@@ -44,11 +51,18 @@ impl<'a> Includes<'a> {
pub fn insert(&mut self, include: impl Into<Include>) {
self.custom.push(include.into());
}
+
+ pub fn has_cxx_header(&self) -> bool {
+ self.custom
+ .iter()
+ .any(|header| header.path == "rust/cxx.h" || header.path == "rust\\cxx.h")
+ }
}
pub(super) fn write(out: &mut OutFile) {
let header = out.header;
let include = &mut out.include;
+ let cxx_header = include.has_cxx_header();
let out = &mut include.content;
if header {
@@ -66,42 +80,96 @@ pub(super) fn write(out: &mut OutFile) {
}
}
- if include.array {
+ let Includes {
+ custom: _,
+ algorithm,
+ array,
+ cassert,
+ cstddef,
+ cstdint,
+ cstring,
+ exception,
+ functional,
+ initializer_list,
+ iterator,
+ memory,
+ new,
+ stdexcept,
+ string,
+ type_traits,
+ utility,
+ vector,
+ basetsd,
+ sys_types,
+ content: _,
+ } = *include;
+
+ if algorithm && !cxx_header {
+ writeln!(out, "#include <algorithm>");
+ }
+ if array && !cxx_header {
writeln!(out, "#include <array>");
}
- if include.cstddef {
+ if cassert && !cxx_header {
+ writeln!(out, "#include <cassert>");
+ }
+ if cstddef && !cxx_header {
writeln!(out, "#include <cstddef>");
}
- if include.cstdint {
+ if cstdint && !cxx_header {
writeln!(out, "#include <cstdint>");
}
- if include.cstring {
+ if cstring {
writeln!(out, "#include <cstring>");
}
- if include.exception {
+ if exception && !cxx_header {
writeln!(out, "#include <exception>");
}
- if include.memory {
+ if functional {
+ writeln!(out, "#include <functional>");
+ }
+ if initializer_list && !cxx_header {
+ writeln!(out, "#include <initializer_list>");
+ }
+ if iterator && !cxx_header {
+ writeln!(out, "#include <iterator>");
+ }
+ if memory {
writeln!(out, "#include <memory>");
}
- if include.new {
+ if new && !cxx_header {
writeln!(out, "#include <new>");
}
- if include.string {
+ if stdexcept && !cxx_header {
+ writeln!(out, "#include <stdexcept>");
+ }
+ if string && !cxx_header {
writeln!(out, "#include <string>");
}
- if include.type_traits {
+ if type_traits && !cxx_header {
writeln!(out, "#include <type_traits>");
}
- if include.utility {
+ if utility && !cxx_header {
writeln!(out, "#include <utility>");
}
- if include.vector {
+ if vector && !cxx_header {
writeln!(out, "#include <vector>");
}
- if include.basetsd {
+ if basetsd && !cxx_header {
writeln!(out, "#if defined(_WIN32)");
writeln!(out, "#include <basetsd.h>");
+ }
+ if sys_types && !cxx_header {
+ if basetsd {
+ writeln!(out, "#else");
+ } else {
+ writeln!(out, "#if not defined(_WIN32)");
+ }
+ }
+ if sys_types && !cxx_header {
+ writeln!(out, "#include <sys/types.h>");
+ }
+ if (basetsd || sys_types) && !cxx_header {
writeln!(out, "#endif");
}
}
diff --git a/gen/src/nested.rs b/gen/src/nested.rs
index 22b0c9f6..2129d10f 100644
--- a/gen/src/nested.rs
+++ b/gen/src/nested.rs
@@ -1,6 +1,6 @@
+use crate::syntax::map::UnorderedMap as Map;
use crate::syntax::Api;
use proc_macro2::Ident;
-use std::collections::HashMap as Map;
pub struct NamespaceEntries<'a> {
direct: Vec<&'a Api>,
@@ -51,10 +51,12 @@ fn sort_by_inner_namespace(apis: Vec<&Api>, depth: usize) -> NamespaceEntries {
#[cfg(test)]
mod tests {
use super::NamespaceEntries;
+ use crate::syntax::attrs::OtherAttrs;
use crate::syntax::namespace::Namespace;
- use crate::syntax::{Api, Doc, ExternType, Pair};
+ use crate::syntax::{Api, Doc, ExternType, ForeignName, Lang, Lifetimes, Pair};
use proc_macro2::{Ident, Span};
use std::iter::FromIterator;
+ use syn::punctuated::Punctuated;
use syn::Token;
#[test]
@@ -117,7 +119,7 @@ mod tests {
fn assert_ident(api: &Api, expected: &str) {
if let Api::CxxType(cxx_type) = api {
- assert_eq!(cxx_type.name.cxx, expected);
+ assert_eq!(cxx_type.name.cxx.to_string(), expected);
} else {
unreachable!()
}
@@ -126,9 +128,24 @@ mod tests {
fn make_api(ns: Option<&str>, ident: &str) -> Api {
let ns = ns.map_or(Namespace::ROOT, |ns| syn::parse_str(ns).unwrap());
Api::CxxType(ExternType {
+ lang: Lang::Rust,
doc: Doc::new(),
+ derives: Vec::new(),
+ attrs: OtherAttrs::none(),
+ visibility: Token![pub](Span::call_site()),
type_token: Token![type](Span::call_site()),
- name: Pair::new(ns, Ident::new(ident, Span::call_site())),
+ name: Pair {
+ namespace: ns,
+ cxx: ForeignName::parse(ident, Span::call_site()).unwrap(),
+ rust: Ident::new(ident, Span::call_site()),
+ },
+ generics: Lifetimes {
+ lt_token: None,
+ lifetimes: Punctuated::new(),
+ gt_token: None,
+ },
+ colon_token: None,
+ bounds: Vec::new(),
semi_token: Token![;](Span::call_site()),
trusted: false,
})
diff --git a/gen/src/out.rs b/gen/src/out.rs
index bf880cc8..3b4d7392 100644
--- a/gen/src/out.rs
+++ b/gen/src/out.rs
@@ -102,7 +102,7 @@ impl<'a> Write for Content<'a> {
}
impl<'a> PartialEq for Content<'a> {
- fn eq(&self, _other: &Content) -> bool {
+ fn eq(&self, _other: &Self) -> bool {
true
}
}
diff --git a/gen/src/write.rs b/gen/src/write.rs
index 5fe8ee30..9f9c0391 100644
--- a/gen/src/write.rs
+++ b/gen/src/write.rs
@@ -3,13 +3,16 @@ use crate::gen::nested::NamespaceEntries;
use crate::gen::out::OutFile;
use crate::gen::{builtin, include, Opt};
use crate::syntax::atom::Atom::{self, *};
+use crate::syntax::instantiate::{ImplKey, NamedImplKey};
+use crate::syntax::map::UnorderedMap as Map;
+use crate::syntax::set::UnorderedSet;
use crate::syntax::symbol::Symbol;
+use crate::syntax::trivial::{self, TrivialReason};
use crate::syntax::{
- mangle, Api, Enum, ExternFn, ExternType, Pair, ResolvableName, Signature, Struct, Type, Types,
- Var,
+ derive, mangle, Api, Enum, ExternFn, ExternType, Pair, Signature, Struct, Trait, Type,
+ TypeAlias, Types, Var,
};
use proc_macro2::Ident;
-use std::collections::{HashMap, HashSet};
pub(super) fn gen(apis: &[Api], types: &Types, opt: &Opt, header: bool) -> Vec<u8> {
let mut out_file = OutFile::new(header, opt, types);
@@ -47,10 +50,10 @@ fn write_forward_declarations(out: &mut OutFile, apis: &[Api]) {
for api in apis {
write!(out, "{:1$}", "", indent);
match api {
- Api::Struct(strct) => write_struct_decl(out, &strct.name.cxx),
+ Api::Struct(strct) => write_struct_decl(out, &strct.name),
Api::Enum(enm) => write_enum_decl(out, enm),
Api::CxxType(ety) => write_struct_using(out, &ety.name),
- Api::RustType(ety) => write_struct_decl(out, &ety.name.cxx),
+ Api::RustType(ety) => write_struct_decl(out, &ety.name),
_ => unreachable!(),
}
}
@@ -64,7 +67,7 @@ fn write_forward_declarations(out: &mut OutFile, apis: &[Api]) {
}
fn write_data_structures<'a>(out: &mut OutFile<'a>, apis: &'a [Api]) {
- let mut methods_for_type = HashMap::new();
+ let mut methods_for_type = Map::new();
for api in apis {
if let Api::CxxFunction(efn) | Api::RustFunction(efn) = api {
if let Some(receiver) = &efn.sig.receiver {
@@ -76,7 +79,7 @@ fn write_data_structures<'a>(out: &mut OutFile<'a>, apis: &'a [Api]) {
}
}
- let mut structs_written = HashSet::new();
+ let mut structs_written = UnorderedSet::new();
let mut toposorted_structs = out.types.toposorted_structs.iter();
for api in apis {
match api {
@@ -105,20 +108,28 @@ fn write_data_structures<'a>(out: &mut OutFile<'a>, apis: &'a [Api]) {
}
}
Api::RustType(ety) => {
- if let Some(methods) = methods_for_type.get(&ety.name.rust) {
- out.next_section();
- write_struct_with_methods(out, ety, methods);
- }
+ out.next_section();
+ let methods = methods_for_type
+ .get(&ety.name.rust)
+ .map(Vec::as_slice)
+ .unwrap_or_default();
+ write_opaque_type(out, ety, methods);
}
_ => {}
}
}
+ if out.header {
+ return;
+ }
+
+ out.set_namespace(Default::default());
+
out.next_section();
for api in apis {
if let Api::TypeAlias(ety) = api {
- if out.types.required_trivial.contains_key(&ety.name.rust) {
- check_trivial_extern_type(out, &ety.name)
+ if let Some(reasons) = out.types.required_trivial.get(&ety.name.rust) {
+ check_trivial_extern_type(out, ety, reasons)
}
}
}
@@ -128,19 +139,60 @@ fn write_functions<'a>(out: &mut OutFile<'a>, apis: &'a [Api]) {
if !out.header {
for api in apis {
match api {
+ Api::Struct(strct) => write_struct_operator_decls(out, strct),
+ Api::RustType(ety) => write_opaque_type_layout_decls(out, ety),
Api::CxxFunction(efn) => write_cxx_function_shim(out, efn),
Api::RustFunction(efn) => write_rust_function_decl(out, efn),
_ => {}
}
}
+
+ write_std_specializations(out, apis);
+ }
+
+ for api in apis {
+ match api {
+ Api::Struct(strct) => write_struct_operators(out, strct),
+ Api::RustType(ety) => write_opaque_type_layout(out, ety),
+ Api::RustFunction(efn) => {
+ out.next_section();
+ write_rust_function_shim(out, efn);
+ }
+ _ => {}
+ }
}
+}
+
+fn write_std_specializations(out: &mut OutFile, apis: &[Api]) {
+ out.set_namespace(Default::default());
+ out.begin_block(Block::Namespace("std"));
for api in apis {
- if let Api::RustFunction(efn) = api {
- out.next_section();
- write_rust_function_shim(out, efn);
+ if let Api::Struct(strct) = api {
+ if derive::contains(&strct.derives, Trait::Hash) {
+ out.next_section();
+ out.include.cstddef = true;
+ out.include.functional = true;
+ let qualified = strct.name.to_fully_qualified();
+ writeln!(out, "template <> struct hash<{}> {{", qualified);
+ writeln!(
+ out,
+ " ::std::size_t operator()(const {} &self) const noexcept {{",
+ qualified,
+ );
+ let link_name = mangle::operator(&strct.name, "hash");
+ write!(out, " return ::");
+ for name in &strct.name.namespace {
+ write!(out, "{}::", name);
+ }
+ writeln!(out, "{}(self);", link_name);
+ writeln!(out, " }}");
+ writeln!(out, "}};");
+ }
}
}
+
+ out.end_block(Block::Namespace("std"));
}
fn pick_includes_and_builtins(out: &mut OutFile, apis: &[Api]) {
@@ -159,41 +211,46 @@ fn pick_includes_and_builtins(out: &mut OutFile, apis: &[Api]) {
Some(Isize) => out.builtin.rust_isize = true,
Some(CxxString) => out.include.string = true,
Some(RustString) => out.builtin.rust_string = true,
- Some(Bool) | Some(F32) | Some(F64) | None => {}
+ Some(Bool) | Some(Char) | Some(F32) | Some(F64) | None => {}
},
Type::RustBox(_) => out.builtin.rust_box = true,
Type::RustVec(_) => out.builtin.rust_vec = true,
Type::UniquePtr(_) => out.include.memory = true,
+ Type::SharedPtr(_) | Type::WeakPtr(_) => out.include.memory = true,
Type::Str(_) => out.builtin.rust_str = true,
Type::CxxVector(_) => out.include.vector = true,
Type::Fn(_) => out.builtin.rust_fn = true,
- Type::Slice(_) => out.builtin.rust_slice = true,
- Type::SliceRefU8(_) => {
- out.include.cstdint = true;
- out.builtin.rust_slice = true;
- }
- Type::Ref(_) | Type::Void(_) => {}
+ Type::SliceRef(_) => out.builtin.rust_slice = true,
+ Type::Array(_) => out.include.array = true,
+ Type::Ref(_) | Type::Void(_) | Type::Ptr(_) => {}
}
}
}
fn write_struct<'a>(out: &mut OutFile<'a>, strct: &'a Struct, methods: &[&ExternFn]) {
+ let operator_eq = derive::contains(&strct.derives, Trait::PartialEq);
+ let operator_ord = derive::contains(&strct.derives, Trait::PartialOrd);
+
out.set_namespace(&strct.name.namespace);
- let guard = format!("CXXBRIDGE05_STRUCT_{}", strct.name.to_symbol());
+ let guard = format!("CXXBRIDGE1_STRUCT_{}", strct.name.to_symbol());
writeln!(out, "#ifndef {}", guard);
writeln!(out, "#define {}", guard);
for line in strct.doc.to_string().lines() {
writeln!(out, "//{}", line);
}
writeln!(out, "struct {} final {{", strct.name.cxx);
+
for field in &strct.fields {
+ for line in field.doc.to_string().lines() {
+ writeln!(out, " //{}", line);
+ }
write!(out, " ");
write_type_space(out, &field.ty);
- writeln!(out, "{};", field.ident);
- }
- if !methods.is_empty() {
- writeln!(out);
+ writeln!(out, "{};", field.name.cxx);
}
+
+ writeln!(out);
+
for method in methods {
write!(out, " ");
let sig = &method.sig;
@@ -201,12 +258,52 @@ fn write_struct<'a>(out: &mut OutFile<'a>, strct: &'a Struct, methods: &[&Extern
write_rust_function_shim_decl(out, &local_name, sig, false);
writeln!(out, ";");
}
+
+ if operator_eq {
+ writeln!(
+ out,
+ " bool operator==(const {} &) const noexcept;",
+ strct.name.cxx,
+ );
+ writeln!(
+ out,
+ " bool operator!=(const {} &) const noexcept;",
+ strct.name.cxx,
+ );
+ }
+
+ if operator_ord {
+ writeln!(
+ out,
+ " bool operator<(const {} &) const noexcept;",
+ strct.name.cxx,
+ );
+ writeln!(
+ out,
+ " bool operator<=(const {} &) const noexcept;",
+ strct.name.cxx,
+ );
+ writeln!(
+ out,
+ " bool operator>(const {} &) const noexcept;",
+ strct.name.cxx,
+ );
+ writeln!(
+ out,
+ " bool operator>=(const {} &) const noexcept;",
+ strct.name.cxx,
+ );
+ }
+
+ out.include.type_traits = true;
+ writeln!(out, " using IsRelocatable = ::std::true_type;");
+
writeln!(out, "}};");
writeln!(out, "#endif // {}", guard);
}
-fn write_struct_decl(out: &mut OutFile, ident: &Ident) {
- writeln!(out, "struct {};", ident);
+fn write_struct_decl(out: &mut OutFile, ident: &Pair) {
+ writeln!(out, "struct {};", ident.cxx);
}
fn write_enum_decl(out: &mut OutFile, enm: &Enum) {
@@ -219,25 +316,22 @@ fn write_struct_using(out: &mut OutFile, ident: &Pair) {
writeln!(out, "using {} = {};", ident.cxx, ident.to_fully_qualified());
}
-fn write_struct_with_methods<'a>(
- out: &mut OutFile<'a>,
- ety: &'a ExternType,
- methods: &[&ExternFn],
-) {
+fn write_opaque_type<'a>(out: &mut OutFile<'a>, ety: &'a ExternType, methods: &[&ExternFn]) {
out.set_namespace(&ety.name.namespace);
- let guard = format!("CXXBRIDGE05_STRUCT_{}", ety.name.to_symbol());
+ let guard = format!("CXXBRIDGE1_STRUCT_{}", ety.name.to_symbol());
writeln!(out, "#ifndef {}", guard);
writeln!(out, "#define {}", guard);
for line in ety.doc.to_string().lines() {
writeln!(out, "//{}", line);
}
- writeln!(out, "struct {} final {{", ety.name.cxx);
- writeln!(out, " {}() = delete;", ety.name.cxx);
+
+ out.builtin.opaque = true;
writeln!(
out,
- " {}(const {} &) = delete;",
- ety.name.cxx, ety.name.cxx,
+ "struct {} final : public ::rust::Opaque {{",
+ ety.name.cxx,
);
+
for method in methods {
write!(out, " ");
let sig = &method.sig;
@@ -245,13 +339,25 @@ fn write_struct_with_methods<'a>(
write_rust_function_shim_decl(out, &local_name, sig, false);
writeln!(out, ";");
}
+
+ writeln!(out, " ~{}() = delete;", ety.name.cxx);
+ writeln!(out);
+
+ out.builtin.layout = true;
+ out.include.cstddef = true;
+ writeln!(out, "private:");
+ writeln!(out, " friend ::rust::layout;");
+ writeln!(out, " struct layout {{");
+ writeln!(out, " static ::std::size_t size() noexcept;");
+ writeln!(out, " static ::std::size_t align() noexcept;");
+ writeln!(out, " }};");
writeln!(out, "}};");
writeln!(out, "#endif // {}", guard);
}
fn write_enum<'a>(out: &mut OutFile<'a>, enm: &'a Enum) {
out.set_namespace(&enm.name.namespace);
- let guard = format!("CXXBRIDGE05_ENUM_{}", enm.name.to_symbol());
+ let guard = format!("CXXBRIDGE1_ENUM_{}", enm.name.to_symbol());
writeln!(out, "#ifndef {}", guard);
writeln!(out, "#define {}", guard);
for line in enm.doc.to_string().lines() {
@@ -261,7 +367,10 @@ fn write_enum<'a>(out: &mut OutFile<'a>, enm: &'a Enum) {
write_atom(out, enm.repr);
writeln!(out, " {{");
for variant in &enm.variants {
- writeln!(out, " {} = {},", variant.ident, variant.discriminant);
+ for line in variant.doc.to_string().lines() {
+ writeln!(out, " //{}", line);
+ }
+ writeln!(out, " {} = {},", variant.name.cxx, variant.discriminant);
}
writeln!(out, "}};");
writeln!(out, "#endif // {}", guard);
@@ -269,6 +378,12 @@ fn write_enum<'a>(out: &mut OutFile<'a>, enm: &'a Enum) {
fn check_enum<'a>(out: &mut OutFile<'a>, enm: &'a Enum) {
out.set_namespace(&enm.name.namespace);
+ out.include.type_traits = true;
+ writeln!(
+ out,
+ "static_assert(::std::is_enum<{}>::value, \"expected enum\");",
+ enm.name.cxx,
+ );
write!(out, "static_assert(sizeof({}) == sizeof(", enm.name.cxx);
write_atom(out, enm.repr);
writeln!(out, "), \"incorrect size\");");
@@ -278,12 +393,12 @@ fn check_enum<'a>(out: &mut OutFile<'a>, enm: &'a Enum) {
writeln!(
out,
">({}::{}) == {}, \"disagrees with the value in #[cxx::bridge]\");",
- enm.name.cxx, variant.ident, variant.discriminant,
+ enm.name.cxx, variant.name.cxx, variant.discriminant,
);
}
}
-fn check_trivial_extern_type(out: &mut OutFile, id: &Pair) {
+fn check_trivial_extern_type(out: &mut OutFile, alias: &TypeAlias, reasons: &[TrivialReason]) {
// NOTE: The following static assertion is just nice-to-have and not
// necessary for soundness. That's because triviality is always declared by
// the user in the form of an unsafe impl of cxx::ExternType:
@@ -317,24 +432,221 @@ fn check_trivial_extern_type(out: &mut OutFile, id: &Pair) {
// + struct rust::IsRelocatable<MyType> : std::true_type {};
//
- let id = id.to_fully_qualified();
+ let id = alias.name.to_fully_qualified();
out.builtin.relocatable = true;
writeln!(out, "static_assert(");
writeln!(out, " ::rust::IsRelocatable<{}>::value,", id);
writeln!(
out,
- " \"type {} marked as Trivial in Rust is not trivially move constructible and trivially destructible in C++\");",
- id,
+ " \"type {} should be trivially move constructible and trivially destructible in C++ to be used as {} in Rust\");",
+ id.trim_start_matches("::"),
+ trivial::as_what(&alias.name, reasons),
);
}
-fn write_cxx_function_shim<'a>(out: &mut OutFile<'a>, efn: &'a ExternFn) {
- out.next_section();
- out.set_namespace(&efn.name.namespace);
+fn write_struct_operator_decls<'a>(out: &mut OutFile<'a>, strct: &'a Struct) {
+ out.set_namespace(&strct.name.namespace);
out.begin_block(Block::ExternC);
+
+ if derive::contains(&strct.derives, Trait::PartialEq) {
+ let link_name = mangle::operator(&strct.name, "eq");
+ writeln!(
+ out,
+ "bool {}(const {1} &, const {1} &) noexcept;",
+ link_name, strct.name.cxx,
+ );
+
+ if !derive::contains(&strct.derives, Trait::Eq) {
+ let link_name = mangle::operator(&strct.name, "ne");
+ writeln!(
+ out,
+ "bool {}(const {1} &, const {1} &) noexcept;",
+ link_name, strct.name.cxx,
+ );
+ }
+ }
+
+ if derive::contains(&strct.derives, Trait::PartialOrd) {
+ let link_name = mangle::operator(&strct.name, "lt");
+ writeln!(
+ out,
+ "bool {}(const {1} &, const {1} &) noexcept;",
+ link_name, strct.name.cxx,
+ );
+
+ let link_name = mangle::operator(&strct.name, "le");
+ writeln!(
+ out,
+ "bool {}(const {1} &, const {1} &) noexcept;",
+ link_name, strct.name.cxx,
+ );
+
+ if !derive::contains(&strct.derives, Trait::Ord) {
+ let link_name = mangle::operator(&strct.name, "gt");
+ writeln!(
+ out,
+ "bool {}(const {1} &, const {1} &) noexcept;",
+ link_name, strct.name.cxx,
+ );
+
+ let link_name = mangle::operator(&strct.name, "ge");
+ writeln!(
+ out,
+ "bool {}(const {1} &, const {1} &) noexcept;",
+ link_name, strct.name.cxx,
+ );
+ }
+ }
+
+ if derive::contains(&strct.derives, Trait::Hash) {
+ out.include.cstddef = true;
+ let link_name = mangle::operator(&strct.name, "hash");
+ writeln!(
+ out,
+ "::std::size_t {}(const {} &) noexcept;",
+ link_name, strct.name.cxx,
+ );
+ }
+
+ out.end_block(Block::ExternC);
+}
+
+fn write_struct_operators<'a>(out: &mut OutFile<'a>, strct: &'a Struct) {
+ if out.header {
+ return;
+ }
+
+ out.set_namespace(&strct.name.namespace);
+
+ if derive::contains(&strct.derives, Trait::PartialEq) {
+ out.next_section();
+ writeln!(
+ out,
+ "bool {0}::operator==(const {0} &rhs) const noexcept {{",
+ strct.name.cxx,
+ );
+ let link_name = mangle::operator(&strct.name, "eq");
+ writeln!(out, " return {}(*this, rhs);", link_name);
+ writeln!(out, "}}");
+
+ out.next_section();
+ writeln!(
+ out,
+ "bool {0}::operator!=(const {0} &rhs) const noexcept {{",
+ strct.name.cxx,
+ );
+ if derive::contains(&strct.derives, Trait::Eq) {
+ writeln!(out, " return !(*this == rhs);");
+ } else {
+ let link_name = mangle::operator(&strct.name, "ne");
+ writeln!(out, " return {}(*this, rhs);", link_name);
+ }
+ writeln!(out, "}}");
+ }
+
+ if derive::contains(&strct.derives, Trait::PartialOrd) {
+ out.next_section();
+ writeln!(
+ out,
+ "bool {0}::operator<(const {0} &rhs) const noexcept {{",
+ strct.name.cxx,
+ );
+ let link_name = mangle::operator(&strct.name, "lt");
+ writeln!(out, " return {}(*this, rhs);", link_name);
+ writeln!(out, "}}");
+
+ out.next_section();
+ writeln!(
+ out,
+ "bool {0}::operator<=(const {0} &rhs) const noexcept {{",
+ strct.name.cxx,
+ );
+ let link_name = mangle::operator(&strct.name, "le");
+ writeln!(out, " return {}(*this, rhs);", link_name);
+ writeln!(out, "}}");
+
+ out.next_section();
+ writeln!(
+ out,
+ "bool {0}::operator>(const {0} &rhs) const noexcept {{",
+ strct.name.cxx,
+ );
+ if derive::contains(&strct.derives, Trait::Ord) {
+ writeln!(out, " return !(*this <= rhs);");
+ } else {
+ let link_name = mangle::operator(&strct.name, "gt");
+ writeln!(out, " return {}(*this, rhs);", link_name);
+ }
+ writeln!(out, "}}");
+
+ out.next_section();
+ writeln!(
+ out,
+ "bool {0}::operator>=(const {0} &rhs) const noexcept {{",
+ strct.name.cxx,
+ );
+ if derive::contains(&strct.derives, Trait::Ord) {
+ writeln!(out, " return !(*this < rhs);");
+ } else {
+ let link_name = mangle::operator(&strct.name, "ge");
+ writeln!(out, " return {}(*this, rhs);", link_name);
+ }
+ writeln!(out, "}}");
+ }
+}
+
+fn write_opaque_type_layout_decls<'a>(out: &mut OutFile<'a>, ety: &'a ExternType) {
+ out.set_namespace(&ety.name.namespace);
+ out.begin_block(Block::ExternC);
+
+ let link_name = mangle::operator(&ety.name, "sizeof");
+ writeln!(out, "::std::size_t {}() noexcept;", link_name);
+
+ let link_name = mangle::operator(&ety.name, "alignof");
+ writeln!(out, "::std::size_t {}() noexcept;", link_name);
+
+ out.end_block(Block::ExternC);
+}
+
+fn write_opaque_type_layout<'a>(out: &mut OutFile<'a>, ety: &'a ExternType) {
+ if out.header {
+ return;
+ }
+
+ out.set_namespace(&ety.name.namespace);
+
+ out.next_section();
+ let link_name = mangle::operator(&ety.name, "sizeof");
+ writeln!(
+ out,
+ "::std::size_t {}::layout::size() noexcept {{",
+ ety.name.cxx,
+ );
+ writeln!(out, " return {}();", link_name);
+ writeln!(out, "}}");
+
+ out.next_section();
+ let link_name = mangle::operator(&ety.name, "alignof");
+ writeln!(
+ out,
+ "::std::size_t {}::layout::align() noexcept {{",
+ ety.name.cxx,
+ );
+ writeln!(out, " return {}();", link_name);
+ writeln!(out, "}}");
+}
+
+fn begin_function_definition(out: &mut OutFile) {
if let Some(annotation) = &out.opt.cxx_impl_annotations {
write!(out, "{} ", annotation);
}
+}
+
+fn write_cxx_function_shim<'a>(out: &mut OutFile<'a>, efn: &'a ExternFn) {
+ out.next_section();
+ out.set_namespace(&efn.name.namespace);
+ out.begin_block(Block::ExternC);
+ begin_function_definition(out);
if efn.throws {
out.builtin.ptr_len = true;
write!(out, "::rust::repr::PtrLen ");
@@ -344,13 +656,13 @@ fn write_cxx_function_shim<'a>(out: &mut OutFile<'a>, efn: &'a ExternFn) {
let mangled = mangle::extern_fn(efn, out.types);
write!(out, "{}(", mangled);
if let Some(receiver) = &efn.receiver {
- if receiver.mutability.is_none() {
+ if !receiver.mutable {
write!(out, "const ");
}
write!(
out,
"{} &self",
- out.types.resolve(&receiver.ty).to_fully_qualified(),
+ out.types.resolve(&receiver.ty).name.to_fully_qualified(),
);
}
for (i, arg) in efn.args.iter().enumerate() {
@@ -380,7 +692,7 @@ fn write_cxx_function_shim<'a>(out: &mut OutFile<'a>, efn: &'a ExternFn) {
Some(receiver) => write!(
out,
"({}::*{}$)(",
- out.types.resolve(&receiver.ty).to_fully_qualified(),
+ out.types.resolve(&receiver.ty).name.to_fully_qualified(),
efn.name.rust,
),
}
@@ -392,7 +704,7 @@ fn write_cxx_function_shim<'a>(out: &mut OutFile<'a>, efn: &'a ExternFn) {
}
write!(out, ")");
if let Some(receiver) = &efn.receiver {
- if receiver.mutability.is_none() {
+ if !receiver.mutable {
write!(out, " const");
}
}
@@ -402,7 +714,7 @@ fn write_cxx_function_shim<'a>(out: &mut OutFile<'a>, efn: &'a ExternFn) {
Some(receiver) => write!(
out,
"&{}::{}",
- out.types.resolve(&receiver.ty).to_fully_qualified(),
+ out.types.resolve(&receiver.ty).name.to_fully_qualified(),
efn.name.cxx,
),
}
@@ -430,9 +742,11 @@ fn write_cxx_function_shim<'a>(out: &mut OutFile<'a>, efn: &'a ExternFn) {
out.builtin.rust_str_repr = true;
write!(out, "::rust::impl<::rust::Str>::repr(");
}
- Some(Type::SliceRefU8(_)) if !indirect_return => {
+ Some(ty @ Type::SliceRef(_)) if !indirect_return => {
out.builtin.rust_slice_repr = true;
- write!(out, "::rust::impl<::rust::Slice<uint8_t>>::repr(")
+ write!(out, "::rust::impl<");
+ write_type(out, ty);
+ write!(out, ">::repr(");
}
_ => {}
}
@@ -446,46 +760,33 @@ fn write_cxx_function_shim<'a>(out: &mut OutFile<'a>, efn: &'a ExternFn) {
}
if let Type::RustBox(_) = &arg.ty {
write_type(out, &arg.ty);
- write!(out, "::from_raw({})", arg.ident);
+ write!(out, "::from_raw({})", arg.name.cxx);
} else if let Type::UniquePtr(_) = &arg.ty {
write_type(out, &arg.ty);
- write!(out, "({})", arg.ident);
- } else if let Type::Str(_) = arg.ty {
- out.builtin.rust_str_new_unchecked = true;
- write!(
- out,
- "::rust::impl<::rust::Str>::new_unchecked({})",
- arg.ident,
- );
+ write!(out, "({})", arg.name.cxx);
} else if arg.ty == RustString {
out.builtin.unsafe_bitcopy = true;
write!(
out,
"::rust::String(::rust::unsafe_bitcopy, *{})",
- arg.ident,
+ arg.name.cxx,
);
} else if let Type::RustVec(_) = arg.ty {
out.builtin.unsafe_bitcopy = true;
write_type(out, &arg.ty);
- write!(out, "(::rust::unsafe_bitcopy, *{})", arg.ident);
- } else if let Type::SliceRefU8(_) = arg.ty {
- write!(
- out,
- "::rust::Slice<uint8_t>(static_cast<const uint8_t *>({0}.ptr), {0}.len)",
- arg.ident,
- );
+ write!(out, "(::rust::unsafe_bitcopy, *{})", arg.name.cxx);
} else if out.types.needs_indirect_abi(&arg.ty) {
out.include.utility = true;
- write!(out, "::std::move(*{})", arg.ident);
+ write!(out, "::std::move(*{})", arg.name.cxx);
} else {
- write!(out, "{}", arg.ident);
+ write!(out, "{}", arg.name.cxx);
}
}
write!(out, ")");
match &efn.ret {
Some(Type::RustBox(_)) => write!(out, ".into_raw()"),
Some(Type::UniquePtr(_)) => write!(out, ".release()"),
- Some(Type::Str(_)) | Some(Type::SliceRefU8(_)) if !indirect_return => write!(out, ")"),
+ Some(Type::Str(_)) | Some(Type::SliceRef(_)) if !indirect_return => write!(out, ")"),
_ => {}
}
if indirect_return {
@@ -501,7 +802,7 @@ fn write_cxx_function_shim<'a>(out: &mut OutFile<'a>, efn: &'a ExternFn) {
writeln!(out, " throw$.len = ::std::strlen(catch$);");
writeln!(
out,
- " throw$.ptr = ::cxxbridge05$exception(catch$, throw$.len);",
+ " throw$.ptr = const_cast<char *>(::cxxbridge1$exception(catch$, throw$.len));",
);
writeln!(out, " }});");
writeln!(out, " return throw$;");
@@ -509,19 +810,14 @@ fn write_cxx_function_shim<'a>(out: &mut OutFile<'a>, efn: &'a ExternFn) {
writeln!(out, "}}");
for arg in &efn.args {
if let Type::Fn(f) = &arg.ty {
- let var = &arg.ident;
+ let var = &arg.name;
write_function_pointer_trampoline(out, efn, var, f);
}
}
out.end_block(Block::ExternC);
}
-fn write_function_pointer_trampoline(
- out: &mut OutFile,
- efn: &ExternFn,
- var: &Ident,
- f: &Signature,
-) {
+fn write_function_pointer_trampoline(out: &mut OutFile, efn: &ExternFn, var: &Pair, f: &Signature) {
let r_trampoline = mangle::r_trampoline(efn, var, out.types);
let indirect_call = true;
write_rust_function_decl_impl(out, &r_trampoline, f, indirect_call);
@@ -556,13 +852,13 @@ fn write_rust_function_decl_impl(
write!(out, "{}(", link_name);
let mut needs_comma = false;
if let Some(receiver) = &sig.receiver {
- if receiver.mutability.is_none() {
+ if !receiver.mutable {
write!(out, "const ");
}
write!(
out,
"{} &self",
- out.types.resolve(&receiver.ty).to_fully_qualified(),
+ out.types.resolve(&receiver.ty).name.to_fully_qualified(),
);
needs_comma = true;
}
@@ -577,7 +873,13 @@ fn write_rust_function_decl_impl(
if needs_comma {
write!(out, ", ");
}
- write_return_type(out, &sig.ret);
+ match sig.ret.as_ref().unwrap() {
+ Type::Ref(ret) => {
+ write_pointee_type(out, &ret.inner, ret.mutable);
+ write!(out, " *");
+ }
+ ret => write_type_space(out, ret),
+ }
write!(out, "*return$");
needs_comma = true;
}
@@ -597,7 +899,11 @@ fn write_rust_function_shim<'a>(out: &mut OutFile<'a>, efn: &'a ExternFn) {
}
let local_name = match &efn.sig.receiver {
None => efn.name.cxx.to_string(),
- Some(receiver) => format!("{}::{}", out.types.resolve(&receiver.ty).cxx, efn.name.cxx),
+ Some(receiver) => format!(
+ "{}::{}",
+ out.types.resolve(&receiver.ty).name.cxx,
+ efn.name.cxx,
+ ),
};
let invoke = mangle::extern_fn(efn, out.types);
let indirect_call = false;
@@ -610,6 +916,7 @@ fn write_rust_function_shim_decl(
sig: &Signature,
indirect_call: bool,
) {
+ begin_function_definition(out);
write_return_type(out, &sig.ret);
write!(out, "{}(", local_name);
for (i, arg) in sig.args.iter().enumerate() {
@@ -617,7 +924,7 @@ fn write_rust_function_shim_decl(
write!(out, ", ");
}
write_type_space(out, &arg.ty);
- write!(out, "{}", arg.ident);
+ write!(out, "{}", arg.name.cxx);
}
if indirect_call {
if !sig.args.is_empty() {
@@ -627,7 +934,7 @@ fn write_rust_function_shim_decl(
}
write!(out, ")");
if let Some(receiver) = &sig.receiver {
- if receiver.mutability.is_none() {
+ if !receiver.mutable {
write!(out, " const");
}
}
@@ -659,7 +966,7 @@ fn write_rust_function_shim_impl(
out.builtin.manually_drop = true;
write!(out, " ::rust::ManuallyDrop<");
write_type(out, &arg.ty);
- writeln!(out, "> {}$(::std::move({0}));", arg.ident);
+ writeln!(out, "> {}$(::std::move({0}));", arg.name.cxx);
}
}
write!(out, " ");
@@ -667,7 +974,13 @@ fn write_rust_function_shim_impl(
if indirect_return {
out.builtin.maybe_uninit = true;
write!(out, "::rust::MaybeUninit<");
- write_type(out, sig.ret.as_ref().unwrap());
+ match sig.ret.as_ref().unwrap() {
+ Type::Ref(ret) => {
+ write_pointee_type(out, &ret.inner, ret.mutable);
+ write!(out, " *");
+ }
+ ret => write_type(out, ret),
+ }
writeln!(out, "> return$;");
write!(out, " ");
} else if let Some(ret) = &sig.ret {
@@ -686,9 +999,11 @@ fn write_rust_function_shim_impl(
out.builtin.rust_str_new_unchecked = true;
write!(out, "::rust::impl<::rust::Str>::new_unchecked(");
}
- Type::SliceRefU8(_) => {
+ Type::SliceRef(_) => {
out.builtin.rust_slice_new = true;
- write!(out, "::rust::impl<::rust::Slice<uint8_t>>::slice(");
+ write!(out, "::rust::impl<");
+ write_type(out, ret);
+ write!(out, ">::slice(");
}
_ => {}
}
@@ -707,23 +1022,13 @@ fn write_rust_function_shim_impl(
if needs_comma {
write!(out, ", ");
}
- match &arg.ty {
- Type::Str(_) => {
- out.builtin.rust_str_repr = true;
- write!(out, "::rust::impl<::rust::Str>::repr(");
- }
- Type::SliceRefU8(_) => {
- out.builtin.rust_slice_repr = true;
- write!(out, "::rust::impl<::rust::Slice<uint8_t>>::repr(");
- }
- ty if out.types.needs_indirect_abi(ty) => write!(out, "&"),
- _ => {}
+ if out.types.needs_indirect_abi(&arg.ty) {
+ write!(out, "&");
}
- write!(out, "{}", arg.ident);
+ write!(out, "{}", arg.name.cxx);
match &arg.ty {
Type::RustBox(_) => write!(out, ".into_raw()"),
Type::UniquePtr(_) => write!(out, ".release()"),
- Type::Str(_) | Type::SliceRefU8(_) => write!(out, ")"),
ty if ty != RustString && out.types.needs_indirect_abi(ty) => write!(out, "$.value"),
_ => {}
}
@@ -745,8 +1050,7 @@ fn write_rust_function_shim_impl(
write!(out, ")");
if !indirect_return {
if let Some(ret) = &sig.ret {
- if let Type::RustBox(_) | Type::UniquePtr(_) | Type::Str(_) | Type::SliceRefU8(_) = ret
- {
+ if let Type::RustBox(_) | Type::UniquePtr(_) | Type::Str(_) | Type::SliceRef(_) = ret {
write!(out, ")");
}
}
@@ -759,8 +1063,15 @@ fn write_rust_function_shim_impl(
writeln!(out, " }}");
}
if indirect_return {
- out.include.utility = true;
- writeln!(out, " return ::std::move(return$.value);");
+ write!(out, " return ");
+ match sig.ret.as_ref().unwrap() {
+ Type::Ref(_) => write!(out, "*return$.value"),
+ _ => {
+ out.include.utility = true;
+ write!(out, "::std::move(return$.value)");
+ }
+ }
+ writeln!(out, ";");
}
writeln!(out, "}}");
}
@@ -785,7 +1096,7 @@ fn write_indirect_return_type(out: &mut OutFile, ty: &Type) {
write!(out, "*");
}
Type::Ref(ty) => {
- if ty.mutability.is_none() {
+ if !ty.mutable {
write!(out, "const ");
}
write_type(out, &ty.inner);
@@ -799,7 +1110,7 @@ fn write_indirect_return_type_space(out: &mut OutFile, ty: &Type) {
write_indirect_return_type(out, ty);
match ty {
Type::RustBox(_) | Type::UniquePtr(_) | Type::Ref(_) => {}
- Type::Str(_) | Type::SliceRefU8(_) => write!(out, " "),
+ Type::Str(_) | Type::SliceRef(_) => write!(out, " "),
_ => write_space_after_type(out, ty),
}
}
@@ -811,15 +1122,15 @@ fn write_extern_return_type_space(out: &mut OutFile, ty: &Option<Type>) {
write!(out, "*");
}
Some(Type::Ref(ty)) => {
- if ty.mutability.is_none() {
+ if !ty.mutable {
write!(out, "const ");
}
write_type(out, &ty.inner);
write!(out, " *");
}
- Some(Type::Str(_)) | Some(Type::SliceRefU8(_)) => {
- out.builtin.ptr_len = true;
- write!(out, "::rust::repr::PtrLen ");
+ Some(Type::Str(_)) | Some(Type::SliceRef(_)) => {
+ out.builtin.repr_fat = true;
+ write!(out, "::rust::repr::Fat ");
}
Some(ty) if out.types.needs_indirect_abi(ty) => write!(out, "void "),
_ => write_return_type(out, ty),
@@ -832,23 +1143,23 @@ fn write_extern_arg(out: &mut OutFile, arg: &Var) {
write_type_space(out, &ty.inner);
write!(out, "*");
}
- Type::Str(_) | Type::SliceRefU8(_) => {
- out.builtin.ptr_len = true;
- write!(out, "::rust::repr::PtrLen ");
- }
_ => write_type_space(out, &arg.ty),
}
if out.types.needs_indirect_abi(&arg.ty) {
write!(out, "*");
}
- write!(out, "{}", arg.ident);
+ write!(out, "{}", arg.name.cxx);
}
fn write_type(out: &mut OutFile, ty: &Type) {
match ty {
Type::Ident(ident) => match Atom::from(&ident.rust) {
Some(atom) => write_atom(out, atom),
- None => write!(out, "{}", out.types.resolve(ident).to_fully_qualified()),
+ None => write!(
+ out,
+ "{}",
+ out.types.resolve(ident).name.to_fully_qualified(),
+ ),
},
Type::RustBox(ty) => {
write!(out, "::rust::Box<");
@@ -865,30 +1176,42 @@ fn write_type(out: &mut OutFile, ty: &Type) {
write_type(out, &ptr.inner);
write!(out, ">");
}
+ Type::SharedPtr(ptr) => {
+ write!(out, "::std::shared_ptr<");
+ write_type(out, &ptr.inner);
+ write!(out, ">");
+ }
+ Type::WeakPtr(ptr) => {
+ write!(out, "::std::weak_ptr<");
+ write_type(out, &ptr.inner);
+ write!(out, ">");
+ }
Type::CxxVector(ty) => {
write!(out, "::std::vector<");
write_type(out, &ty.inner);
write!(out, ">");
}
Type::Ref(r) => {
- if r.mutability.is_none() {
- write!(out, "const ");
- }
- write_type(out, &r.inner);
+ write_pointee_type(out, &r.inner, r.mutable);
write!(out, " &");
}
- Type::Slice(_) => {
- // For now, only U8 slices are supported, which are covered separately below
- unreachable!()
+ Type::Ptr(p) => {
+ write_pointee_type(out, &p.inner, p.mutable);
+ write!(out, " *");
}
Type::Str(_) => {
write!(out, "::rust::Str");
}
- Type::SliceRefU8(_) => {
- write!(out, "::rust::Slice<uint8_t>");
+ Type::SliceRef(slice) => {
+ write!(out, "::rust::Slice<");
+ if slice.mutability.is_none() {
+ write!(out, "const ");
+ }
+ write_type(out, &slice.inner);
+ write!(out, ">");
}
Type::Fn(f) => {
- write!(out, "::rust::{}<", if f.throws { "TryFn" } else { "Fn" });
+ write!(out, "::rust::Fn<");
match &f.ret {
Some(ret) => write_type(out, ret),
None => write!(out, "void"),
@@ -902,22 +1225,43 @@ fn write_type(out: &mut OutFile, ty: &Type) {
}
write!(out, ")>");
}
+ Type::Array(a) => {
+ write!(out, "::std::array<");
+ write_type(out, &a.inner);
+ write!(out, ", {}>", &a.len);
+ }
Type::Void(_) => unreachable!(),
}
}
+// Write just the T type behind a &T or &mut T or *const T or *mut T.
+fn write_pointee_type(out: &mut OutFile, inner: &Type, mutable: bool) {
+ if let Type::Ptr(_) = inner {
+ write_type_space(out, inner);
+ if !mutable {
+ write!(out, "const");
+ }
+ } else {
+ if !mutable {
+ write!(out, "const ");
+ }
+ write_type(out, inner);
+ }
+}
+
fn write_atom(out: &mut OutFile, atom: Atom) {
match atom {
Bool => write!(out, "bool"),
- U8 => write!(out, "uint8_t"),
- U16 => write!(out, "uint16_t"),
- U32 => write!(out, "uint32_t"),
- U64 => write!(out, "uint64_t"),
- Usize => write!(out, "size_t"),
- I8 => write!(out, "int8_t"),
- I16 => write!(out, "int16_t"),
- I32 => write!(out, "int32_t"),
- I64 => write!(out, "int64_t"),
+ Char => write!(out, "char"),
+ U8 => write!(out, "::std::uint8_t"),
+ U16 => write!(out, "::std::uint16_t"),
+ U32 => write!(out, "::std::uint32_t"),
+ U64 => write!(out, "::std::uint64_t"),
+ Usize => write!(out, "::std::size_t"),
+ I8 => write!(out, "::std::int8_t"),
+ I16 => write!(out, "::std::int16_t"),
+ I32 => write!(out, "::std::int32_t"),
+ I64 => write!(out, "::std::int64_t"),
Isize => write!(out, "::rust::isize"),
F32 => write!(out, "float"),
F64 => write!(out, "double"),
@@ -936,33 +1280,62 @@ fn write_space_after_type(out: &mut OutFile, ty: &Type) {
Type::Ident(_)
| Type::RustBox(_)
| Type::UniquePtr(_)
+ | Type::SharedPtr(_)
+ | Type::WeakPtr(_)
| Type::Str(_)
| Type::CxxVector(_)
| Type::RustVec(_)
- | Type::SliceRefU8(_)
- | Type::Fn(_) => write!(out, " "),
- Type::Ref(_) => {}
- Type::Void(_) | Type::Slice(_) => unreachable!(),
+ | Type::SliceRef(_)
+ | Type::Fn(_)
+ | Type::Array(_) => write!(out, " "),
+ Type::Ref(_) | Type::Ptr(_) => {}
+ Type::Void(_) => unreachable!(),
}
}
-// Only called for legal referent types of unique_ptr and element types of
-// std::vector and Vec.
-fn to_typename(ty: &Type, types: &Types) -> String {
- match ty {
- Type::Ident(ident) => types.resolve(&ident).to_fully_qualified(),
- Type::CxxVector(ptr) => format!("::std::vector<{}>", to_typename(&ptr.inner, types)),
- _ => unreachable!(),
+#[derive(Copy, Clone)]
+enum UniquePtr<'a> {
+ Ident(&'a Ident),
+ CxxVector(&'a Ident),
+}
+
+trait ToTypename {
+ fn to_typename(&self, types: &Types) -> String;
+}
+
+impl ToTypename for Ident {
+ fn to_typename(&self, types: &Types) -> String {
+ types.resolve(self).name.to_fully_qualified()
}
}
-// Only called for legal referent types of unique_ptr and element types of
-// std::vector and Vec.
-fn to_mangled(ty: &Type, types: &Types) -> Symbol {
- match ty {
- Type::Ident(ident) => ident.to_symbol(types),
- Type::CxxVector(ptr) => to_mangled(&ptr.inner, types).prefix_with("std$vector$"),
- _ => unreachable!(),
+impl<'a> ToTypename for UniquePtr<'a> {
+ fn to_typename(&self, types: &Types) -> String {
+ match self {
+ UniquePtr::Ident(ident) => ident.to_typename(types),
+ UniquePtr::CxxVector(element) => {
+ format!("::std::vector<{}>", element.to_typename(types))
+ }
+ }
+ }
+}
+
+trait ToMangled {
+ fn to_mangled(&self, types: &Types) -> Symbol;
+}
+
+impl ToMangled for Ident {
+ fn to_mangled(&self, types: &Types) -> Symbol {
+ types.resolve(self).name.to_symbol()
+ }
+}
+
+impl<'a> ToMangled for UniquePtr<'a> {
+ fn to_mangled(&self, types: &Types) -> Symbol {
+ match self {
+ UniquePtr::Ident(ident) => ident.to_mangled(types),
+ UniquePtr::CxxVector(element) => element.to_mangled(types).prefix_with("std$vector$"),
+ }
}
}
@@ -974,233 +1347,252 @@ fn write_generic_instantiations(out: &mut OutFile) {
out.next_section();
out.set_namespace(Default::default());
out.begin_block(Block::ExternC);
- for ty in out.types {
- if let Type::RustBox(ty) = ty {
- if let Type::Ident(inner) = &ty.inner {
- out.next_section();
- write_rust_box_extern(out, &out.types.resolve(&inner));
- }
- } else if let Type::RustVec(ty) = ty {
- if let Type::Ident(inner) = &ty.inner {
- if Atom::from(&inner.rust).is_none() {
- out.next_section();
- write_rust_vec_extern(out, inner);
- }
- }
- } else if let Type::UniquePtr(ptr) = ty {
- if let Type::Ident(inner) = &ptr.inner {
- if Atom::from(&inner.rust).is_none()
- && (!out.types.aliases.contains_key(&inner.rust)
- || out.types.explicit_impls.contains(ty))
- {
- out.next_section();
- write_unique_ptr(out, inner);
- }
- }
- } else if let Type::CxxVector(ptr) = ty {
- if let Type::Ident(inner) = &ptr.inner {
- if Atom::from(&inner.rust).is_none()
- && (!out.types.aliases.contains_key(&inner.rust)
- || out.types.explicit_impls.contains(ty))
- {
- out.next_section();
- write_cxx_vector(out, ty, inner);
- }
- }
+ for impl_key in out.types.impls.keys() {
+ out.next_section();
+ match *impl_key {
+ ImplKey::RustBox(ident) => write_rust_box_extern(out, ident),
+ ImplKey::RustVec(ident) => write_rust_vec_extern(out, ident),
+ ImplKey::UniquePtr(ident) => write_unique_ptr(out, ident),
+ ImplKey::SharedPtr(ident) => write_shared_ptr(out, ident),
+ ImplKey::WeakPtr(ident) => write_weak_ptr(out, ident),
+ ImplKey::CxxVector(ident) => write_cxx_vector(out, ident),
}
}
out.end_block(Block::ExternC);
out.begin_block(Block::Namespace("rust"));
- out.begin_block(Block::InlineNamespace("cxxbridge05"));
- for ty in out.types {
- if let Type::RustBox(ty) = ty {
- if let Type::Ident(inner) = &ty.inner {
- write_rust_box_impl(out, &out.types.resolve(&inner));
- }
- } else if let Type::RustVec(ty) = ty {
- if let Type::Ident(inner) = &ty.inner {
- if Atom::from(&inner.rust).is_none() {
- write_rust_vec_impl(out, inner);
- }
- }
+ out.begin_block(Block::InlineNamespace("cxxbridge1"));
+ for impl_key in out.types.impls.keys() {
+ match *impl_key {
+ ImplKey::RustBox(ident) => write_rust_box_impl(out, ident),
+ ImplKey::RustVec(ident) => write_rust_vec_impl(out, ident),
+ _ => {}
}
}
- out.end_block(Block::InlineNamespace("cxxbridge05"));
+ out.end_block(Block::InlineNamespace("cxxbridge1"));
out.end_block(Block::Namespace("rust"));
}
-fn write_rust_box_extern(out: &mut OutFile, ident: &Pair) {
- let inner = ident.to_fully_qualified();
- let instance = ident.to_symbol();
+fn write_rust_box_extern(out: &mut OutFile, key: NamedImplKey) {
+ let resolve = out.types.resolve(&key);
+ let inner = resolve.name.to_fully_qualified();
+ let instance = resolve.name.to_symbol();
- writeln!(out, "#ifndef CXXBRIDGE05_RUST_BOX_{}", instance);
- writeln!(out, "#define CXXBRIDGE05_RUST_BOX_{}", instance);
writeln!(
out,
- "void cxxbridge05$box${}$uninit(::rust::Box<{}> *ptr) noexcept;",
+ "{} *cxxbridge1$box${}$alloc() noexcept;",
+ inner, instance,
+ );
+ writeln!(
+ out,
+ "void cxxbridge1$box${}$dealloc({} *) noexcept;",
instance, inner,
);
writeln!(
out,
- "void cxxbridge05$box${}$drop(::rust::Box<{}> *ptr) noexcept;",
+ "void cxxbridge1$box${}$drop(::rust::Box<{}> *ptr) noexcept;",
instance, inner,
);
- writeln!(out, "#endif // CXXBRIDGE05_RUST_BOX_{}", instance);
}
-fn write_rust_vec_extern(out: &mut OutFile, element: &ResolvableName) {
- let element = Type::Ident(element.clone());
- let inner = to_typename(&element, out.types);
- let instance = to_mangled(&element, out.types);
+fn write_rust_vec_extern(out: &mut OutFile, key: NamedImplKey) {
+ let element = key.rust;
+ let inner = element.to_typename(out.types);
+ let instance = element.to_mangled(out.types);
+
+ out.include.cstddef = true;
- writeln!(out, "#ifndef CXXBRIDGE05_RUST_VEC_{}", instance);
- writeln!(out, "#define CXXBRIDGE05_RUST_VEC_{}", instance);
writeln!(
out,
- "void cxxbridge05$rust_vec${}$new(const ::rust::Vec<{}> *ptr) noexcept;",
+ "void cxxbridge1$rust_vec${}$new(const ::rust::Vec<{}> *ptr) noexcept;",
instance, inner,
);
writeln!(
out,
- "void cxxbridge05$rust_vec${}$drop(::rust::Vec<{}> *ptr) noexcept;",
+ "void cxxbridge1$rust_vec${}$drop(::rust::Vec<{}> *ptr) noexcept;",
instance, inner,
);
writeln!(
out,
- "size_t cxxbridge05$rust_vec${}$len(const ::rust::Vec<{}> *ptr) noexcept;",
+ "::std::size_t cxxbridge1$rust_vec${}$len(const ::rust::Vec<{}> *ptr) noexcept;",
instance, inner,
);
writeln!(
out,
- "const {} *cxxbridge05$rust_vec${}$data(const ::rust::Vec<{0}> *ptr) noexcept;",
- inner, instance,
+ "::std::size_t cxxbridge1$rust_vec${}$capacity(const ::rust::Vec<{}> *ptr) noexcept;",
+ instance, inner,
);
writeln!(
out,
- "void cxxbridge05$rust_vec${}$reserve_total(::rust::Vec<{}> *ptr, size_t cap) noexcept;",
- instance, inner,
+ "const {} *cxxbridge1$rust_vec${}$data(const ::rust::Vec<{0}> *ptr) noexcept;",
+ inner, instance,
);
writeln!(
out,
- "void cxxbridge05$rust_vec${}$set_len(::rust::Vec<{}> *ptr, size_t len) noexcept;",
+ "void cxxbridge1$rust_vec${}$reserve_total(::rust::Vec<{}> *ptr, ::std::size_t cap) noexcept;",
instance, inner,
);
writeln!(
out,
- "size_t cxxbridge05$rust_vec${}$stride() noexcept;",
- instance,
+ "void cxxbridge1$rust_vec${}$set_len(::rust::Vec<{}> *ptr, ::std::size_t len) noexcept;",
+ instance, inner,
);
- writeln!(out, "#endif // CXXBRIDGE05_RUST_VEC_{}", instance);
}
-fn write_rust_box_impl(out: &mut OutFile, ident: &Pair) {
- let inner = ident.to_fully_qualified();
- let instance = ident.to_symbol();
+fn write_rust_box_impl(out: &mut OutFile, key: NamedImplKey) {
+ let resolve = out.types.resolve(&key);
+ let inner = resolve.name.to_fully_qualified();
+ let instance = resolve.name.to_symbol();
+
+ writeln!(out, "template <>");
+ begin_function_definition(out);
+ writeln!(
+ out,
+ "{} *Box<{}>::allocation::alloc() noexcept {{",
+ inner, inner,
+ );
+ writeln!(out, " return cxxbridge1$box${}$alloc();", instance);
+ writeln!(out, "}}");
writeln!(out, "template <>");
- writeln!(out, "void Box<{}>::uninit() noexcept {{", inner);
- writeln!(out, " cxxbridge05$box${}$uninit(this);", instance);
+ begin_function_definition(out);
+ writeln!(
+ out,
+ "void Box<{}>::allocation::dealloc({} *ptr) noexcept {{",
+ inner, inner,
+ );
+ writeln!(out, " cxxbridge1$box${}$dealloc(ptr);", instance);
writeln!(out, "}}");
writeln!(out, "template <>");
+ begin_function_definition(out);
writeln!(out, "void Box<{}>::drop() noexcept {{", inner);
- writeln!(out, " cxxbridge05$box${}$drop(this);", instance);
+ writeln!(out, " cxxbridge1$box${}$drop(this);", instance);
writeln!(out, "}}");
}
-fn write_rust_vec_impl(out: &mut OutFile, element: &ResolvableName) {
- let element = Type::Ident(element.clone());
- let inner = to_typename(&element, out.types);
- let instance = to_mangled(&element, out.types);
+fn write_rust_vec_impl(out: &mut OutFile, key: NamedImplKey) {
+ let element = key.rust;
+ let inner = element.to_typename(out.types);
+ let instance = element.to_mangled(out.types);
+
+ out.include.cstddef = true;
writeln!(out, "template <>");
+ begin_function_definition(out);
writeln!(out, "Vec<{}>::Vec() noexcept {{", inner);
- writeln!(out, " cxxbridge05$rust_vec${}$new(this);", instance);
+ writeln!(out, " cxxbridge1$rust_vec${}$new(this);", instance);
writeln!(out, "}}");
writeln!(out, "template <>");
+ begin_function_definition(out);
writeln!(out, "void Vec<{}>::drop() noexcept {{", inner);
- writeln!(
- out,
- " return cxxbridge05$rust_vec${}$drop(this);",
- instance,
- );
+ writeln!(out, " return cxxbridge1$rust_vec${}$drop(this);", instance);
writeln!(out, "}}");
writeln!(out, "template <>");
- writeln!(out, "size_t Vec<{}>::size() const noexcept {{", inner);
- writeln!(out, " return cxxbridge05$rust_vec${}$len(this);", instance);
+ begin_function_definition(out);
+ writeln!(
+ out,
+ "::std::size_t Vec<{}>::size() const noexcept {{",
+ inner,
+ );
+ writeln!(out, " return cxxbridge1$rust_vec${}$len(this);", instance);
writeln!(out, "}}");
writeln!(out, "template <>");
- writeln!(out, "const {} *Vec<{0}>::data() const noexcept {{", inner);
+ begin_function_definition(out);
writeln!(
out,
- " return cxxbridge05$rust_vec${}$data(this);",
+ "::std::size_t Vec<{}>::capacity() const noexcept {{",
+ inner,
+ );
+ writeln!(
+ out,
+ " return cxxbridge1$rust_vec${}$capacity(this);",
instance,
);
writeln!(out, "}}");
writeln!(out, "template <>");
+ begin_function_definition(out);
+ writeln!(out, "const {} *Vec<{0}>::data() const noexcept {{", inner);
+ writeln!(out, " return cxxbridge1$rust_vec${}$data(this);", instance);
+ writeln!(out, "}}");
+
+ writeln!(out, "template <>");
+ begin_function_definition(out);
writeln!(
out,
- "void Vec<{}>::reserve_total(size_t cap) noexcept {{",
+ "void Vec<{}>::reserve_total(::std::size_t cap) noexcept {{",
inner,
);
writeln!(
out,
- " return cxxbridge05$rust_vec${}$reserve_total(this, cap);",
+ " return cxxbridge1$rust_vec${}$reserve_total(this, cap);",
instance,
);
writeln!(out, "}}");
writeln!(out, "template <>");
- writeln!(out, "void Vec<{}>::set_len(size_t len) noexcept {{", inner);
+ begin_function_definition(out);
writeln!(
out,
- " return cxxbridge05$rust_vec${}$set_len(this, len);",
+ "void Vec<{}>::set_len(::std::size_t len) noexcept {{",
+ inner,
+ );
+ writeln!(
+ out,
+ " return cxxbridge1$rust_vec${}$set_len(this, len);",
instance,
);
writeln!(out, "}}");
-
- writeln!(out, "template <>");
- writeln!(out, "size_t Vec<{}>::stride() noexcept {{", inner);
- writeln!(out, " return cxxbridge05$rust_vec${}$stride();", instance);
- writeln!(out, "}}");
}
-fn write_unique_ptr(out: &mut OutFile, ident: &ResolvableName) {
- let ty = Type::Ident(ident.clone());
- let instance = to_mangled(&ty, out.types);
-
- writeln!(out, "#ifndef CXXBRIDGE05_UNIQUE_PTR_{}", instance);
- writeln!(out, "#define CXXBRIDGE05_UNIQUE_PTR_{}", instance);
-
- write_unique_ptr_common(out, &ty);
-
- writeln!(out, "#endif // CXXBRIDGE05_UNIQUE_PTR_{}", instance);
+fn write_unique_ptr(out: &mut OutFile, key: NamedImplKey) {
+ let ty = UniquePtr::Ident(key.rust);
+ write_unique_ptr_common(out, ty);
}
// Shared by UniquePtr<T> and UniquePtr<CxxVector<T>>.
-fn write_unique_ptr_common(out: &mut OutFile, ty: &Type) {
+fn write_unique_ptr_common(out: &mut OutFile, ty: UniquePtr) {
out.include.new = true;
out.include.utility = true;
- let inner = to_typename(ty, out.types);
- let instance = to_mangled(ty, out.types);
+ let inner = ty.to_typename(out.types);
+ let instance = ty.to_mangled(out.types);
let can_construct_from_value = match ty {
// Some aliases are to opaque types; some are to trivial types. We can't
// 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.
- Type::Ident(ident) => {
- out.types.structs.contains_key(&ident.rust)
- || out.types.aliases.contains_key(&ident.rust)
+ UniquePtr::Ident(ident) => {
+ out.types.structs.contains_key(ident)
+ || out.types.enums.contains_key(ident)
+ || out.types.aliases.contains_key(ident)
}
- _ => false,
+ UniquePtr::CxxVector(_) => false,
+ };
+
+ let conditional_delete = match ty {
+ UniquePtr::Ident(ident) => {
+ !out.types.structs.contains_key(ident) && !out.types.enums.contains_key(ident)
+ }
+ UniquePtr::CxxVector(_) => false,
};
+ if conditional_delete {
+ out.builtin.is_complete = true;
+ let definition = match ty {
+ UniquePtr::Ident(ty) => &out.types.resolve(ty).name.cxx,
+ UniquePtr::CxxVector(_) => unreachable!(),
+ };
+ writeln!(
+ out,
+ "static_assert(::rust::detail::is_complete<{}>::value, \"definition of {} is required\");",
+ inner, definition,
+ );
+ }
writeln!(
out,
"static_assert(sizeof(::std::unique_ptr<{}>) == sizeof(void *), \"\");",
@@ -1213,77 +1605,220 @@ fn write_unique_ptr_common(out: &mut OutFile, ty: &Type) {
);
writeln!(
out,
- "void cxxbridge05$unique_ptr${}$null(::std::unique_ptr<{}> *ptr) noexcept {{",
+ "void cxxbridge1$unique_ptr${}$null(::std::unique_ptr<{}> *ptr) noexcept {{",
instance, inner,
);
- writeln!(out, " new (ptr) ::std::unique_ptr<{}>();", inner);
+ writeln!(out, " ::new (ptr) ::std::unique_ptr<{}>();", inner);
writeln!(out, "}}");
if can_construct_from_value {
+ out.builtin.maybe_uninit = true;
writeln!(
out,
- "void cxxbridge05$unique_ptr${}$new(::std::unique_ptr<{}> *ptr, {} *value) noexcept {{",
- instance, inner, inner,
+ "{} *cxxbridge1$unique_ptr${}$uninit(::std::unique_ptr<{}> *ptr) noexcept {{",
+ inner, instance, inner,
);
writeln!(
out,
- " new (ptr) ::std::unique_ptr<{}>(new {}(::std::move(*value)));",
- inner, inner,
+ " {} *uninit = reinterpret_cast<{} *>(new ::rust::MaybeUninit<{}>);",
+ inner, inner, inner,
);
+ writeln!(out, " ::new (ptr) ::std::unique_ptr<{}>(uninit);", inner);
+ writeln!(out, " return uninit;");
writeln!(out, "}}");
}
writeln!(
out,
- "void cxxbridge05$unique_ptr${}$raw(::std::unique_ptr<{}> *ptr, {} *raw) noexcept {{",
+ "void cxxbridge1$unique_ptr${}$raw(::std::unique_ptr<{}> *ptr, {} *raw) noexcept {{",
instance, inner, inner,
);
- writeln!(out, " new (ptr) ::std::unique_ptr<{}>(raw);", inner);
+ writeln!(out, " ::new (ptr) ::std::unique_ptr<{}>(raw);", inner);
writeln!(out, "}}");
writeln!(
out,
- "const {} *cxxbridge05$unique_ptr${}$get(const ::std::unique_ptr<{}>& ptr) noexcept {{",
+ "const {} *cxxbridge1$unique_ptr${}$get(const ::std::unique_ptr<{}>& ptr) noexcept {{",
inner, instance, inner,
);
writeln!(out, " return ptr.get();");
writeln!(out, "}}");
writeln!(
out,
- "{} *cxxbridge05$unique_ptr${}$release(::std::unique_ptr<{}>& ptr) noexcept {{",
+ "{} *cxxbridge1$unique_ptr${}$release(::std::unique_ptr<{}>& ptr) noexcept {{",
inner, instance, inner,
);
writeln!(out, " return ptr.release();");
writeln!(out, "}}");
writeln!(
out,
- "void cxxbridge05$unique_ptr${}$drop(::std::unique_ptr<{}> *ptr) noexcept {{",
+ "void cxxbridge1$unique_ptr${}$drop(::std::unique_ptr<{}> *ptr) noexcept {{",
instance, inner,
);
- writeln!(out, " ptr->~unique_ptr();");
+ if conditional_delete {
+ out.builtin.deleter_if = true;
+ writeln!(
+ out,
+ " ::rust::deleter_if<::rust::detail::is_complete<{}>::value>{{}}(ptr);",
+ inner,
+ );
+ } else {
+ writeln!(out, " ptr->~unique_ptr();");
+ }
writeln!(out, "}}");
}
-fn write_cxx_vector(out: &mut OutFile, vector_ty: &Type, element: &ResolvableName) {
- let element = Type::Ident(element.clone());
- let inner = to_typename(&element, out.types);
- let instance = to_mangled(&element, out.types);
+fn write_shared_ptr(out: &mut OutFile, key: NamedImplKey) {
+ let ident = key.rust;
+ let resolve = out.types.resolve(ident);
+ let inner = resolve.name.to_fully_qualified();
+ let instance = resolve.name.to_symbol();
+
+ out.include.new = true;
+ out.include.utility = true;
- writeln!(out, "#ifndef CXXBRIDGE05_VECTOR_{}", instance);
- writeln!(out, "#define CXXBRIDGE05_VECTOR_{}", instance);
+ // Some aliases are to opaque types; some are to trivial types. We can't
+ // 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);
+
+ writeln!(
+ out,
+ "static_assert(sizeof(::std::shared_ptr<{}>) == 2 * sizeof(void *), \"\");",
+ inner,
+ );
writeln!(
out,
- "size_t cxxbridge05$std$vector${}$size(const ::std::vector<{}> &s) noexcept {{",
+ "static_assert(alignof(::std::shared_ptr<{}>) == alignof(void *), \"\");",
+ inner,
+ );
+ writeln!(
+ out,
+ "void cxxbridge1$shared_ptr${}$null(::std::shared_ptr<{}> *ptr) noexcept {{",
instance, inner,
);
- writeln!(out, " return s.size();");
+ writeln!(out, " ::new (ptr) ::std::shared_ptr<{}>();", inner);
writeln!(out, "}}");
+ if can_construct_from_value {
+ out.builtin.maybe_uninit = true;
+ writeln!(
+ out,
+ "{} *cxxbridge1$shared_ptr${}$uninit(::std::shared_ptr<{}> *ptr) noexcept {{",
+ inner, instance, inner,
+ );
+ writeln!(
+ out,
+ " {} *uninit = reinterpret_cast<{} *>(new ::rust::MaybeUninit<{}>);",
+ inner, inner, inner,
+ );
+ writeln!(out, " ::new (ptr) ::std::shared_ptr<{}>(uninit);", inner);
+ writeln!(out, " return uninit;");
+ writeln!(out, "}}");
+ }
writeln!(
out,
- "const {} *cxxbridge05$std$vector${}$get_unchecked(const ::std::vector<{}> &s, size_t pos) noexcept {{",
+ "void cxxbridge1$shared_ptr${}$clone(const ::std::shared_ptr<{}>& self, ::std::shared_ptr<{}> *ptr) noexcept {{",
+ instance, inner, inner,
+ );
+ writeln!(out, " ::new (ptr) ::std::shared_ptr<{}>(self);", inner);
+ writeln!(out, "}}");
+ writeln!(
+ out,
+ "const {} *cxxbridge1$shared_ptr${}$get(const ::std::shared_ptr<{}>& self) noexcept {{",
inner, instance, inner,
);
- writeln!(out, " return &s[pos];");
+ writeln!(out, " return self.get();");
+ writeln!(out, "}}");
+ writeln!(
+ out,
+ "void cxxbridge1$shared_ptr${}$drop(::std::shared_ptr<{}> *self) noexcept {{",
+ instance, inner,
+ );
+ writeln!(out, " self->~shared_ptr();");
+ writeln!(out, "}}");
+}
+
+fn write_weak_ptr(out: &mut OutFile, key: NamedImplKey) {
+ let resolve = out.types.resolve(&key);
+ let inner = resolve.name.to_fully_qualified();
+ let instance = resolve.name.to_symbol();
+
+ out.include.new = true;
+ out.include.utility = true;
+
+ writeln!(
+ out,
+ "static_assert(sizeof(::std::weak_ptr<{}>) == 2 * sizeof(void *), \"\");",
+ inner,
+ );
+ writeln!(
+ out,
+ "static_assert(alignof(::std::weak_ptr<{}>) == alignof(void *), \"\");",
+ inner,
+ );
+ writeln!(
+ out,
+ "void cxxbridge1$weak_ptr${}$null(::std::weak_ptr<{}> *ptr) noexcept {{",
+ instance, inner,
+ );
+ writeln!(out, " ::new (ptr) ::std::weak_ptr<{}>();", inner);
writeln!(out, "}}");
+ writeln!(
+ out,
+ "void cxxbridge1$weak_ptr${}$clone(const ::std::weak_ptr<{}>& self, ::std::weak_ptr<{}> *ptr) noexcept {{",
+ instance, inner, inner,
+ );
+ writeln!(out, " ::new (ptr) ::std::weak_ptr<{}>(self);", inner);
+ writeln!(out, "}}");
+ writeln!(
+ out,
+ "void cxxbridge1$weak_ptr${}$downgrade(const ::std::shared_ptr<{}>& shared, ::std::weak_ptr<{}> *weak) noexcept {{",
+ instance, inner, inner,
+ );
+ writeln!(out, " ::new (weak) ::std::weak_ptr<{}>(shared);", inner);
+ writeln!(out, "}}");
+ writeln!(
+ out,
+ "void cxxbridge1$weak_ptr${}$upgrade(const ::std::weak_ptr<{}>& weak, ::std::shared_ptr<{}> *shared) noexcept {{",
+ instance, inner, inner,
+ );
+ writeln!(
+ out,
+ " ::new (shared) ::std::shared_ptr<{}>(weak.lock());",
+ inner,
+ );
+ writeln!(out, "}}");
+ writeln!(
+ out,
+ "void cxxbridge1$weak_ptr${}$drop(::std::weak_ptr<{}> *self) noexcept {{",
+ instance, inner,
+ );
+ writeln!(out, " self->~weak_ptr();");
+ writeln!(out, "}}");
+}
+
+fn write_cxx_vector(out: &mut OutFile, key: NamedImplKey) {
+ let element = key.rust;
+ let inner = element.to_typename(out.types);
+ let instance = element.to_mangled(out.types);
- write_unique_ptr_common(out, vector_ty);
+ out.include.cstddef = true;
+
+ writeln!(
+ out,
+ "::std::size_t cxxbridge1$std$vector${}$size(const ::std::vector<{}> &s) noexcept {{",
+ instance, inner,
+ );
+ writeln!(out, " return s.size();");
+ writeln!(out, "}}");
+ writeln!(
+ out,
+ "{} *cxxbridge1$std$vector${}$get_unchecked(::std::vector<{}> *s, ::std::size_t pos) noexcept {{",
+ inner, instance, inner,
+ );
+ writeln!(out, " return &(*s)[pos];");
+ writeln!(out, "}}");
- writeln!(out, "#endif // CXXBRIDGE05_VECTOR_{}", instance);
+ out.include.memory = true;
+ write_unique_ptr_common(out, UniquePtr::CxxVector(element));
}
diff --git a/include/cxx.h b/include/cxx.h
index 7dfbbcaf..cdc63fbf 100644
--- a/include/cxx.h
+++ b/include/cxx.h
@@ -1,9 +1,13 @@
#pragma once
+#include <algorithm>
#include <array>
+#include <cassert>
#include <cstddef>
#include <cstdint>
#include <exception>
+#include <initializer_list>
#include <iosfwd>
+#include <iterator>
#include <new>
#include <stdexcept>
#include <string>
@@ -12,10 +16,12 @@
#include <vector>
#if defined(_WIN32)
#include <basetsd.h>
+#else
+#include <sys/types.h>
#endif
namespace rust {
-inline namespace cxxbridge05 {
+inline namespace cxxbridge1 {
struct unsafe_bitcopy_t;
@@ -24,8 +30,9 @@ template <typename T>
class impl;
}
-#ifndef CXXBRIDGE05_RUST_STRING
-#define CXXBRIDGE05_RUST_STRING
+#ifndef CXXBRIDGE1_RUST_STRING
+#define CXXBRIDGE1_RUST_STRING
+// https://cxx.rs/binding/string.html
class String final {
public:
String() noexcept;
@@ -35,102 +42,213 @@ public:
String(const std::string &);
String(const char *);
- String(const char *, size_t);
+ String(const char *, std::size_t);
- String &operator=(const String &) noexcept;
- String &operator=(String &&) noexcept;
+ String &operator=(const String &) &noexcept;
+ String &operator=(String &&) &noexcept;
explicit operator std::string() const;
// Note: no null terminator.
const char *data() const noexcept;
- size_t size() const noexcept;
- size_t length() const noexcept;
+ std::size_t size() const noexcept;
+ std::size_t length() const noexcept;
+
+ const char *c_str() noexcept;
+
+ using iterator = char *;
+ iterator begin() noexcept;
+ iterator end() noexcept;
+
+ using const_iterator = const char *;
+ const_iterator begin() const noexcept;
+ const_iterator end() const noexcept;
+ const_iterator cbegin() const noexcept;
+ const_iterator cend() const noexcept;
+
+ bool operator==(const String &) const noexcept;
+ bool operator!=(const String &) const noexcept;
+ bool operator<(const String &) const noexcept;
+ bool operator<=(const String &) const noexcept;
+ bool operator>(const String &) const noexcept;
+ bool operator>=(const String &) const noexcept;
+
+ void swap(String &) noexcept;
// Internal API only intended for the cxxbridge code generator.
String(unsafe_bitcopy_t, const String &) noexcept;
private:
+ friend void swap(String &lhs, String &rhs) noexcept { lhs.swap(rhs); }
+
// Size and alignment statically verified by rust_string.rs.
- std::array<uintptr_t, 3> repr;
+ std::array<std::uintptr_t, 3> repr;
};
-#endif // CXXBRIDGE05_RUST_STRING
+#endif // CXXBRIDGE1_RUST_STRING
-#ifndef CXXBRIDGE05_RUST_STR
+#ifndef CXXBRIDGE1_RUST_STR
+#define CXXBRIDGE1_RUST_STR
+// https://cxx.rs/binding/str.html
class Str final {
public:
Str() noexcept;
+ Str(const String &) noexcept;
Str(const std::string &);
Str(const char *);
- Str(const char *, size_t);
- Str(std::string &&) = delete;
+ Str(const char *, std::size_t);
- Str &operator=(const Str &) noexcept = default;
+ Str &operator=(const Str &) &noexcept = default;
explicit operator std::string() const;
// Note: no null terminator.
const char *data() const noexcept;
- size_t size() const noexcept;
- size_t length() const noexcept;
+ std::size_t size() const noexcept;
+ std::size_t length() const noexcept;
// Important in order for System V ABI to pass in registers.
Str(const Str &) noexcept = default;
~Str() noexcept = default;
+ using iterator = const char *;
+ using const_iterator = const char *;
+ const_iterator begin() const noexcept;
+ const_iterator end() const noexcept;
+ const_iterator cbegin() const noexcept;
+ const_iterator cend() const noexcept;
+
+ bool operator==(const Str &) const noexcept;
+ bool operator!=(const Str &) const noexcept;
+ bool operator<(const Str &) const noexcept;
+ bool operator<=(const Str &) const noexcept;
+ bool operator>(const Str &) const noexcept;
+ bool operator>=(const Str &) const noexcept;
+
+ void swap(Str &) noexcept;
+
private:
+ class uninit;
+ Str(uninit) noexcept;
friend impl<Str>;
- // Not necessarily ABI compatible with &str. Codegen will translate to
- // cxx::rust_str::RustStr which matches this layout.
- const char *ptr;
- size_t len;
+
+ std::array<std::uintptr_t, 2> repr;
};
-#endif // CXXBRIDGE05_RUST_STR
+#endif // CXXBRIDGE1_RUST_STR
-#ifndef CXXBRIDGE05_RUST_SLICE
+#ifndef CXXBRIDGE1_RUST_SLICE
+namespace detail {
+template <bool>
+struct copy_assignable_if {};
+
+template <>
+struct copy_assignable_if<false> {
+ copy_assignable_if() noexcept = default;
+ copy_assignable_if(const copy_assignable_if &) noexcept = default;
+ copy_assignable_if &operator=(const copy_assignable_if &) &noexcept = delete;
+ copy_assignable_if &operator=(copy_assignable_if &&) &noexcept = default;
+};
+} // namespace detail
+
+// https://cxx.rs/binding/slice.html
template <typename T>
-class Slice final {
+class Slice final
+ : private detail::copy_assignable_if<std::is_const<T>::value> {
public:
+ using value_type = T;
+
Slice() noexcept;
- Slice(const T *, size_t count) noexcept;
+ Slice(T *, std::size_t count) noexcept;
- Slice &operator=(const Slice<T> &) noexcept = default;
+ Slice &operator=(const Slice<T> &) &noexcept = default;
+ Slice &operator=(Slice<T> &&) &noexcept = default;
- const T *data() const noexcept;
- size_t size() const noexcept;
- size_t length() const noexcept;
+ T *data() const noexcept;
+ std::size_t size() const noexcept;
+ std::size_t length() const noexcept;
+ bool empty() const noexcept;
+
+ T &operator[](std::size_t n) const noexcept;
+ T &at(std::size_t n) const;
+ T &front() const noexcept;
+ T &back() const noexcept;
// Important in order for System V ABI to pass in registers.
Slice(const Slice<T> &) noexcept = default;
~Slice() noexcept = default;
+ class iterator;
+ iterator begin() const noexcept;
+ iterator end() const noexcept;
+
+ void swap(Slice &) noexcept;
+
private:
+ class uninit;
+ Slice(uninit) noexcept;
friend impl<Slice>;
- // Not necessarily ABI compatible with &[T]. Codegen will translate to
- // cxx::rust_sliceu8::RustSliceU8 which matches this layout.
- const T *ptr;
- size_t len;
+ friend void sliceInit(void *, const void *, std::size_t) noexcept;
+ friend void *slicePtr(const void *) noexcept;
+ friend std::size_t sliceLen(const void *) noexcept;
+
+ std::array<std::uintptr_t, 2> repr;
};
-#endif // CXXBRIDGE05_RUST_SLICE
-#ifndef CXXBRIDGE05_RUST_BOX
template <typename T>
-class Box final {
+class Slice<T>::iterator final {
public:
+ using iterator_category = std::random_access_iterator_tag;
using value_type = T;
+ using difference_type = std::ptrdiff_t;
+ using pointer = typename std::add_pointer<T>::type;
+ using reference = typename std::add_lvalue_reference<T>::type;
+
+ reference operator*() const noexcept;
+ pointer operator->() const noexcept;
+ reference operator[](difference_type) const noexcept;
+
+ iterator &operator++() noexcept;
+ iterator operator++(int) noexcept;
+ iterator &operator--() noexcept;
+ iterator operator--(int) noexcept;
+
+ iterator &operator+=(difference_type) noexcept;
+ iterator &operator-=(difference_type) noexcept;
+ iterator operator+(difference_type) const noexcept;
+ iterator operator-(difference_type) const noexcept;
+ difference_type operator-(const iterator &) const noexcept;
+
+ bool operator==(const iterator &) const noexcept;
+ bool operator!=(const iterator &) const noexcept;
+ bool operator<(const iterator &) const noexcept;
+ bool operator<=(const iterator &) const noexcept;
+ bool operator>(const iterator &) const noexcept;
+ bool operator>=(const iterator &) const noexcept;
+
+private:
+ friend class Slice;
+ void *pos;
+ std::size_t stride;
+};
+#endif // CXXBRIDGE1_RUST_SLICE
+
+#ifndef CXXBRIDGE1_RUST_BOX
+// https://cxx.rs/binding/box.html
+template <typename T>
+class Box final {
+public:
+ using element_type = T;
using const_pointer =
typename std::add_pointer<typename std::add_const<T>::type>::type;
using pointer = typename std::add_pointer<T>::type;
- Box(const Box &);
+ Box() = delete;
Box(Box &&) noexcept;
~Box() noexcept;
explicit Box(const T &);
explicit Box(T &&);
- Box &operator=(const Box &);
- Box &operator=(Box &&) noexcept;
+ Box &operator=(Box &&) &noexcept;
const T *operator->() const noexcept;
const T &operator*() const noexcept;
@@ -140,118 +258,121 @@ public:
template <typename... Fields>
static Box in_place(Fields &&...);
+ void swap(Box &) noexcept;
+
// Important: requires that `raw` came from an into_raw call. Do not pass a
// pointer from `new` or any other source.
static Box from_raw(T *) noexcept;
T *into_raw() noexcept;
+ /* Deprecated */ using value_type = element_type;
+
private:
- Box() noexcept;
- void uninit() noexcept;
+ class uninit;
+ class allocation;
+ Box(uninit) noexcept;
void drop() noexcept;
+
+ friend void swap(Box &lhs, Box &rhs) noexcept { lhs.swap(rhs); }
+
T *ptr;
};
-#endif // CXXBRIDGE05_RUST_BOX
+#endif // CXXBRIDGE1_RUST_BOX
-#ifndef CXXBRIDGE05_RUST_VEC
+#ifndef CXXBRIDGE1_RUST_VEC
+// https://cxx.rs/binding/vec.html
template <typename T>
class Vec final {
public:
using value_type = T;
Vec() noexcept;
+ Vec(std::initializer_list<T>);
+ Vec(const Vec &);
Vec(Vec &&) noexcept;
~Vec() noexcept;
- Vec &operator=(Vec &&) noexcept;
+ Vec &operator=(Vec &&) &noexcept;
+ Vec &operator=(const Vec &) &;
- size_t size() const noexcept;
+ std::size_t size() const noexcept;
bool empty() const noexcept;
const T *data() const noexcept;
T *data() noexcept;
+ std::size_t capacity() const noexcept;
- const T &operator[](size_t n) const noexcept;
- const T &at(size_t n) const;
+ const T &operator[](std::size_t n) const noexcept;
+ const T &at(std::size_t n) const;
+ const T &front() const noexcept;
+ const T &back() const noexcept;
- const T &front() const;
- const T &back() const;
+ T &operator[](std::size_t n) noexcept;
+ T &at(std::size_t n);
+ T &front() noexcept;
+ T &back() noexcept;
- void reserve(size_t new_cap);
+ void reserve(std::size_t new_cap);
void push_back(const T &value);
void push_back(T &&value);
- template <class... Args>
- void emplace_back(Args &&... args);
-
- class const_iterator final {
- public:
- using difference_type = ptrdiff_t;
- using value_type = typename std::add_const<T>::type;
- using pointer =
- typename std::add_pointer<typename std::add_const<T>::type>::type;
- using reference = typename std::add_lvalue_reference<
- typename std::add_const<T>::type>::type;
- using iterator_category = std::forward_iterator_tag;
-
- const T &operator*() const noexcept;
- const T *operator->() const noexcept;
- const_iterator &operator++() noexcept;
- const_iterator operator++(int) noexcept;
- bool operator==(const const_iterator &) const noexcept;
- bool operator!=(const const_iterator &) const noexcept;
-
- private:
- friend class Vec;
- const void *pos;
- size_t stride;
- };
+ template <typename... Args>
+ void emplace_back(Args &&...args);
+ using iterator = typename Slice<T>::iterator;
+ iterator begin() noexcept;
+ iterator end() noexcept;
+
+ using const_iterator = typename Slice<const T>::iterator;
const_iterator begin() const noexcept;
const_iterator end() const noexcept;
+ const_iterator cbegin() const noexcept;
+ const_iterator cend() const noexcept;
+
+ void swap(Vec &) noexcept;
// Internal API only intended for the cxxbridge code generator.
Vec(unsafe_bitcopy_t, const Vec &) noexcept;
private:
- static size_t stride() noexcept;
- void reserve_total(size_t cap) noexcept;
- void set_len(size_t len) noexcept;
+ void reserve_total(std::size_t cap) noexcept;
+ void set_len(std::size_t len) noexcept;
void drop() noexcept;
+ friend void swap(Vec &lhs, Vec &rhs) noexcept { lhs.swap(rhs); }
+
// Size and alignment statically verified by rust_vec.rs.
- std::array<uintptr_t, 3> repr;
+ std::array<std::uintptr_t, 3> repr;
};
-#endif // CXXBRIDGE05_RUST_VEC
+#endif // CXXBRIDGE1_RUST_VEC
-#ifndef CXXBRIDGE05_RUST_FN
-template <typename Signature, bool Throws = false>
+#ifndef CXXBRIDGE1_RUST_FN
+// https://cxx.rs/binding/fn.html
+template <typename Signature>
class Fn;
-template <typename Ret, typename... Args, bool Throws>
-class Fn<Ret(Args...), Throws> final {
+template <typename Ret, typename... Args>
+class Fn<Ret(Args...)> final {
public:
- Ret operator()(Args... args) const noexcept(!Throws);
+ Ret operator()(Args... args) const noexcept;
Fn operator*() const noexcept;
private:
- Ret (*trampoline)(Args..., void *fn) noexcept(!Throws);
+ Ret (*trampoline)(Args..., void *fn) noexcept;
void *fn;
};
+#endif // CXXBRIDGE1_RUST_FN
-template <typename Signature>
-using TryFn = Fn<Signature, true>;
-#endif // CXXBRIDGE05_RUST_FN
-
-#ifndef CXXBRIDGE05_RUST_ERROR
-#define CXXBRIDGE05_RUST_ERROR
+#ifndef CXXBRIDGE1_RUST_ERROR
+#define CXXBRIDGE1_RUST_ERROR
+// https://cxx.rs/binding/result.html
class Error final : public std::exception {
public:
Error(const Error &);
Error(Error &&) noexcept;
- ~Error() noexcept;
+ ~Error() noexcept override;
- Error &operator=(const Error &);
- Error &operator=(Error &&) noexcept;
+ Error &operator=(const Error &) &;
+ Error &operator=(Error &&) &noexcept;
const char *what() const noexcept override;
@@ -259,22 +380,38 @@ private:
Error() noexcept = default;
friend impl<Error>;
const char *msg;
- size_t len;
+ std::size_t len;
};
-#endif // CXXBRIDGE05_RUST_ERROR
+#endif // CXXBRIDGE1_RUST_ERROR
-#ifndef CXXBRIDGE05_RUST_ISIZE
-#define CXXBRIDGE05_RUST_ISIZE
+#ifndef CXXBRIDGE1_RUST_ISIZE
+#define CXXBRIDGE1_RUST_ISIZE
#if defined(_WIN32)
using isize = SSIZE_T;
#else
using isize = ssize_t;
#endif
-#endif // CXXBRIDGE05_RUST_ISIZE
+#endif // CXXBRIDGE1_RUST_ISIZE
std::ostream &operator<<(std::ostream &, const String &);
std::ostream &operator<<(std::ostream &, const Str &);
+#ifndef CXXBRIDGE1_RUST_OPAQUE
+#define CXXBRIDGE1_RUST_OPAQUE
+// Base class of generated opaque Rust types.
+class Opaque {
+public:
+ Opaque() = delete;
+ Opaque(const Opaque &) = delete;
+ ~Opaque() = delete;
+};
+#endif // CXXBRIDGE1_RUST_OPAQUE
+
+template <typename T>
+std::size_t size_of();
+template <typename T>
+std::size_t align_of();
+
// IsRelocatable<T> is used in assertions that a C++ type passed by value
// between Rust and C++ is soundly relocatable by Rust.
//
@@ -296,20 +433,30 @@ std::ostream &operator<<(std::ostream &, const Str &);
template <typename T>
struct IsRelocatable;
+using u8 = std::uint8_t;
+using u16 = std::uint16_t;
+using u32 = std::uint32_t;
+using u64 = std::uint64_t;
+using usize = std::size_t; // see static asserts in cxx.cc
+using i8 = std::int8_t;
+using i16 = std::int16_t;
+using i32 = std::int32_t;
+using i64 = std::int64_t;
+using f32 = float;
+using f64 = double;
+
// Snake case aliases for use in code that uses this style for type names.
using string = String;
using str = Str;
-template <class T>
+template <typename T>
using slice = Slice<T>;
-template <class T>
+template <typename T>
using box = Box<T>;
-template <class T>
+template <typename T>
using vec = Vec<T>;
using error = Error;
-template <typename Signature, bool Throws = false>
-using fn = Fn<Signature, Throws>;
template <typename Signature>
-using try_fn = TryFn<Signature>;
+using fn = Fn<Signature>;
template <typename T>
using is_relocatable = IsRelocatable<T>;
@@ -318,71 +465,254 @@ using is_relocatable = IsRelocatable<T>;
////////////////////////////////////////////////////////////////////////////////
/// end public API, begin implementation details
-#ifndef CXXBRIDGE05_PANIC
-#define CXXBRIDGE05_PANIC
+#ifndef CXXBRIDGE1_PANIC
+#define CXXBRIDGE1_PANIC
template <typename Exception>
void panic [[noreturn]] (const char *msg);
-#endif // CXXBRIDGE05_PANIC
+#endif // CXXBRIDGE1_PANIC
-#ifndef CXXBRIDGE05_RUST_FN
-#define CXXBRIDGE05_RUST_FN
-template <typename Ret, typename... Args, bool Throws>
-Ret Fn<Ret(Args...), Throws>::operator()(Args... args) const noexcept(!Throws) {
+#ifndef CXXBRIDGE1_RUST_FN
+#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);
}
-template <typename Ret, typename... Args, bool Throws>
-Fn<Ret(Args...), Throws> Fn<Ret(Args...), Throws>::operator*() const noexcept {
+template <typename Ret, typename... Args>
+Fn<Ret(Args...)> Fn<Ret(Args...)>::operator*() const noexcept {
return *this;
}
-#endif // CXXBRIDGE05_RUST_FN
+#endif // CXXBRIDGE1_RUST_FN
-#ifndef CXXBRIDGE05_RUST_BITCOPY
-#define CXXBRIDGE05_RUST_BITCOPY
+#ifndef CXXBRIDGE1_RUST_BITCOPY_T
+#define CXXBRIDGE1_RUST_BITCOPY_T
struct unsafe_bitcopy_t final {
explicit unsafe_bitcopy_t() = default;
};
+#endif // CXXBRIDGE1_RUST_BITCOPY_T
+#ifndef CXXBRIDGE1_RUST_BITCOPY
+#define CXXBRIDGE1_RUST_BITCOPY
constexpr unsafe_bitcopy_t unsafe_bitcopy{};
-#endif // CXXBRIDGE05_RUST_BITCOPY
+#endif // CXXBRIDGE1_RUST_BITCOPY
-#ifndef CXXBRIDGE05_RUST_STR
-#define CXXBRIDGE05_RUST_STR
-inline const char *Str::data() const noexcept { return this->ptr; }
+#ifndef CXXBRIDGE1_RUST_SLICE
+#define CXXBRIDGE1_RUST_SLICE
+template <typename T>
+Slice<T>::Slice() noexcept {
+ sliceInit(this, reinterpret_cast<void *>(align_of<T>()), 0);
+}
-inline size_t Str::size() const noexcept { return this->len; }
+template <typename T>
+Slice<T>::Slice(T *s, std::size_t count) noexcept {
+ assert(s != nullptr || count == 0);
+ sliceInit(this,
+ s == nullptr && count == 0
+ ? reinterpret_cast<void *>(align_of<T>())
+ : const_cast<typename std::remove_const<T>::type *>(s),
+ count);
+}
-inline size_t Str::length() const noexcept { return this->len; }
-#endif // CXXBRIDGE05_RUST_STR
+template <typename T>
+T *Slice<T>::data() const noexcept {
+ return reinterpret_cast<T *>(slicePtr(this));
+}
-#ifndef CXXBRIDGE05_RUST_SLICE
-#define CXXBRIDGE05_RUST_SLICE
template <typename T>
-Slice<T>::Slice() noexcept : ptr(reinterpret_cast<const T *>(this)), len(0) {}
+std::size_t Slice<T>::size() const noexcept {
+ return sliceLen(this);
+}
template <typename T>
-Slice<T>::Slice(const T *s, size_t count) noexcept : ptr(s), len(count) {}
+std::size_t Slice<T>::length() const noexcept {
+ return this->size();
+}
template <typename T>
-const T *Slice<T>::data() const noexcept {
- return this->ptr;
+bool Slice<T>::empty() const noexcept {
+ return this->size() == 0;
+}
+
+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);
+}
+
+template <typename T>
+T &Slice<T>::at(std::size_t n) const {
+ if (n >= this->size()) {
+ panic<std::out_of_range>("rust::Slice index out of range");
+ }
+ return (*this)[n];
+}
+
+template <typename T>
+T &Slice<T>::front() const noexcept {
+ assert(!this->empty());
+ return (*this)[0];
+}
+
+template <typename T>
+T &Slice<T>::back() const noexcept {
+ assert(!this->empty());
+ return (*this)[this->size() - 1];
+}
+
+template <typename T>
+typename Slice<T>::iterator::reference
+Slice<T>::iterator::operator*() const noexcept {
+ return *static_cast<T *>(this->pos);
+}
+
+template <typename T>
+typename Slice<T>::iterator::pointer
+Slice<T>::iterator::operator->() const noexcept {
+ return static_cast<T *>(this->pos);
}
template <typename T>
-size_t Slice<T>::size() const noexcept {
- return this->len;
+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);
}
template <typename T>
-size_t Slice<T>::length() const noexcept {
- return this->len;
+typename Slice<T>::iterator &Slice<T>::iterator::operator++() noexcept {
+ this->pos = static_cast<char *>(this->pos) + this->stride;
+ return *this;
+}
+
+template <typename T>
+typename Slice<T>::iterator Slice<T>::iterator::operator++(int) noexcept {
+ auto ret = iterator(*this);
+ this->pos = static_cast<char *>(this->pos) + this->stride;
+ return ret;
}
-#endif // CXXBRIDGE05_RUST_SLICE
-#ifndef CXXBRIDGE05_RUST_BOX
-#define CXXBRIDGE05_RUST_BOX
template <typename T>
-Box<T>::Box(const Box &other) : Box(*other) {}
+typename Slice<T>::iterator &Slice<T>::iterator::operator--() noexcept {
+ this->pos = static_cast<char *>(this->pos) - this->stride;
+ return *this;
+}
+
+template <typename T>
+typename Slice<T>::iterator Slice<T>::iterator::operator--(int) noexcept {
+ auto ret = iterator(*this);
+ this->pos = static_cast<char *>(this->pos) - this->stride;
+ return ret;
+}
+
+template <typename T>
+typename Slice<T>::iterator &Slice<T>::iterator::operator+=(
+ typename Slice<T>::iterator::difference_type n) noexcept {
+ this->pos = static_cast<char *>(this->pos) + this->stride * n;
+ return *this;
+}
+
+template <typename T>
+typename Slice<T>::iterator &Slice<T>::iterator::operator-=(
+ typename Slice<T>::iterator::difference_type n) noexcept {
+ this->pos = static_cast<char *>(this->pos) - this->stride * n;
+ return *this;
+}
+
+template <typename T>
+typename Slice<T>::iterator Slice<T>::iterator::operator+(
+ typename Slice<T>::iterator::difference_type n) const noexcept {
+ auto ret = iterator(*this);
+ ret.pos = static_cast<char *>(this->pos) + this->stride * n;
+ return ret;
+}
+
+template <typename T>
+typename Slice<T>::iterator Slice<T>::iterator::operator-(
+ typename Slice<T>::iterator::difference_type n) const noexcept {
+ auto ret = iterator(*this);
+ ret.pos = static_cast<char *>(this->pos) - this->stride * n;
+ return ret;
+}
+
+template <typename T>
+typename Slice<T>::iterator::difference_type
+Slice<T>::iterator::operator-(const iterator &other) const noexcept {
+ auto diff = std::distance(static_cast<char *>(other.pos),
+ static_cast<char *>(this->pos));
+ return diff / this->stride;
+}
+
+template <typename T>
+bool Slice<T>::iterator::operator==(const iterator &other) const noexcept {
+ return this->pos == other.pos;
+}
+
+template <typename T>
+bool Slice<T>::iterator::operator!=(const iterator &other) const noexcept {
+ return this->pos != other.pos;
+}
+
+template <typename T>
+bool Slice<T>::iterator::operator<(const iterator &other) const noexcept {
+ return this->pos < other.pos;
+}
+
+template <typename T>
+bool Slice<T>::iterator::operator<=(const iterator &other) const noexcept {
+ return this->pos <= other.pos;
+}
+
+template <typename T>
+bool Slice<T>::iterator::operator>(const iterator &other) const noexcept {
+ return this->pos > other.pos;
+}
+
+template <typename T>
+bool Slice<T>::iterator::operator>=(const iterator &other) const noexcept {
+ return this->pos >= other.pos;
+}
+
+template <typename T>
+typename Slice<T>::iterator Slice<T>::begin() const noexcept {
+ iterator it;
+ it.pos = slicePtr(this);
+ it.stride = size_of<T>();
+ return it;
+}
+
+template <typename T>
+typename Slice<T>::iterator Slice<T>::end() const noexcept {
+ iterator it = this->begin();
+ it.pos = static_cast<char *>(it.pos) + it.stride * this->size();
+ return it;
+}
+
+template <typename T>
+void Slice<T>::swap(Slice &rhs) noexcept {
+ std::swap(*this, rhs);
+}
+#endif // CXXBRIDGE1_RUST_SLICE
+
+#ifndef CXXBRIDGE1_RUST_BOX
+#define CXXBRIDGE1_RUST_BOX
+template <typename T>
+class Box<T>::uninit {};
+
+template <typename T>
+class Box<T>::allocation {
+ static T *alloc() noexcept;
+ static void dealloc(T *) noexcept;
+
+public:
+ allocation() noexcept : ptr(alloc()) {}
+ ~allocation() noexcept {
+ if (this->ptr) {
+ dealloc(this->ptr);
+ }
+ }
+ T *ptr;
+};
template <typename T>
Box<T>::Box(Box &&other) noexcept : ptr(other.ptr) {
@@ -391,14 +721,18 @@ Box<T>::Box(Box &&other) noexcept : ptr(other.ptr) {
template <typename T>
Box<T>::Box(const T &val) {
- this->uninit();
- ::new (this->ptr) T(val);
+ allocation alloc;
+ ::new (alloc.ptr) T(val);
+ this->ptr = alloc.ptr;
+ alloc.ptr = nullptr;
}
template <typename T>
Box<T>::Box(T &&val) {
- this->uninit();
- ::new (this->ptr) T(std::move(val));
+ allocation alloc;
+ ::new (alloc.ptr) T(std::move(val));
+ this->ptr = alloc.ptr;
+ alloc.ptr = nullptr;
}
template <typename T>
@@ -409,20 +743,7 @@ Box<T>::~Box() noexcept {
}
template <typename T>
-Box<T> &Box<T>::operator=(const Box &other) {
- if (this != &other) {
- if (this->ptr) {
- **this = *other;
- } else {
- this->uninit();
- ::new (this->ptr) T(*other);
- }
- }
- return *this;
-}
-
-template <typename T>
-Box<T> &Box<T>::operator=(Box &&other) noexcept {
+Box<T> &Box<T>::operator=(Box &&other) &noexcept {
if (this->ptr) {
this->drop();
}
@@ -453,16 +774,23 @@ T &Box<T>::operator*() noexcept {
template <typename T>
template <typename... Fields>
-Box<T> Box<T>::in_place(Fields &&... fields) {
- Box box;
- box.uninit();
- ::new (box.ptr) T{std::forward<Fields>(fields)...};
- return box;
+Box<T> Box<T>::in_place(Fields &&...fields) {
+ allocation alloc;
+ auto ptr = alloc.ptr;
+ ::new (ptr) T{std::forward<Fields>(fields)...};
+ alloc.ptr = nullptr;
+ return from_raw(ptr);
+}
+
+template <typename T>
+void Box<T>::swap(Box &rhs) noexcept {
+ using std::swap;
+ swap(this->ptr, rhs.ptr);
}
template <typename T>
Box<T> Box<T>::from_raw(T *raw) noexcept {
- Box box;
+ Box box = uninit{};
box.ptr = raw;
return box;
}
@@ -475,14 +803,25 @@ T *Box<T>::into_raw() noexcept {
}
template <typename T>
-Box<T>::Box() noexcept {}
-#endif // CXXBRIDGE05_RUST_BOX
+Box<T>::Box(uninit) noexcept {}
+#endif // CXXBRIDGE1_RUST_BOX
-#ifndef CXXBRIDGE05_RUST_VEC
-#define CXXBRIDGE05_RUST_VEC
+#ifndef CXXBRIDGE1_RUST_VEC
+#define CXXBRIDGE1_RUST_VEC
template <typename T>
-Vec<T>::Vec(Vec &&other) noexcept {
- this->repr = other.repr;
+Vec<T>::Vec(std::initializer_list<T> init) : Vec{} {
+ this->reserve_total(init.size());
+ std::move(init.begin(), init.end(), std::back_inserter(*this));
+}
+
+template <typename T>
+Vec<T>::Vec(const Vec &other) : Vec() {
+ this->reserve_total(other.size());
+ std::copy(other.begin(), other.end(), std::back_inserter(*this));
+}
+
+template <typename T>
+Vec<T>::Vec(Vec &&other) noexcept : repr(other.repr) {
new (&other) Vec();
}
@@ -492,18 +831,25 @@ Vec<T>::~Vec() noexcept {
}
template <typename T>
-Vec<T> &Vec<T>::operator=(Vec &&other) noexcept {
+Vec<T> &Vec<T>::operator=(Vec &&other) &noexcept {
+ this->drop();
+ this->repr = other.repr;
+ new (&other) Vec();
+ return *this;
+}
+
+template <typename T>
+Vec<T> &Vec<T>::operator=(const Vec &other) & {
if (this != &other) {
this->drop();
- this->repr = other.repr;
- new (&other) Vec();
+ new (this) Vec(other);
}
return *this;
}
template <typename T>
bool Vec<T>::empty() const noexcept {
- return size() == 0;
+ return this->size() == 0;
}
template <typename T>
@@ -512,13 +858,41 @@ T *Vec<T>::data() noexcept {
}
template <typename T>
-const T &Vec<T>::operator[](size_t n) const noexcept {
+const T &Vec<T>::operator[](std::size_t n) const noexcept {
+ assert(n < this->size());
auto data = reinterpret_cast<const char *>(this->data());
- return *reinterpret_cast<const T *>(data + n * this->stride());
+ return *reinterpret_cast<const T *>(data + n * size_of<T>());
+}
+
+template <typename T>
+const T &Vec<T>::at(std::size_t n) const {
+ if (n >= this->size()) {
+ panic<std::out_of_range>("rust::Vec index out of range");
+ }
+ return (*this)[n];
+}
+
+template <typename T>
+const T &Vec<T>::front() const noexcept {
+ assert(!this->empty());
+ return (*this)[0];
+}
+
+template <typename T>
+const T &Vec<T>::back() const noexcept {
+ assert(!this->empty());
+ return (*this)[this->size() - 1];
+}
+
+template <typename T>
+T &Vec<T>::operator[](std::size_t n) noexcept {
+ assert(n < this->size());
+ auto data = reinterpret_cast<char *>(this->data());
+ return *reinterpret_cast<T *>(data + n * size_of<T>());
}
template <typename T>
-const T &Vec<T>::at(size_t n) const {
+T &Vec<T>::at(std::size_t n) {
if (n >= this->size()) {
panic<std::out_of_range>("rust::Vec index out of range");
}
@@ -526,17 +900,19 @@ const T &Vec<T>::at(size_t n) const {
}
template <typename T>
-const T &Vec<T>::front() const {
+T &Vec<T>::front() noexcept {
+ assert(!this->empty());
return (*this)[0];
}
template <typename T>
-const T &Vec<T>::back() const {
+T &Vec<T>::back() noexcept {
+ assert(!this->empty());
return (*this)[this->size() - 1];
}
template <typename T>
-void Vec<T>::reserve(size_t new_cap) {
+void Vec<T>::reserve(std::size_t new_cap) {
this->reserve_total(new_cap);
}
@@ -552,73 +928,126 @@ void Vec<T>::push_back(T &&value) {
template <typename T>
template <typename... Args>
-void Vec<T>::emplace_back(Args &&... args) {
+void Vec<T>::emplace_back(Args &&...args) {
auto size = this->size();
this->reserve_total(size + 1);
::new (reinterpret_cast<T *>(reinterpret_cast<char *>(this->data()) +
- size * this->stride()))
+ size * size_of<T>()))
T(std::forward<Args>(args)...);
this->set_len(size + 1);
}
template <typename T>
-const T &Vec<T>::const_iterator::operator*() const noexcept {
- return *static_cast<const T *>(this->pos);
+typename Vec<T>::iterator Vec<T>::begin() noexcept {
+ return Slice<T>(this->data(), this->size()).begin();
}
template <typename T>
-const T *Vec<T>::const_iterator::operator->() const noexcept {
- return static_cast<const T *>(this->pos);
+typename Vec<T>::iterator Vec<T>::end() noexcept {
+ return Slice<T>(this->data(), this->size()).end();
}
template <typename T>
-typename Vec<T>::const_iterator &Vec<T>::const_iterator::operator++() noexcept {
- this->pos = static_cast<const uint8_t *>(this->pos) + this->stride;
- return *this;
+typename Vec<T>::const_iterator Vec<T>::begin() const noexcept {
+ return this->cbegin();
}
template <typename T>
-typename Vec<T>::const_iterator
-Vec<T>::const_iterator::operator++(int) noexcept {
- auto ret = const_iterator(*this);
- this->pos = static_cast<const uint8_t *>(this->pos) + this->stride;
- return ret;
+typename Vec<T>::const_iterator Vec<T>::end() const noexcept {
+ return this->cend();
}
template <typename T>
-bool Vec<T>::const_iterator::operator==(
- const const_iterator &other) const noexcept {
- return this->pos == other.pos;
+typename Vec<T>::const_iterator Vec<T>::cbegin() const noexcept {
+ return Slice<const T>(this->data(), this->size()).begin();
}
template <typename T>
-bool Vec<T>::const_iterator::operator!=(
- const const_iterator &other) const noexcept {
- return this->pos != other.pos;
+typename Vec<T>::const_iterator Vec<T>::cend() const noexcept {
+ return Slice<const T>(this->data(), this->size()).end();
}
template <typename T>
-typename Vec<T>::const_iterator Vec<T>::begin() const noexcept {
- const_iterator it;
- it.pos = this->data();
- it.stride = this->stride();
- return it;
+void Vec<T>::swap(Vec &rhs) noexcept {
+ using std::swap;
+ swap(this->repr, rhs.repr);
}
+// Internal API only intended for the cxxbridge code generator.
template <typename T>
-typename Vec<T>::const_iterator Vec<T>::end() const noexcept {
- const_iterator it = this->begin();
- it.pos = static_cast<const uint8_t *>(it.pos) + it.stride * this->size();
- return it;
+Vec<T>::Vec(unsafe_bitcopy_t, const Vec &bits) noexcept : repr(bits.repr) {}
+#endif // CXXBRIDGE1_RUST_VEC
+
+#ifndef CXXBRIDGE1_IS_COMPLETE
+#define CXXBRIDGE1_IS_COMPLETE
+namespace detail {
+namespace {
+template <typename T, typename = std::size_t>
+struct is_complete : std::false_type {};
+template <typename T>
+struct is_complete<T, decltype(sizeof(T))> : std::true_type {};
+} // namespace
+} // namespace detail
+#endif // CXXBRIDGE1_IS_COMPLETE
+
+#ifndef CXXBRIDGE1_LAYOUT
+#define CXXBRIDGE1_LAYOUT
+class layout {
+ template <typename T>
+ friend std::size_t size_of();
+ template <typename T>
+ friend std::size_t align_of();
+ template <typename T>
+ static typename std::enable_if<std::is_base_of<Opaque, T>::value,
+ std::size_t>::type
+ do_size_of() {
+ return T::layout::size();
+ }
+ template <typename T>
+ static typename std::enable_if<!std::is_base_of<Opaque, T>::value,
+ std::size_t>::type
+ do_size_of() {
+ return sizeof(T);
+ }
+ template <typename T>
+ static
+ typename std::enable_if<detail::is_complete<T>::value, std::size_t>::type
+ size_of() {
+ return do_size_of<T>();
+ }
+ template <typename T>
+ static typename std::enable_if<std::is_base_of<Opaque, T>::value,
+ std::size_t>::type
+ do_align_of() {
+ return T::layout::align();
+ }
+ template <typename T>
+ static typename std::enable_if<!std::is_base_of<Opaque, T>::value,
+ std::size_t>::type
+ do_align_of() {
+ return alignof(T);
+ }
+ template <typename T>
+ static
+ typename std::enable_if<detail::is_complete<T>::value, std::size_t>::type
+ align_of() {
+ return do_align_of<T>();
+ }
+};
+
+template <typename T>
+std::size_t size_of() {
+ return layout::size_of<T>();
}
-// Internal API only intended for the cxxbridge code generator.
template <typename T>
-Vec<T>::Vec(unsafe_bitcopy_t, const Vec &bits) noexcept : repr(bits.repr) {}
-#endif // CXXBRIDGE05_RUST_VEC
+std::size_t align_of() {
+ return layout::align_of<T>();
+}
+#endif // CXXBRIDGE1_LAYOUT
-#ifndef CXXBRIDGE05_RELOCATABLE
-#define CXXBRIDGE05_RELOCATABLE
+#ifndef CXXBRIDGE1_RELOCATABLE
+#define CXXBRIDGE1_RELOCATABLE
namespace detail {
template <typename... Ts>
struct make_void {
@@ -652,7 +1081,7 @@ struct IsRelocatable
std::integral_constant<
bool, std::is_trivially_move_constructible<T>::value &&
std::is_trivially_destructible<T>::value>>::type {};
-#endif // CXXBRIDGE05_RELOCATABLE
+#endif // CXXBRIDGE1_RELOCATABLE
-} // namespace cxxbridge05
+} // namespace cxxbridge1
} // namespace rust
diff --git a/macro/Cargo.toml b/macro/Cargo.toml
index 6b07a073..a3df1018 100644
--- a/macro/Cargo.toml
+++ b/macro/Cargo.toml
@@ -1,11 +1,12 @@
[package]
name = "cxxbridge-macro"
-version = "0.5.9"
+version = "1.0.42"
authors = ["David Tolnay <dtolnay@gmail.com>"]
edition = "2018"
license = "MIT OR Apache-2.0"
description = "Implementation detail of the `cxx` crate."
repository = "https://github.com/dtolnay/cxx"
+homepage = "https://cxx.rs"
exclude = ["build.rs", "README.md"]
keywords = ["ffi"]
categories = ["development-tools::ffi"]
@@ -16,10 +17,10 @@ proc-macro = true
[dependencies]
proc-macro2 = "1.0"
quote = "1.0.4"
-syn = { version = "1.0.20", features = ["full"] }
+syn = { version = "1.0.68", features = ["full"] }
[dev-dependencies]
-cxx = { version = "0.5", path = ".." }
+cxx = { version = "1.0", path = ".." }
[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]
diff --git a/macro/src/derive.rs b/macro/src/derive.rs
index 1abc5dab..2f770951 100644
--- a/macro/src/derive.rs
+++ b/macro/src/derive.rs
@@ -1,14 +1,281 @@
-use crate::syntax::Derive;
-use proc_macro2::TokenStream;
-use quote::{quote, ToTokens};
+use crate::syntax::{derive, Enum, Struct, Trait};
+use proc_macro2::{Ident, Span, TokenStream};
+use quote::{quote, quote_spanned, ToTokens};
-pub struct DeriveAttribute<'a>(pub &'a [Derive]);
+pub use crate::syntax::derive::*;
-impl<'a> ToTokens for DeriveAttribute<'a> {
- fn to_tokens(&self, tokens: &mut TokenStream) {
- if !self.0.is_empty() {
- let derives = self.0;
- tokens.extend(quote!(#[derive(#(#derives),*)]));
+pub fn expand_struct(strct: &Struct, actual_derives: &mut Option<TokenStream>) -> TokenStream {
+ let mut expanded = TokenStream::new();
+ let mut traits = Vec::new();
+
+ for derive in &strct.derives {
+ let span = derive.span;
+ match derive.what {
+ Trait::Copy => expanded.extend(struct_copy(strct, span)),
+ Trait::Clone => expanded.extend(struct_clone(strct, span)),
+ Trait::Debug => expanded.extend(struct_debug(strct, span)),
+ Trait::Default => expanded.extend(struct_default(strct, span)),
+ Trait::Eq => traits.push(quote_spanned!(span=> ::std::cmp::Eq)),
+ Trait::ExternType => unreachable!(),
+ Trait::Hash => traits.push(quote_spanned!(span=> ::std::hash::Hash)),
+ Trait::Ord => expanded.extend(struct_ord(strct, span)),
+ Trait::PartialEq => traits.push(quote_spanned!(span=> ::std::cmp::PartialEq)),
+ Trait::PartialOrd => expanded.extend(struct_partial_ord(strct, span)),
+ }
+ }
+
+ if traits.is_empty() {
+ *actual_derives = None;
+ } else {
+ *actual_derives = Some(quote!(#[derive(#(#traits),*)]));
+ }
+
+ expanded
+}
+
+pub fn expand_enum(enm: &Enum, actual_derives: &mut Option<TokenStream>) -> TokenStream {
+ let mut expanded = TokenStream::new();
+ let mut traits = Vec::new();
+ let mut has_copy = false;
+ let mut has_clone = false;
+ let mut has_eq = false;
+ let mut has_partial_eq = false;
+
+ for derive in &enm.derives {
+ let span = derive.span;
+ match derive.what {
+ Trait::Copy => {
+ expanded.extend(enum_copy(enm, span));
+ has_copy = true;
+ }
+ Trait::Clone => {
+ expanded.extend(enum_clone(enm, span));
+ has_clone = true;
+ }
+ Trait::Debug => expanded.extend(enum_debug(enm, span)),
+ Trait::Default => unreachable!(),
+ Trait::Eq => {
+ traits.push(quote_spanned!(span=> ::std::cmp::Eq));
+ has_eq = true;
+ }
+ Trait::ExternType => unreachable!(),
+ Trait::Hash => traits.push(quote_spanned!(span=> ::std::hash::Hash)),
+ Trait::Ord => expanded.extend(enum_ord(enm, span)),
+ Trait::PartialEq => {
+ traits.push(quote_spanned!(span=> ::std::cmp::PartialEq));
+ has_partial_eq = true;
+ }
+ Trait::PartialOrd => expanded.extend(enum_partial_ord(enm, span)),
+ }
+ }
+
+ let span = enm.name.rust.span();
+ if !has_copy {
+ expanded.extend(enum_copy(enm, span));
+ }
+ if !has_clone {
+ expanded.extend(enum_clone(enm, span));
+ }
+ if !has_eq {
+ // Required to be derived in order for the enum's "variants" to be
+ // usable in patterns.
+ traits.push(quote!(::std::cmp::Eq));
+ }
+ if !has_partial_eq {
+ traits.push(quote!(::std::cmp::PartialEq));
+ }
+
+ *actual_derives = Some(quote!(#[derive(#(#traits),*)]));
+
+ expanded
+}
+
+fn struct_copy(strct: &Struct, span: Span) -> TokenStream {
+ let ident = &strct.name.rust;
+ let generics = &strct.generics;
+
+ quote_spanned! {span=>
+ impl #generics ::std::marker::Copy for #ident #generics {}
+ }
+}
+
+fn struct_clone(strct: &Struct, span: Span) -> TokenStream {
+ let ident = &strct.name.rust;
+ let generics = &strct.generics;
+
+ let body = if derive::contains(&strct.derives, Trait::Copy) {
+ quote!(*self)
+ } else {
+ let fields = strct.fields.iter().map(|field| &field.name.rust);
+ let values = strct.fields.iter().map(|field| {
+ let ident = &field.name.rust;
+ let ty = field.ty.to_token_stream();
+ let span = ty.into_iter().last().unwrap().span();
+ quote_spanned!(span=> &self.#ident)
+ });
+ quote_spanned!(span=> #ident {
+ #(#fields: ::std::clone::Clone::clone(#values),)*
+ })
+ };
+
+ quote_spanned! {span=>
+ impl #generics ::std::clone::Clone for #ident #generics {
+ fn clone(&self) -> Self {
+ #body
+ }
+ }
+ }
+}
+
+fn struct_debug(strct: &Struct, span: Span) -> TokenStream {
+ let ident = &strct.name.rust;
+ let generics = &strct.generics;
+ let struct_name = ident.to_string();
+ let fields = strct.fields.iter().map(|field| &field.name.rust);
+ let field_names = fields.clone().map(Ident::to_string);
+
+ quote_spanned! {span=>
+ impl #generics ::std::fmt::Debug for #ident #generics {
+ fn fmt(&self, formatter: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
+ formatter.debug_struct(#struct_name)
+ #(.field(#field_names, &self.#fields))*
+ .finish()
+ }
+ }
+ }
+}
+
+fn struct_default(strct: &Struct, span: Span) -> TokenStream {
+ let ident = &strct.name.rust;
+ let generics = &strct.generics;
+ let fields = strct.fields.iter().map(|field| &field.name.rust);
+
+ quote_spanned! {span=>
+ impl #generics ::std::default::Default for #ident #generics {
+ fn default() -> Self {
+ #ident {
+ #(
+ #fields: ::std::default::Default::default(),
+ )*
+ }
+ }
+ }
+ }
+}
+
+fn struct_ord(strct: &Struct, span: Span) -> TokenStream {
+ let ident = &strct.name.rust;
+ let generics = &strct.generics;
+ let fields = strct.fields.iter().map(|field| &field.name.rust);
+
+ quote_spanned! {span=>
+ impl #generics ::std::cmp::Ord for #ident #generics {
+ fn cmp(&self, other: &Self) -> ::std::cmp::Ordering {
+ #(
+ match ::std::cmp::Ord::cmp(&self.#fields, &other.#fields) {
+ ::std::cmp::Ordering::Equal => {}
+ ordering => return ordering,
+ }
+ )*
+ ::std::cmp::Ordering::Equal
+ }
+ }
+ }
+}
+
+fn struct_partial_ord(strct: &Struct, span: Span) -> TokenStream {
+ let ident = &strct.name.rust;
+ let generics = &strct.generics;
+
+ let body = if derive::contains(&strct.derives, Trait::Ord) {
+ quote! {
+ ::std::option::Option::Some(::std::cmp::Ord::cmp(self, other))
+ }
+ } else {
+ let fields = strct.fields.iter().map(|field| &field.name.rust);
+ quote! {
+ #(
+ match ::std::cmp::PartialOrd::partial_cmp(&self.#fields, &other.#fields) {
+ ::std::option::Option::Some(::std::cmp::Ordering::Equal) => {}
+ ordering => return ordering,
+ }
+ )*
+ ::std::option::Option::Some(::std::cmp::Ordering::Equal)
+ }
+ };
+
+ quote_spanned! {span=>
+ impl #generics ::std::cmp::PartialOrd for #ident #generics {
+ fn partial_cmp(&self, other: &Self) -> ::std::option::Option<::std::cmp::Ordering> {
+ #body
+ }
+ }
+ }
+}
+
+fn enum_copy(enm: &Enum, span: Span) -> TokenStream {
+ let ident = &enm.name.rust;
+
+ quote_spanned! {span=>
+ impl ::std::marker::Copy for #ident {}
+ }
+}
+
+fn enum_clone(enm: &Enum, span: Span) -> TokenStream {
+ let ident = &enm.name.rust;
+
+ quote_spanned! {span=>
+ impl ::std::clone::Clone for #ident {
+ fn clone(&self) -> Self {
+ *self
+ }
+ }
+ }
+}
+
+fn enum_debug(enm: &Enum, span: Span) -> TokenStream {
+ let ident = &enm.name.rust;
+ let variants = enm.variants.iter().map(|variant| {
+ let variant = &variant.name.rust;
+ let name = variant.to_string();
+ quote_spanned! {span=>
+ #ident::#variant => formatter.write_str(#name),
+ }
+ });
+ let fallback = format!("{}({{}})", ident);
+
+ quote_spanned! {span=>
+ impl ::std::fmt::Debug for #ident {
+ fn fmt(&self, formatter: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
+ match *self {
+ #(#variants)*
+ _ => ::std::write!(formatter, #fallback, self.repr),
+ }
+ }
+ }
+ }
+}
+
+fn enum_ord(enm: &Enum, span: Span) -> TokenStream {
+ let ident = &enm.name.rust;
+
+ quote_spanned! {span=>
+ impl ::std::cmp::Ord for #ident {
+ fn cmp(&self, other: &Self) -> ::std::cmp::Ordering {
+ ::std::cmp::Ord::cmp(&self.repr, &other.repr)
+ }
+ }
+ }
+}
+
+fn enum_partial_ord(enm: &Enum, span: Span) -> TokenStream {
+ let ident = &enm.name.rust;
+
+ quote_spanned! {span=>
+ impl ::std::cmp::PartialOrd for #ident {
+ fn partial_cmp(&self, other: &Self) -> ::std::option::Option<::std::cmp::Ordering> {
+ ::std::cmp::PartialOrd::partial_cmp(&self.repr, &other.repr)
+ }
}
}
}
diff --git a/macro/src/expand.rs b/macro/src/expand.rs
index 903585ec..7c194ad2 100644
--- a/macro/src/expand.rs
+++ b/macro/src/expand.rs
@@ -1,19 +1,34 @@
-use crate::derive::DeriveAttribute;
-use crate::syntax::atom::Atom::{self, *};
+use crate::syntax::atom::Atom::*;
+use crate::syntax::attrs::{self, OtherAttrs};
use crate::syntax::file::Module;
+use crate::syntax::instantiate::{ImplKey, NamedImplKey};
+use crate::syntax::qualified::QualifiedName;
use crate::syntax::report::Errors;
use crate::syntax::symbol::Symbol;
use crate::syntax::{
- self, check, mangle, Api, Enum, ExternFn, ExternType, Impl, Pair, ResolvableName, Signature,
- Struct, Type, TypeAlias, Types,
+ self, check, mangle, Api, Doc, Enum, ExternFn, ExternType, Impl, Lifetimes, Pair, Signature,
+ Struct, Trait, Type, TypeAlias, Types,
};
+use crate::type_id::Crate;
+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, Result, Token};
+use syn::{parse_quote, punctuated, Lifetime, Result, Token};
pub fn bridge(mut ffi: Module) -> Result<TokenStream> {
let ref mut errors = Errors::new();
+
+ let mut doc = Doc::new();
+ let attrs = attrs::parse(
+ errors,
+ mem::take(&mut ffi.attrs),
+ attrs::Parser {
+ doc: Some(&mut doc),
+ ..Default::default()
+ },
+ );
+
let content = mem::take(&mut ffi.content);
let trusted = ffi.unsafety.is_some();
let namespace = &ffi.namespace;
@@ -23,34 +38,44 @@ pub fn bridge(mut ffi: Module) -> Result<TokenStream> {
check::typecheck(errors, apis, types);
errors.propagate()?;
- Ok(expand(ffi, apis, types))
+ Ok(expand(ffi, doc, attrs, apis, types))
}
-fn expand(ffi: Module, apis: &[Api], types: &Types) -> TokenStream {
+fn expand(ffi: Module, doc: Doc, attrs: OtherAttrs, apis: &[Api], types: &Types) -> TokenStream {
let mut expanded = TokenStream::new();
let mut hidden = TokenStream::new();
+ let mut forbid = TokenStream::new();
for api in apis {
if let Api::RustType(ety) = api {
- expanded.extend(expand_rust_type(ety));
+ expanded.extend(expand_rust_type_import(ety));
hidden.extend(expand_rust_type_assert_sized(ety));
}
}
for api in apis {
match api {
- Api::Include(_) | Api::RustType(_) | Api::Impl(_) => {}
- Api::Struct(strct) => expanded.extend(expand_struct(strct)),
+ Api::Include(_) | Api::Impl(_) => {}
+ Api::Struct(strct) => {
+ expanded.extend(expand_struct(strct));
+ hidden.extend(expand_struct_operators(strct));
+ forbid.extend(expand_struct_forbid_drop(strct));
+ }
Api::Enum(enm) => expanded.extend(expand_enum(enm)),
Api::CxxType(ety) => {
let ident = &ety.name.rust;
if !types.structs.contains_key(ident) && !types.enums.contains_key(ident) {
expanded.extend(expand_cxx_type(ety));
+ hidden.extend(expand_cxx_type_assert_pinned(ety));
}
}
Api::CxxFunction(efn) => {
expanded.extend(expand_cxx_function_shim(efn, types));
}
+ Api::RustType(ety) => {
+ expanded.extend(expand_rust_type_impl(ety));
+ hidden.extend(expand_rust_type_layout(ety));
+ }
Api::RustFunction(efn) => hidden.extend(expand_rust_function_shim(efn, types)),
Api::TypeAlias(alias) => {
expanded.extend(expand_type_alias(alias));
@@ -59,42 +84,33 @@ fn expand(ffi: Module, apis: &[Api], types: &Types) -> TokenStream {
}
}
- for ty in types {
- let explicit_impl = types.explicit_impls.get(ty);
- if let Type::RustBox(ty) = ty {
- if let Type::Ident(ident) = &ty.inner {
- if Atom::from(&ident.rust).is_none() {
- hidden.extend(expand_rust_box(ident, types));
- }
+ for (impl_key, &explicit_impl) in &types.impls {
+ match *impl_key {
+ ImplKey::RustBox(ident) => {
+ hidden.extend(expand_rust_box(ident, types, explicit_impl));
}
- } else if let Type::RustVec(ty) = ty {
- if let Type::Ident(ident) = &ty.inner {
- if Atom::from(&ident.rust).is_none() {
- hidden.extend(expand_rust_vec(ident, types));
- }
+ ImplKey::RustVec(ident) => {
+ hidden.extend(expand_rust_vec(ident, types, explicit_impl));
}
- } else if let Type::UniquePtr(ptr) = ty {
- if let Type::Ident(ident) = &ptr.inner {
- if Atom::from(&ident.rust).is_none()
- && (explicit_impl.is_some() || !types.aliases.contains_key(&ident.rust))
- {
- expanded.extend(expand_unique_ptr(ident, types, explicit_impl));
- }
+ ImplKey::UniquePtr(ident) => {
+ expanded.extend(expand_unique_ptr(ident, types, explicit_impl));
}
- } else if let Type::CxxVector(ptr) = ty {
- if let Type::Ident(ident) = &ptr.inner {
- if Atom::from(&ident.rust).is_none()
- && (explicit_impl.is_some() || !types.aliases.contains_key(&ident.rust))
- {
- // Generate impl for CxxVector<T> if T is a struct or opaque
- // C++ type. Impl for primitives is already provided by cxx
- // crate.
- expanded.extend(expand_cxx_vector(ident, explicit_impl, types));
- }
+ ImplKey::SharedPtr(ident) => {
+ expanded.extend(expand_shared_ptr(ident, types, explicit_impl));
+ }
+ ImplKey::WeakPtr(ident) => {
+ expanded.extend(expand_weak_ptr(ident, types, explicit_impl));
+ }
+ ImplKey::CxxVector(ident) => {
+ expanded.extend(expand_cxx_vector(ident, explicit_impl, types));
}
}
}
+ if !forbid.is_empty() {
+ hidden.extend(expand_forbid(forbid));
+ }
+
// Work around https://github.com/rust-lang/rust/issues/67851.
if !hidden.is_empty() {
expanded.extend(quote! {
@@ -105,70 +121,209 @@ fn expand(ffi: Module, apis: &[Api], types: &Types) -> TokenStream {
});
}
- let attrs = ffi
- .attrs
- .into_iter()
- .filter(|attr| attr.path.is_ident("doc"));
let vis = &ffi.vis;
+ let mod_token = &ffi.mod_token;
let ident = &ffi.ident;
+ let span = ffi.brace_token.span;
+ let expanded = quote_spanned!(span=> {#expanded});
quote! {
- #(#attrs)*
+ #doc
+ #attrs
#[deny(improper_ctypes)]
- #[allow(non_snake_case)]
- #vis mod #ident {
- #expanded
- }
+ #[allow(non_camel_case_types, non_snake_case, clippy::upper_case_acronyms, clippy::unknown_clippy_lints)]
+ #vis #mod_token #ident #expanded
}
}
fn expand_struct(strct: &Struct) -> TokenStream {
let ident = &strct.name.rust;
let doc = &strct.doc;
- let derives = DeriveAttribute(&strct.derives);
+ let attrs = &strct.attrs;
+ let generics = &strct.generics;
let type_id = type_id(&strct.name);
let fields = strct.fields.iter().map(|field| {
+ let doc = &field.doc;
+ let attrs = &field.attrs;
// This span on the pub makes "private type in public interface" errors
// appear in the right place.
- let vis = Token![pub](field.ident.span());
- quote!(#vis #field)
+ let vis = field.visibility;
+ quote!(#doc #attrs #vis #field)
});
+ let mut derives = None;
+ let derived_traits = derive::expand_struct(strct, &mut derives);
+
+ let span = ident.span();
+ let visibility = strct.visibility;
+ let struct_token = strct.struct_token;
+ let struct_def = quote_spanned! {span=>
+ #visibility #struct_token #ident #generics {
+ #(#fields,)*
+ }
+ };
quote! {
#doc
+ #attrs
#derives
#[repr(C)]
- pub struct #ident {
- #(#fields,)*
- }
+ #struct_def
- unsafe impl ::cxx::ExternType for #ident {
+ unsafe impl #generics ::cxx::ExternType for #ident #generics {
+ #[doc(hidden)]
type Id = #type_id;
type Kind = ::cxx::kind::Trivial;
}
+
+ #derived_traits
+ }
+}
+
+fn expand_struct_operators(strct: &Struct) -> TokenStream {
+ let ident = &strct.name.rust;
+ let mut operators = TokenStream::new();
+
+ for derive in &strct.derives {
+ let span = derive.span;
+ match derive.what {
+ Trait::PartialEq => {
+ let link_name = mangle::operator(&strct.name, "eq");
+ let local_name = format_ident!("__operator_eq_{}", strct.name.rust);
+ operators.extend(quote_spanned! {span=>
+ #[doc(hidden)]
+ #[export_name = #link_name]
+ extern "C" fn #local_name(lhs: &#ident, rhs: &#ident) -> bool {
+ *lhs == *rhs
+ }
+ });
+
+ if !derive::contains(&strct.derives, Trait::Eq) {
+ let link_name = mangle::operator(&strct.name, "ne");
+ let local_name = format_ident!("__operator_ne_{}", strct.name.rust);
+ operators.extend(quote_spanned! {span=>
+ #[doc(hidden)]
+ #[export_name = #link_name]
+ extern "C" fn #local_name(lhs: &#ident, rhs: &#ident) -> bool {
+ *lhs != *rhs
+ }
+ });
+ }
+ }
+ Trait::PartialOrd => {
+ let link_name = mangle::operator(&strct.name, "lt");
+ let local_name = format_ident!("__operator_lt_{}", strct.name.rust);
+ operators.extend(quote_spanned! {span=>
+ #[doc(hidden)]
+ #[export_name = #link_name]
+ extern "C" fn #local_name(lhs: &#ident, rhs: &#ident) -> bool {
+ *lhs < *rhs
+ }
+ });
+
+ let link_name = mangle::operator(&strct.name, "le");
+ let local_name = format_ident!("__operator_le_{}", strct.name.rust);
+ operators.extend(quote_spanned! {span=>
+ #[doc(hidden)]
+ #[export_name = #link_name]
+ extern "C" fn #local_name(lhs: &#ident, rhs: &#ident) -> bool {
+ *lhs <= *rhs
+ }
+ });
+
+ if !derive::contains(&strct.derives, Trait::Ord) {
+ let link_name = mangle::operator(&strct.name, "gt");
+ let local_name = format_ident!("__operator_gt_{}", strct.name.rust);
+ operators.extend(quote_spanned! {span=>
+ #[doc(hidden)]
+ #[export_name = #link_name]
+ extern "C" fn #local_name(lhs: &#ident, rhs: &#ident) -> bool {
+ *lhs > *rhs
+ }
+ });
+
+ let link_name = mangle::operator(&strct.name, "ge");
+ let local_name = format_ident!("__operator_ge_{}", strct.name.rust);
+ operators.extend(quote_spanned! {span=>
+ #[doc(hidden)]
+ #[export_name = #link_name]
+ extern "C" fn #local_name(lhs: &#ident, rhs: &#ident) -> bool {
+ *lhs >= *rhs
+ }
+ });
+ }
+ }
+ Trait::Hash => {
+ let link_name = mangle::operator(&strct.name, "hash");
+ let local_name = format_ident!("__operator_hash_{}", strct.name.rust);
+ operators.extend(quote_spanned! {span=>
+ #[doc(hidden)]
+ #[export_name = #link_name]
+ #[allow(clippy::cast_possible_truncation)]
+ extern "C" fn #local_name(this: &#ident) -> usize {
+ let mut hasher = ::std::collections::hash_map::DefaultHasher::new();
+ ::std::hash::Hash::hash(this, &mut hasher);
+ ::std::hash::Hasher::finish(&hasher) as usize
+ }
+ });
+ }
+ _ => {}
+ }
+ }
+
+ operators
+}
+
+fn expand_struct_forbid_drop(strct: &Struct) -> TokenStream {
+ let ident = &strct.name.rust;
+ let generics = &strct.generics;
+ let span = ident.span();
+ let impl_token = Token![impl](strct.visibility.span);
+
+ quote_spanned! {span=>
+ #impl_token #generics self::Drop for super::#ident #generics {}
}
}
fn expand_enum(enm: &Enum) -> TokenStream {
let ident = &enm.name.rust;
let doc = &enm.doc;
+ let attrs = &enm.attrs;
let repr = enm.repr;
let type_id = type_id(&enm.name);
let variants = enm.variants.iter().map(|variant| {
- let variant_ident = &variant.ident;
+ let doc = &variant.doc;
+ let attrs = &variant.attrs;
+ let variant_ident = &variant.name.rust;
let discriminant = &variant.discriminant;
- Some(quote! {
+ let span = variant_ident.span();
+ Some(quote_spanned! {span=>
+ #doc
+ #attrs
pub const #variant_ident: Self = #ident { repr: #discriminant };
})
});
+ let mut derives = None;
+ let derived_traits = derive::expand_enum(enm, &mut derives);
+
+ let span = ident.span();
+ let visibility = enm.visibility;
+ let struct_token = Token![struct](enm.enum_token.span);
+ let enum_repr = quote! {
+ #[allow(missing_docs)]
+ pub repr: #repr,
+ };
+ let enum_def = quote_spanned! {span=>
+ #visibility #struct_token #ident {
+ #enum_repr
+ }
+ };
quote! {
#doc
- #[derive(Copy, Clone, PartialEq, Eq)]
+ #attrs
+ #derives
#[repr(transparent)]
- pub struct #ident {
- pub repr: #repr,
- }
+ #enum_def
#[allow(non_upper_case_globals)]
impl #ident {
@@ -176,38 +331,95 @@ fn expand_enum(enm: &Enum) -> TokenStream {
}
unsafe impl ::cxx::ExternType for #ident {
+ #[doc(hidden)]
type Id = #type_id;
type Kind = ::cxx::kind::Trivial;
}
+
+ #derived_traits
}
}
fn expand_cxx_type(ety: &ExternType) -> TokenStream {
let ident = &ety.name.rust;
let doc = &ety.doc;
+ let attrs = &ety.attrs;
+ let generics = &ety.generics;
let type_id = type_id(&ety.name);
+ let lifetime_fields = ety.generics.lifetimes.iter().map(|lifetime| {
+ let field = format_ident!("_lifetime_{}", lifetime.ident);
+ quote!(#field: ::std::marker::PhantomData<&#lifetime ()>)
+ });
+ let repr_fields = quote! {
+ _private: ::cxx::private::Opaque,
+ #(#lifetime_fields,)*
+ };
+
+ let span = ident.span();
+ let visibility = &ety.visibility;
+ let struct_token = Token![struct](ety.type_token.span);
+ let extern_type_def = quote_spanned! {span=>
+ #visibility #struct_token #ident #generics {
+ #repr_fields
+ }
+ };
+
quote! {
#doc
+ #attrs
#[repr(C)]
- pub struct #ident {
- _private: ::cxx::private::Opaque,
- }
+ #extern_type_def
- unsafe impl ::cxx::ExternType for #ident {
+ unsafe impl #generics ::cxx::ExternType for #ident #generics {
+ #[doc(hidden)]
type Id = #type_id;
type Kind = ::cxx::kind::Opaque;
}
}
}
+fn expand_cxx_type_assert_pinned(ety: &ExternType) -> TokenStream {
+ let ident = &ety.name.rust;
+ let infer = Token![_](ident.span());
+
+ quote! {
+ let _ = {
+ // Derived from https://github.com/nvzqz/static-assertions-rs.
+ trait __AmbiguousIfImpl<A> {
+ fn infer() {}
+ }
+
+ impl<T> __AmbiguousIfImpl<()> for T
+ where
+ T: ?::std::marker::Sized
+ {}
+
+ #[allow(dead_code)]
+ struct __Invalid;
+
+ impl<T> __AmbiguousIfImpl<__Invalid> for T
+ where
+ T: ?::std::marker::Sized + ::std::marker::Unpin,
+ {}
+
+ // If there is only one specialized trait impl, type inference with
+ // `_` can be resolved and this can compile. Fails to compile if
+ // user has added a manual Unpin impl for their opaque C++ type as
+ // then `__AmbiguousIfImpl<__Invalid>` also exists.
+ <#ident as __AmbiguousIfImpl<#infer>>::infer
+ };
+ }
+}
+
fn expand_cxx_function_decl(efn: &ExternFn, types: &Types) -> TokenStream {
+ let generics = &efn.generics;
let receiver = efn.receiver.iter().map(|receiver| {
let receiver_type = receiver.ty();
quote!(_: #receiver_type)
});
let args = efn.args.iter().map(|arg| {
- let ident = &arg.ident;
+ let ident = &arg.name.rust;
let ty = expand_extern_type(&arg.ty, types, true);
if arg.ty == RustString {
quote!(#ident: *const #ty)
@@ -236,18 +448,25 @@ fn expand_cxx_function_decl(efn: &ExternFn, types: &Types) -> TokenStream {
let local_name = format_ident!("__{}", efn.name.rust);
quote! {
#[link_name = #link_name]
- fn #local_name(#(#all_args,)* #outparam) #ret;
+ fn #local_name #generics(#(#all_args,)* #outparam) #ret;
}
}
fn expand_cxx_function_shim(efn: &ExternFn, types: &Types) -> TokenStream {
let doc = &efn.doc;
+ let attrs = &efn.attrs;
let decl = expand_cxx_function_decl(efn, types);
let receiver = efn.receiver.iter().map(|receiver| {
- let ampersand = receiver.ampersand;
- let mutability = receiver.mutability;
let var = receiver.var;
- quote!(#ampersand #mutability #var)
+ if receiver.pinned {
+ let ty = receiver.ty_self();
+ quote!(#var: #ty)
+ } else {
+ let ampersand = receiver.ampersand;
+ let lifetime = &receiver.lifetime;
+ let mutability = receiver.mutability;
+ quote!(#ampersand #lifetime #mutability #var)
+ }
});
let args = efn.args.iter().map(|arg| quote!(#arg));
let all_args = receiver.chain(args);
@@ -266,7 +485,7 @@ fn expand_cxx_function_shim(efn: &ExternFn, types: &Types) -> TokenStream {
.iter()
.map(|receiver| receiver.var.to_token_stream());
let arg_vars = efn.args.iter().map(|arg| {
- let var = &arg.ident;
+ let var = &arg.name.rust;
match &arg.ty {
Type::Ident(ident) if ident.rust == RustString => {
quote!(#var.as_mut_ptr() as *const ::cxx::private::RustString)
@@ -275,26 +494,44 @@ fn expand_cxx_function_shim(efn: &ExternFn, types: &Types) -> TokenStream {
Type::UniquePtr(_) => quote!(::cxx::UniquePtr::into_raw(#var)),
Type::RustVec(_) => quote!(#var.as_mut_ptr() as *const ::cxx::private::RustVec<_>),
Type::Ref(ty) => match &ty.inner {
- Type::Ident(ident) if ident.rust == RustString => match ty.mutability {
- None => quote!(::cxx::private::RustString::from_ref(#var)),
- Some(_) => quote!(::cxx::private::RustString::from_mut(#var)),
+ 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)),
},
- Type::RustVec(vec) if vec.inner == RustString => match ty.mutability {
- None => quote!(::cxx::private::RustVec::from_ref_vec_string(#var)),
- Some(_) => quote!(::cxx::private::RustVec::from_mut_vec_string(#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)),
},
- Type::RustVec(_) => match ty.mutability {
- None => quote!(::cxx::private::RustVec::from_ref(#var)),
- Some(_) => quote!(::cxx::private::RustVec::from_mut(#var)),
- },
- inner if types.is_considered_improper_ctype(inner) => match ty.mutability {
- None => quote!(#var as *const #inner as *const ::std::ffi::c_void),
- Some(_) => quote!(#var as *mut #inner as *mut ::std::ffi::c_void),
+ Type::RustVec(_) => match ty.mutable {
+ false => quote!(::cxx::private::RustVec::from_ref(#var)),
+ true => quote!(::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)),
+ };
+ match ty.mutable {
+ false => {
+ quote!(#var as *const #inner as *const ::std::ffi::c_void)
+ }
+ true => quote!(#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())
+ } else {
+ quote!(#var)
+ }
+ }
Type::Str(_) => quote!(::cxx::private::RustStr::from(#var)),
- Type::SliceRefU8(_) => quote!(::cxx::private::RustSliceU8::from(#var)),
+ Type::SliceRef(ty) => match ty.mutable {
+ false => quote!(::cxx::private::RustSlice::from_ref(#var)),
+ true => quote!(::cxx::private::RustSlice::from_mut(#var)),
+ },
ty if types.needs_indirect_abi(ty) => quote!(#var.as_mut_ptr()),
_ => quote!(#var),
}
@@ -305,7 +542,7 @@ fn expand_cxx_function_shim(efn: &ExternFn, types: &Types) -> TokenStream {
.iter()
.filter_map(|arg| {
if let Type::Fn(f) = &arg.ty {
- let var = &arg.ident;
+ let var = &arg.name;
Some(expand_function_pointer_trampoline(efn, var, f, types))
} else {
None
@@ -317,7 +554,7 @@ fn expand_cxx_function_shim(efn: &ExternFn, types: &Types) -> TokenStream {
.iter()
.filter(|arg| types.needs_indirect_abi(&arg.ty))
.map(|arg| {
- let var = &arg.ident;
+ let var = &arg.name.rust;
// These are arguments for which C++ has taken ownership of the data
// behind the mut reference it received.
quote! {
@@ -368,26 +605,43 @@ fn expand_cxx_function_shim(efn: &ExternFn, types: &Types) -> TokenStream {
}
Type::UniquePtr(_) => quote!(::cxx::UniquePtr::from_raw(#call)),
Type::Ref(ty) => match &ty.inner {
- Type::Ident(ident) if ident.rust == RustString => match ty.mutability {
- None => quote!(#call.as_string()),
- Some(_) => quote!(#call.as_mut_string()),
+ Type::Ident(ident) if ident.rust == RustString => match ty.mutable {
+ false => quote!(#call.as_string()),
+ true => quote!(#call.as_mut_string()),
},
- Type::RustVec(vec) if vec.inner == RustString => match ty.mutability {
- None => quote!(#call.as_vec_string()),
- Some(_) => quote!(#call.as_mut_vec_string()),
+ Type::RustVec(vec) if vec.inner == RustString => match ty.mutable {
+ false => quote!(#call.as_vec_string()),
+ true => quote!(#call.as_mut_vec_string()),
},
- Type::RustVec(_) => match ty.mutability {
- None => quote!(#call.as_vec()),
- Some(_) => quote!(#call.as_mut_vec()),
+ Type::RustVec(_) => match ty.mutable {
+ false => quote!(#call.as_vec()),
+ true => quote!(#call.as_mut_vec()),
},
inner if types.is_considered_improper_ctype(inner) => {
let mutability = ty.mutability;
- quote!(&#mutability *#call.cast())
+ let deref_mut = quote!(&#mutability *#call.cast());
+ match ty.pinned {
+ false => deref_mut,
+ true => quote!(::std::pin::Pin::new_unchecked(#deref_mut)),
+ }
}
_ => call,
},
+ Type::Ptr(ty) => {
+ if types.is_considered_improper_ctype(&ty.inner) {
+ quote!(#call.cast())
+ } else {
+ call
+ }
+ }
Type::Str(_) => quote!(#call.as_str()),
- Type::SliceRefU8(_) => quote!(#call.as_slice()),
+ Type::SliceRef(slice) => {
+ let inner = &slice.inner;
+ match slice.mutable {
+ false => quote!(#call.as_slice::<#inner>()),
+ true => quote!(#call.as_mut_slice::<#inner>()),
+ }
+ }
_ => call,
},
};
@@ -396,40 +650,74 @@ fn expand_cxx_function_shim(efn: &ExternFn, types: &Types) -> TokenStream {
}
};
let mut dispatch = quote!(#setup #expr);
+ let visibility = efn.visibility;
let unsafety = &efn.sig.unsafety;
if unsafety.is_none() {
dispatch = quote!(unsafe { #dispatch });
}
+ let fn_token = efn.sig.fn_token;
let ident = &efn.name.rust;
- let function_shim = quote! {
- #doc
- pub #unsafety fn #ident(#(#all_args,)*) #ret {
- extern "C" {
- #decl
- }
- #trampolines
- #dispatch
+ 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=> {
+ extern "C" {
+ #decl
}
- };
+ #trampolines
+ #dispatch
+ });
match &efn.receiver {
- None => function_shim,
+ None => {
+ quote! {
+ #doc
+ #attrs
+ #visibility #unsafety #fn_token #ident #generics #arg_list #ret #fn_body
+ }
+ }
Some(receiver) => {
- let receiver_type = &receiver.ty;
- quote!(impl #receiver_type { #function_shim })
+ let elided_generics;
+ let receiver_ident = &receiver.ty.rust;
+ let resolve = types.resolve(&receiver.ty);
+ let receiver_generics = if receiver.ty.generics.lt_token.is_some() {
+ &receiver.ty.generics
+ } else {
+ elided_generics = Lifetimes {
+ lt_token: resolve.generics.lt_token,
+ lifetimes: resolve
+ .generics
+ .lifetimes
+ .pairs()
+ .map(|pair| {
+ let lifetime = Lifetime::new("'_", pair.value().apostrophe);
+ let punct = pair.punct().map(|&&comma| comma);
+ punctuated::Pair::new(lifetime, punct)
+ })
+ .collect(),
+ gt_token: resolve.generics.gt_token,
+ };
+ &elided_generics
+ };
+ quote! {
+ impl #generics #receiver_ident #receiver_generics {
+ #doc
+ #attrs
+ #visibility #unsafety #fn_token #ident #arg_list #ret #fn_body
+ }
+ }
}
}
}
fn expand_function_pointer_trampoline(
efn: &ExternFn,
- var: &Ident,
+ var: &Pair,
sig: &Signature,
types: &Types,
) -> TokenStream {
let c_trampoline = mangle::c_trampoline(efn, var, types);
let r_trampoline = mangle::r_trampoline(efn, var, types);
let local_name = parse_quote!(__);
- let catch_unwind_label = format!("::{}::{}", efn.name.rust, var);
+ let catch_unwind_label = format!("::{}::{}", efn.name.rust, var.rust);
let shim = expand_rust_function_shim_impl(
sig,
types,
@@ -438,6 +726,7 @@ fn expand_function_pointer_trampoline(
catch_unwind_label,
None,
);
+ let var = &var.rust;
quote! {
let #var = ::cxx::private::FatFunction {
@@ -454,13 +743,43 @@ fn expand_function_pointer_trampoline(
}
}
-fn expand_rust_type(ety: &ExternType) -> TokenStream {
+fn expand_rust_type_import(ety: &ExternType) -> TokenStream {
let ident = &ety.name.rust;
- quote! {
+ let span = ident.span();
+
+ quote_spanned! {span=>
use super::#ident;
}
}
+fn expand_rust_type_impl(ety: &ExternType) -> TokenStream {
+ let ident = &ety.name.rust;
+ let generics = &ety.generics;
+ let span = ident.span();
+ let unsafe_impl = quote_spanned!(ety.type_token.span=> unsafe impl);
+
+ let mut impls = quote_spanned! {span=>
+ #[doc(hidden)]
+ #unsafe_impl #generics ::cxx::private::RustType for #ident #generics {}
+ };
+
+ for derive in &ety.derives {
+ if derive.what == Trait::ExternType {
+ let type_id = type_id(&ety.name);
+ let span = derive.span;
+ impls.extend(quote_spanned! {span=>
+ unsafe impl #generics ::cxx::ExternType for #ident #generics {
+ #[doc(hidden)]
+ type Id = #type_id;
+ type Kind = ::cxx::kind::Opaque;
+ }
+ });
+ }
+ }
+
+ impls
+}
+
fn expand_rust_type_assert_sized(ety: &ExternType) -> TokenStream {
// Rustc will render as follows if not sized:
//
@@ -472,21 +791,71 @@ fn expand_rust_type_assert_sized(ety: &ExternType) -> TokenStream {
let ident = &ety.name.rust;
let begin_span = Token![::](ety.type_token.span);
- let sized = quote_spanned! {ety.semi_token.span=>
- #begin_span std::marker::Sized
+ let unpin = quote_spanned! {ety.semi_token.span=>
+ #begin_span std::marker::Unpin
};
quote_spanned! {ident.span()=>
let _ = {
- fn __AssertSized<T: ?#sized + #sized>() {}
- __AssertSized::<#ident>
+ fn __AssertUnpin<T: ?::std::marker::Sized + #unpin>() {}
+ __AssertUnpin::<#ident>
};
}
}
+fn expand_rust_type_layout(ety: &ExternType) -> TokenStream {
+ let ident = &ety.name.rust;
+ let begin_span = Token![::](ety.type_token.span);
+ let sized = quote_spanned! {ety.semi_token.span=>
+ #begin_span std::marker::Sized
+ };
+
+ let link_sizeof = mangle::operator(&ety.name, "sizeof");
+ let link_alignof = mangle::operator(&ety.name, "alignof");
+
+ let local_sizeof = format_ident!("__sizeof_{}", ety.name.rust);
+ let local_alignof = format_ident!("__alignof_{}", ety.name.rust);
+
+ quote_spanned! {ident.span()=>
+ {
+ #[doc(hidden)]
+ fn __AssertSized<T: ?#sized + #sized>() -> ::std::alloc::Layout {
+ ::std::alloc::Layout::new::<T>()
+ }
+ #[doc(hidden)]
+ #[export_name = #link_sizeof]
+ extern "C" fn #local_sizeof() -> usize {
+ __AssertSized::<#ident>().size()
+ }
+ #[doc(hidden)]
+ #[export_name = #link_alignof]
+ extern "C" fn #local_alignof() -> usize {
+ __AssertSized::<#ident>().align()
+ }
+ }
+ }
+}
+
+fn expand_forbid(impls: TokenStream) -> TokenStream {
+ quote! {
+ mod forbid {
+ pub trait Drop {}
+ #[allow(drop_bounds)]
+ impl<T: ?::std::marker::Sized + ::std::ops::Drop> self::Drop for T {}
+ #impls
+ }
+ }
+}
+
fn expand_rust_function_shim(efn: &ExternFn, types: &Types) -> TokenStream {
let link_name = mangle::extern_fn(efn, types);
- let local_name = format_ident!("__{}", efn.name.rust);
- let catch_unwind_label = format!("::{}", efn.name.rust);
+ let local_name = match &efn.receiver {
+ None => format_ident!("__{}", efn.name.rust),
+ Some(receiver) => format_ident!("__{}__{}", receiver.ty.rust, efn.name.rust),
+ };
+ let catch_unwind_label = match &efn.receiver {
+ None => format!("::{}", efn.name.rust),
+ Some(receiver) => format!("::{}::{}", receiver.ty.rust, efn.name.rust),
+ };
let invoke = Some(&efn.name.rust);
expand_rust_function_shim_impl(
efn,
@@ -506,6 +875,7 @@ fn expand_rust_function_shim_impl(
catch_unwind_label: String,
invoke: Option<&Ident>,
) -> TokenStream {
+ let generics = &sig.generics;
let receiver_var = sig
.receiver
.as_ref()
@@ -515,7 +885,7 @@ fn expand_rust_function_shim_impl(
quote!(#receiver_var: #receiver_type)
});
let args = sig.args.iter().map(|arg| {
- let ident = &arg.ident;
+ let ident = &arg.name.rust;
let ty = expand_extern_type(&arg.ty, types, false);
if types.needs_indirect_abi(&arg.ty) {
quote!(#ident: *mut #ty)
@@ -526,7 +896,7 @@ fn expand_rust_function_shim_impl(
let all_args = receiver.into_iter().chain(args);
let arg_vars = sig.args.iter().map(|arg| {
- let ident = &arg.ident;
+ let ident = &arg.name.rust;
match &arg.ty {
Type::Ident(i) if i.rust == RustString => {
quote!(::std::mem::take((*#ident).as_mut_string()))
@@ -541,22 +911,28 @@ fn expand_rust_function_shim_impl(
}
Type::UniquePtr(_) => quote!(::cxx::UniquePtr::from_raw(#ident)),
Type::Ref(ty) => match &ty.inner {
- Type::Ident(i) if i.rust == RustString => match ty.mutability {
- None => quote!(#ident.as_string()),
- Some(_) => quote!(#ident.as_mut_string()),
+ Type::Ident(i) if i.rust == RustString => match ty.mutable {
+ false => quote!(#ident.as_string()),
+ true => quote!(#ident.as_mut_string()),
},
- Type::RustVec(vec) if vec.inner == RustString => match ty.mutability {
- None => quote!(#ident.as_vec_string()),
- Some(_) => quote!(#ident.as_mut_vec_string()),
+ Type::RustVec(vec) if vec.inner == RustString => match ty.mutable {
+ false => quote!(#ident.as_vec_string()),
+ true => quote!(#ident.as_mut_vec_string()),
},
- Type::RustVec(_) => match ty.mutability {
- None => quote!(#ident.as_vec()),
- Some(_) => quote!(#ident.as_mut_vec()),
+ Type::RustVec(_) => match ty.mutable {
+ false => quote!(#ident.as_vec()),
+ true => quote!(#ident.as_mut_vec()),
},
_ => quote!(#ident),
},
Type::Str(_) => quote!(#ident.as_str()),
- Type::SliceRefU8(_) => quote!(#ident.as_slice()),
+ Type::SliceRef(slice) => {
+ let inner = &slice.inner;
+ match slice.mutable {
+ false => quote!(#ident.as_slice::<#inner>()),
+ true => quote!(#ident.as_mut_slice::<#inner>()),
+ }
+ }
ty if types.needs_indirect_abi(ty) => quote!(::std::ptr::read(#ident)),
_ => quote!(#ident),
}
@@ -585,22 +961,25 @@ fn expand_rust_function_shim_impl(
}
Type::UniquePtr(_) => Some(quote!(::cxx::UniquePtr::into_raw)),
Type::Ref(ty) => match &ty.inner {
- Type::Ident(ident) if ident.rust == RustString => match ty.mutability {
- None => Some(quote!(::cxx::private::RustString::from_ref)),
- Some(_) => Some(quote!(::cxx::private::RustString::from_mut)),
+ 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)),
},
- Type::RustVec(vec) if vec.inner == RustString => match ty.mutability {
- None => Some(quote!(::cxx::private::RustVec::from_ref_vec_string)),
- Some(_) => Some(quote!(::cxx::private::RustVec::from_mut_vec_string)),
+ 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)),
},
- Type::RustVec(_) => match ty.mutability {
- None => Some(quote!(::cxx::private::RustVec::from_ref)),
- Some(_) => Some(quote!(::cxx::private::RustVec::from_mut)),
+ Type::RustVec(_) => match ty.mutable {
+ false => Some(quote!(::cxx::private::RustVec::from_ref)),
+ true => Some(quote!(::cxx::private::RustVec::from_mut)),
},
_ => None,
},
Type::Str(_) => Some(quote!(::cxx::private::RustStr::from)),
- Type::SliceRefU8(_) => Some(quote!(::cxx::private::RustSliceU8::from)),
+ Type::SliceRef(ty) => match ty.mutable {
+ false => Some(quote!(::cxx::private::RustSlice::from_ref)),
+ true => Some(quote!(::cxx::private::RustSlice::from_mut)),
+ },
_ => None,
});
@@ -642,7 +1021,7 @@ fn expand_rust_function_shim_impl(
quote! {
#[doc(hidden)]
#[export_name = #link_name]
- unsafe extern "C" fn #local_name(#(#all_args,)* #outparam #pointer) #ret {
+ unsafe extern "C" fn #local_name #generics(#(#all_args,)* #outparam #pointer) #ret {
let __fn = concat!(module_path!(), #catch_unwind_label);
#wrap_super
#expr
@@ -658,6 +1037,7 @@ fn expand_rust_function_shim_super(
invoke: &Ident,
) -> TokenStream {
let unsafety = sig.unsafety;
+ let generics = &sig.generics;
let receiver_var = sig
.receiver
@@ -682,20 +1062,20 @@ fn expand_rust_function_shim_super(
expand_return_type(&sig.ret)
};
- let arg_vars = sig.args.iter().map(|arg| &arg.ident);
+ let arg_vars = sig.args.iter().map(|arg| &arg.name.rust);
let vars = receiver_var.iter().chain(arg_vars);
let span = invoke.span();
let call = match &sig.receiver {
None => quote_spanned!(span=> super::#invoke),
Some(receiver) => {
- let receiver_type = &receiver.ty;
+ let receiver_type = &receiver.ty.rust;
quote_spanned!(span=> #receiver_type::#invoke)
}
};
quote_spanned! {span=>
- #unsafety fn #local_name(#(#all_args,)*) #ret {
+ #unsafety fn #local_name #generics(#(#all_args,)*) #ret {
#call(#(#vars,)*)
}
}
@@ -703,11 +1083,19 @@ fn expand_rust_function_shim_super(
fn expand_type_alias(alias: &TypeAlias) -> TokenStream {
let doc = &alias.doc;
+ let attrs = &alias.attrs;
+ let visibility = alias.visibility;
+ let type_token = alias.type_token;
let ident = &alias.name.rust;
+ let generics = &alias.generics;
+ let eq_token = alias.eq_token;
let ty = &alias.ty;
+ let semi_token = alias.semi_token;
+
quote! {
#doc
- pub type #ident = #ty;
+ #attrs
+ #visibility #type_token #ident #generics #eq_token #ty #semi_token
}
}
@@ -734,139 +1122,171 @@ fn expand_type_alias_verify(alias: &TypeAlias, types: &Types) -> TokenStream {
}
fn type_id(name: &Pair) -> TokenStream {
- let path = name.to_fully_qualified();
- quote! {
- ::cxx::type_id!(#path)
- }
+ let namespace_segments = name.namespace.iter();
+ let mut segments = Vec::with_capacity(namespace_segments.len() + 1);
+ segments.extend(namespace_segments.cloned());
+ segments.push(Ident::new(&name.cxx.to_string(), Span::call_site()));
+ let qualified = QualifiedName { segments };
+ crate::type_id::expand(Crate::Cxx, qualified)
}
-fn expand_rust_box(ident: &ResolvableName, types: &Types) -> TokenStream {
- let link_prefix = format!("cxxbridge05$box${}$", types.resolve(ident).to_symbol());
- let link_uninit = format!("{}uninit", link_prefix);
+fn expand_rust_box(key: NamedImplKey, types: &Types, explicit_impl: Option<&Impl>) -> TokenStream {
+ let ident = key.rust;
+ let resolve = types.resolve(ident);
+ let link_prefix = format!("cxxbridge1$box${}$", resolve.name.to_symbol());
+ let link_alloc = format!("{}alloc", link_prefix);
+ let link_dealloc = format!("{}dealloc", link_prefix);
let link_drop = format!("{}drop", link_prefix);
- let local_prefix = format_ident!("{}__box_", &ident.rust);
- let local_uninit = format_ident!("{}uninit", local_prefix);
+ let local_prefix = format_ident!("{}__box_", ident);
+ let local_alloc = format_ident!("{}alloc", local_prefix);
+ let local_dealloc = format_ident!("{}dealloc", local_prefix);
let local_drop = format_ident!("{}drop", local_prefix);
- let span = ident.span();
- quote_spanned! {span=>
+ let (impl_generics, ty_generics) = generics::split_for_impl(key, explicit_impl, resolve);
+
+ let begin_span = explicit_impl.map_or(key.begin_span, |explicit| explicit.impl_token.span);
+ let end_span = explicit_impl.map_or(key.end_span, |explicit| explicit.brace_token.span);
+ let unsafe_token = format_ident!("unsafe", span = begin_span);
+
+ quote_spanned! {end_span=>
+ #[doc(hidden)]
+ #unsafe_token impl #impl_generics ::cxx::private::ImplBox for #ident #ty_generics {}
#[doc(hidden)]
- #[export_name = #link_uninit]
- unsafe extern "C" fn #local_uninit(
- this: *mut ::std::boxed::Box<::std::mem::MaybeUninit<#ident>>,
- ) {
- ::std::ptr::write(
- this,
- ::std::boxed::Box::new(::std::mem::MaybeUninit::uninit()),
- );
+ #[export_name = #link_alloc]
+ unsafe extern "C" fn #local_alloc #impl_generics() -> *mut ::std::mem::MaybeUninit<#ident #ty_generics> {
+ ::std::boxed::Box::into_raw(::std::boxed::Box::new(::std::mem::MaybeUninit::uninit()))
+ }
+ #[doc(hidden)]
+ #[export_name = #link_dealloc]
+ unsafe extern "C" fn #local_dealloc #impl_generics(ptr: *mut ::std::mem::MaybeUninit<#ident #ty_generics>) {
+ ::std::boxed::Box::from_raw(ptr);
}
#[doc(hidden)]
#[export_name = #link_drop]
- unsafe extern "C" fn #local_drop(this: *mut ::std::boxed::Box<#ident>) {
+ unsafe extern "C" fn #local_drop #impl_generics(this: *mut ::std::boxed::Box<#ident #ty_generics>) {
::std::ptr::drop_in_place(this);
}
}
}
-fn expand_rust_vec(elem: &ResolvableName, types: &Types) -> TokenStream {
- let link_prefix = format!("cxxbridge05$rust_vec${}$", elem.to_symbol(types));
+fn expand_rust_vec(key: NamedImplKey, types: &Types, explicit_impl: Option<&Impl>) -> TokenStream {
+ let elem = key.rust;
+ let resolve = types.resolve(elem);
+ let link_prefix = format!("cxxbridge1$rust_vec${}$", resolve.name.to_symbol());
let link_new = format!("{}new", link_prefix);
let link_drop = format!("{}drop", link_prefix);
let link_len = format!("{}len", link_prefix);
+ let link_capacity = format!("{}capacity", link_prefix);
let link_data = format!("{}data", link_prefix);
let link_reserve_total = format!("{}reserve_total", link_prefix);
let link_set_len = format!("{}set_len", link_prefix);
- let link_stride = format!("{}stride", link_prefix);
- let local_prefix = format_ident!("{}__vec_", elem.rust);
+ let local_prefix = format_ident!("{}__vec_", elem);
let local_new = format_ident!("{}new", local_prefix);
let local_drop = format_ident!("{}drop", local_prefix);
let local_len = format_ident!("{}len", local_prefix);
+ let local_capacity = format_ident!("{}capacity", local_prefix);
let local_data = format_ident!("{}data", local_prefix);
let local_reserve_total = format_ident!("{}reserve_total", local_prefix);
let local_set_len = format_ident!("{}set_len", local_prefix);
- let local_stride = format_ident!("{}stride", local_prefix);
- let span = elem.span();
- quote_spanned! {span=>
+ let (impl_generics, ty_generics) = generics::split_for_impl(key, explicit_impl, resolve);
+
+ let begin_span = explicit_impl.map_or(key.begin_span, |explicit| explicit.impl_token.span);
+ let end_span = explicit_impl.map_or(key.end_span, |explicit| explicit.brace_token.span);
+ let unsafe_token = format_ident!("unsafe", span = begin_span);
+
+ quote_spanned! {end_span=>
+ #[doc(hidden)]
+ #unsafe_token impl #impl_generics ::cxx::private::ImplVec for #elem #ty_generics {}
#[doc(hidden)]
#[export_name = #link_new]
- unsafe extern "C" fn #local_new(this: *mut ::cxx::private::RustVec<#elem>) {
+ unsafe extern "C" fn #local_new #impl_generics(this: *mut ::cxx::private::RustVec<#elem #ty_generics>) {
::std::ptr::write(this, ::cxx::private::RustVec::new());
}
#[doc(hidden)]
#[export_name = #link_drop]
- unsafe extern "C" fn #local_drop(this: *mut ::cxx::private::RustVec<#elem>) {
+ unsafe extern "C" fn #local_drop #impl_generics(this: *mut ::cxx::private::RustVec<#elem #ty_generics>) {
::std::ptr::drop_in_place(this);
}
#[doc(hidden)]
#[export_name = #link_len]
- unsafe extern "C" fn #local_len(this: *const ::cxx::private::RustVec<#elem>) -> usize {
+ unsafe extern "C" fn #local_len #impl_generics(this: *const ::cxx::private::RustVec<#elem #ty_generics>) -> usize {
(*this).len()
}
#[doc(hidden)]
+ #[export_name = #link_capacity]
+ unsafe extern "C" fn #local_capacity #impl_generics(this: *const ::cxx::private::RustVec<#elem #ty_generics>) -> usize {
+ (*this).capacity()
+ }
+ #[doc(hidden)]
#[export_name = #link_data]
- unsafe extern "C" fn #local_data(this: *const ::cxx::private::RustVec<#elem>) -> *const #elem {
+ unsafe extern "C" fn #local_data #impl_generics(this: *const ::cxx::private::RustVec<#elem #ty_generics>) -> *const #elem #ty_generics {
(*this).as_ptr()
}
#[doc(hidden)]
#[export_name = #link_reserve_total]
- unsafe extern "C" fn #local_reserve_total(this: *mut ::cxx::private::RustVec<#elem>, cap: usize) {
+ unsafe extern "C" fn #local_reserve_total #impl_generics(this: *mut ::cxx::private::RustVec<#elem #ty_generics>, cap: usize) {
(*this).reserve_total(cap);
}
#[doc(hidden)]
#[export_name = #link_set_len]
- unsafe extern "C" fn #local_set_len(this: *mut ::cxx::private::RustVec<#elem>, len: usize) {
+ unsafe extern "C" fn #local_set_len #impl_generics(this: *mut ::cxx::private::RustVec<#elem #ty_generics>, len: usize) {
(*this).set_len(len);
}
- #[doc(hidden)]
- #[export_name = #link_stride]
- unsafe extern "C" fn #local_stride() -> usize {
- ::std::mem::size_of::<#elem>()
- }
}
}
fn expand_unique_ptr(
- ident: &ResolvableName,
+ key: NamedImplKey,
types: &Types,
explicit_impl: Option<&Impl>,
) -> TokenStream {
- let name = ident.rust.to_string();
- let prefix = format!("cxxbridge05$unique_ptr${}$", ident.to_symbol(types));
+ let ident = key.rust;
+ let name = ident.to_string();
+ let resolve = types.resolve(ident);
+ let prefix = format!("cxxbridge1$unique_ptr${}$", resolve.name.to_symbol());
let link_null = format!("{}null", prefix);
- let link_new = format!("{}new", prefix);
+ let link_uninit = format!("{}uninit", prefix);
let link_raw = format!("{}raw", prefix);
let link_get = format!("{}get", prefix);
let link_release = format!("{}release", prefix);
let link_drop = format!("{}drop", prefix);
- let new_method =
- if types.structs.contains_key(&ident.rust) || types.aliases.contains_key(&ident.rust) {
- Some(quote! {
- fn __new(mut value: Self) -> *mut ::std::ffi::c_void {
- extern "C" {
- #[link_name = #link_new]
- fn __new(this: *mut *mut ::std::ffi::c_void, value: *mut #ident);
- }
- let mut repr = ::std::ptr::null_mut::<::std::ffi::c_void>();
- unsafe { __new(&mut repr, &mut value) }
- repr
+ 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 new_method = if can_construct_from_value {
+ Some(quote! {
+ #[doc(hidden)]
+ fn __new(value: Self) -> *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;
}
- })
- } else {
- None
- };
+ let mut repr = ::std::ptr::null_mut::<::std::ffi::c_void>();
+ unsafe { __uninit(&mut repr).cast::<#ident #ty_generics>().write(value) }
+ repr
+ }
+ })
+ } else {
+ None
+ };
- let begin_span =
- explicit_impl.map_or_else(Span::call_site, |explicit| explicit.impl_token.span);
- let end_span = explicit_impl.map_or_else(Span::call_site, |explicit| explicit.brace_token.span);
+ let begin_span = explicit_impl.map_or(key.begin_span, |explicit| explicit.impl_token.span);
+ let end_span = explicit_impl.map_or(key.end_span, |explicit| explicit.brace_token.span);
let unsafe_token = format_ident!("unsafe", span = begin_span);
quote_spanned! {end_span=>
- #unsafe_token impl ::cxx::private::UniquePtrTarget for #ident {
- const __NAME: &'static dyn ::std::fmt::Display = &#name;
+ #unsafe_token impl #impl_generics ::cxx::private::UniquePtrTarget for #ident #ty_generics {
+ #[doc(hidden)]
+ fn __typename(f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
+ f.write_str(#name)
+ }
+ #[doc(hidden)]
fn __null() -> *mut ::std::ffi::c_void {
extern "C" {
#[link_name = #link_null]
@@ -877,29 +1297,33 @@ fn expand_unique_ptr(
repr
}
#new_method
+ #[doc(hidden)]
unsafe fn __raw(raw: *mut Self) -> *mut ::std::ffi::c_void {
extern "C" {
#[link_name = #link_raw]
- fn __raw(this: *mut *mut ::std::ffi::c_void, raw: *mut #ident);
+ fn __raw(this: *mut *mut ::std::ffi::c_void, raw: *mut ::std::ffi::c_void);
}
let mut repr = ::std::ptr::null_mut::<::std::ffi::c_void>();
- __raw(&mut repr, raw);
+ __raw(&mut repr, raw.cast());
repr
}
+ #[doc(hidden)]
unsafe fn __get(repr: *mut ::std::ffi::c_void) -> *const Self {
extern "C" {
#[link_name = #link_get]
- fn __get(this: *const *mut ::std::ffi::c_void) -> *const #ident;
+ fn __get(this: *const *mut ::std::ffi::c_void) -> *const ::std::ffi::c_void;
}
- __get(&repr)
+ __get(&repr).cast()
}
+ #[doc(hidden)]
unsafe fn __release(mut repr: *mut ::std::ffi::c_void) -> *mut Self {
extern "C" {
#[link_name = #link_release]
- fn __release(this: *mut *mut ::std::ffi::c_void) -> *mut #ident;
+ fn __release(this: *mut *mut ::std::ffi::c_void) -> *mut ::std::ffi::c_void;
}
- __release(&mut repr)
+ __release(&mut repr).cast()
}
+ #[doc(hidden)]
unsafe fn __drop(mut repr: *mut ::std::ffi::c_void) {
extern "C" {
#[link_name = #link_drop]
@@ -911,19 +1335,169 @@ fn expand_unique_ptr(
}
}
+fn expand_shared_ptr(
+ key: NamedImplKey,
+ types: &Types,
+ explicit_impl: Option<&Impl>,
+) -> TokenStream {
+ let ident = key.rust;
+ let name = ident.to_string();
+ let resolve = types.resolve(ident);
+ let prefix = format!("cxxbridge1$shared_ptr${}$", resolve.name.to_symbol());
+ let link_null = format!("{}null", prefix);
+ let link_uninit = format!("{}uninit", prefix);
+ let link_clone = format!("{}clone", prefix);
+ let link_get = format!("{}get", prefix);
+ let link_drop = format!("{}drop", prefix);
+
+ 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 new_method = if can_construct_from_value {
+ Some(quote! {
+ #[doc(hidden)]
+ unsafe fn __new(value: Self, new: *mut ::std::ffi::c_void) {
+ extern "C" {
+ #[link_name = #link_uninit]
+ fn __uninit(new: *mut ::std::ffi::c_void) -> *mut ::std::ffi::c_void;
+ }
+ __uninit(new).cast::<#ident #ty_generics>().write(value);
+ }
+ })
+ } else {
+ None
+ };
+
+ let begin_span = explicit_impl.map_or(key.begin_span, |explicit| explicit.impl_token.span);
+ let end_span = explicit_impl.map_or(key.end_span, |explicit| explicit.brace_token.span);
+ let unsafe_token = format_ident!("unsafe", span = begin_span);
+
+ quote_spanned! {end_span=>
+ #unsafe_token impl #impl_generics ::cxx::private::SharedPtrTarget for #ident #ty_generics {
+ #[doc(hidden)]
+ fn __typename(f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
+ f.write_str(#name)
+ }
+ #[doc(hidden)]
+ unsafe fn __null(new: *mut ::std::ffi::c_void) {
+ extern "C" {
+ #[link_name = #link_null]
+ fn __null(new: *mut ::std::ffi::c_void);
+ }
+ __null(new);
+ }
+ #new_method
+ #[doc(hidden)]
+ unsafe fn __clone(this: *const ::std::ffi::c_void, new: *mut ::std::ffi::c_void) {
+ extern "C" {
+ #[link_name = #link_clone]
+ fn __clone(this: *const ::std::ffi::c_void, new: *mut ::std::ffi::c_void);
+ }
+ __clone(this, new);
+ }
+ #[doc(hidden)]
+ unsafe fn __get(this: *const ::std::ffi::c_void) -> *const Self {
+ extern "C" {
+ #[link_name = #link_get]
+ fn __get(this: *const ::std::ffi::c_void) -> *const ::std::ffi::c_void;
+ }
+ __get(this).cast()
+ }
+ #[doc(hidden)]
+ unsafe fn __drop(this: *mut ::std::ffi::c_void) {
+ extern "C" {
+ #[link_name = #link_drop]
+ fn __drop(this: *mut ::std::ffi::c_void);
+ }
+ __drop(this);
+ }
+ }
+ }
+}
+
+fn expand_weak_ptr(key: NamedImplKey, types: &Types, explicit_impl: Option<&Impl>) -> TokenStream {
+ let ident = key.rust;
+ let name = ident.to_string();
+ let resolve = types.resolve(ident);
+ let prefix = format!("cxxbridge1$weak_ptr${}$", resolve.name.to_symbol());
+ let link_null = format!("{}null", prefix);
+ let link_clone = format!("{}clone", prefix);
+ let link_downgrade = format!("{}downgrade", prefix);
+ let link_upgrade = format!("{}upgrade", prefix);
+ let link_drop = format!("{}drop", prefix);
+
+ let (impl_generics, ty_generics) = generics::split_for_impl(key, explicit_impl, resolve);
+
+ let begin_span = explicit_impl.map_or(key.begin_span, |explicit| explicit.impl_token.span);
+ let end_span = explicit_impl.map_or(key.end_span, |explicit| explicit.brace_token.span);
+ let unsafe_token = format_ident!("unsafe", span = begin_span);
+
+ quote_spanned! {end_span=>
+ #unsafe_token impl #impl_generics ::cxx::private::WeakPtrTarget for #ident #ty_generics {
+ #[doc(hidden)]
+ fn __typename(f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
+ f.write_str(#name)
+ }
+ #[doc(hidden)]
+ unsafe fn __null(new: *mut ::std::ffi::c_void) {
+ extern "C" {
+ #[link_name = #link_null]
+ fn __null(new: *mut ::std::ffi::c_void);
+ }
+ __null(new);
+ }
+ #[doc(hidden)]
+ unsafe fn __clone(this: *const ::std::ffi::c_void, new: *mut ::std::ffi::c_void) {
+ extern "C" {
+ #[link_name = #link_clone]
+ fn __clone(this: *const ::std::ffi::c_void, new: *mut ::std::ffi::c_void);
+ }
+ __clone(this, new);
+ }
+ #[doc(hidden)]
+ unsafe fn __downgrade(shared: *const ::std::ffi::c_void, weak: *mut ::std::ffi::c_void) {
+ extern "C" {
+ #[link_name = #link_downgrade]
+ fn __downgrade(shared: *const ::std::ffi::c_void, weak: *mut ::std::ffi::c_void);
+ }
+ __downgrade(shared, weak);
+ }
+ #[doc(hidden)]
+ unsafe fn __upgrade(weak: *const ::std::ffi::c_void, shared: *mut ::std::ffi::c_void) {
+ extern "C" {
+ #[link_name = #link_upgrade]
+ fn __upgrade(weak: *const ::std::ffi::c_void, shared: *mut ::std::ffi::c_void);
+ }
+ __upgrade(weak, shared);
+ }
+ #[doc(hidden)]
+ unsafe fn __drop(this: *mut ::std::ffi::c_void) {
+ extern "C" {
+ #[link_name = #link_drop]
+ fn __drop(this: *mut ::std::ffi::c_void);
+ }
+ __drop(this);
+ }
+ }
+ }
+}
+
fn expand_cxx_vector(
- elem: &ResolvableName,
+ key: NamedImplKey,
explicit_impl: Option<&Impl>,
types: &Types,
) -> TokenStream {
- let _ = explicit_impl;
- let name = elem.rust.to_string();
- let prefix = format!("cxxbridge05$std$vector${}$", elem.to_symbol(types));
+ let elem = key.rust;
+ let name = elem.to_string();
+ let resolve = types.resolve(elem);
+ 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 unique_ptr_prefix = format!(
- "cxxbridge05$unique_ptr$std$vector${}$",
- elem.to_symbol(types)
+ "cxxbridge1$unique_ptr$std$vector${}$",
+ resolve.name.to_symbol(),
);
let link_unique_ptr_null = format!("{}null", unique_ptr_prefix);
let link_unique_ptr_raw = format!("{}raw", unique_ptr_prefix);
@@ -931,28 +1505,35 @@ fn expand_cxx_vector(
let link_unique_ptr_release = format!("{}release", unique_ptr_prefix);
let link_unique_ptr_drop = format!("{}drop", unique_ptr_prefix);
- let begin_span =
- explicit_impl.map_or_else(Span::call_site, |explicit| explicit.impl_token.span);
- let end_span = explicit_impl.map_or_else(Span::call_site, |explicit| explicit.brace_token.span);
+ let (impl_generics, ty_generics) = generics::split_for_impl(key, explicit_impl, resolve);
+
+ let begin_span = explicit_impl.map_or(key.begin_span, |explicit| explicit.impl_token.span);
+ let end_span = explicit_impl.map_or(key.end_span, |explicit| explicit.brace_token.span);
let unsafe_token = format_ident!("unsafe", span = begin_span);
quote_spanned! {end_span=>
- #unsafe_token impl ::cxx::private::VectorElement for #elem {
- const __NAME: &'static dyn ::std::fmt::Display = &#name;
+ #unsafe_token impl #impl_generics ::cxx::private::VectorElement for #elem #ty_generics {
+ #[doc(hidden)]
+ fn __typename(f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
+ f.write_str(#name)
+ }
+ #[doc(hidden)]
fn __vector_size(v: &::cxx::CxxVector<Self>) -> usize {
extern "C" {
#[link_name = #link_size]
- fn __vector_size(_: &::cxx::CxxVector<#elem>) -> usize;
+ fn __vector_size #impl_generics(_: &::cxx::CxxVector<#elem #ty_generics>) -> usize;
}
unsafe { __vector_size(v) }
}
- unsafe fn __get_unchecked(v: &::cxx::CxxVector<Self>, pos: usize) -> *const Self {
+ #[doc(hidden)]
+ unsafe fn __get_unchecked(v: *mut ::cxx::CxxVector<Self>, pos: usize) -> *mut Self {
extern "C" {
#[link_name = #link_get_unchecked]
- fn __get_unchecked(_: &::cxx::CxxVector<#elem>, _: usize) -> *const #elem;
+ fn __get_unchecked #impl_generics(_: *mut ::cxx::CxxVector<#elem #ty_generics>, _: usize) -> *mut #elem #ty_generics;
}
__get_unchecked(v, pos)
}
+ #[doc(hidden)]
fn __unique_ptr_null() -> *mut ::std::ffi::c_void {
extern "C" {
#[link_name = #link_unique_ptr_null]
@@ -962,29 +1543,33 @@ fn expand_cxx_vector(
unsafe { __unique_ptr_null(&mut repr) }
repr
}
+ #[doc(hidden)]
unsafe fn __unique_ptr_raw(raw: *mut ::cxx::CxxVector<Self>) -> *mut ::std::ffi::c_void {
extern "C" {
#[link_name = #link_unique_ptr_raw]
- fn __unique_ptr_raw(this: *mut *mut ::std::ffi::c_void, raw: *mut ::cxx::CxxVector<#elem>);
+ fn __unique_ptr_raw #impl_generics(this: *mut *mut ::std::ffi::c_void, raw: *mut ::cxx::CxxVector<#elem #ty_generics>);
}
let mut repr = ::std::ptr::null_mut::<::std::ffi::c_void>();
__unique_ptr_raw(&mut repr, raw);
repr
}
+ #[doc(hidden)]
unsafe fn __unique_ptr_get(repr: *mut ::std::ffi::c_void) -> *const ::cxx::CxxVector<Self> {
extern "C" {
#[link_name = #link_unique_ptr_get]
- fn __unique_ptr_get(this: *const *mut ::std::ffi::c_void) -> *const ::cxx::CxxVector<#elem>;
+ fn __unique_ptr_get #impl_generics(this: *const *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> {
extern "C" {
#[link_name = #link_unique_ptr_release]
- fn __unique_ptr_release(this: *mut *mut ::std::ffi::c_void) -> *mut ::cxx::CxxVector<#elem>;
+ fn __unique_ptr_release #impl_generics(this: *mut *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) {
extern "C" {
#[link_name = #link_unique_ptr_drop]
@@ -1030,15 +1615,24 @@ fn expand_extern_type(ty: &Type, types: &Types, proper: bool) -> TokenStream {
let inner = expand_extern_type(&ty.inner, types, proper);
quote!(&#mutability ::cxx::private::RustVec<#inner>)
}
- inner if proper && types.is_considered_improper_ctype(inner) => match mutability {
- None => quote!(*const ::std::ffi::c_void),
- Some(_) => quote!(*#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 mutability = ty.mutability;
+ let constness = ty.constness;
+ quote!(*#mutability #constness ::std::ffi::c_void)
+ } else {
+ quote!(#ty)
+ }
+ }
Type::Str(_) => quote!(::cxx::private::RustStr),
- Type::SliceRefU8(_) => quote!(::cxx::private::RustSliceU8),
+ Type::SliceRef(_) => quote!(::cxx::private::RustSlice),
_ => quote!(#ty),
}
}
diff --git a/macro/src/generics.rs b/macro/src/generics.rs
new file mode 100644
index 00000000..3de0330b
--- /dev/null
+++ b/macro/src/generics.rs
@@ -0,0 +1,63 @@
+use crate::syntax::instantiate::NamedImplKey;
+use crate::syntax::resolve::Resolution;
+use crate::syntax::Impl;
+use proc_macro2::TokenStream;
+use quote::ToTokens;
+use syn::Token;
+
+pub struct ImplGenerics<'a> {
+ explicit_impl: Option<&'a Impl>,
+ resolve: Resolution<'a>,
+}
+
+pub struct TyGenerics<'a> {
+ key: NamedImplKey<'a>,
+ explicit_impl: Option<&'a Impl>,
+ resolve: Resolution<'a>,
+}
+
+pub fn split_for_impl<'a>(
+ key: NamedImplKey<'a>,
+ explicit_impl: Option<&'a Impl>,
+ resolve: Resolution<'a>,
+) -> (ImplGenerics<'a>, TyGenerics<'a>) {
+ let impl_generics = ImplGenerics {
+ explicit_impl,
+ resolve,
+ };
+ let ty_generics = TyGenerics {
+ key,
+ explicit_impl,
+ resolve,
+ };
+ (impl_generics, ty_generics)
+}
+
+impl<'a> ToTokens for ImplGenerics<'a> {
+ fn to_tokens(&self, tokens: &mut TokenStream) {
+ if let Some(imp) = self.explicit_impl {
+ imp.impl_generics.to_tokens(tokens);
+ } else {
+ self.resolve.generics.to_tokens(tokens);
+ }
+ }
+}
+
+impl<'a> ToTokens for TyGenerics<'a> {
+ fn to_tokens(&self, tokens: &mut TokenStream) {
+ if let Some(imp) = self.explicit_impl {
+ imp.ty_generics.to_tokens(tokens);
+ } else if !self.resolve.generics.lifetimes.is_empty() {
+ let span = self.key.rust.span();
+ self.key
+ .lt_token
+ .unwrap_or_else(|| Token![<](span))
+ .to_tokens(tokens);
+ self.resolve.generics.lifetimes.to_tokens(tokens);
+ self.key
+ .gt_token
+ .unwrap_or_else(|| Token![>](span))
+ .to_tokens(tokens);
+ }
+ }
+}
diff --git a/macro/src/lib.rs b/macro/src/lib.rs
index e55a08ac..f8e79034 100644
--- a/macro/src/lib.rs
+++ b/macro/src/lib.rs
@@ -1,22 +1,45 @@
#![allow(
+ clippy::cast_sign_loss,
+ clippy::default_trait_access,
+ clippy::doc_markdown,
+ clippy::enum_glob_use,
+ clippy::filter_map,
clippy::inherent_to_string,
+ clippy::items_after_statements,
clippy::large_enum_variant,
+ clippy::match_bool,
+ clippy::match_same_arms,
+ clippy::module_name_repetitions,
+ clippy::needless_pass_by_value,
clippy::new_without_default,
+ clippy::nonminimal_bool,
+ clippy::option_if_let_else,
clippy::or_fun_call,
+ clippy::redundant_else,
+ clippy::shadow_unrelated,
+ clippy::similar_names,
+ clippy::single_match,
+ clippy::single_match_else,
+ clippy::too_many_arguments,
+ clippy::too_many_lines,
clippy::toplevel_ref_arg,
- clippy::useless_let_if_seq
+ clippy::useless_let_if_seq,
+ // clippy bug: https://github.com/rust-lang/rust-clippy/issues/6983
+ clippy::wrong_self_convention
)]
extern crate proc_macro;
mod derive;
mod expand;
+mod generics;
mod syntax;
mod type_id;
use crate::syntax::file::Module;
use crate::syntax::namespace::Namespace;
use crate::syntax::qualified::QualifiedName;
+use crate::type_id::Crate;
use proc_macro::TokenStream;
use syn::parse::{Parse, ParseStream, Parser, Result};
use syn::parse_macro_input;
@@ -27,7 +50,7 @@ use syn::parse_macro_input;
/// is intended to be used.
///
/// The only additional thing to note here is namespace support &mdash; if the
-/// types and functions on the `extern "C"` side of our bridge are in a
+/// types and functions on the `extern "C++"` side of our bridge are in a
/// namespace, specify that namespace as an argument of the cxx::bridge
/// attribute macro.
///
@@ -54,16 +77,22 @@ pub fn bridge(args: TokenStream, input: TokenStream) -> TokenStream {
.into()
}
+#[doc(hidden)]
#[proc_macro]
pub fn type_id(input: TokenStream) -> TokenStream {
- struct TypeId(QualifiedName);
+ struct TypeId {
+ krate: Crate,
+ path: QualifiedName,
+ }
impl Parse for TypeId {
fn parse(input: ParseStream) -> Result<Self> {
- QualifiedName::parse_quoted_or_unquoted(input).map(TypeId)
+ let krate = input.parse().map(Crate::DollarCrate)?;
+ let path = QualifiedName::parse_quoted_or_unquoted(input)?;
+ Ok(TypeId { krate, path })
}
}
let arg = parse_macro_input!(input as TypeId);
- type_id::expand(arg.0).into()
+ type_id::expand(arg.krate, arg.path).into()
}
diff --git a/macro/src/type_id.rs b/macro/src/type_id.rs
index 5c5d9ccc..74f0c1c9 100644
--- a/macro/src/type_id.rs
+++ b/macro/src/type_id.rs
@@ -1,9 +1,23 @@
use crate::syntax::qualified::QualifiedName;
-use proc_macro2::TokenStream;
-use quote::{format_ident, quote};
+use proc_macro2::{TokenStream, TokenTree};
+use quote::{format_ident, quote, ToTokens};
+
+pub enum Crate {
+ Cxx,
+ DollarCrate(TokenTree),
+}
+
+impl ToTokens for Crate {
+ fn to_tokens(&self, tokens: &mut TokenStream) {
+ match self {
+ Crate::Cxx => tokens.extend(quote!(::cxx)),
+ Crate::DollarCrate(krate) => krate.to_tokens(tokens),
+ }
+ }
+}
// "folly::File" => `(f, o, l, l, y, (), F, i, l, e)`
-pub fn expand(arg: QualifiedName) -> TokenStream {
+pub fn expand(krate: Crate, arg: QualifiedName) -> TokenStream {
let mut ids = Vec::new();
for word in arg.segments {
@@ -14,11 +28,11 @@ pub fn expand(arg: QualifiedName) -> TokenStream {
ids.push(match ch {
'A'..='Z' | 'a'..='z' => {
let t = format_ident!("{}", ch);
- quote!(::cxx::#t)
+ quote!(#krate::#t)
}
'0'..='9' | '_' => {
let t = format_ident!("_{}", ch);
- quote!(::cxx::#t)
+ quote!(#krate::#t)
}
_ => quote!([(); #ch as _]),
});
diff --git a/src/cxx.cc b/src/cxx.cc
index cbaecbf4..c0ef7389 100644
--- a/src/cxx.cc
+++ b/src/cxx.cc
@@ -1,38 +1,60 @@
#include "../include/cxx.h"
-#include <cassert>
#include <cstring>
-#include <exception>
#include <iostream>
#include <memory>
-#include <stdexcept>
-#include <type_traits>
-#include <vector>
extern "C" {
-const char *cxxbridge05$cxx_string$data(const std::string &s) noexcept {
+void cxxbridge1$cxx_string$init(std::string *s, const std::uint8_t *ptr,
+ std::size_t len) noexcept {
+ new (s) std::string(reinterpret_cast<const char *>(ptr), len);
+}
+
+void cxxbridge1$cxx_string$destroy(std::string *s) noexcept {
+ using std::string;
+ s->~string();
+}
+
+const char *cxxbridge1$cxx_string$data(const std::string &s) noexcept {
return s.data();
}
-size_t cxxbridge05$cxx_string$length(const std::string &s) noexcept {
+std::size_t cxxbridge1$cxx_string$length(const std::string &s) noexcept {
return s.length();
}
+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);
+}
+
// rust::String
-void cxxbridge05$string$new(rust::String *self) noexcept;
-void cxxbridge05$string$clone(rust::String *self,
- const rust::String &other) noexcept;
-bool cxxbridge05$string$from(rust::String *self, const char *ptr,
- size_t len) noexcept;
-void cxxbridge05$string$drop(rust::String *self) noexcept;
-const char *cxxbridge05$string$ptr(const rust::String *self) noexcept;
-size_t cxxbridge05$string$len(const rust::String *self) noexcept;
+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;
+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;
// rust::Str
-bool cxxbridge05$str$valid(const char *ptr, size_t len) noexcept;
+void cxxbridge1$str$new(rust::Str *self) noexcept;
+void cxxbridge1$str$ref(rust::Str *self, const rust::String *string) noexcept;
+bool cxxbridge1$str$from(rust::Str *self, const char *ptr,
+ std::size_t len) noexcept;
+const char *cxxbridge1$str$ptr(const rust::Str *self) noexcept;
+std::size_t cxxbridge1$str$len(const rust::Str *self) noexcept;
+
+// rust::Slice
+void cxxbridge1$slice$new(void *self, const void *ptr,
+ std::size_t len) noexcept;
+void *cxxbridge1$slice$ptr(const void *self) noexcept;
+std::size_t cxxbridge1$slice$len(const void *self) noexcept;
} // extern "C"
namespace rust {
-inline namespace cxxbridge05 {
+inline namespace cxxbridge1 {
template <typename Exception>
void panic [[noreturn]] (const char *msg) {
@@ -44,23 +66,22 @@ void panic [[noreturn]] (const char *msg) {
#endif
}
-template void panic<std::out_of_range>[[noreturn]] (const char *msg);
+template void panic<std::out_of_range> [[noreturn]] (const char *msg);
-String::String() noexcept { cxxbridge05$string$new(this); }
+String::String() noexcept { cxxbridge1$string$new(this); }
String::String(const String &other) noexcept {
- cxxbridge05$string$clone(this, other);
+ cxxbridge1$string$clone(this, other);
}
-String::String(String &&other) noexcept {
- this->repr = other.repr;
- cxxbridge05$string$new(&other);
+String::String(String &&other) noexcept : repr(other.repr) {
+ cxxbridge1$string$new(&other);
}
-String::~String() noexcept { cxxbridge05$string$drop(this); }
+String::~String() noexcept { cxxbridge1$string$drop(this); }
-static void initString(String *self, const char *s, size_t len) {
- if (!cxxbridge05$string$from(self, s, len)) {
+static void initString(String *self, const char *s, std::size_t len) {
+ if (!cxxbridge1$string$from(self, s, len)) {
panic<std::invalid_argument>("data for rust::String is not utf-8");
}
}
@@ -72,27 +93,25 @@ String::String(const char *s) {
initString(this, s, std::strlen(s));
}
-String::String(const char *s, size_t len) {
+String::String(const char *s, std::size_t len) {
assert(s != nullptr || len == 0);
initString(this,
s == nullptr && len == 0 ? reinterpret_cast<const char *>(1) : s,
len);
}
-String &String::operator=(const String &other) noexcept {
+String &String::operator=(const String &other) &noexcept {
if (this != &other) {
- cxxbridge05$string$drop(this);
- cxxbridge05$string$clone(this, other);
+ cxxbridge1$string$drop(this);
+ cxxbridge1$string$clone(this, other);
}
return *this;
}
-String &String::operator=(String &&other) noexcept {
- if (this != &other) {
- cxxbridge05$string$drop(this);
- this->repr = other.repr;
- cxxbridge05$string$new(&other);
- }
+String &String::operator=(String &&other) &noexcept {
+ cxxbridge1$string$drop(this);
+ this->repr = other.repr;
+ cxxbridge1$string$new(&other);
return *this;
}
@@ -101,12 +120,71 @@ String::operator std::string() const {
}
const char *String::data() const noexcept {
- return cxxbridge05$string$ptr(this);
+ return cxxbridge1$string$ptr(this);
+}
+
+std::size_t String::size() const noexcept {
+ return cxxbridge1$string$len(this);
}
-size_t String::size() const noexcept { return cxxbridge05$string$len(this); }
+std::size_t String::length() const noexcept {
+ return cxxbridge1$string$len(this);
+}
+
+const char *String::c_str() noexcept {
+ auto len = this->length();
+ cxxbridge1$string$reserve_total(this, len + 1);
+ auto ptr = this->data();
+ const_cast<char *>(ptr)[len] = '\0';
+ return ptr;
+}
+
+String::iterator String::begin() noexcept {
+ return const_cast<char *>(this->data());
+}
+
+String::iterator String::end() noexcept {
+ return const_cast<char *>(this->data()) + this->size();
+}
+
+String::const_iterator String::begin() const noexcept { return this->cbegin(); }
+
+String::const_iterator String::end() const noexcept { return this->cend(); }
-size_t String::length() const noexcept { return cxxbridge05$string$len(this); }
+String::const_iterator String::cbegin() const noexcept { return this->data(); }
+
+String::const_iterator String::cend() const noexcept {
+ return this->data() + this->size();
+}
+
+bool String::operator==(const String &rhs) const noexcept {
+ return rust::Str(*this) == rust::Str(rhs);
+}
+
+bool String::operator!=(const String &rhs) const noexcept {
+ return rust::Str(*this) != rust::Str(rhs);
+}
+
+bool String::operator<(const String &rhs) const noexcept {
+ return rust::Str(*this) < rust::Str(rhs);
+}
+
+bool String::operator<=(const String &rhs) const noexcept {
+ return rust::Str(*this) <= rust::Str(rhs);
+}
+
+bool String::operator>(const String &rhs) const noexcept {
+ return rust::Str(*this) > rust::Str(rhs);
+}
+
+bool String::operator>=(const String &rhs) const noexcept {
+ return rust::Str(*this) >= rust::Str(rhs);
+}
+
+void String::swap(String &rhs) noexcept {
+ using std::swap;
+ swap(this->repr, rhs.repr);
+}
String::String(unsafe_bitcopy_t, const String &bits) noexcept
: repr(bits.repr) {}
@@ -116,55 +194,178 @@ std::ostream &operator<<(std::ostream &os, const String &s) {
return os;
}
-Str::Str() noexcept : ptr(reinterpret_cast<const char *>(1)), len(0) {}
+Str::Str() noexcept { cxxbridge1$str$new(this); }
+
+Str::Str(const String &s) noexcept { cxxbridge1$str$ref(this, &s); }
-static void initStr(const char *ptr, size_t len) {
- if (!cxxbridge05$str$valid(ptr, len)) {
+static void initStr(Str *self, const char *ptr, std::size_t len) {
+ if (!cxxbridge1$str$from(self, ptr, len)) {
panic<std::invalid_argument>("data for rust::Str is not utf-8");
}
}
-Str::Str(const std::string &s) : ptr(s.data()), len(s.length()) {
- initStr(this->ptr, this->len);
-}
+Str::Str(const std::string &s) { initStr(this, s.data(), s.length()); }
-Str::Str(const char *s) : ptr(s), len(std::strlen(s)) {
+Str::Str(const char *s) {
assert(s != nullptr);
- initStr(this->ptr, this->len);
+ initStr(this, s, std::strlen(s));
}
-Str::Str(const char *s, size_t len)
- : ptr(s == nullptr && len == 0 ? reinterpret_cast<const char *>(1) : s),
- len(len) {
+Str::Str(const char *s, std::size_t len) {
assert(s != nullptr || len == 0);
- initStr(this->ptr, this->len);
+ initStr(this,
+ s == nullptr && len == 0 ? reinterpret_cast<const char *>(1) : s,
+ len);
}
Str::operator std::string() const {
return std::string(this->data(), this->size());
}
+const char *Str::data() const noexcept { return cxxbridge1$str$ptr(this); }
+
+std::size_t Str::size() const noexcept { return cxxbridge1$str$len(this); }
+
+std::size_t Str::length() const noexcept { return this->size(); }
+
+Str::const_iterator Str::begin() const noexcept { return this->cbegin(); }
+
+Str::const_iterator Str::end() const noexcept { return this->cend(); }
+
+Str::const_iterator Str::cbegin() const noexcept { return this->data(); }
+
+Str::const_iterator Str::cend() const noexcept {
+ return this->data() + this->size();
+}
+
+bool Str::operator==(const Str &rhs) const noexcept {
+ return this->size() == rhs.size() &&
+ std::equal(this->begin(), this->end(), rhs.begin());
+}
+
+bool Str::operator!=(const Str &rhs) const noexcept { return !(*this == rhs); }
+
+bool Str::operator<(const Str &rhs) const noexcept {
+ return std::lexicographical_compare(this->begin(), this->end(), rhs.begin(),
+ rhs.end());
+}
+
+bool Str::operator<=(const Str &rhs) const noexcept {
+ // std::mismatch(this->begin(), this->end(), rhs.begin(), rhs.end()), except
+ // without Undefined Behavior on C++11 if rhs is shorter than *this.
+ const_iterator liter = this->begin(), lend = this->end(), riter = rhs.begin(),
+ rend = rhs.end();
+ while (liter != lend && riter != rend && *liter == *riter) {
+ ++liter, ++riter;
+ }
+ if (liter == lend) {
+ return true; // equal or *this is a prefix of rhs
+ } else if (riter == rend) {
+ return false; // rhs is a prefix of *this
+ } else {
+ return *liter <= *riter;
+ }
+}
+
+bool Str::operator>(const Str &rhs) const noexcept { return rhs < *this; }
+
+bool Str::operator>=(const Str &rhs) const noexcept { return rhs <= *this; }
+
+void Str::swap(Str &rhs) noexcept {
+ using std::swap;
+ swap(this->repr, rhs.repr);
+}
+
std::ostream &operator<<(std::ostream &os, const Str &s) {
os.write(s.data(), s.size());
return os;
}
+void sliceInit(void *self, const void *ptr, std::size_t len) noexcept {
+ cxxbridge1$slice$new(self, ptr, len);
+}
+
+void *slicePtr(const void *self) noexcept { return cxxbridge1$slice$ptr(self); }
+
+std::size_t sliceLen(const void *self) noexcept {
+ return cxxbridge1$slice$len(self);
+}
+
+// Rust specifies that usize is ABI compatible with C's uintptr_t.
+// https://rust-lang.github.io/unsafe-code-guidelines/layout/scalars.html#isize-and-usize
+// However there is no direct Rust equivalent for size_t. C does not guarantee
+// that size_t and uintptr_t are compatible. In practice though, on all
+// platforms supported by Rust, they are identical for ABI purposes. See the
+// libc crate which unconditionally defines libc::size_t = usize. We expect the
+// same here and these assertions are just here to explicitly document that.
+// *Note that no assumption is made about C++ name mangling of signatures
+// containing these types, not here nor anywhere in CXX.*
+static_assert(sizeof(std::size_t) == sizeof(std::uintptr_t),
+ "unsupported size_t size");
+static_assert(alignof(std::size_t) == alignof(std::uintptr_t),
+ "unsupported size_t alignment");
+static_assert(sizeof(rust::isize) == sizeof(std::intptr_t),
+ "unsupported ssize_t size");
+static_assert(alignof(rust::isize) == alignof(std::intptr_t),
+ "unsupported ssize_t alignment");
+
static_assert(std::is_trivially_copy_constructible<Str>::value,
"trivial Str(const Str &)");
static_assert(std::is_trivially_copy_assignable<Str>::value,
"trivial operator=(const Str &)");
static_assert(std::is_trivially_destructible<Str>::value, "trivial ~Str()");
-extern "C" {
-const char *cxxbridge05$error(const char *ptr, size_t len) {
+static_assert(
+ std::is_trivially_copy_constructible<Slice<const std::uint8_t>>::value,
+ "trivial Slice(const Slice &)");
+static_assert(
+ std::is_trivially_move_constructible<Slice<const std::uint8_t>>::value,
+ "trivial Slice(Slice &&)");
+static_assert(
+ std::is_trivially_copy_assignable<Slice<const std::uint8_t>>::value,
+ "trivial Slice::operator=(const Slice &) for const slices");
+static_assert(
+ std::is_trivially_move_assignable<Slice<const std::uint8_t>>::value,
+ "trivial Slice::operator=(Slice &&)");
+static_assert(std::is_trivially_destructible<Slice<const std::uint8_t>>::value,
+ "trivial ~Slice()");
+
+static_assert(std::is_trivially_copy_constructible<Slice<std::uint8_t>>::value,
+ "trivial Slice(const Slice &)");
+static_assert(std::is_trivially_move_constructible<Slice<std::uint8_t>>::value,
+ "trivial Slice(Slice &&)");
+static_assert(!std::is_copy_assignable<Slice<std::uint8_t>>::value,
+ "delete Slice::operator=(const Slice &) for mut slices");
+static_assert(std::is_trivially_move_assignable<Slice<std::uint8_t>>::value,
+ "trivial Slice::operator=(Slice &&)");
+static_assert(std::is_trivially_destructible<Slice<std::uint8_t>>::value,
+ "trivial ~Slice()");
+
+static_assert(std::is_same<Vec<std::uint8_t>::const_iterator,
+ Vec<const std::uint8_t>::iterator>::value,
+ "Vec<T>::const_iterator == Vec<const T>::iterator");
+static_assert(std::is_same<Vec<const std::uint8_t>::const_iterator,
+ Vec<const std::uint8_t>::iterator>::value,
+ "Vec<const T>::const_iterator == Vec<const T>::iterator");
+static_assert(!std::is_same<Vec<std::uint8_t>::const_iterator,
+ Vec<std::uint8_t>::iterator>::value,
+ "Vec<T>::const_iterator != Vec<T>::iterator");
+
+static const char *errorCopy(const char *ptr, std::size_t len) {
char *copy = new char[len];
- std::strncpy(copy, ptr, len);
+ std::memcpy(copy, ptr, len);
return copy;
}
+
+extern "C" {
+const char *cxxbridge1$error(const char *ptr, std::size_t len) noexcept {
+ return errorCopy(ptr, len);
+}
} // extern "C"
Error::Error(const Error &other)
- : std::exception(other), msg(cxxbridge05$error(other.msg, other.len)),
+ : std::exception(other),
+ msg(other.msg ? errorCopy(other.msg, other.len) : nullptr),
len(other.len) {}
Error::Error(Error &&other) noexcept
@@ -175,165 +376,263 @@ Error::Error(Error &&other) noexcept
Error::~Error() noexcept { delete[] this->msg; }
-Error &Error::operator=(const Error &other) {
+Error &Error::operator=(const Error &other) & {
if (this != &other) {
std::exception::operator=(other);
delete[] this->msg;
this->msg = nullptr;
- this->msg = cxxbridge05$error(other.msg, other.len);
- this->len = other.len;
+ if (other.msg) {
+ this->msg = errorCopy(other.msg, other.len);
+ this->len = other.len;
+ }
}
return *this;
}
-Error &Error::operator=(Error &&other) noexcept {
- if (this != &other) {
- std::exception::operator=(std::move(other));
- this->msg = other.msg;
- this->len = other.len;
- other.msg = nullptr;
- other.len = 0;
- }
+Error &Error::operator=(Error &&other) &noexcept {
+ std::exception::operator=(std::move(other));
+ this->msg = other.msg;
+ this->len = other.len;
+ other.msg = nullptr;
+ other.len = 0;
return *this;
}
const char *Error::what() const noexcept { return this->msg; }
-} // namespace cxxbridge05
+namespace {
+template <typename T>
+union MaybeUninit {
+ T value;
+ MaybeUninit() {}
+ ~MaybeUninit() {}
+};
+} // namespace
+
+namespace detail {
+// On some platforms size_t is the same C++ type as one of the sized integer
+// types; on others it is a distinct type. Only in the latter case do we need to
+// define a specialized impl of rust::Vec<size_t>, because in the former case it
+// would collide with one of the other specializations.
+using usize_if_unique =
+ typename std::conditional<std::is_same<size_t, uint64_t>::value ||
+ std::is_same<size_t, uint32_t>::value,
+ struct usize_ignore, size_t>::type;
+using isize_if_unique =
+ typename std::conditional<std::is_same<rust::isize, int64_t>::value ||
+ std::is_same<rust::isize, int32_t>::value,
+ struct isize_ignore, rust::isize>::type;
+} // namespace detail
+
+} // namespace cxxbridge1
} // namespace rust
extern "C" {
-void cxxbridge05$unique_ptr$std$string$null(
+void cxxbridge1$unique_ptr$std$string$null(
std::unique_ptr<std::string> *ptr) noexcept {
new (ptr) std::unique_ptr<std::string>();
}
-void cxxbridge05$unique_ptr$std$string$raw(std::unique_ptr<std::string> *ptr,
- std::string *raw) noexcept {
+void cxxbridge1$unique_ptr$std$string$raw(std::unique_ptr<std::string> *ptr,
+ std::string *raw) noexcept {
new (ptr) std::unique_ptr<std::string>(raw);
}
-const std::string *cxxbridge05$unique_ptr$std$string$get(
+const std::string *cxxbridge1$unique_ptr$std$string$get(
const std::unique_ptr<std::string> &ptr) noexcept {
return ptr.get();
}
-std::string *cxxbridge05$unique_ptr$std$string$release(
+std::string *cxxbridge1$unique_ptr$std$string$release(
std::unique_ptr<std::string> &ptr) noexcept {
return ptr.release();
}
-void cxxbridge05$unique_ptr$std$string$drop(
+void cxxbridge1$unique_ptr$std$string$drop(
std::unique_ptr<std::string> *ptr) noexcept {
ptr->~unique_ptr();
}
} // extern "C"
+namespace {
+const std::size_t kMaxExpectedWordsInString = 8;
+static_assert(alignof(std::string) <= alignof(void *),
+ "unexpectedly large std::string alignment");
+static_assert(sizeof(std::string) <= kMaxExpectedWordsInString * sizeof(void *),
+ "unexpectedly large std::string size");
+} // namespace
+
#define STD_VECTOR_OPS(RUST_TYPE, CXX_TYPE) \
- size_t cxxbridge05$std$vector$##RUST_TYPE##$size( \
+ std::size_t cxxbridge1$std$vector$##RUST_TYPE##$size( \
const std::vector<CXX_TYPE> &s) noexcept { \
return s.size(); \
} \
- const CXX_TYPE *cxxbridge05$std$vector$##RUST_TYPE##$get_unchecked( \
- const std::vector<CXX_TYPE> &s, size_t pos) noexcept { \
- return &s[pos]; \
+ CXX_TYPE *cxxbridge1$std$vector$##RUST_TYPE##$get_unchecked( \
+ std::vector<CXX_TYPE> *s, std::size_t pos) noexcept { \
+ return &(*s)[pos]; \
} \
- void cxxbridge05$unique_ptr$std$vector$##RUST_TYPE##$null( \
+ void cxxbridge1$unique_ptr$std$vector$##RUST_TYPE##$null( \
std::unique_ptr<std::vector<CXX_TYPE>> *ptr) noexcept { \
new (ptr) std::unique_ptr<std::vector<CXX_TYPE>>(); \
} \
- void cxxbridge05$unique_ptr$std$vector$##RUST_TYPE##$raw( \
+ void cxxbridge1$unique_ptr$std$vector$##RUST_TYPE##$raw( \
std::unique_ptr<std::vector<CXX_TYPE>> *ptr, \
std::vector<CXX_TYPE> *raw) noexcept { \
new (ptr) std::unique_ptr<std::vector<CXX_TYPE>>(raw); \
} \
const std::vector<CXX_TYPE> \
- *cxxbridge05$unique_ptr$std$vector$##RUST_TYPE##$get( \
+ *cxxbridge1$unique_ptr$std$vector$##RUST_TYPE##$get( \
const std::unique_ptr<std::vector<CXX_TYPE>> &ptr) noexcept { \
return ptr.get(); \
} \
std::vector<CXX_TYPE> \
- *cxxbridge05$unique_ptr$std$vector$##RUST_TYPE##$release( \
+ *cxxbridge1$unique_ptr$std$vector$##RUST_TYPE##$release( \
std::unique_ptr<std::vector<CXX_TYPE>> &ptr) noexcept { \
return ptr.release(); \
} \
- void cxxbridge05$unique_ptr$std$vector$##RUST_TYPE##$drop( \
+ void cxxbridge1$unique_ptr$std$vector$##RUST_TYPE##$drop( \
std::unique_ptr<std::vector<CXX_TYPE>> *ptr) noexcept { \
ptr->~unique_ptr(); \
}
#define RUST_VEC_EXTERNS(RUST_TYPE, CXX_TYPE) \
- void cxxbridge05$rust_vec$##RUST_TYPE##$new( \
+ void cxxbridge1$rust_vec$##RUST_TYPE##$new( \
rust::Vec<CXX_TYPE> *ptr) noexcept; \
- void cxxbridge05$rust_vec$##RUST_TYPE##$drop( \
+ void cxxbridge1$rust_vec$##RUST_TYPE##$drop( \
rust::Vec<CXX_TYPE> *ptr) noexcept; \
- size_t cxxbridge05$rust_vec$##RUST_TYPE##$len( \
+ std::size_t cxxbridge1$rust_vec$##RUST_TYPE##$len( \
+ const rust::Vec<CXX_TYPE> *ptr) noexcept; \
+ std::size_t cxxbridge1$rust_vec$##RUST_TYPE##$capacity( \
const rust::Vec<CXX_TYPE> *ptr) noexcept; \
- const CXX_TYPE *cxxbridge05$rust_vec$##RUST_TYPE##$data( \
+ const CXX_TYPE *cxxbridge1$rust_vec$##RUST_TYPE##$data( \
const rust::Vec<CXX_TYPE> *ptr) noexcept; \
- void cxxbridge05$rust_vec$##RUST_TYPE##$reserve_total( \
- rust::Vec<CXX_TYPE> *ptr, size_t cap) noexcept; \
- void cxxbridge05$rust_vec$##RUST_TYPE##$set_len(rust::Vec<CXX_TYPE> *ptr, \
- size_t len) noexcept; \
- size_t cxxbridge05$rust_vec$##RUST_TYPE##$stride() noexcept;
+ void cxxbridge1$rust_vec$##RUST_TYPE##$reserve_total( \
+ rust::Vec<CXX_TYPE> *ptr, std::size_t cap) noexcept; \
+ void cxxbridge1$rust_vec$##RUST_TYPE##$set_len(rust::Vec<CXX_TYPE> *ptr, \
+ std::size_t len) noexcept;
#define RUST_VEC_OPS(RUST_TYPE, CXX_TYPE) \
template <> \
Vec<CXX_TYPE>::Vec() noexcept { \
- cxxbridge05$rust_vec$##RUST_TYPE##$new(this); \
+ cxxbridge1$rust_vec$##RUST_TYPE##$new(this); \
} \
template <> \
void Vec<CXX_TYPE>::drop() noexcept { \
- return cxxbridge05$rust_vec$##RUST_TYPE##$drop(this); \
+ return cxxbridge1$rust_vec$##RUST_TYPE##$drop(this); \
} \
template <> \
- size_t Vec<CXX_TYPE>::size() const noexcept { \
- return cxxbridge05$rust_vec$##RUST_TYPE##$len(this); \
+ std::size_t Vec<CXX_TYPE>::size() const noexcept { \
+ return cxxbridge1$rust_vec$##RUST_TYPE##$len(this); \
} \
template <> \
- const CXX_TYPE *Vec<CXX_TYPE>::data() const noexcept { \
- return cxxbridge05$rust_vec$##RUST_TYPE##$data(this); \
+ std::size_t Vec<CXX_TYPE>::capacity() const noexcept { \
+ return cxxbridge1$rust_vec$##RUST_TYPE##$capacity(this); \
} \
template <> \
- void Vec<CXX_TYPE>::reserve_total(size_t cap) noexcept { \
- cxxbridge05$rust_vec$##RUST_TYPE##$reserve_total(this, cap); \
+ const CXX_TYPE *Vec<CXX_TYPE>::data() const noexcept { \
+ return cxxbridge1$rust_vec$##RUST_TYPE##$data(this); \
} \
template <> \
- void Vec<CXX_TYPE>::set_len(size_t len) noexcept { \
- cxxbridge05$rust_vec$##RUST_TYPE##$set_len(this, len); \
+ void Vec<CXX_TYPE>::reserve_total(std::size_t cap) noexcept { \
+ cxxbridge1$rust_vec$##RUST_TYPE##$reserve_total(this, cap); \
} \
template <> \
- size_t Vec<CXX_TYPE>::stride() noexcept { \
- return cxxbridge05$rust_vec$##RUST_TYPE##$stride(); \
+ void Vec<CXX_TYPE>::set_len(std::size_t len) noexcept { \
+ cxxbridge1$rust_vec$##RUST_TYPE##$set_len(this, len); \
+ }
+
+#define SHARED_PTR_OPS(RUST_TYPE, CXX_TYPE) \
+ static_assert(sizeof(std::shared_ptr<CXX_TYPE>) == 2 * sizeof(void *), ""); \
+ static_assert(alignof(std::shared_ptr<CXX_TYPE>) == alignof(void *), ""); \
+ void cxxbridge1$std$shared_ptr$##RUST_TYPE##$null( \
+ std::shared_ptr<CXX_TYPE> *ptr) noexcept { \
+ new (ptr) std::shared_ptr<CXX_TYPE>(); \
+ } \
+ CXX_TYPE *cxxbridge1$std$shared_ptr$##RUST_TYPE##$uninit( \
+ std::shared_ptr<CXX_TYPE> *ptr) noexcept { \
+ CXX_TYPE *uninit = \
+ reinterpret_cast<CXX_TYPE *>(new rust::MaybeUninit<CXX_TYPE>); \
+ new (ptr) std::shared_ptr<CXX_TYPE>(uninit); \
+ return uninit; \
+ } \
+ void cxxbridge1$std$shared_ptr$##RUST_TYPE##$clone( \
+ const std::shared_ptr<CXX_TYPE> &self, \
+ std::shared_ptr<CXX_TYPE> *ptr) noexcept { \
+ new (ptr) std::shared_ptr<CXX_TYPE>(self); \
+ } \
+ const CXX_TYPE *cxxbridge1$std$shared_ptr$##RUST_TYPE##$get( \
+ const std::shared_ptr<CXX_TYPE> &self) noexcept { \
+ return self.get(); \
+ } \
+ void cxxbridge1$std$shared_ptr$##RUST_TYPE##$drop( \
+ const std::shared_ptr<CXX_TYPE> *self) noexcept { \
+ self->~shared_ptr(); \
+ } \
+ static_assert(sizeof(std::weak_ptr<CXX_TYPE>) == 2 * sizeof(void *), ""); \
+ static_assert(alignof(std::weak_ptr<CXX_TYPE>) == alignof(void *), ""); \
+ void cxxbridge1$std$weak_ptr$##RUST_TYPE##$null( \
+ std::weak_ptr<CXX_TYPE> *ptr) noexcept { \
+ new (ptr) std::weak_ptr<CXX_TYPE>(); \
+ } \
+ void cxxbridge1$std$weak_ptr$##RUST_TYPE##$clone( \
+ const std::weak_ptr<CXX_TYPE> &self, \
+ std::weak_ptr<CXX_TYPE> *ptr) noexcept { \
+ new (ptr) std::weak_ptr<CXX_TYPE>(self); \
+ } \
+ void cxxbridge1$std$weak_ptr$##RUST_TYPE##$downgrade( \
+ const std::shared_ptr<CXX_TYPE> &shared, \
+ std::weak_ptr<CXX_TYPE> *weak) noexcept { \
+ new (weak) std::weak_ptr<CXX_TYPE>(shared); \
+ } \
+ void cxxbridge1$std$weak_ptr$##RUST_TYPE##$upgrade( \
+ const std::weak_ptr<CXX_TYPE> &weak, \
+ std::shared_ptr<CXX_TYPE> *shared) noexcept { \
+ new (shared) std::shared_ptr<CXX_TYPE>(weak.lock()); \
+ } \
+ void cxxbridge1$std$weak_ptr$##RUST_TYPE##$drop( \
+ const std::weak_ptr<CXX_TYPE> *self) noexcept { \
+ self->~weak_ptr(); \
}
// Usize and isize are the same type as one of the below.
#define FOR_EACH_NUMERIC(MACRO) \
- MACRO(u8, uint8_t) \
- MACRO(u16, uint16_t) \
- MACRO(u32, uint32_t) \
- MACRO(u64, uint64_t) \
- MACRO(i8, int8_t) \
- MACRO(i16, int16_t) \
- MACRO(i32, int32_t) \
- MACRO(i64, int64_t) \
+ MACRO(u8, std::uint8_t) \
+ MACRO(u16, std::uint16_t) \
+ MACRO(u32, std::uint32_t) \
+ MACRO(u64, std::uint64_t) \
+ MACRO(i8, std::int8_t) \
+ MACRO(i16, std::int16_t) \
+ MACRO(i32, std::int32_t) \
+ MACRO(i64, std::int64_t) \
MACRO(f32, float) \
MACRO(f64, double)
#define FOR_EACH_STD_VECTOR(MACRO) \
FOR_EACH_NUMERIC(MACRO) \
- MACRO(usize, size_t) \
+ MACRO(usize, std::size_t) \
MACRO(isize, rust::isize) \
MACRO(string, std::string)
#define FOR_EACH_RUST_VEC(MACRO) \
FOR_EACH_NUMERIC(MACRO) \
MACRO(bool, bool) \
- MACRO(string, rust::String)
+ MACRO(char, char) \
+ MACRO(usize, rust::detail::usize_if_unique) \
+ MACRO(isize, rust::detail::isize_if_unique) \
+ MACRO(string, rust::String) \
+ MACRO(str, rust::Str)
+
+#define FOR_EACH_SHARED_PTR(MACRO) \
+ FOR_EACH_NUMERIC(MACRO) \
+ MACRO(bool, bool) \
+ MACRO(usize, std::size_t) \
+ MACRO(isize, rust::isize) \
+ MACRO(string, std::string)
extern "C" {
FOR_EACH_STD_VECTOR(STD_VECTOR_OPS)
FOR_EACH_RUST_VEC(RUST_VEC_EXTERNS)
+FOR_EACH_SHARED_PTR(SHARED_PTR_OPS)
} // extern "C"
namespace rust {
-inline namespace cxxbridge05 {
+inline namespace cxxbridge1 {
FOR_EACH_RUST_VEC(RUST_VEC_OPS)
-} // namespace cxxbridge05
+} // namespace cxxbridge1
} // namespace rust
diff --git a/src/cxx_string.rs b/src/cxx_string.rs
index 7b47feb7..dce7053d 100644
--- a/src/cxx_string.rs
+++ b/src/cxx_string.rs
@@ -1,14 +1,26 @@
+use crate::actually_private::Private;
use alloc::borrow::Cow;
use alloc::string::String;
+use core::cmp::Ordering;
use core::fmt::{self, Debug, Display};
+use core::hash::{Hash, Hasher};
+use core::marker::{PhantomData, PhantomPinned};
+use core::mem::MaybeUninit;
+use core::pin::Pin;
use core::slice;
use core::str::{self, Utf8Error};
extern "C" {
- #[link_name = "cxxbridge05$cxx_string$data"]
- fn string_data(_: &CxxString) -> *const u8;
- #[link_name = "cxxbridge05$cxx_string$length"]
- fn string_length(_: &CxxString) -> usize;
+ #[link_name = "cxxbridge1$cxx_string$init"]
+ fn string_init(this: &mut MaybeUninit<CxxString>, ptr: *const u8, len: usize);
+ #[link_name = "cxxbridge1$cxx_string$destroy"]
+ fn string_destroy(this: &mut MaybeUninit<CxxString>);
+ #[link_name = "cxxbridge1$cxx_string$data"]
+ fn string_data(this: &CxxString) -> *const u8;
+ #[link_name = "cxxbridge1$cxx_string$length"]
+ fn string_length(this: &CxxString) -> usize;
+ #[link_name = "cxxbridge1$cxx_string$push"]
+ fn string_push(this: Pin<&mut CxxString>, ptr: *const u8, len: usize);
}
/// Binding to C++ `std::string`.
@@ -24,9 +36,58 @@ extern "C" {
#[repr(C)]
pub struct CxxString {
_private: [u8; 0],
+ _pinned: PhantomData<PhantomPinned>,
+}
+
+/// Construct a C++ std::string on the Rust stack.
+///
+/// # Syntax
+///
+/// In statement position:
+///
+/// ```
+/// # use cxx::let_cxx_string;
+/// # let expression = "";
+/// let_cxx_string!(var = expression);
+/// ```
+///
+/// The `expression` may have any type that implements `AsRef<[u8]>`. Commonly
+/// it will be a string literal, but for example `&[u8]` and `String` would work
+/// as well.
+///
+/// The macro expands to something resembling `let $var: Pin<&mut CxxString> =
+/// /*???*/;`. The resulting [`Pin`] can be deref'd to `&CxxString` as needed.
+///
+/// # Example
+///
+/// ```
+/// use cxx::{let_cxx_string, CxxString};
+///
+/// fn f(s: &CxxString) {/* ... */}
+///
+/// fn main() {
+/// let_cxx_string!(s = "example");
+/// f(&s);
+/// }
+/// ```
+#[macro_export]
+macro_rules! let_cxx_string {
+ ($var:ident = $value:expr $(,)?) => {
+ let mut cxx_stack_string = $crate::private::StackString::new();
+ #[allow(unused_mut, unused_unsafe)]
+ let mut $var = match $value {
+ let_cxx_string => unsafe { cxx_stack_string.init(let_cxx_string) },
+ };
+ };
}
impl CxxString {
+ /// `CxxString` is not constructible via `new`. Instead, use the
+ /// [`let_cxx_string!`] macro.
+ pub fn new<T: Private>() -> Self {
+ unreachable!()
+ }
+
/// Returns the length of the string in bytes.
///
/// Matches the behavior of C++ [std::string::size][size].
@@ -82,6 +143,16 @@ impl CxxString {
pub fn to_string_lossy(&self) -> Cow<str> {
String::from_utf8_lossy(self.as_bytes())
}
+
+ /// 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());
+ }
+
+ /// Appends arbitrary bytes onto the end of this C++ string.
+ pub fn push_bytes(self: Pin<&mut Self>, bytes: &[u8]) {
+ unsafe { string_push(self, bytes.as_ptr(), bytes.len()) }
+ }
}
impl Display for CxxString {
@@ -97,7 +168,7 @@ impl Debug for CxxString {
}
impl PartialEq for CxxString {
- fn eq(&self, other: &CxxString) -> bool {
+ fn eq(&self, other: &Self) -> bool {
self.as_bytes() == other.as_bytes()
}
}
@@ -113,3 +184,55 @@ impl PartialEq<str> for CxxString {
self.as_bytes() == other.as_bytes()
}
}
+
+impl Eq for CxxString {}
+
+impl PartialOrd for CxxString {
+ fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
+ self.as_bytes().partial_cmp(other.as_bytes())
+ }
+}
+
+impl Ord for CxxString {
+ fn cmp(&self, other: &Self) -> Ordering {
+ self.as_bytes().cmp(other.as_bytes())
+ }
+}
+
+impl Hash for CxxString {
+ fn hash<H: Hasher>(&self, state: &mut H) {
+ self.as_bytes().hash(state);
+ }
+}
+
+#[doc(hidden)]
+#[repr(C)]
+pub struct StackString {
+ // Static assertions in cxx.cc validate that this is large enough and
+ // aligned enough.
+ space: MaybeUninit<[usize; 8]>,
+}
+
+impl StackString {
+ pub fn new() -> Self {
+ StackString {
+ space: MaybeUninit::uninit(),
+ }
+ }
+
+ 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())
+ }
+}
+
+impl Drop for StackString {
+ fn drop(&mut self) {
+ unsafe {
+ let this = &mut *self.space.as_mut_ptr().cast::<MaybeUninit<CxxString>>();
+ string_destroy(this);
+ }
+ }
+}
diff --git a/src/cxx_vector.rs b/src/cxx_vector.rs
index 5fb08071..16433417 100644
--- a/src/cxx_vector.rs
+++ b/src/cxx_vector.rs
@@ -1,8 +1,15 @@
-use crate::cxx_string::CxxString;
+//! Less used details of `CxxVector` are exposed in this module. `CxxVector`
+//! itself is exposed at the crate root.
+
+use crate::extern_type::ExternType;
+use crate::kind::Trivial;
+use crate::string::CxxString;
use core::ffi::c_void;
-use core::fmt::{self, Display};
-use core::marker::PhantomData;
+use core::fmt::{self, Debug};
+use core::iter::FusedIterator;
+use core::marker::{PhantomData, PhantomPinned};
use core::mem;
+use core::pin::Pin;
use core::ptr;
use core::slice;
@@ -17,6 +24,7 @@ use core::slice;
#[repr(C, packed)]
pub struct CxxVector<T> {
_private: [T; 0],
+ _pinned: PhantomData<PhantomPinned>,
}
impl<T> CxxVector<T>
@@ -51,6 +59,16 @@ where
}
}
+ /// Returns a pinned mutable reference to an element at the given position,
+ /// or `None` if out of bounds.
+ pub fn index_mut(self: Pin<&mut Self>, pos: usize) -> Option<Pin<&mut T>> {
+ if pos < self.len() {
+ Some(unsafe { self.index_unchecked_mut(pos) })
+ } else {
+ None
+ }
+ }
+
/// Returns a reference to an element without doing bounds checking.
///
/// This is generally not recommended, use with caution! Calling this method
@@ -58,15 +76,36 @@ where
/// reference is not used.
///
/// Matches the behavior of C++
- /// [std::vector\<T\>::operator\[\]][operator_at].
+ /// [std::vector\<T\>::operator\[\] const][operator_at].
///
/// [operator_at]: https://en.cppreference.com/w/cpp/container/vector/operator_at
pub unsafe fn get_unchecked(&self, pos: usize) -> &T {
- &*T::__get_unchecked(self, pos)
+ let this = self as *const CxxVector<T> as *mut CxxVector<T>;
+ let ptr = T::__get_unchecked(this, pos) as *const T;
+ &*ptr
+ }
+
+ /// Returns a pinned mutable reference to an element without doing bounds
+ /// checking.
+ ///
+ /// This is generally not recommended, use with caution! Calling this method
+ /// with an out-of-bounds index is undefined behavior even if the resulting
+ /// reference is not used.
+ ///
+ /// Matches the behavior of C++
+ /// [std::vector\<T\>::operator\[\]][operator_at].
+ ///
+ /// [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)
}
/// Returns a slice to the underlying contiguous array of elements.
- pub fn as_slice(&self) -> &[T] {
+ pub fn as_slice(&self) -> &[T]
+ where
+ T: ExternType<Kind = Trivial>,
+ {
let len = self.len();
if len == 0 {
// The slice::from_raw_parts in the other branch requires a nonnull
@@ -77,12 +116,41 @@ where
// which upholds the invariants.
&[]
} else {
- let ptr = unsafe { T::__get_unchecked(self, 0) };
+ let this = self as *const CxxVector<T> as *mut CxxVector<T>;
+ let ptr = unsafe { T::__get_unchecked(this, 0) };
unsafe { slice::from_raw_parts(ptr, len) }
}
}
+
+ /// Returns a slice to the underlying contiguous array of elements by
+ /// mutable reference.
+ pub fn as_mut_slice(self: Pin<&mut Self>) -> &mut [T]
+ where
+ T: ExternType<Kind = Trivial>,
+ {
+ let len = self.len();
+ if len == 0 {
+ &mut []
+ } else {
+ let ptr = unsafe { T::__get_unchecked(self.get_unchecked_mut(), 0) };
+ unsafe { slice::from_raw_parts_mut(ptr, len) }
+ }
+ }
+
+ /// Returns an iterator over elements of type `&T`.
+ pub fn iter(&self) -> Iter<T> {
+ Iter { v: self, index: 0 }
+ }
+
+ /// Returns an iterator over elements of type `Pin<&mut T>`.
+ pub fn iter_mut(self: Pin<&mut Self>) -> IterMut<T> {
+ IterMut { v: self, index: 0 }
+ }
}
+/// Iterator over elements of a `CxxVector` by shared reference.
+///
+/// The iterator element type is `&'a T`.
pub struct Iter<'a, T> {
v: &'a CxxVector<T>,
index: usize,
@@ -96,7 +164,7 @@ where
type IntoIter = Iter<'a, T>;
fn into_iter(self) -> Self::IntoIter {
- Iter { v: self, index: 0 }
+ self.iter()
}
}
@@ -107,44 +175,135 @@ where
type Item = &'a T;
fn next(&mut self) -> Option<Self::Item> {
- let next = self.v.get(self.index);
+ let next = self.v.get(self.index)?;
self.index += 1;
- next
+ Some(next)
+ }
+
+ fn size_hint(&self) -> (usize, Option<usize>) {
+ let len = self.len();
+ (len, Some(len))
+ }
+}
+
+impl<'a, T> ExactSizeIterator for Iter<'a, T>
+where
+ T: VectorElement,
+{
+ fn len(&self) -> usize {
+ self.v.len() - self.index
}
}
-pub struct TypeName<T> {
- element: PhantomData<T>,
+impl<'a, T> FusedIterator for Iter<'a, T> where T: VectorElement {}
+
+/// Iterator over elements of a `CxxVector` by pinned mutable reference.
+///
+/// The iterator element type is `Pin<&'a mut T>`.
+pub struct IterMut<'a, T> {
+ v: Pin<&'a mut CxxVector<T>>,
+ index: usize,
}
-impl<T> TypeName<T> {
- pub const fn new() -> Self {
- TypeName {
- element: PhantomData,
+impl<'a, T> IntoIterator for Pin<&'a mut CxxVector<T>>
+where
+ T: VectorElement,
+{
+ type Item = Pin<&'a mut T>;
+ type IntoIter = IterMut<'a, T>;
+
+ fn into_iter(self) -> Self::IntoIter {
+ self.iter_mut()
+ }
+}
+
+impl<'a, T> Iterator for IterMut<'a, T>
+where
+ T: VectorElement,
+{
+ type Item = Pin<&'a mut T>;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ let next = self.v.as_mut().index_mut(self.index)?;
+ self.index += 1;
+ // Extend lifetime to allow simultaneous holding of nonoverlapping
+ // elements, analogous to slice::split_first_mut.
+ unsafe {
+ let ptr = Pin::into_inner_unchecked(next) as *mut T;
+ Some(Pin::new_unchecked(&mut *ptr))
}
}
+
+ fn size_hint(&self) -> (usize, Option<usize>) {
+ let len = self.len();
+ (len, Some(len))
+ }
}
-impl<T> Display for TypeName<T>
+impl<'a, T> ExactSizeIterator for IterMut<'a, T>
where
T: VectorElement,
{
+ fn len(&self) -> usize {
+ self.v.len() - self.index
+ }
+}
+
+impl<'a, T> FusedIterator for IterMut<'a, T> where T: VectorElement {}
+
+impl<T> Debug for CxxVector<T>
+where
+ T: VectorElement + Debug,
+{
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
- write!(formatter, "CxxVector<{}>", T::__NAME)
+ formatter.debug_list().entries(self).finish()
}
}
-// Methods are private; not intended to be implemented outside of cxxbridge
-// codebase.
-#[doc(hidden)]
+/// Trait bound for types which may be used as the `T` inside of a
+/// `CxxVector<T>` in generic code.
+///
+/// This trait has no publicly callable or implementable methods. Implementing
+/// it outside of the CXX codebase is not supported.
+///
+/// # Example
+///
+/// A bound `T: VectorElement` may be necessary when manipulating [`CxxVector`]
+/// in generic code.
+///
+/// ```
+/// use cxx::vector::{CxxVector, VectorElement};
+/// use std::fmt::Display;
+///
+/// pub fn take_generic_vector<T>(vector: &CxxVector<T>)
+/// where
+/// T: VectorElement + Display,
+/// {
+/// println!("the vector elements are:");
+/// for element in vector {
+/// println!(" • {}", element);
+/// }
+/// }
+/// ```
+///
+/// Writing the same generic function without a `VectorElement` trait bound
+/// would not compile.
pub unsafe trait VectorElement: Sized {
- const __NAME: &'static dyn Display;
+ #[doc(hidden)]
+ fn __typename(f: &mut fmt::Formatter) -> fmt::Result;
+ #[doc(hidden)]
fn __vector_size(v: &CxxVector<Self>) -> usize;
- unsafe fn __get_unchecked(v: &CxxVector<Self>, pos: usize) -> *const Self;
+ #[doc(hidden)]
+ unsafe fn __get_unchecked(v: *mut CxxVector<Self>, pos: usize) -> *mut Self;
+ #[doc(hidden)]
fn __unique_ptr_null() -> *mut c_void;
+ #[doc(hidden)]
unsafe fn __unique_ptr_raw(raw: *mut CxxVector<Self>) -> *mut c_void;
+ #[doc(hidden)]
unsafe fn __unique_ptr_get(repr: *mut c_void) -> *const CxxVector<Self>;
+ #[doc(hidden)]
unsafe fn __unique_ptr_release(repr: *mut c_void) -> *mut CxxVector<Self>;
+ #[doc(hidden)]
unsafe fn __unique_ptr_drop(repr: *mut c_void);
}
@@ -153,29 +312,35 @@ macro_rules! impl_vector_element {
const_assert_eq!(1, mem::align_of::<CxxVector<$ty>>());
unsafe impl VectorElement for $ty {
- const __NAME: &'static dyn Display = &$name;
+ #[doc(hidden)]
+ fn __typename(f: &mut fmt::Formatter) -> fmt::Result {
+ f.write_str($name)
+ }
+ #[doc(hidden)]
fn __vector_size(v: &CxxVector<$ty>) -> usize {
extern "C" {
attr! {
- #[link_name = concat!("cxxbridge05$std$vector$", $segment, "$size")]
+ #[link_name = concat!("cxxbridge1$std$vector$", $segment, "$size")]
fn __vector_size(_: &CxxVector<$ty>) -> usize;
}
}
unsafe { __vector_size(v) }
}
- unsafe fn __get_unchecked(v: &CxxVector<$ty>, pos: usize) -> *const $ty {
+ #[doc(hidden)]
+ unsafe fn __get_unchecked(v: *mut CxxVector<$ty>, pos: usize) -> *mut $ty {
extern "C" {
attr! {
- #[link_name = concat!("cxxbridge05$std$vector$", $segment, "$get_unchecked")]
- fn __get_unchecked(_: &CxxVector<$ty>, _: usize) -> *const $ty;
+ #[link_name = concat!("cxxbridge1$std$vector$", $segment, "$get_unchecked")]
+ fn __get_unchecked(_: *mut CxxVector<$ty>, _: usize) -> *mut $ty;
}
}
__get_unchecked(v, pos)
}
+ #[doc(hidden)]
fn __unique_ptr_null() -> *mut c_void {
extern "C" {
attr! {
- #[link_name = concat!("cxxbridge05$unique_ptr$std$vector$", $segment, "$null")]
+ #[link_name = concat!("cxxbridge1$unique_ptr$std$vector$", $segment, "$null")]
fn __unique_ptr_null(this: *mut *mut c_void);
}
}
@@ -183,10 +348,11 @@ macro_rules! impl_vector_element {
unsafe { __unique_ptr_null(&mut repr) }
repr
}
+ #[doc(hidden)]
unsafe fn __unique_ptr_raw(raw: *mut CxxVector<Self>) -> *mut c_void {
extern "C" {
attr! {
- #[link_name = concat!("cxxbridge05$unique_ptr$std$vector$", $segment, "$raw")]
+ #[link_name = concat!("cxxbridge1$unique_ptr$std$vector$", $segment, "$raw")]
fn __unique_ptr_raw(this: *mut *mut c_void, raw: *mut CxxVector<$ty>);
}
}
@@ -194,28 +360,31 @@ macro_rules! impl_vector_element {
__unique_ptr_raw(&mut repr, raw);
repr
}
+ #[doc(hidden)]
unsafe fn __unique_ptr_get(repr: *mut c_void) -> *const CxxVector<Self> {
extern "C" {
attr! {
- #[link_name = concat!("cxxbridge05$unique_ptr$std$vector$", $segment, "$get")]
+ #[link_name = concat!("cxxbridge1$unique_ptr$std$vector$", $segment, "$get")]
fn __unique_ptr_get(this: *const *mut c_void) -> *const CxxVector<$ty>;
}
}
__unique_ptr_get(&repr)
}
+ #[doc(hidden)]
unsafe fn __unique_ptr_release(mut repr: *mut c_void) -> *mut CxxVector<Self> {
extern "C" {
attr! {
- #[link_name = concat!("cxxbridge05$unique_ptr$std$vector$", $segment, "$release")]
+ #[link_name = concat!("cxxbridge1$unique_ptr$std$vector$", $segment, "$release")]
fn __unique_ptr_release(this: *mut *mut c_void) -> *mut CxxVector<$ty>;
}
}
__unique_ptr_release(&mut repr)
}
+ #[doc(hidden)]
unsafe fn __unique_ptr_drop(mut repr: *mut c_void) {
extern "C" {
attr! {
- #[link_name = concat!("cxxbridge05$unique_ptr$std$vector$", $segment, "$drop")]
+ #[link_name = concat!("cxxbridge1$unique_ptr$std$vector$", $segment, "$drop")]
fn __unique_ptr_drop(this: *mut *mut c_void);
}
}
diff --git a/src/exception.rs b/src/exception.rs
index 0ffca66a..f61e8fa9 100644
--- a/src/exception.rs
+++ b/src/exception.rs
@@ -1,7 +1,7 @@
use alloc::boxed::Box;
use core::fmt::{self, Debug, Display};
-/// Exception thrown from an `extern "C"` function.
+/// Exception thrown from an `extern "C++"` function.
#[derive(Debug)]
pub struct Exception {
pub(crate) what: Box<str>,
diff --git a/src/extern_type.rs b/src/extern_type.rs
index f92ff40c..35057acb 100644
--- a/src/extern_type.rs
+++ b/src/extern_type.rs
@@ -1,4 +1,6 @@
use self::kind::{Kind, Opaque, Trivial};
+use crate::CxxString;
+use alloc::string::String;
/// A type for which the layout is determined by its C++ definition.
///
@@ -30,7 +32,7 @@ use self::kind::{Kind, Opaque, Trivial};
/// # mod file1 {
/// #[cxx::bridge(namespace = "example")]
/// pub mod ffi {
-/// extern "C" {
+/// unsafe extern "C++" {
/// type Demo;
///
/// fn create_demo() -> UniquePtr<Demo>;
@@ -41,7 +43,7 @@ use self::kind::{Kind, Opaque, Trivial};
/// // file2.rs
/// #[cxx::bridge(namespace = "example")]
/// pub mod ffi {
-/// extern "C" {
+/// unsafe extern "C++" {
/// type Demo = crate::file1::ffi::Demo;
///
/// fn take_ref_demo(demo: &Demo);
@@ -80,7 +82,7 @@ use self::kind::{Kind, Opaque, Trivial};
///
/// #[cxx::bridge(namespace = "folly")]
/// pub mod ffi {
-/// extern "C" {
+/// unsafe extern "C++" {
/// include!("rust_cxx_bindings.h");
///
/// type StringPiece = crate::folly_sys::StringPiece;
@@ -181,3 +183,36 @@ pub fn verify_extern_type<T: ExternType<Id = Id>, Id>() {}
#[doc(hidden)]
pub fn verify_extern_kind<T: ExternType<Kind = Kind>, Kind: self::Kind>() {}
+
+macro_rules! impl_extern_type {
+ ($([$kind:ident] $($ty:path = $cxxpath:literal)*)*) => {
+ $($(
+ unsafe impl ExternType for $ty {
+ #[doc(hidden)]
+ type Id = crate::type_id!($cxxpath);
+ type Kind = $kind;
+ }
+ )*)*
+ };
+}
+
+impl_extern_type! {
+ [Trivial]
+ bool = "bool"
+ u8 = "std::uint8_t"
+ u16 = "std::uint16_t"
+ u32 = "std::uint32_t"
+ u64 = "std::uint64_t"
+ usize = "size_t"
+ i8 = "std::int8_t"
+ i16 = "std::int16_t"
+ i32 = "std::int32_t"
+ i64 = "std::int64_t"
+ isize = "rust::isize"
+ f32 = "float"
+ f64 = "double"
+ String = "rust::String"
+
+ [Opaque]
+ CxxString = "std::string"
+}
diff --git a/src/fmt.rs b/src/fmt.rs
new file mode 100644
index 00000000..db14a7c0
--- /dev/null
+++ b/src/fmt.rs
@@ -0,0 +1,16 @@
+use std::fmt::{self, Display};
+
+pub(crate) fn display(fmt: impl Fn(&mut fmt::Formatter) -> fmt::Result) -> impl Display {
+ DisplayInvoke(fmt)
+}
+
+struct DisplayInvoke<T>(T);
+
+impl<T> Display for DisplayInvoke<T>
+where
+ T: Fn(&mut fmt::Formatter) -> fmt::Result,
+{
+ fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+ (self.0)(formatter)
+ }
+}
diff --git a/src/lib.rs b/src/lib.rs
index 17d7548b..19a708c5 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -18,11 +18,18 @@
//!
//! <br>
//!
-//! *Compiler support: requires rustc 1.43+ and c++11 or newer*<br>
+//! *Compiler support: requires rustc 1.48+ and c++11 or newer*<br>
//! *[Release notes](https://github.com/dtolnay/cxx/releases)*
//!
//! <br>
//!
+//! # Guide
+//!
+//! Please see **<https://cxx.rs>** for a tutorial, reference material, and
+//! example code.
+//!
+//! <br>
+//!
//! # Overview
//!
//! The idea is that we define the signatures of both sides of our FFI boundary
@@ -86,7 +93,7 @@
//! fn next_chunk(buf: &mut MultiBuf) -> &[u8];
//! }
//!
-//! extern "C++" {
+//! unsafe extern "C++" {
//! // One or more headers with the matching C++ declarations. Our code
//! // generators don't read it but it gets #include'd and used in static
//! // assertions to ensure our picture of the FFI boundary is accurate.
@@ -156,20 +163,20 @@
//! - **Functions** &mdash; implemented in either language, callable from the
//! other language.
//!
-//! Within the `extern "C"` part of the CXX bridge we list the types and
-//! functions for which C++ is the source of truth, as well as the header(s)
-//! that declare those APIs. In the future it's possible that this section could
-//! be generated bindgen-style from the headers but for now we need the
-//! signatures written out; static assertions will verify that they are
-//! accurate.
-//!
-//! Within the `extern "Rust"` part, we list types and functions for which Rust
-//! is the source of truth. These all implicitly refer to the `super` module,
-//! the parent module of the CXX bridge. You can think of the two items listed
-//! in the example above as being like `use super::ThingR` and `use
-//! super::print_r` except re-exported to C++. The parent module will either
-//! contain the definitions directly for simple things, or contain the relevant
-//! `use` statements to bring them into scope from elsewhere.
+//! Within the `extern "Rust"` part of the CXX bridge we list the types and
+//! functions for which Rust is the source of truth. These all implicitly refer
+//! to the `super` module, the parent module of the CXX bridge. You can think of
+//! the two items listed in the example above as being like `use
+//! super::MultiBuf` and `use super::next_chunk` except re-exported to C++. The
+//! parent module will either contain the definitions directly for simple
+//! things, or contain the relevant `use` statements to bring them into scope
+//! from elsewhere.
+//!
+//! Within the `extern "C++"` part, we list types and functions for which C++ is
+//! the source of truth, as well as the header(s) that declare those APIs. In
+//! the future it's possible that this section could be generated bindgen-style
+//! from the headers but for now we need the signatures written out; static
+//! assertions will verify that they are accurate.
//!
//! Your function implementations themselves, whether in C++ or Rust, *do not*
//! need to be defined as `extern "C"` ABI or no\_mangle. CXX will put in the
@@ -235,7 +242,7 @@
//! # Cargo.toml
//!
//! [build-dependencies]
-//! cxx-build = "0.5"
+//! cxx-build = "1.0"
//! ```
//!
//! ```no_run
@@ -324,12 +331,16 @@
//! <tr><th>name in Rust</th><th>name in C++</th><th>restrictions</th></tr>
//! <tr><td>String</td><td>rust::String</td><td></td></tr>
//! <tr><td>&amp;str</td><td>rust::Str</td><td></td></tr>
-//! <tr><td>&amp;[u8]</td><td>rust::Slice&lt;uint8_t&gt;</td><td><sup><i>arbitrary &amp;[T] not implemented yet</i></sup></td></tr>
+//! <tr><td>&amp;[T]</td><td>rust::Slice&lt;const T&gt;</td><td><sup><i>cannot hold opaque C++ type</i></sup></td></tr>
+//! <tr><td>&amp;mut [T]</td><td>rust::Slice&lt;T&gt;</td><td><sup><i>cannot hold opaque C++ type</i></sup></td></tr>
//! <tr><td><a href="struct.CxxString.html">CxxString</a></td><td>std::string</td><td><sup><i>cannot be passed by value</i></sup></td></tr>
//! <tr><td>Box&lt;T&gt;</td><td>rust::Box&lt;T&gt;</td><td><sup><i>cannot hold opaque C++ type</i></sup></td></tr>
//! <tr><td><a href="struct.UniquePtr.html">UniquePtr&lt;T&gt;</a></td><td>std::unique_ptr&lt;T&gt;</td><td><sup><i>cannot hold opaque Rust type</i></sup></td></tr>
+//! <tr><td><a href="struct.SharedPtr.html">SharedPtr&lt;T&gt;</a></td><td>std::shared_ptr&lt;T&gt;</td><td><sup><i>cannot hold opaque Rust type</i></sup></td></tr>
+//! <tr><td>[T; N]</td><td>std::array&lt;T, N&gt;</td><td><sup><i>cannot hold opaque C++ type</i></sup></td></tr>
//! <tr><td>Vec&lt;T&gt;</td><td>rust::Vec&lt;T&gt;</td><td><sup><i>cannot hold opaque C++ type</i></sup></td></tr>
//! <tr><td><a href="struct.CxxVector.html">CxxVector&lt;T&gt;</a></td><td>std::vector&lt;T&gt;</td><td><sup><i>cannot be passed by value, cannot hold opaque Rust type</i></sup></td></tr>
+//! <tr><td>*mut T, *const T</td><td>T*, const T*</td><td><sup><i>fn with a raw pointer argument must be declared unsafe to call</i></sup></td></tr>
//! <tr><td>fn(T, U) -&gt; V</td><td>rust::Fn&lt;V(T, U)&gt;</td><td><sup><i>only passing from Rust to C++ is implemented so far</i></sup></td></tr>
//! <tr><td>Result&lt;T&gt;</td><td>throw/catch</td><td><sup><i>allowed as return type only</i></sup></td></tr>
//! </table>
@@ -350,65 +361,77 @@
//! <tr><td>Option&lt;T&gt;</td><td><sup><i>tbd</i></sup></td></tr>
//! <tr><td><sup><i>tbd</i></sup></td><td>std::map&lt;K, V&gt;</td></tr>
//! <tr><td><sup><i>tbd</i></sup></td><td>std::unordered_map&lt;K, V&gt;</td></tr>
-//! <tr><td><sup><i>tbd</i></sup></td><td>std::shared_ptr&lt;T&gt;</td></tr>
//! </table>
#![no_std]
-#![doc(html_root_url = "https://docs.rs/cxx/0.5.9")]
+#![doc(html_root_url = "https://docs.rs/cxx/1.0.42")]
#![deny(improper_ctypes)]
#![allow(non_camel_case_types)]
#![allow(
clippy::cognitive_complexity,
clippy::declare_interior_mutable_const,
+ clippy::doc_markdown,
+ clippy::empty_enum,
clippy::inherent_to_string,
+ clippy::items_after_statements,
clippy::large_enum_variant,
clippy::len_without_is_empty,
+ clippy::missing_errors_doc,
clippy::missing_safety_doc,
clippy::module_inception,
+ clippy::module_name_repetitions,
+ clippy::must_use_candidate,
clippy::needless_doctest_main,
clippy::new_without_default,
clippy::or_fun_call,
clippy::ptr_arg,
clippy::toplevel_ref_arg,
- clippy::useless_let_if_seq
+ clippy::useless_let_if_seq,
+ clippy::wrong_self_convention
)]
#[cfg(built_with_cargo)]
extern crate link_cplusplus;
extern crate alloc;
+extern crate self as cxx;
extern crate std;
#[macro_use]
mod macros;
-mod cxx_string;
mod cxx_vector;
mod exception;
mod extern_type;
+mod fmt;
mod function;
+pub mod memory;
mod opaque;
mod result;
-mod rust_sliceu8;
+mod rust_slice;
mod rust_str;
mod rust_string;
+mod rust_type;
mod rust_vec;
+mod shared_ptr;
+#[path = "cxx_string.rs"]
+mod string;
mod symbols;
+mod type_id;
mod unique_ptr;
mod unwind;
+pub mod vector;
+mod weak_ptr;
-pub use crate::cxx_string::CxxString;
pub use crate::cxx_vector::CxxVector;
pub use crate::exception::Exception;
pub use crate::extern_type::{kind, ExternType};
+pub use crate::shared_ptr::SharedPtr;
+pub use crate::string::CxxString;
pub use crate::unique_ptr::UniquePtr;
+pub use crate::weak_ptr::WeakPtr;
pub use cxxbridge_macro::bridge;
-/// For use in impls of the `ExternType` trait. See [`ExternType`].
-///
-/// [`ExternType`]: trait.ExternType.html
-pub use cxxbridge_macro::type_id;
-
/// Synonym for `CxxString`.
///
/// To avoid confusion with Rust's standard library string you probably
@@ -431,12 +454,21 @@ pub mod private {
pub use crate::function::FatFunction;
pub use crate::opaque::Opaque;
pub use crate::result::{r#try, Result};
- pub use crate::rust_sliceu8::RustSliceU8;
+ pub use crate::rust_slice::RustSlice;
pub use crate::rust_str::RustStr;
pub use crate::rust_string::RustString;
+ pub use crate::rust_type::{ImplBox, ImplVec, RustType};
pub use crate::rust_vec::RustVec;
+ pub use crate::shared_ptr::SharedPtrTarget;
+ pub use crate::string::StackString;
pub use crate::unique_ptr::UniquePtrTarget;
pub use crate::unwind::catch_unwind;
+ pub use crate::weak_ptr::WeakPtrTarget;
+ pub use cxxbridge_macro::type_id;
+}
+
+mod actually_private {
+ pub trait Private {}
}
macro_rules! chars {
diff --git a/src/memory.rs b/src/memory.rs
new file mode 100644
index 00000000..441d3d89
--- /dev/null
+++ b/src/memory.rs
@@ -0,0 +1,8 @@
+//! Less used details of `UniquePtr` and `SharedPtr`.
+//!
+//! The pointer types themselves are exposed at the crate root.
+
+pub use crate::shared_ptr::SharedPtrTarget;
+pub use crate::unique_ptr::UniquePtrTarget;
+#[doc(no_inline)]
+pub use cxx::{SharedPtr, UniquePtr};
diff --git a/src/opaque.rs b/src/opaque.rs
index bad57e76..3c8f5362 100644
--- a/src/opaque.rs
+++ b/src/opaque.rs
@@ -1,3 +1,4 @@
+use core::marker::{PhantomData, PhantomPinned};
use core::mem;
// . size = 0
@@ -5,9 +6,11 @@ use core::mem;
// . ffi-safe
// . !Send
// . !Sync
+// . !Unpin
#[repr(C, packed)]
pub struct Opaque {
_private: [*const u8; 0],
+ _pinned: PhantomData<PhantomPinned>,
}
const_assert_eq!(0, mem::size_of::<Opaque>());
diff --git a/src/result.rs b/src/result.rs
index fcced769..f41639a1 100644
--- a/src/result.rs
+++ b/src/result.rs
@@ -1,16 +1,22 @@
use crate::exception::Exception;
-use crate::rust_str::RustStr;
use alloc::boxed::Box;
use alloc::string::{String, ToString};
use core::fmt::Display;
-use core::ptr;
+use core::ptr::{self, NonNull};
use core::result::Result as StdResult;
use core::slice;
use core::str;
#[repr(C)]
+#[derive(Copy, Clone)]
+struct PtrLen {
+ ptr: NonNull<u8>,
+ len: usize,
+}
+
+#[repr(C)]
pub union Result {
- err: RustStr,
+ err: PtrLen,
ok: *const u8, // null
}
@@ -34,14 +40,12 @@ unsafe fn to_c_error(msg: String) -> Result {
let len = msg.len();
extern "C" {
- #[link_name = "cxxbridge05$error"]
- fn error(ptr: *const u8, len: usize) -> *const u8;
+ #[link_name = "cxxbridge1$error"]
+ fn error(ptr: *const u8, len: usize) -> NonNull<u8>;
}
let copy = error(ptr, len);
- let slice = slice::from_raw_parts(copy, len);
- let string = str::from_utf8_unchecked(slice);
- let err = RustStr::from(string);
+ let err = PtrLen { ptr: copy, len };
Result { err }
}
diff --git a/src/rust_slice.rs b/src/rust_slice.rs
new file mode 100644
index 00000000..b4b5f2cb
--- /dev/null
+++ b/src/rust_slice.rs
@@ -0,0 +1,41 @@
+use core::mem;
+use core::ptr::{self, NonNull};
+use core::slice;
+
+#[repr(C)]
+pub struct RustSlice {
+ pub(crate) repr: NonNull<[()]>,
+}
+
+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 _) },
+ }
+ }
+
+ 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) },
+ }
+ }
+
+ 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)
+ }
+
+ 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)
+ }
+}
+
+const_assert_eq!(
+ mem::size_of::<Option<RustSlice>>(),
+ mem::size_of::<RustSlice>(),
+);
diff --git a/src/rust_sliceu8.rs b/src/rust_sliceu8.rs
deleted file mode 100644
index 32f8798b..00000000
--- a/src/rust_sliceu8.rs
+++ /dev/null
@@ -1,29 +0,0 @@
-use core::mem;
-use core::ptr::NonNull;
-use core::slice;
-
-// Not necessarily ABI compatible with &[u8]. Codegen performs the translation.
-#[repr(C)]
-#[derive(Copy, Clone)]
-pub struct RustSliceU8 {
- pub(crate) ptr: NonNull<u8>,
- pub(crate) len: usize,
-}
-
-impl RustSliceU8 {
- pub fn from(s: &[u8]) -> Self {
- RustSliceU8 {
- ptr: NonNull::from(s).cast::<u8>(),
- len: s.len(),
- }
- }
-
- pub unsafe fn as_slice<'a>(self) -> &'a [u8] {
- slice::from_raw_parts(self.ptr.as_ptr(), self.len)
- }
-}
-
-const_assert_eq!(
- mem::size_of::<Option<RustSliceU8>>(),
- mem::size_of::<RustSliceU8>(),
-);
diff --git a/src/rust_str.rs b/src/rust_str.rs
index 38e5cabf..9bc26145 100644
--- a/src/rust_str.rs
+++ b/src/rust_str.rs
@@ -1,27 +1,20 @@
use core::mem;
use core::ptr::NonNull;
-use core::slice;
use core::str;
-// Not necessarily ABI compatible with &str. Codegen performs the translation.
#[repr(C)]
-#[derive(Copy, Clone)]
pub struct RustStr {
- pub(crate) ptr: NonNull<u8>,
- pub(crate) len: usize,
+ repr: NonNull<str>,
}
impl RustStr {
- pub fn from(s: &str) -> Self {
- RustStr {
- ptr: NonNull::from(s).cast::<u8>(),
- len: s.len(),
- }
+ pub fn from(repr: &str) -> Self {
+ let repr = NonNull::from(repr);
+ RustStr { repr }
}
pub unsafe fn as_str<'a>(self) -> &'a str {
- let slice = slice::from_raw_parts(self.ptr.as_ptr(), self.len);
- str::from_utf8_unchecked(slice)
+ &*self.repr.as_ptr()
}
}
diff --git a/src/rust_type.rs b/src/rust_type.rs
new file mode 100644
index 00000000..7bcf440d
--- /dev/null
+++ b/src/rust_type.rs
@@ -0,0 +1,3 @@
+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 8ddc4a72..126fdbf9 100644
--- a/src/rust_vec.rs
+++ b/src/rust_vec.rs
@@ -41,6 +41,10 @@ impl<T> RustVec<T> {
self.repr.len()
}
+ pub fn capacity(&self) -> usize {
+ self.repr.capacity()
+ }
+
pub fn as_ptr(&self) -> *const T {
self.repr.as_ptr()
}
diff --git a/src/shared_ptr.rs b/src/shared_ptr.rs
new file mode 100644
index 00000000..66b988ba
--- /dev/null
+++ b/src/shared_ptr.rs
@@ -0,0 +1,285 @@
+use crate::fmt::display;
+use crate::kind::Trivial;
+use crate::string::CxxString;
+use crate::weak_ptr::{WeakPtr, WeakPtrTarget};
+use crate::ExternType;
+use core::ffi::c_void;
+use core::fmt::{self, Debug, Display};
+use core::marker::PhantomData;
+use core::mem::MaybeUninit;
+use core::ops::Deref;
+
+/// Binding to C++ `std::shared_ptr<T>`.
+#[repr(C)]
+pub struct SharedPtr<T>
+where
+ T: SharedPtrTarget,
+{
+ repr: [*mut c_void; 2],
+ ty: PhantomData<T>,
+}
+
+impl<T> SharedPtr<T>
+where
+ T: SharedPtrTarget,
+{
+ /// Makes a new SharedPtr wrapping a null pointer.
+ ///
+ /// Matches the behavior of default-constructing a std::shared\_ptr.
+ pub fn null() -> Self {
+ let mut shared_ptr = MaybeUninit::<SharedPtr<T>>::uninit();
+ let new = shared_ptr.as_mut_ptr().cast();
+ unsafe {
+ T::__null(new);
+ shared_ptr.assume_init()
+ }
+ }
+
+ /// Allocates memory on the heap and makes a SharedPtr owner for it.
+ pub fn new(value: T) -> Self
+ where
+ T: ExternType<Kind = Trivial>,
+ {
+ let mut shared_ptr = MaybeUninit::<SharedPtr<T>>::uninit();
+ let new = shared_ptr.as_mut_ptr().cast();
+ unsafe {
+ T::__new(value, new);
+ shared_ptr.assume_init()
+ }
+ }
+
+ /// Checks whether the SharedPtr does not own an object.
+ ///
+ /// This is the opposite of [std::shared_ptr\<T\>::operator bool](https://en.cppreference.com/w/cpp/memory/shared_ptr/operator_bool).
+ pub fn is_null(&self) -> bool {
+ let this = self as *const Self as *const c_void;
+ let ptr = unsafe { T::__get(this) };
+ ptr.is_null()
+ }
+
+ /// Returns a reference to the object owned by this SharedPtr if any,
+ /// otherwise None.
+ pub fn as_ref(&self) -> Option<&T> {
+ let this = self as *const Self as *const c_void;
+ unsafe { T::__get(this).as_ref() }
+ }
+
+ /// Constructs new WeakPtr as a non-owning reference to the object managed
+ /// by `self`. If `self` manages no object, the WeakPtr manages no object
+ /// too.
+ ///
+ /// Matches the behavior of [std::weak_ptr\<T\>::weak_ptr(const std::shared_ptr\<T\> \&)](https://en.cppreference.com/w/cpp/memory/weak_ptr/weak_ptr).
+ pub fn downgrade(self: &SharedPtr<T>) -> WeakPtr<T>
+ where
+ T: WeakPtrTarget,
+ {
+ let this = self as *const Self as *const c_void;
+ let mut weak_ptr = MaybeUninit::<WeakPtr<T>>::uninit();
+ let new = weak_ptr.as_mut_ptr().cast();
+ unsafe {
+ T::__downgrade(this, new);
+ weak_ptr.assume_init()
+ }
+ }
+}
+
+unsafe impl<T> Send for SharedPtr<T> where T: Send + Sync + SharedPtrTarget {}
+unsafe impl<T> Sync for SharedPtr<T> where T: Send + Sync + SharedPtrTarget {}
+
+impl<T> Clone for SharedPtr<T>
+where
+ T: SharedPtrTarget,
+{
+ fn clone(&self) -> Self {
+ let mut shared_ptr = MaybeUninit::<SharedPtr<T>>::uninit();
+ let new = shared_ptr.as_mut_ptr().cast();
+ let this = self as *const Self as *mut c_void;
+ unsafe {
+ T::__clone(this, new);
+ shared_ptr.assume_init()
+ }
+ }
+}
+
+impl<T> Drop for SharedPtr<T>
+where
+ T: SharedPtrTarget,
+{
+ fn drop(&mut self) {
+ let this = self as *mut Self as *mut c_void;
+ unsafe { T::__drop(this) }
+ }
+}
+
+impl<T> Deref for SharedPtr<T>
+where
+ T: SharedPtrTarget,
+{
+ type Target = T;
+
+ fn deref(&self) -> &Self::Target {
+ match self.as_ref() {
+ Some(target) => target,
+ None => panic!(
+ "called deref on a null SharedPtr<{}>",
+ display(T::__typename),
+ ),
+ }
+ }
+}
+
+impl<T> Debug for SharedPtr<T>
+where
+ T: Debug + SharedPtrTarget,
+{
+ fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+ match self.as_ref() {
+ None => formatter.write_str("nullptr"),
+ Some(value) => Debug::fmt(value, formatter),
+ }
+ }
+}
+
+impl<T> Display for SharedPtr<T>
+where
+ T: Display + SharedPtrTarget,
+{
+ fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+ match self.as_ref() {
+ None => formatter.write_str("nullptr"),
+ Some(value) => Display::fmt(value, formatter),
+ }
+ }
+}
+
+/// Trait bound for types which may be used as the `T` inside of a
+/// `SharedPtr<T>` in generic code.
+///
+/// This trait has no publicly callable or implementable methods. Implementing
+/// it outside of the CXX codebase is not supported.
+///
+/// # Example
+///
+/// A bound `T: SharedPtrTarget` may be necessary when manipulating
+/// [`SharedPtr`] in generic code.
+///
+/// ```
+/// use cxx::memory::{SharedPtr, SharedPtrTarget};
+/// use std::fmt::Display;
+///
+/// pub fn take_generic_ptr<T>(ptr: SharedPtr<T>)
+/// where
+/// T: SharedPtrTarget + Display,
+/// {
+/// println!("the shared_ptr points to: {}", *ptr);
+/// }
+/// ```
+///
+/// Writing the same generic function without a `SharedPtrTarget` trait bound
+/// would not compile.
+pub unsafe trait SharedPtrTarget {
+ #[doc(hidden)]
+ fn __typename(f: &mut fmt::Formatter) -> fmt::Result;
+ #[doc(hidden)]
+ unsafe fn __null(new: *mut c_void);
+ #[doc(hidden)]
+ unsafe fn __new(value: Self, new: *mut c_void)
+ where
+ Self: Sized,
+ {
+ // Opoaque C types do not get this method because they can never exist
+ // by value on the Rust side of the bridge.
+ let _ = value;
+ let _ = new;
+ unreachable!()
+ }
+ #[doc(hidden)]
+ unsafe fn __clone(this: *const c_void, new: *mut c_void);
+ #[doc(hidden)]
+ unsafe fn __get(this: *const c_void) -> *const Self;
+ #[doc(hidden)]
+ unsafe fn __drop(this: *mut c_void);
+}
+
+macro_rules! impl_shared_ptr_target {
+ ($segment:expr, $name:expr, $ty:ty) => {
+ unsafe impl SharedPtrTarget for $ty {
+ #[doc(hidden)]
+ fn __typename(f: &mut fmt::Formatter) -> fmt::Result {
+ f.write_str($name)
+ }
+ #[doc(hidden)]
+ unsafe fn __null(new: *mut c_void) {
+ extern "C" {
+ attr! {
+ #[link_name = concat!("cxxbridge1$std$shared_ptr$", $segment, "$null")]
+ fn __null(new: *mut c_void);
+ }
+ }
+ __null(new);
+ }
+ #[doc(hidden)]
+ unsafe fn __new(value: Self, new: *mut c_void) {
+ extern "C" {
+ attr! {
+ #[link_name = concat!("cxxbridge1$std$shared_ptr$", $segment, "$uninit")]
+ fn __uninit(new: *mut c_void) -> *mut c_void;
+ }
+ }
+ __uninit(new).cast::<$ty>().write(value);
+ }
+ #[doc(hidden)]
+ unsafe fn __clone(this: *const c_void, new: *mut c_void) {
+ extern "C" {
+ attr! {
+ #[link_name = concat!("cxxbridge1$std$shared_ptr$", $segment, "$clone")]
+ fn __clone(this: *const c_void, new: *mut c_void);
+ }
+ }
+ __clone(this, new);
+ }
+ #[doc(hidden)]
+ unsafe fn __get(this: *const c_void) -> *const Self {
+ extern "C" {
+ attr! {
+ #[link_name = concat!("cxxbridge1$std$shared_ptr$", $segment, "$get")]
+ fn __get(this: *const c_void) -> *const c_void;
+ }
+ }
+ __get(this).cast()
+ }
+ #[doc(hidden)]
+ unsafe fn __drop(this: *mut c_void) {
+ extern "C" {
+ attr! {
+ #[link_name = concat!("cxxbridge1$std$shared_ptr$", $segment, "$drop")]
+ fn __drop(this: *mut c_void);
+ }
+ }
+ __drop(this);
+ }
+ }
+ };
+}
+
+macro_rules! impl_shared_ptr_target_for_primitive {
+ ($ty:ident) => {
+ impl_shared_ptr_target!(stringify!($ty), stringify!($ty), $ty);
+ };
+}
+
+impl_shared_ptr_target_for_primitive!(bool);
+impl_shared_ptr_target_for_primitive!(u8);
+impl_shared_ptr_target_for_primitive!(u16);
+impl_shared_ptr_target_for_primitive!(u32);
+impl_shared_ptr_target_for_primitive!(u64);
+impl_shared_ptr_target_for_primitive!(usize);
+impl_shared_ptr_target_for_primitive!(i8);
+impl_shared_ptr_target_for_primitive!(i16);
+impl_shared_ptr_target_for_primitive!(i32);
+impl_shared_ptr_target_for_primitive!(i64);
+impl_shared_ptr_target_for_primitive!(isize);
+impl_shared_ptr_target_for_primitive!(f32);
+impl_shared_ptr_target_for_primitive!(f64);
+
+impl_shared_ptr_target!("string", "CxxString", CxxString);
diff --git a/src/symbols/exception.rs b/src/symbols/exception.rs
index b408f255..0c1bb876 100644
--- a/src/symbols/exception.rs
+++ b/src/symbols/exception.rs
@@ -2,7 +2,7 @@ use alloc::boxed::Box;
use alloc::string::String;
use core::slice;
-#[export_name = "cxxbridge05$exception"]
+#[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 boxed = String::from_utf8_lossy(slice).into_owned().into_boxed_str();
diff --git a/src/symbols/mod.rs b/src/symbols/mod.rs
index a9d158db..e00bb550 100644
--- a/src/symbols/mod.rs
+++ b/src/symbols/mod.rs
@@ -1,4 +1,5 @@
mod exception;
+mod rust_slice;
mod rust_str;
mod rust_string;
mod rust_vec;
diff --git a/src/symbols/rust_slice.rs b/src/symbols/rust_slice.rs
new file mode 100644
index 00000000..055b4dee
--- /dev/null
+++ b/src/symbols/rust_slice.rs
@@ -0,0 +1,22 @@
+use crate::rust_slice::RustSlice;
+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);
+}
+
+#[export_name = "cxxbridge1$slice$ptr"]
+unsafe extern "C" fn slice_ptr(this: &RustSlice) -> *const () {
+ this.repr.as_ptr().cast()
+}
+
+#[export_name = "cxxbridge1$slice$len"]
+unsafe extern "C" fn slice_len(this: &RustSlice) -> usize {
+ this.repr.as_ref().len()
+}
diff --git a/src/symbols/rust_str.rs b/src/symbols/rust_str.rs
index b6553818..a9e84efb 100644
--- a/src/symbols/rust_str.rs
+++ b/src/symbols/rust_str.rs
@@ -1,8 +1,37 @@
+use alloc::string::String;
+use core::mem::MaybeUninit;
+use core::ptr;
use core::slice;
use core::str;
-#[export_name = "cxxbridge05$str$valid"]
-unsafe extern "C" fn str_valid(ptr: *const u8, len: usize) -> bool {
+#[export_name = "cxxbridge1$str$new"]
+unsafe extern "C" fn str_new(this: &mut MaybeUninit<&str>) {
+ ptr::write(this.as_mut_ptr(), "");
+}
+
+#[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());
+}
+
+#[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);
- str::from_utf8(slice).is_ok()
+ match str::from_utf8(slice) {
+ Ok(s) => {
+ ptr::write(this.as_mut_ptr(), s);
+ true
+ }
+ Err(_) => false,
+ }
+}
+
+#[export_name = "cxxbridge1$str$ptr"]
+unsafe extern "C" fn str_ptr(this: &&str) -> *const u8 {
+ this.as_ptr()
+}
+
+#[export_name = "cxxbridge1$str$len"]
+unsafe extern "C" fn str_len(this: &&str) -> usize {
+ this.len()
}
diff --git a/src/symbols/rust_string.rs b/src/symbols/rust_string.rs
index e5ab9eaa..91fd78a3 100644
--- a/src/symbols/rust_string.rs
+++ b/src/symbols/rust_string.rs
@@ -5,17 +5,17 @@ use core::ptr;
use core::slice;
use core::str;
-#[export_name = "cxxbridge05$string$new"]
+#[export_name = "cxxbridge1$string$new"]
unsafe extern "C" fn string_new(this: &mut MaybeUninit<String>) {
ptr::write(this.as_mut_ptr(), String::new());
}
-#[export_name = "cxxbridge05$string$clone"]
+#[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());
}
-#[export_name = "cxxbridge05$string$from"]
+#[export_name = "cxxbridge1$string$from"]
unsafe extern "C" fn string_from(
this: &mut MaybeUninit<String>,
ptr: *const u8,
@@ -31,17 +31,22 @@ unsafe extern "C" fn string_from(
}
}
-#[export_name = "cxxbridge05$string$drop"]
+#[export_name = "cxxbridge1$string$drop"]
unsafe extern "C" fn string_drop(this: &mut ManuallyDrop<String>) {
ManuallyDrop::drop(this);
}
-#[export_name = "cxxbridge05$string$ptr"]
+#[export_name = "cxxbridge1$string$ptr"]
unsafe extern "C" fn string_ptr(this: &String) -> *const u8 {
this.as_ptr()
}
-#[export_name = "cxxbridge05$string$len"]
+#[export_name = "cxxbridge1$string$len"]
unsafe extern "C" fn string_len(this: &String) -> usize {
this.len()
}
+
+#[export_name = "cxxbridge1$string$reserve_total"]
+unsafe extern "C" fn string_reserve_total(this: &mut String, cap: usize) {
+ this.reserve(cap);
+}
diff --git a/src/symbols/rust_vec.rs b/src/symbols/rust_vec.rs
index 90fbc542..a26a1569 100644
--- a/src/symbols/rust_vec.rs
+++ b/src/symbols/rust_vec.rs
@@ -3,6 +3,7 @@ use crate::rust_vec::RustVec;
use alloc::vec::Vec;
use core::mem;
use core::ptr;
+use std::os::raw::c_char;
macro_rules! rust_vec_shims {
($segment:expr, $ty:ty) => {
@@ -11,47 +12,47 @@ macro_rules! rust_vec_shims {
const _: () = {
attr! {
- #[export_name = concat!("cxxbridge05$rust_vec$", $segment, "$new")]
+ #[export_name = concat!("cxxbridge1$rust_vec$", $segment, "$new")]
unsafe extern "C" fn __new(this: *mut RustVec<$ty>) {
ptr::write(this, RustVec { repr: Vec::new() });
}
}
attr! {
- #[export_name = concat!("cxxbridge05$rust_vec$", $segment, "$drop")]
+ #[export_name = concat!("cxxbridge1$rust_vec$", $segment, "$drop")]
unsafe extern "C" fn __drop(this: *mut RustVec<$ty>) {
ptr::drop_in_place(this);
}
}
attr! {
- #[export_name = concat!("cxxbridge05$rust_vec$", $segment, "$len")]
+ #[export_name = concat!("cxxbridge1$rust_vec$", $segment, "$len")]
unsafe extern "C" fn __len(this: *const RustVec<$ty>) -> usize {
(*this).repr.len()
}
}
attr! {
- #[export_name = concat!("cxxbridge05$rust_vec$", $segment, "$data")]
+ #[export_name = concat!("cxxbridge1$rust_vec$", $segment, "$capacity")]
+ unsafe extern "C" fn __capacity(this: *const RustVec<$ty>) -> usize {
+ (*this).repr.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()
}
}
attr! {
- #[export_name = concat!("cxxbridge05$rust_vec$", $segment, "$reserve_total")]
+ #[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);
}
}
attr! {
- #[export_name = concat!("cxxbridge05$rust_vec$", $segment, "$set_len")]
+ #[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);
}
}
- attr! {
- #[export_name = concat!("cxxbridge05$rust_vec$", $segment, "$stride")]
- unsafe extern "C" fn __stride() -> usize {
- mem::size_of::<$ty>()
- }
- }
};
};
}
@@ -67,11 +68,15 @@ rust_vec_shims_for_primitive!(u8);
rust_vec_shims_for_primitive!(u16);
rust_vec_shims_for_primitive!(u32);
rust_vec_shims_for_primitive!(u64);
+rust_vec_shims_for_primitive!(usize);
rust_vec_shims_for_primitive!(i8);
rust_vec_shims_for_primitive!(i16);
rust_vec_shims_for_primitive!(i32);
rust_vec_shims_for_primitive!(i64);
+rust_vec_shims_for_primitive!(isize);
rust_vec_shims_for_primitive!(f32);
rust_vec_shims_for_primitive!(f64);
+rust_vec_shims!("char", c_char);
rust_vec_shims!("string", RustString);
+rust_vec_shims!("str", &str);
diff --git a/src/type_id.rs b/src/type_id.rs
new file mode 100644
index 00000000..bd2b4ea6
--- /dev/null
+++ b/src/type_id.rs
@@ -0,0 +1,9 @@
+/// For use in impls of the `ExternType` trait. See [`ExternType`].
+///
+/// [`ExternType`]: trait.ExternType.html
+#[macro_export]
+macro_rules! type_id {
+ ($($path:tt)*) => {
+ $crate::private::type_id! { $crate $($path)* }
+ };
+}
diff --git a/src/unique_ptr.rs b/src/unique_ptr.rs
index 3a5fc669..836f4677 100644
--- a/src/unique_ptr.rs
+++ b/src/unique_ptr.rs
@@ -1,12 +1,14 @@
-use crate::cxx_string::CxxString;
-use crate::cxx_vector::{self, CxxVector, VectorElement};
+use crate::cxx_vector::{CxxVector, VectorElement};
+use crate::fmt::display;
use crate::kind::Trivial;
+use crate::string::CxxString;
use crate::ExternType;
use core::ffi::c_void;
use core::fmt::{self, Debug, Display};
use core::marker::PhantomData;
use core::mem;
use core::ops::{Deref, DerefMut};
+use core::pin::Pin;
use core::ptr;
/// Binding to C++ `std::unique_ptr<T, std::default_delete<T>>`.
@@ -58,10 +60,29 @@ where
unsafe { T::__get(self.repr).as_ref() }
}
- /// Returns a mutable reference to the object owned by this UniquePtr if
- /// any, otherwise None.
- pub fn as_mut(&mut self) -> Option<&mut T> {
- unsafe { (T::__get(self.repr) as *mut T).as_mut() }
+ /// Returns a mutable pinned reference to the object owned by this UniquePtr
+ /// if any, otherwise None.
+ pub fn as_mut(&mut self) -> Option<Pin<&mut T>> {
+ unsafe {
+ let mut_reference = (T::__get(self.repr) as *mut T).as_mut()?;
+ Some(Pin::new_unchecked(mut_reference))
+ }
+ }
+
+ /// Returns a mutable pinned reference to the object owned by this
+ /// UniquePtr.
+ ///
+ /// # Panics
+ ///
+ /// Panics if the UniquePtr holds a null pointer.
+ pub fn pin_mut(&mut self) -> Pin<&mut T> {
+ match self.as_mut() {
+ Some(target) => target,
+ None => panic!(
+ "called pin_mut on a null UniquePtr<{}>",
+ display(T::__typename),
+ ),
+ }
}
/// Consumes the UniquePtr, releasing its ownership of the heap-allocated T.
@@ -110,19 +131,25 @@ where
fn deref(&self) -> &Self::Target {
match self.as_ref() {
Some(target) => target,
- None => panic!("called deref on a null UniquePtr<{}>", T::__NAME),
+ None => panic!(
+ "called deref on a null UniquePtr<{}>",
+ display(T::__typename),
+ ),
}
}
}
impl<T> DerefMut for UniquePtr<T>
where
- T: UniquePtrTarget,
+ T: UniquePtrTarget + Unpin,
{
fn deref_mut(&mut self) -> &mut Self::Target {
match self.as_mut() {
- Some(target) => target,
- None => panic!("called deref_mut on a null UniquePtr<{}>", T::__NAME),
+ Some(target) => Pin::into_inner(target),
+ None => panic!(
+ "called deref_mut on a null UniquePtr<{}>",
+ display(T::__typename),
+ ),
}
}
}
@@ -151,11 +178,34 @@ 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
+/// `UniquePtr<T>` in generic code.
+///
+/// This trait has no publicly callable or implementable methods. Implementing
+/// it outside of the CXX codebase is not supported.
+///
+/// # Example
+///
+/// A bound `T: UniquePtrTarget` may be necessary when manipulating
+/// [`UniquePtr`] in generic code.
+///
+/// ```
+/// use cxx::memory::{UniquePtr, UniquePtrTarget};
+/// use std::fmt::Display;
+///
+/// pub fn take_generic_ptr<T>(ptr: UniquePtr<T>)
+/// where
+/// T: UniquePtrTarget + Display,
+/// {
+/// println!("the unique_ptr points to: {}", *ptr);
+/// }
+/// ```
+///
+/// Writing the same generic function without a `UniquePtrTarget` trait bound
+/// would not compile.
pub unsafe trait UniquePtrTarget {
#[doc(hidden)]
- const __NAME: &'static dyn Display;
+ fn __typename(f: &mut fmt::Formatter) -> fmt::Result;
#[doc(hidden)]
fn __null() -> *mut c_void;
#[doc(hidden)]
@@ -179,20 +229,24 @@ pub unsafe trait UniquePtrTarget {
}
extern "C" {
- #[link_name = "cxxbridge05$unique_ptr$std$string$null"]
+ #[link_name = "cxxbridge1$unique_ptr$std$string$null"]
fn unique_ptr_std_string_null(this: *mut *mut c_void);
- #[link_name = "cxxbridge05$unique_ptr$std$string$raw"]
+ #[link_name = "cxxbridge1$unique_ptr$std$string$raw"]
fn unique_ptr_std_string_raw(this: *mut *mut c_void, raw: *mut CxxString);
- #[link_name = "cxxbridge05$unique_ptr$std$string$get"]
+ #[link_name = "cxxbridge1$unique_ptr$std$string$get"]
fn unique_ptr_std_string_get(this: *const *mut c_void) -> *const CxxString;
- #[link_name = "cxxbridge05$unique_ptr$std$string$release"]
+ #[link_name = "cxxbridge1$unique_ptr$std$string$release"]
fn unique_ptr_std_string_release(this: *mut *mut c_void) -> *mut CxxString;
- #[link_name = "cxxbridge05$unique_ptr$std$string$drop"]
+ #[link_name = "cxxbridge1$unique_ptr$std$string$drop"]
fn unique_ptr_std_string_drop(this: *mut *mut c_void);
}
unsafe impl UniquePtrTarget for CxxString {
- const __NAME: &'static dyn Display = &"CxxString";
+ #[doc(hidden)]
+ fn __typename(f: &mut fmt::Formatter) -> fmt::Result {
+ f.write_str("CxxString")
+ }
+ #[doc(hidden)]
fn __null() -> *mut c_void {
let mut repr = ptr::null_mut::<c_void>();
unsafe {
@@ -200,17 +254,21 @@ unsafe impl UniquePtrTarget for CxxString {
}
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);
repr
}
+ #[doc(hidden)]
unsafe fn __get(repr: *mut c_void) -> *const Self {
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)
}
+ #[doc(hidden)]
unsafe fn __drop(mut repr: *mut c_void) {
unique_ptr_std_string_drop(&mut repr);
}
@@ -218,21 +276,29 @@ unsafe impl UniquePtrTarget for CxxString {
unsafe impl<T> UniquePtrTarget for CxxVector<T>
where
- T: VectorElement + 'static,
+ T: VectorElement,
{
- const __NAME: &'static dyn Display = &cxx_vector::TypeName::<T>::new();
+ #[doc(hidden)]
+ fn __typename(f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "CxxVector<{}>", display(T::__typename))
+ }
+ #[doc(hidden)]
fn __null() -> *mut c_void {
T::__unique_ptr_null()
}
+ #[doc(hidden)]
unsafe fn __raw(raw: *mut Self) -> *mut c_void {
T::__unique_ptr_raw(raw)
}
+ #[doc(hidden)]
unsafe fn __get(repr: *mut c_void) -> *const Self {
T::__unique_ptr_get(repr)
}
+ #[doc(hidden)]
unsafe fn __release(repr: *mut c_void) -> *mut Self {
T::__unique_ptr_release(repr)
}
+ #[doc(hidden)]
unsafe fn __drop(repr: *mut c_void) {
T::__unique_ptr_drop(repr);
}
diff --git a/src/vector.rs b/src/vector.rs
new file mode 100644
index 00000000..4afd4879
--- /dev/null
+++ b/src/vector.rs
@@ -0,0 +1,9 @@
+//! Less used details of `CxxVector`.
+//!
+//! `CxxVector` itself is exposed at the crate root.
+
+pub use crate::cxx_vector::{Iter, IterMut, VectorElement};
+#[doc(inline)]
+pub use crate::Vector;
+#[doc(no_inline)]
+pub use cxx::CxxVector;
diff --git a/src/weak_ptr.rs b/src/weak_ptr.rs
new file mode 100644
index 00000000..8291d59b
--- /dev/null
+++ b/src/weak_ptr.rs
@@ -0,0 +1,192 @@
+use crate::shared_ptr::{SharedPtr, SharedPtrTarget};
+use crate::string::CxxString;
+use core::ffi::c_void;
+use core::fmt::{self, Debug};
+use core::marker::PhantomData;
+use core::mem::MaybeUninit;
+
+/// Binding to C++ `std::weak_ptr<T>`.
+///
+/// The typical way to construct a WeakPtr from Rust is by [downgrading] from a
+/// SharedPtr.
+///
+/// [downgrading]: crate::SharedPtr::downgrade
+#[repr(C)]
+pub struct WeakPtr<T>
+where
+ T: WeakPtrTarget,
+{
+ repr: [*mut c_void; 2],
+ ty: PhantomData<T>,
+}
+
+impl<T> WeakPtr<T>
+where
+ T: WeakPtrTarget,
+{
+ /// Makes a new WeakPtr wrapping a null pointer.
+ ///
+ /// Matches the behavior of default-constructing a std::weak\_ptr.
+ pub fn null() -> Self {
+ let mut weak_ptr = MaybeUninit::<WeakPtr<T>>::uninit();
+ let new = weak_ptr.as_mut_ptr().cast();
+ unsafe {
+ T::__null(new);
+ weak_ptr.assume_init()
+ }
+ }
+
+ /// Upgrades a non-owning reference into an owning reference if possible,
+ /// otherwise to a null reference.
+ ///
+ /// Matches the behavior of [std::weak_ptr\<T\>::lock](https://en.cppreference.com/w/cpp/memory/weak_ptr/lock).
+ pub fn upgrade(&self) -> SharedPtr<T>
+ where
+ T: SharedPtrTarget,
+ {
+ let this = self as *const Self as *const c_void;
+ let mut shared_ptr = MaybeUninit::<SharedPtr<T>>::uninit();
+ let new = shared_ptr.as_mut_ptr().cast();
+ unsafe {
+ T::__upgrade(this, new);
+ shared_ptr.assume_init()
+ }
+ }
+}
+
+unsafe impl<T> Send for WeakPtr<T> where T: Send + Sync + WeakPtrTarget {}
+unsafe impl<T> Sync for WeakPtr<T> where T: Send + Sync + WeakPtrTarget {}
+
+impl<T> Clone for WeakPtr<T>
+where
+ T: WeakPtrTarget,
+{
+ fn clone(&self) -> Self {
+ let mut weak_ptr = MaybeUninit::<WeakPtr<T>>::uninit();
+ let new = weak_ptr.as_mut_ptr().cast();
+ let this = self as *const Self as *mut c_void;
+ unsafe {
+ T::__clone(this, new);
+ weak_ptr.assume_init()
+ }
+ }
+}
+
+impl<T> Drop for WeakPtr<T>
+where
+ T: WeakPtrTarget,
+{
+ fn drop(&mut self) {
+ let this = self as *mut Self as *mut c_void;
+ unsafe { T::__drop(this) }
+ }
+}
+
+impl<T> Debug for WeakPtr<T>
+where
+ T: Debug + WeakPtrTarget + SharedPtrTarget,
+{
+ fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+ Debug::fmt(&self.upgrade(), formatter)
+ }
+}
+
+// Methods are private; not intended to be implemented outside of cxxbridge
+// codebase.
+pub unsafe trait WeakPtrTarget {
+ #[doc(hidden)]
+ fn __typename(f: &mut fmt::Formatter) -> fmt::Result;
+ #[doc(hidden)]
+ unsafe fn __null(new: *mut c_void);
+ #[doc(hidden)]
+ unsafe fn __clone(this: *const c_void, new: *mut c_void);
+ #[doc(hidden)]
+ unsafe fn __downgrade(shared: *const c_void, new: *mut c_void);
+ #[doc(hidden)]
+ unsafe fn __upgrade(weak: *const c_void, shared: *mut c_void);
+ #[doc(hidden)]
+ unsafe fn __drop(this: *mut c_void);
+}
+
+macro_rules! impl_weak_ptr_target {
+ ($segment:expr, $name:expr, $ty:ty) => {
+ unsafe impl WeakPtrTarget for $ty {
+ #[doc(hidden)]
+ fn __typename(f: &mut fmt::Formatter) -> fmt::Result {
+ f.write_str($name)
+ }
+ #[doc(hidden)]
+ unsafe fn __null(new: *mut c_void) {
+ extern "C" {
+ attr! {
+ #[link_name = concat!("cxxbridge1$std$weak_ptr$", $segment, "$null")]
+ fn __null(new: *mut c_void);
+ }
+ }
+ __null(new);
+ }
+ #[doc(hidden)]
+ unsafe fn __clone(this: *const c_void, new: *mut c_void) {
+ extern "C" {
+ attr! {
+ #[link_name = concat!("cxxbridge1$std$weak_ptr$", $segment, "$clone")]
+ fn __clone(this: *const c_void, new: *mut c_void);
+ }
+ }
+ __clone(this, new);
+ }
+ #[doc(hidden)]
+ unsafe fn __downgrade(shared: *const c_void, weak: *mut c_void) {
+ extern "C" {
+ attr! {
+ #[link_name = concat!("cxxbridge1$std$weak_ptr$", $segment, "$downgrade")]
+ fn __downgrade(shared: *const c_void, weak: *mut c_void);
+ }
+ }
+ __downgrade(shared, weak);
+ }
+ #[doc(hidden)]
+ unsafe fn __upgrade(weak: *const c_void, shared: *mut c_void) {
+ extern "C" {
+ attr! {
+ #[link_name = concat!("cxxbridge1$std$weak_ptr$", $segment, "$upgrade")]
+ fn __upgrade(weak: *const c_void, shared: *mut c_void);
+ }
+ }
+ __upgrade(weak, shared);
+ }
+ #[doc(hidden)]
+ unsafe fn __drop(this: *mut c_void) {
+ extern "C" {
+ attr! {
+ #[link_name = concat!("cxxbridge1$std$weak_ptr$", $segment, "$drop")]
+ fn __drop(this: *mut c_void);
+ }
+ }
+ __drop(this);
+ }
+ }
+ };
+}
+
+macro_rules! impl_weak_ptr_target_for_primitive {
+ ($ty:ident) => {
+ impl_weak_ptr_target!(stringify!($ty), stringify!($ty), $ty);
+ };
+}
+
+impl_weak_ptr_target_for_primitive!(bool);
+impl_weak_ptr_target_for_primitive!(u8);
+impl_weak_ptr_target_for_primitive!(u16);
+impl_weak_ptr_target_for_primitive!(u32);
+impl_weak_ptr_target_for_primitive!(u64);
+impl_weak_ptr_target_for_primitive!(usize);
+impl_weak_ptr_target_for_primitive!(i8);
+impl_weak_ptr_target_for_primitive!(i16);
+impl_weak_ptr_target_for_primitive!(i32);
+impl_weak_ptr_target_for_primitive!(i64);
+impl_weak_ptr_target_for_primitive!(isize);
+impl_weak_ptr_target_for_primitive!(f32);
+impl_weak_ptr_target_for_primitive!(f64);
+
+impl_weak_ptr_target!("string", "CxxString", CxxString);
diff --git a/syntax/atom.rs b/syntax/atom.rs
index 7d0ef6b6..d4ad78f1 100644
--- a/syntax/atom.rs
+++ b/syntax/atom.rs
@@ -5,6 +5,7 @@ use std::fmt::{self, Display};
#[derive(Copy, Clone, PartialEq)]
pub enum Atom {
Bool,
+ Char, // C char, not Rust char
U8,
U16,
U32,
@@ -30,6 +31,7 @@ impl Atom {
use self::Atom::*;
match s {
"bool" => Some(Bool),
+ "c_char" => Some(Char),
"u8" => Some(U8),
"u16" => Some(U16),
"u32" => Some(U32),
@@ -60,6 +62,7 @@ impl AsRef<str> for Atom {
use self::Atom::*;
match self {
Bool => "bool",
+ Char => "c_char",
U8 => "u8",
U16 => "u16",
U32 => "u32",
diff --git a/syntax/attrs.rs b/syntax/attrs.rs
index af73b7a5..4808f2e1 100644
--- a/syntax/attrs.rs
+++ b/syntax/attrs.rs
@@ -1,22 +1,47 @@
use crate::syntax::namespace::Namespace;
use crate::syntax::report::Errors;
use crate::syntax::Atom::{self, *};
-use crate::syntax::{Derive, Doc};
-use proc_macro2::Ident;
+use crate::syntax::{Derive, Doc, ForeignName};
+use proc_macro2::{Ident, TokenStream};
+use quote::ToTokens;
use syn::parse::{ParseStream, Parser as _};
use syn::{Attribute, Error, LitStr, Path, Result, Token};
+// Intended usage:
+//
+// let mut doc = Doc::new();
+// let mut cxx_name = None;
+// let mut rust_name = None;
+// /* ... */
+// let attrs = attrs::parse(
+// cx,
+// item.attrs,
+// attrs::Parser {
+// doc: Some(&mut doc),
+// cxx_name: Some(&mut cxx_name),
+// rust_name: Some(&mut rust_name),
+// /* ... */
+// ..Default::default()
+// },
+// );
+//
#[derive(Default)]
pub struct Parser<'a> {
pub doc: Option<&'a mut Doc>,
pub derives: Option<&'a mut Vec<Derive>>,
pub repr: Option<&'a mut Option<Atom>>,
- pub cxx_name: Option<&'a mut Option<Ident>>,
- pub rust_name: Option<&'a mut Option<Ident>>,
pub namespace: Option<&'a mut Namespace>,
+ pub cxx_name: Option<&'a mut Option<ForeignName>>,
+ pub rust_name: Option<&'a mut Option<Ident>>,
+
+ // Suppress clippy needless_update lint ("struct update has no effect, all
+ // the fields in the struct have already been specified") when preemptively
+ // writing `..Default::default()`.
+ pub(crate) _more: (),
}
-pub(super) fn parse(cx: &mut Errors, attrs: &[Attribute], mut parser: Parser) {
+pub fn parse(cx: &mut Errors, attrs: Vec<Attribute>, mut parser: Parser) -> OtherAttrs {
+ let mut passthrough_attrs = Vec::new();
for attr in attrs {
if attr.path.is_ident("doc") {
match parse_doc_attribute.parse2(attr.tokens.clone()) {
@@ -26,17 +51,23 @@ pub(super) fn parse(cx: &mut Errors, attrs: &[Attribute], mut parser: Parser) {
continue;
}
}
- Err(err) => return cx.push(err),
+ Err(err) => {
+ cx.push(err);
+ break;
+ }
}
} else if attr.path.is_ident("derive") {
- match attr.parse_args_with(parse_derive_attribute) {
+ match attr.parse_args_with(|attr: ParseStream| parse_derive_attribute(cx, attr)) {
Ok(attr) => {
if let Some(derives) = &mut parser.derives {
derives.extend(attr);
continue;
}
}
- Err(err) => return cx.push(err),
+ Err(err) => {
+ cx.push(err);
+ break;
+ }
}
} else if attr.path.is_ident("repr") {
match attr.parse_args_with(parse_repr_attribute) {
@@ -46,41 +77,74 @@ pub(super) fn parse(cx: &mut Errors, attrs: &[Attribute], mut parser: Parser) {
continue;
}
}
- Err(err) => return cx.push(err),
+ Err(err) => {
+ cx.push(err);
+ break;
+ }
+ }
+ } else if attr.path.is_ident("namespace") {
+ match parse_namespace_attribute.parse2(attr.tokens.clone()) {
+ Ok(attr) => {
+ if let Some(namespace) = &mut parser.namespace {
+ **namespace = attr;
+ continue;
+ }
+ }
+ Err(err) => {
+ cx.push(err);
+ break;
+ }
}
} else if attr.path.is_ident("cxx_name") {
- match parse_function_alias_attribute.parse2(attr.tokens.clone()) {
+ match parse_cxx_name_attribute.parse2(attr.tokens.clone()) {
Ok(attr) => {
if let Some(cxx_name) = &mut parser.cxx_name {
**cxx_name = Some(attr);
continue;
}
}
- Err(err) => return cx.push(err),
+ Err(err) => {
+ cx.push(err);
+ break;
+ }
}
} else if attr.path.is_ident("rust_name") {
- match parse_function_alias_attribute.parse2(attr.tokens.clone()) {
+ match parse_rust_name_attribute.parse2(attr.tokens.clone()) {
Ok(attr) => {
if let Some(rust_name) = &mut parser.rust_name {
**rust_name = Some(attr);
continue;
}
}
- Err(err) => return cx.push(err),
- }
- } else if attr.path.is_ident("namespace") {
- match parse_namespace_attribute.parse2(attr.tokens.clone()) {
- Ok(attr) => {
- if let Some(namespace) = &mut parser.namespace {
- **namespace = attr;
- continue;
- }
+ Err(err) => {
+ cx.push(err);
+ break;
}
- Err(err) => return cx.push(err),
+ }
+ } else if attr.path.is_ident("allow")
+ || attr.path.is_ident("warn")
+ || attr.path.is_ident("deny")
+ || attr.path.is_ident("forbid")
+ || attr.path.is_ident("deprecated")
+ || attr.path.is_ident("must_use")
+ {
+ // https://doc.rust-lang.org/reference/attributes/diagnostics.html
+ passthrough_attrs.push(attr);
+ continue;
+ } else if attr.path.segments.len() > 1 {
+ let tool = &attr.path.segments.first().unwrap().ident;
+ if tool == "rustfmt" {
+ // Skip, rustfmt only needs to find it in the pre-expansion source file.
+ continue;
+ } else if tool == "clippy" {
+ passthrough_attrs.push(attr);
+ continue;
}
}
- return cx.error(attr, "unsupported attribute");
+ cx.error(attr, "unsupported attribute");
+ break;
}
+ OtherAttrs(passthrough_attrs)
}
fn parse_doc_attribute(input: ParseStream) -> Result<LitStr> {
@@ -89,19 +153,20 @@ fn parse_doc_attribute(input: ParseStream) -> Result<LitStr> {
Ok(lit)
}
-fn parse_derive_attribute(input: ParseStream) -> Result<Vec<Derive>> {
- input
- .parse_terminated::<Path, Token![,]>(Path::parse_mod_style)?
- .into_iter()
- .map(|path| {
- if let Some(ident) = path.get_ident() {
- if let Some(derive) = Derive::from(ident) {
- return Ok(derive);
- }
+fn parse_derive_attribute(cx: &mut Errors, input: ParseStream) -> Result<Vec<Derive>> {
+ let paths = input.parse_terminated::<Path, Token![,]>(Path::parse_mod_style)?;
+
+ let mut derives = Vec::new();
+ for path in paths {
+ if let Some(ident) = path.get_ident() {
+ if let Some(derive) = Derive::from(ident) {
+ derives.push(derive);
+ continue;
}
- Err(Error::new_spanned(path, "unsupported derive"))
- })
- .collect()
+ }
+ cx.error(path, "unsupported derive");
+ }
+ Ok(derives)
}
fn parse_repr_attribute(input: ParseStream) -> Result<Atom> {
@@ -121,7 +186,24 @@ fn parse_repr_attribute(input: ParseStream) -> Result<Atom> {
))
}
-fn parse_function_alias_attribute(input: ParseStream) -> Result<Ident> {
+fn parse_namespace_attribute(input: ParseStream) -> Result<Namespace> {
+ input.parse::<Token![=]>()?;
+ let namespace = input.parse::<Namespace>()?;
+ Ok(namespace)
+}
+
+fn parse_cxx_name_attribute(input: ParseStream) -> Result<ForeignName> {
+ input.parse::<Token![=]>()?;
+ if input.peek(LitStr) {
+ let lit: LitStr = input.parse()?;
+ ForeignName::parse(&lit.value(), lit.span())
+ } else {
+ let ident: Ident = input.parse()?;
+ ForeignName::parse(&ident.to_string(), ident.span())
+ }
+}
+
+fn parse_rust_name_attribute(input: ParseStream) -> Result<Ident> {
input.parse::<Token![=]>()?;
if input.peek(LitStr) {
let lit: LitStr = input.parse()?;
@@ -131,8 +213,18 @@ fn parse_function_alias_attribute(input: ParseStream) -> Result<Ident> {
}
}
-fn parse_namespace_attribute(input: ParseStream) -> Result<Namespace> {
- input.parse::<Token![=]>()?;
- let namespace = input.parse::<Namespace>()?;
- Ok(namespace)
+pub struct OtherAttrs(Vec<Attribute>);
+
+impl OtherAttrs {
+ pub fn none() -> Self {
+ OtherAttrs(Vec::new())
+ }
+}
+
+impl ToTokens for OtherAttrs {
+ fn to_tokens(&self, tokens: &mut TokenStream) {
+ for attr in &self.0 {
+ attr.to_tokens(tokens);
+ }
+ }
}
diff --git a/syntax/check.rs b/syntax/check.rs
index e8a64486..bab2dec5 100644
--- a/syntax/check.rs
+++ b/syntax/check.rs
@@ -1,9 +1,9 @@
use crate::syntax::atom::Atom::{self, *};
use crate::syntax::report::Errors;
-use crate::syntax::types::TrivialReason;
+use crate::syntax::visit::{self, Visit};
use crate::syntax::{
- error, ident, Api, Enum, ExternFn, ExternType, Impl, Lang, Receiver, Ref, Slice, Struct, Ty1,
- Type, Types,
+ error, ident, trivial, Api, Array, Enum, ExternFn, ExternType, Impl, Lang, NamedType, Ptr,
+ Receiver, Ref, Signature, SliceRef, Struct, Trait, Ty1, Type, TypeAlias, Types,
};
use proc_macro2::{Delimiter, Group, Ident, TokenStream};
use quote::{quote, ToTokens};
@@ -28,25 +28,31 @@ fn do_typecheck(cx: &mut Check) {
for ty in cx.types {
match ty {
- Type::Ident(ident) => check_type_ident(cx, &ident.rust),
+ Type::Ident(ident) => check_type_ident(cx, ident),
Type::RustBox(ptr) => check_type_box(cx, ptr),
Type::RustVec(ty) => check_type_rust_vec(cx, ty),
Type::UniquePtr(ptr) => check_type_unique_ptr(cx, ptr),
+ Type::SharedPtr(ptr) => check_type_shared_ptr(cx, ptr),
+ Type::WeakPtr(ptr) => check_type_weak_ptr(cx, ptr),
Type::CxxVector(ptr) => check_type_cxx_vector(cx, ptr),
Type::Ref(ty) => check_type_ref(cx, ty),
- Type::Slice(ty) => check_type_slice(cx, ty),
- _ => {}
+ Type::Ptr(ty) => check_type_ptr(cx, ty),
+ Type::Array(array) => check_type_array(cx, array),
+ Type::Fn(ty) => check_type_fn(cx, ty),
+ Type::SliceRef(ty) => check_type_slice_ref(cx, ty),
+ Type::Str(_) | Type::Void(_) => {}
}
}
for api in cx.apis {
match api {
+ Api::Include(_) => {}
Api::Struct(strct) => check_api_struct(cx, strct),
Api::Enum(enm) => check_api_enum(cx, enm),
Api::CxxType(ety) | Api::RustType(ety) => check_api_type(cx, ety),
Api::CxxFunction(efn) | Api::RustFunction(efn) => check_api_fn(cx, efn),
+ Api::TypeAlias(alias) => check_api_type_alias(cx, alias),
Api::Impl(imp) => check_api_impl(cx, imp),
- _ => {}
}
}
}
@@ -57,7 +63,8 @@ impl Check<'_> {
}
}
-fn check_type_ident(cx: &mut Check, ident: &Ident) {
+fn check_type_ident(cx: &mut Check, name: &NamedType) {
+ let ident = &name.rust;
if Atom::from(ident).is_none()
&& !cx.types.structs.contains_key(ident)
&& !cx.types.enums.contains_key(ident)
@@ -72,6 +79,7 @@ fn check_type_ident(cx: &mut Check, ident: &Ident) {
fn check_type_box(cx: &mut Check, ptr: &Ty1) {
if let Type::Ident(ident) = &ptr.inner {
if cx.types.cxx.contains(&ident.rust)
+ && !cx.types.aliases.contains_key(&ident.rust)
&& !cx.types.structs.contains_key(&ident.rust)
&& !cx.types.enums.contains_key(&ident.rust)
{
@@ -87,22 +95,27 @@ fn check_type_box(cx: &mut Check, ptr: &Ty1) {
}
fn check_type_rust_vec(cx: &mut Check, ty: &Ty1) {
- if let Type::Ident(ident) = &ty.inner {
- if cx.types.cxx.contains(&ident.rust)
- && !cx.types.structs.contains_key(&ident.rust)
- && !cx.types.enums.contains_key(&ident.rust)
- {
- cx.error(ty, "Rust Vec containing C++ type is not supported yet");
- return;
- }
+ match &ty.inner {
+ Type::Ident(ident) => {
+ if cx.types.cxx.contains(&ident.rust)
+ && !cx.types.aliases.contains_key(&ident.rust)
+ && !cx.types.structs.contains_key(&ident.rust)
+ && !cx.types.enums.contains_key(&ident.rust)
+ {
+ cx.error(ty, "Rust Vec containing C++ type is not supported yet");
+ return;
+ }
- match Atom::from(&ident.rust) {
- None | Some(U8) | Some(U16) | Some(U32) | Some(U64) | Some(Usize) | Some(I8)
- | Some(I16) | Some(I32) | Some(I64) | Some(Isize) | Some(F32) | Some(F64)
- | Some(RustString) => return,
- Some(Bool) => { /* todo */ }
- Some(CxxString) => {}
+ match Atom::from(&ident.rust) {
+ None | Some(Char) | Some(U8) | Some(U16) | Some(U32) | Some(U64) | Some(Usize)
+ | Some(I8) | Some(I16) | Some(I32) | Some(I64) | Some(Isize) | Some(F32)
+ | Some(F64) | Some(RustString) => return,
+ Some(Bool) => { /* todo */ }
+ Some(CxxString) => {}
+ }
}
+ Type::Str(_) => return,
+ _ => {}
}
cx.error(ty, "unsupported element type of Vec");
@@ -112,6 +125,7 @@ fn check_type_unique_ptr(cx: &mut Check, ptr: &Ty1) {
if let Type::Ident(ident) = &ptr.inner {
if cx.types.rust.contains(&ident.rust) {
cx.error(ptr, "unique_ptr of a Rust type is not supported yet");
+ return;
}
match Atom::from(&ident.rust) {
@@ -125,6 +139,48 @@ fn check_type_unique_ptr(cx: &mut Check, ptr: &Ty1) {
cx.error(ptr, "unsupported unique_ptr target type");
}
+fn check_type_shared_ptr(cx: &mut Check, ptr: &Ty1) {
+ if let Type::Ident(ident) = &ptr.inner {
+ if cx.types.rust.contains(&ident.rust) {
+ cx.error(ptr, "shared_ptr of a Rust type is not supported yet");
+ return;
+ }
+
+ match Atom::from(&ident.rust) {
+ None | Some(Bool) | Some(U8) | Some(U16) | Some(U32) | Some(U64) | Some(Usize)
+ | Some(I8) | Some(I16) | Some(I32) | Some(I64) | Some(Isize) | Some(F32)
+ | Some(F64) | Some(CxxString) => return,
+ Some(Char) | Some(RustString) => {}
+ }
+ } else if let Type::CxxVector(_) = &ptr.inner {
+ cx.error(ptr, "std::shared_ptr<std::vector> is not supported yet");
+ return;
+ }
+
+ cx.error(ptr, "unsupported shared_ptr target type");
+}
+
+fn check_type_weak_ptr(cx: &mut Check, ptr: &Ty1) {
+ if let Type::Ident(ident) = &ptr.inner {
+ if cx.types.rust.contains(&ident.rust) {
+ cx.error(ptr, "weak_ptr of a Rust type is not supported yet");
+ return;
+ }
+
+ match Atom::from(&ident.rust) {
+ None | Some(Bool) | Some(U8) | Some(U16) | Some(U32) | Some(U64) | Some(Usize)
+ | Some(I8) | Some(I16) | Some(I32) | Some(I64) | Some(Isize) | Some(F32)
+ | Some(F64) | Some(CxxString) => return,
+ Some(Char) | Some(RustString) => {}
+ }
+ } else if let Type::CxxVector(_) = &ptr.inner {
+ cx.error(ptr, "std::weak_ptr<std::vector> is not supported yet");
+ return;
+ }
+
+ cx.error(ptr, "unsupported weak_ptr target type");
+}
+
fn check_type_cxx_vector(cx: &mut Check, ptr: &Ty1) {
if let Type::Ident(ident) = &ptr.inner {
if cx.types.rust.contains(&ident.rust) {
@@ -132,22 +188,38 @@ fn check_type_cxx_vector(cx: &mut Check, ptr: &Ty1) {
ptr,
"C++ vector containing a Rust type is not supported yet",
);
+ return;
}
match Atom::from(&ident.rust) {
None | Some(U8) | Some(U16) | Some(U32) | Some(U64) | Some(Usize) | Some(I8)
| Some(I16) | Some(I32) | Some(I64) | Some(Isize) | Some(F32) | Some(F64)
| Some(CxxString) => return,
+ Some(Char) => { /* todo */ }
Some(Bool) | Some(RustString) => {}
}
}
- cx.error(ptr, "unsupported vector target type");
+ cx.error(ptr, "unsupported vector element type");
}
fn check_type_ref(cx: &mut Check, ty: &Ref) {
- if ty.lifetime.is_some() {
- cx.error(ty, "references with explicit lifetimes are not supported");
+ if ty.mutable && !ty.pinned {
+ if let Some(requires_pin) = match &ty.inner {
+ Type::Ident(ident) if ident.rust == CxxString || is_opaque_cxx(cx, &ident.rust) => {
+ Some(ident.rust.to_string())
+ }
+ Type::CxxVector(_) => Some("CxxVector<...>".to_owned()),
+ _ => None,
+ } {
+ cx.error(
+ ty,
+ format!(
+ "mutable reference to C++ type requires a pin -- use Pin<&mut {}>",
+ requires_pin,
+ ),
+ );
+ }
}
match ty.inner {
@@ -162,8 +234,61 @@ fn check_type_ref(cx: &mut Check, ty: &Ref) {
cx.error(ty, "unsupported reference type");
}
-fn check_type_slice(cx: &mut Check, ty: &Slice) {
- cx.error(ty, "only &[u8] is supported so far, not other slice types");
+fn check_type_ptr(cx: &mut Check, ty: &Ptr) {
+ match ty.inner {
+ Type::Fn(_) | Type::Void(_) => {}
+ Type::Ref(_) => {
+ cx.error(ty, "C++ does not allow pointer to reference as a type");
+ return;
+ }
+ _ => return,
+ }
+
+ cx.error(ty, "unsupported pointer type");
+}
+
+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),
+ _ => false,
+ };
+
+ if !supported {
+ let mutable = if ty.mutable { "mut " } else { "" };
+ let mut msg = format!("unsupported &{}[T] element type", mutable);
+ if let Type::Ident(ident) = &ty.inner {
+ if is_opaque_cxx(cx, &ident.rust) {
+ msg += ": opaque C++ type is not supported yet";
+ }
+ }
+ cx.error(ty, msg);
+ }
+}
+
+fn check_type_array(cx: &mut Check, ty: &Array) {
+ let supported = !is_unsized(cx, &ty.inner);
+
+ if !supported {
+ cx.error(ty, "unsupported array element type");
+ }
+}
+
+fn check_type_fn(cx: &mut Check, ty: &Signature) {
+ if ty.throws {
+ cx.error(ty, "function pointer returning Result is not supported yet");
+ }
+
+ for arg in &ty.args {
+ if let Type::Ptr(_) = arg.ty {
+ if ty.unsafety.is_none() {
+ cx.error(
+ arg,
+ "pointer argument requires that the function pointer be marked unsafe",
+ );
+ }
+ }
+ }
}
fn check_api_struct(cx: &mut Check, strct: &Struct) {
@@ -182,17 +307,23 @@ fn check_api_struct(cx: &mut Check, strct: &Struct) {
}
}
- for field in &strct.fields {
- if is_unsized(cx, &field.ty) {
- let desc = describe(cx, &field.ty);
- let msg = format!("using {} by value is not supported", desc);
- cx.error(field, msg);
+ for derive in &strct.derives {
+ if derive.what == Trait::ExternType {
+ let msg = format!("derive({}) on shared struct is not supported", derive);
+ cx.error(derive, msg);
}
+ }
+
+ for field in &strct.fields {
if let Type::Fn(_) = field.ty {
cx.error(
field,
"function pointers in a struct field are not implemented yet",
);
+ } else if is_unsized(cx, &field.ty) {
+ let desc = describe(cx, &field.ty);
+ let msg = format!("using {} by value is not supported", desc);
+ cx.error(field, msg);
}
}
}
@@ -200,63 +331,110 @@ 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);
- if enm.variants.is_empty() {
+ if enm.variants.is_empty() && !enm.explicit_repr {
let span = span_for_enum_error(enm);
- cx.error(span, "enums without any variants are not supported");
+ cx.error(
+ span,
+ "explicit #[repr(...)] is required for enum without any variants",
+ );
+ }
+
+ for derive in &enm.derives {
+ if derive.what == Trait::Default || derive.what == Trait::ExternType {
+ let msg = format!("derive({}) on shared enum is not supported", derive);
+ cx.error(derive, msg);
+ }
}
}
fn check_api_type(cx: &mut Check, ety: &ExternType) {
check_reserved_name(cx, &ety.name.rust);
- if let Some(reason) = cx.types.required_trivial.get(&ety.name.rust) {
- let what = match reason {
- TrivialReason::StructField(strct) => format!("a field of `{}`", strct.name.rust),
- TrivialReason::FunctionArgument(efn) => format!("an argument of `{}`", efn.name.rust),
- TrivialReason::FunctionReturn(efn) => format!("a return value of `{}`", efn.name.rust),
+ for derive in &ety.derives {
+ if derive.what == Trait::ExternType && ety.lang == Lang::Rust {
+ continue;
+ }
+ let lang = match ety.lang {
+ Lang::Rust => "Rust",
+ Lang::Cxx => "C++",
};
let msg = format!(
+ "derive({}) on opaque {} type is not supported yet",
+ derive, lang,
+ );
+ cx.error(derive, msg);
+ }
+
+ if !ety.bounds.is_empty() {
+ let bounds = &ety.bounds;
+ let span = quote!(#(#bounds)*);
+ cx.error(span, "extern type bounds are not implemented yet");
+ }
+
+ if let Some(reasons) = cx.types.required_trivial.get(&ety.name.rust) {
+ let msg = format!(
"needs a cxx::ExternType impl in order to be used as {}",
- what,
+ trivial::as_what(&ety.name, reasons),
);
cx.error(ety, msg);
}
}
fn check_api_fn(cx: &mut Check, efn: &ExternFn) {
+ match efn.lang {
+ Lang::Cxx => {
+ if !efn.generics.params.is_empty() && !efn.trusted {
+ let ref span = span_for_generics_error(efn);
+ cx.error(span, "extern C++ function with lifetimes must be declared in `unsafe extern \"C++\"` block");
+ }
+ }
+ Lang::Rust => {
+ if !efn.generics.params.is_empty() && efn.unsafety.is_none() {
+ let ref span = span_for_generics_error(efn);
+ let message = format!(
+ "must be `unsafe fn {}` in order to expose explicit lifetimes to C++",
+ efn.name.rust,
+ );
+ cx.error(span, message);
+ }
+ }
+ }
+
if let Some(receiver) = &efn.receiver {
let ref span = span_for_receiver_error(receiver);
- if receiver.ty.is_self() {
- let mutability = match receiver.mutability {
- Some(_) => "mut ",
- None => "",
+ if receiver.ty.rust == "Self" {
+ let mutability = match receiver.mutable {
+ true => "mut ",
+ false => "",
};
let msg = format!(
- "unnamed receiver type is only allowed if the surrounding \
- extern block contains exactly one extern type; \
- use `self: &{mutability}TheType`",
+ "unnamed receiver type is only allowed if the surrounding extern block contains exactly one extern type; use `self: &{mutability}TheType`",
mutability = mutability,
);
cx.error(span, msg);
+ } else if cx.types.enums.contains_key(&receiver.ty.rust) {
+ cx.error(
+ span,
+ "unsupported receiver type; C++ does not allow member functions on enums",
+ );
} else if !cx.types.structs.contains_key(&receiver.ty.rust)
&& !cx.types.cxx.contains(&receiver.ty.rust)
&& !cx.types.rust.contains(&receiver.ty.rust)
{
cx.error(span, "unrecognized receiver type");
- }
-
- if receiver.lifetime.is_some() {
- cx.error(span, "references with explicit lifetimes are not supported");
+ } else if receiver.mutable && !receiver.pinned && is_opaque_cxx(cx, &receiver.ty.rust) {
+ cx.error(
+ span,
+ format!(
+ "mutable reference to opaque C++ type requires a pin -- use `self: Pin<&mut {}>`",
+ receiver.ty.rust,
+ ),
+ );
}
}
for arg in &efn.args {
- if is_unsized(cx, &arg.ty) {
- let desc = describe(cx, &arg.ty);
- let msg = format!("passing {} by value is not supported", desc);
- cx.error(arg, msg);
- }
if let Type::Fn(_) = arg.ty {
if efn.lang == Lang::Rust {
cx.error(
@@ -264,89 +442,141 @@ fn check_api_fn(cx: &mut Check, efn: &ExternFn) {
"passing a function pointer from C++ to Rust is not implemented yet",
);
}
+ } else if let Type::Ptr(_) = arg.ty {
+ if efn.sig.unsafety.is_none() {
+ cx.error(
+ arg,
+ "pointer argument requires that the function be marked unsafe",
+ );
+ }
+ } else if is_unsized(cx, &arg.ty) {
+ let desc = describe(cx, &arg.ty);
+ let msg = format!("passing {} by value is not supported", desc);
+ cx.error(arg, msg);
}
}
if let Some(ty) = &efn.ret {
- if is_unsized(cx, ty) {
+ if let Type::Fn(_) = ty {
+ cx.error(ty, "returning a function pointer is not implemented yet");
+ } else if is_unsized(cx, ty) {
let desc = describe(cx, ty);
let msg = format!("returning {} by value is not supported", desc);
cx.error(ty, msg);
}
- if let Type::Fn(_) = ty {
- cx.error(ty, "returning a function pointer is not implemented yet");
- }
}
if efn.lang == Lang::Cxx {
check_mut_return_restriction(cx, efn);
}
+}
- check_multiple_arg_lifetimes(cx, efn);
+fn check_api_type_alias(cx: &mut Check, alias: &TypeAlias) {
+ for derive in &alias.derives {
+ let msg = format!("derive({}) on extern type alias is not supported", derive);
+ cx.error(derive, msg);
+ }
}
fn check_api_impl(cx: &mut Check, imp: &Impl) {
- if let Type::UniquePtr(ty) | Type::CxxVector(ty) = &imp.ty {
- if let Type::Ident(inner) = &ty.inner {
- if Atom::from(&inner.rust).is_none() {
- return;
+ let ty = &imp.ty;
+
+ if let Some(negative) = imp.negative_token {
+ let span = quote!(#negative #ty);
+ cx.error(span, "negative impl is not supported yet");
+ return;
+ }
+
+ match ty {
+ Type::RustBox(ty)
+ | Type::RustVec(ty)
+ | Type::UniquePtr(ty)
+ | Type::SharedPtr(ty)
+ | Type::WeakPtr(ty)
+ | Type::CxxVector(ty) => {
+ if let Type::Ident(inner) = &ty.inner {
+ if Atom::from(&inner.rust).is_none() {
+ return;
+ }
}
}
+ _ => {}
}
cx.error(imp, "unsupported Self type of explicit impl");
}
fn check_mut_return_restriction(cx: &mut Check, efn: &ExternFn) {
+ if efn.sig.unsafety.is_some() {
+ // Unrestricted as long as the function is made unsafe-to-call.
+ return;
+ }
+
match &efn.ret {
- Some(Type::Ref(ty)) if ty.mutability.is_some() => {}
+ Some(Type::Ref(ty)) if ty.mutable => {}
+ Some(Type::SliceRef(slice)) if slice.mutable => {}
_ => return,
}
- for arg in &efn.args {
- if let Type::Ref(ty) = &arg.ty {
- if ty.mutability.is_some() {
- return;
- }
+ if let Some(receiver) = &efn.receiver {
+ if receiver.mutable {
+ return;
+ }
+ let resolve = match cx.types.try_resolve(&receiver.ty) {
+ Some(resolve) => resolve,
+ None => return,
+ };
+ if !resolve.generics.lifetimes.is_empty() {
+ return;
}
}
- cx.error(
- efn,
- "&mut return type is not allowed unless there is a &mut argument",
- );
-}
-
-fn check_multiple_arg_lifetimes(cx: &mut Check, efn: &ExternFn) {
- match &efn.ret {
- Some(Type::Ref(_)) => {}
- _ => return,
+ struct FindLifetimeMut<'a> {
+ cx: &'a Check<'a>,
+ found: bool,
}
- let mut reference_args = 0;
- for arg in &efn.args {
- if let Type::Ref(_) = &arg.ty {
- reference_args += 1;
+ impl<'t, 'a> Visit<'t> for FindLifetimeMut<'a> {
+ fn visit_type(&mut self, ty: &'t Type) {
+ self.found |= match ty {
+ Type::Ref(ty) => ty.mutable,
+ Type::SliceRef(slice) => slice.mutable,
+ Type::Ident(ident) if Atom::from(&ident.rust).is_none() => {
+ match self.cx.types.try_resolve(ident) {
+ Some(resolve) => !resolve.generics.lifetimes.is_empty(),
+ None => true,
+ }
+ }
+ _ => false,
+ };
+ visit::visit_type(self, ty);
}
}
- if efn.receiver.is_some() {
- reference_args += 1;
+ let mut visitor = FindLifetimeMut { cx, found: false };
+
+ for arg in &efn.args {
+ visitor.visit_type(&arg.ty);
}
- if reference_args != 1 {
- cx.error(
- efn,
- "functions that return a reference must take exactly one input reference",
- );
+ if visitor.found {
+ return;
}
+
+ cx.error(
+ efn,
+ "&mut return type is not allowed unless there is a &mut argument",
+ );
}
fn check_reserved_name(cx: &mut Check, ident: &Ident) {
if ident == "Box"
|| ident == "UniquePtr"
+ || ident == "SharedPtr"
+ || ident == "WeakPtr"
|| ident == "Vec"
|| ident == "CxxVector"
+ || ident == "str"
|| Atom::from(ident).is_some()
{
cx.error(ident, "reserved name");
@@ -354,18 +584,30 @@ fn check_reserved_name(cx: &mut Check, ident: &Ident) {
}
fn is_unsized(cx: &mut Check, ty: &Type) -> bool {
- let ident = match ty {
- Type::Ident(ident) => &ident.rust,
- Type::CxxVector(_) | Type::Slice(_) | Type::Void(_) => return true,
- _ => return false,
- };
- ident == CxxString
- || cx.types.cxx.contains(ident)
- && !cx.types.structs.contains_key(ident)
- && !cx.types.enums.contains_key(ident)
- && !(cx.types.aliases.contains_key(ident)
- && cx.types.required_trivial.contains_key(ident))
- || cx.types.rust.contains(ident)
+ match ty {
+ Type::Ident(ident) => {
+ let ident = &ident.rust;
+ ident == CxxString || is_opaque_cxx(cx, ident) || cx.types.rust.contains(ident)
+ }
+ Type::Array(array) => is_unsized(cx, &array.inner),
+ Type::CxxVector(_) | Type::Fn(_) | Type::Void(_) => true,
+ Type::RustBox(_)
+ | Type::RustVec(_)
+ | Type::UniquePtr(_)
+ | Type::SharedPtr(_)
+ | Type::WeakPtr(_)
+ | Type::Ref(_)
+ | Type::Ptr(_)
+ | Type::Str(_)
+ | Type::SliceRef(_) => false,
+ }
+}
+
+fn is_opaque_cxx(cx: &mut Check, ty: &Ident) -> bool {
+ cx.types.cxx.contains(ty)
+ && !cx.types.structs.contains_key(ty)
+ && !cx.types.enums.contains_key(ty)
+ && !(cx.types.aliases.contains_key(ty) && cx.types.required_trivial.contains_key(ty))
}
fn span_for_struct_error(strct: &Struct) -> TokenStream {
@@ -395,6 +637,13 @@ fn span_for_receiver_error(receiver: &Receiver) -> TokenStream {
}
}
+fn span_for_generics_error(efn: &ExternFn) -> TokenStream {
+ let unsafety = efn.unsafety;
+ let fn_token = efn.fn_token;
+ let generics = &efn.generics;
+ quote!(#unsafety #fn_token #generics)
+}
+
fn describe(cx: &mut Check, ty: &Type) -> String {
match ty {
Type::Ident(ident) => {
@@ -410,6 +659,8 @@ fn describe(cx: &mut Check, ty: &Type) -> String {
"opaque Rust type".to_owned()
} else if Atom::from(&ident.rust) == Some(CxxString) {
"C++ string".to_owned()
+ } else if Atom::from(&ident.rust) == Some(Char) {
+ "C char".to_owned()
} else {
ident.rust.to_string()
}
@@ -417,12 +668,15 @@ fn describe(cx: &mut Check, ty: &Type) -> String {
Type::RustBox(_) => "Box".to_owned(),
Type::RustVec(_) => "Vec".to_owned(),
Type::UniquePtr(_) => "unique_ptr".to_owned(),
+ Type::SharedPtr(_) => "shared_ptr".to_owned(),
+ Type::WeakPtr(_) => "weak_ptr".to_owned(),
Type::Ref(_) => "reference".to_owned(),
+ Type::Ptr(_) => "raw pointer".to_owned(),
Type::Str(_) => "&str".to_owned(),
Type::CxxVector(_) => "C++ vector".to_owned(),
- Type::Slice(_) => "slice".to_owned(),
- Type::SliceRefU8(_) => "&[u8]".to_owned(),
+ Type::SliceRef(_) => "slice".to_owned(),
Type::Fn(_) => "function pointer".to_owned(),
Type::Void(_) => "()".to_owned(),
+ Type::Array(_) => "array".to_owned(),
}
}
diff --git a/syntax/derive.rs b/syntax/derive.rs
index 435aa209..96b3eea7 100644
--- a/syntax/derive.rs
+++ b/syntax/derive.rs
@@ -1,26 +1,75 @@
-use proc_macro2::Ident;
+use proc_macro2::{Ident, Span};
+use std::fmt::{self, Display};
+
+#[derive(Copy, Clone)]
+pub struct Derive {
+ pub what: Trait,
+ pub span: Span,
+}
#[derive(Copy, Clone, PartialEq)]
-pub enum Derive {
+pub enum Trait {
Clone,
Copy,
+ Debug,
+ Default,
+ Eq,
+ ExternType,
+ Hash,
+ Ord,
+ PartialEq,
+ PartialOrd,
}
impl Derive {
pub fn from(ident: &Ident) -> Option<Self> {
- match ident.to_string().as_str() {
- "Clone" => Some(Derive::Clone),
- "Copy" => Some(Derive::Copy),
- _ => None,
- }
+ let what = match ident.to_string().as_str() {
+ "Clone" => Trait::Clone,
+ "Copy" => Trait::Copy,
+ "Debug" => Trait::Debug,
+ "Default" => Trait::Default,
+ "Eq" => Trait::Eq,
+ "ExternType" => Trait::ExternType,
+ "Hash" => Trait::Hash,
+ "Ord" => Trait::Ord,
+ "PartialEq" => Trait::PartialEq,
+ "PartialOrd" => Trait::PartialOrd,
+ _ => return None,
+ };
+ let span = ident.span();
+ Some(Derive { what, span })
+ }
+}
+
+impl PartialEq<Trait> for Derive {
+ fn eq(&self, other: &Trait) -> bool {
+ self.what == *other
}
}
-impl AsRef<str> for Derive {
+impl AsRef<str> for Trait {
fn as_ref(&self) -> &str {
match self {
- Derive::Clone => "Clone",
- Derive::Copy => "Copy",
+ Trait::Clone => "Clone",
+ Trait::Copy => "Copy",
+ Trait::Debug => "Debug",
+ Trait::Default => "Default",
+ Trait::Eq => "Eq",
+ Trait::ExternType => "ExternType",
+ Trait::Hash => "Hash",
+ Trait::Ord => "Ord",
+ Trait::PartialEq => "PartialEq",
+ Trait::PartialOrd => "PartialOrd",
}
}
}
+
+impl Display for Derive {
+ fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+ formatter.write_str(self.what.as_ref())
+ }
+}
+
+pub fn contains(derives: &[Derive], query: Trait) -> bool {
+ derives.iter().any(|derive| derive.what == query)
+}
diff --git a/syntax/discriminant.rs b/syntax/discriminant.rs
index 6a04d881..80f7c0be 100644
--- a/syntax/discriminant.rs
+++ b/syntax/discriminant.rs
@@ -16,10 +16,16 @@ pub struct DiscriminantSet {
#[derive(Copy, Clone, Eq, PartialEq)]
pub struct Discriminant {
- negative: bool,
+ sign: Sign,
magnitude: u64,
}
+#[derive(Copy, Clone, Eq, PartialEq)]
+enum Sign {
+ Negative,
+ Positive,
+}
+
impl DiscriminantSet {
pub fn new(repr: Option<Atom>) -> Self {
DiscriminantSet {
@@ -59,21 +65,23 @@ impl DiscriminantSet {
pub fn insert_next(&mut self) -> Result<Discriminant> {
let discriminant = match self.previous {
None => Discriminant::zero(),
- Some(mut discriminant) if discriminant.negative => {
- discriminant.magnitude -= 1;
- if discriminant.magnitude == 0 {
- discriminant.negative = false;
+ Some(mut discriminant) => match discriminant.sign {
+ Sign::Negative => {
+ discriminant.magnitude -= 1;
+ if discriminant.magnitude == 0 {
+ discriminant.sign = Sign::Positive;
+ }
+ discriminant
}
- discriminant
- }
- Some(mut discriminant) => {
- if discriminant.magnitude == u64::MAX {
- let msg = format!("discriminant overflow on value after {}", u64::MAX);
- return Err(Error::new(Span::call_site(), msg));
+ Sign::Positive => {
+ if discriminant.magnitude == u64::MAX {
+ let msg = format!("discriminant overflow on value after {}", u64::MAX);
+ return Err(Error::new(Span::call_site(), msg));
+ }
+ discriminant.magnitude += 1;
+ discriminant
}
- discriminant.magnitude += 1;
- discriminant
- }
+ },
};
insert(self, discriminant)
}
@@ -109,7 +117,10 @@ fn expr_to_discriminant(expr: &Expr) -> Result<(Discriminant, Option<Atom>)> {
Expr::Unary(unary) => {
if let UnOp::Neg(_) = unary.op {
let (mut discriminant, repr) = expr_to_discriminant(&unary.expr)?;
- discriminant.negative ^= true;
+ discriminant.sign = match discriminant.sign {
+ Sign::Positive => Sign::Negative,
+ Sign::Negative => Sign::Positive,
+ };
return Ok((discriminant, repr));
}
}
@@ -133,33 +144,33 @@ fn insert(set: &mut DiscriminantSet, discriminant: Discriminant) -> Result<Discr
}
}
}
- if set.values.insert(discriminant) {
- set.previous = Some(discriminant);
- Ok(discriminant)
- } else {
- let msg = format!("discriminant value `{}` already exists", discriminant);
- Err(Error::new(Span::call_site(), msg))
- }
+ set.values.insert(discriminant);
+ set.previous = Some(discriminant);
+ Ok(discriminant)
}
impl Discriminant {
const fn zero() -> Self {
Discriminant {
- negative: false,
+ sign: Sign::Positive,
magnitude: 0,
}
}
const fn pos(u: u64) -> Self {
Discriminant {
- negative: false,
+ sign: Sign::Positive,
magnitude: u,
}
}
const fn neg(i: i64) -> Self {
Discriminant {
- negative: i < 0,
+ sign: if i < 0 {
+ Sign::Negative
+ } else {
+ Sign::Positive
+ },
// This is `i.abs() as u64` but without overflow on MIN. Uses the
// fact that MIN.wrapping_abs() wraps back to MIN whose binary
// representation is 1<<63, and thus the `as u64` conversion
@@ -172,7 +183,7 @@ impl Discriminant {
impl Display for Discriminant {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- if self.negative {
+ if self.sign == Sign::Negative {
f.write_str("-")?;
}
Display::fmt(&self.magnitude, f)
@@ -181,7 +192,7 @@ impl Display for Discriminant {
impl ToTokens for Discriminant {
fn to_tokens(&self, tokens: &mut TokenStream) {
- if self.negative {
+ if self.sign == Sign::Negative {
Token![-](Span::call_site()).to_tokens(tokens);
}
Literal::u64_unsuffixed(self.magnitude).to_tokens(tokens);
@@ -192,15 +203,14 @@ impl FromStr for Discriminant {
type Err = Error;
fn from_str(mut s: &str) -> Result<Self> {
- let negative = s.starts_with('-');
- if negative {
+ let sign = if s.starts_with('-') {
s = &s[1..];
- }
+ Sign::Negative
+ } else {
+ Sign::Positive
+ };
match s.parse::<u64>() {
- Ok(magnitude) => Ok(Discriminant {
- negative,
- magnitude,
- }),
+ Ok(magnitude) => Ok(Discriminant { sign, magnitude }),
Err(_) => Err(Error::new(
Span::call_site(),
"discriminant value outside of supported range",
@@ -211,11 +221,12 @@ impl FromStr for Discriminant {
impl Ord for Discriminant {
fn cmp(&self, other: &Self) -> Ordering {
- match (self.negative, other.negative) {
- (true, true) => self.magnitude.cmp(&other.magnitude).reverse(),
- (true, false) => Ordering::Less, // negative < positive
- (false, true) => Ordering::Greater, // positive > negative
- (false, false) => self.magnitude.cmp(&other.magnitude),
+ use self::Sign::{Negative, Positive};
+ match (self.sign, other.sign) {
+ (Negative, Negative) => self.magnitude.cmp(&other.magnitude).reverse(),
+ (Negative, Positive) => Ordering::Less, // negative < positive
+ (Positive, Negative) => Ordering::Greater, // positive > negative
+ (Positive, Positive) => self.magnitude.cmp(&other.magnitude),
}
}
}
diff --git a/syntax/error.rs b/syntax/error.rs
index d0ae0210..a6723293 100644
--- a/syntax/error.rs
+++ b/syntax/error.rs
@@ -29,7 +29,7 @@ pub static ERRORS: &[Error] = &[
pub static BOX_CXX_TYPE: Error = Error {
msg: "Box of a C++ type is not supported yet",
label: None,
- note: Some("hint: use UniquePtr<>"),
+ note: Some("hint: use UniquePtr<> or SharedPtr<>"),
};
pub static CXXBRIDGE_RESERVED: Error = Error {
@@ -47,7 +47,7 @@ pub static CXX_STRING_BY_VALUE: Error = Error {
pub static CXX_TYPE_BY_VALUE: Error = Error {
msg: "C++ type by value is not supported",
label: None,
- note: Some("hint: wrap it in a UniquePtr<>"),
+ note: Some("hint: wrap it in a UniquePtr<> or SharedPtr<>"),
};
pub static DISCRIMINANT_OVERFLOW: Error = Error {
diff --git a/syntax/ident.rs b/syntax/ident.rs
index aaf78326..bb2281e7 100644
--- a/syntax/ident.rs
+++ b/syntax/ident.rs
@@ -1,22 +1,27 @@
use crate::syntax::check::Check;
use crate::syntax::{error, Api, Pair};
-use proc_macro2::Ident;
-fn check(cx: &mut Check, ident: &Ident) {
- let s = ident.to_string();
- if s.starts_with("cxxbridge") {
- cx.error(ident, error::CXXBRIDGE_RESERVED.msg);
+fn check(cx: &mut Check, name: &Pair) {
+ for segment in &name.namespace {
+ check_cxx_ident(cx, &segment.to_string());
}
- if s.contains("__") {
- cx.error(ident, error::DOUBLE_UNDERSCORE.msg);
+ check_cxx_ident(cx, &name.cxx.to_string());
+ check_rust_ident(cx, &name.rust.to_string());
+
+ fn check_cxx_ident(cx: &mut Check, ident: &str) {
+ if ident.starts_with("cxxbridge") {
+ cx.error(ident, error::CXXBRIDGE_RESERVED.msg);
+ }
+ if ident.contains("__") {
+ cx.error(ident, error::DOUBLE_UNDERSCORE.msg);
+ }
}
-}
-fn check_ident(cx: &mut Check, name: &Pair) {
- for segment in &name.namespace {
- check(cx, segment);
+ fn check_rust_ident(cx: &mut Check, ident: &str) {
+ if ident.starts_with("cxxbridge") {
+ cx.error(ident, error::CXXBRIDGE_RESERVED.msg);
+ }
}
- check(cx, &name.cxx);
}
pub(crate) fn check_all(cx: &mut Check, apis: &[Api]) {
@@ -24,28 +29,28 @@ pub(crate) fn check_all(cx: &mut Check, apis: &[Api]) {
match api {
Api::Include(_) | Api::Impl(_) => {}
Api::Struct(strct) => {
- check_ident(cx, &strct.name);
+ check(cx, &strct.name);
for field in &strct.fields {
- check(cx, &field.ident);
+ check(cx, &field.name);
}
}
Api::Enum(enm) => {
- check_ident(cx, &enm.name);
+ check(cx, &enm.name);
for variant in &enm.variants {
- check(cx, &variant.ident);
+ check(cx, &variant.name);
}
}
Api::CxxType(ety) | Api::RustType(ety) => {
- check_ident(cx, &ety.name);
+ check(cx, &ety.name);
}
Api::CxxFunction(efn) | Api::RustFunction(efn) => {
- check(cx, &efn.name.rust);
+ check(cx, &efn.name);
for arg in &efn.args {
- check(cx, &arg.ident);
+ check(cx, &arg.name);
}
}
Api::TypeAlias(alias) => {
- check_ident(cx, &alias.name);
+ check(cx, &alias.name);
}
}
}
diff --git a/syntax/impls.rs b/syntax/impls.rs
index a4b393ae..8b0743b6 100644
--- a/syntax/impls.rs
+++ b/syntax/impls.rs
@@ -1,11 +1,12 @@
-use crate::syntax::{ExternFn, Impl, Include, Receiver, Ref, Signature, Slice, Ty1, Type};
-use std::borrow::Borrow;
+use crate::syntax::{
+ Array, ExternFn, Include, Lifetimes, Ptr, Receiver, Ref, Signature, SliceRef, Ty1, Type, Var,
+};
use std::hash::{Hash, Hasher};
use std::mem;
use std::ops::{Deref, DerefMut};
impl PartialEq for Include {
- fn eq(&self, other: &Include) -> bool {
+ fn eq(&self, other: &Self) -> bool {
let Include {
path,
kind,
@@ -43,13 +44,16 @@ impl Hash for Type {
Type::Ident(t) => t.hash(state),
Type::RustBox(t) => t.hash(state),
Type::UniquePtr(t) => t.hash(state),
+ Type::SharedPtr(t) => t.hash(state),
+ Type::WeakPtr(t) => t.hash(state),
Type::Ref(t) => t.hash(state),
+ Type::Ptr(t) => t.hash(state),
Type::Str(t) => t.hash(state),
Type::RustVec(t) => t.hash(state),
Type::CxxVector(t) => t.hash(state),
Type::Fn(t) => t.hash(state),
- Type::Slice(t) => t.hash(state),
- Type::SliceRefU8(t) => t.hash(state),
+ Type::SliceRef(t) => t.hash(state),
+ Type::Array(t) => t.hash(state),
Type::Void(_) => {}
}
}
@@ -58,28 +62,61 @@ impl Hash for Type {
impl Eq for Type {}
impl PartialEq for Type {
- fn eq(&self, other: &Type) -> bool {
+ fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Type::Ident(lhs), Type::Ident(rhs)) => lhs == rhs,
(Type::RustBox(lhs), Type::RustBox(rhs)) => lhs == rhs,
(Type::UniquePtr(lhs), Type::UniquePtr(rhs)) => lhs == rhs,
+ (Type::SharedPtr(lhs), Type::SharedPtr(rhs)) => lhs == rhs,
+ (Type::WeakPtr(lhs), Type::WeakPtr(rhs)) => lhs == rhs,
(Type::Ref(lhs), Type::Ref(rhs)) => lhs == rhs,
(Type::Str(lhs), Type::Str(rhs)) => lhs == rhs,
(Type::RustVec(lhs), Type::RustVec(rhs)) => lhs == rhs,
(Type::CxxVector(lhs), Type::CxxVector(rhs)) => lhs == rhs,
(Type::Fn(lhs), Type::Fn(rhs)) => lhs == rhs,
- (Type::Slice(lhs), Type::Slice(rhs)) => lhs == rhs,
- (Type::SliceRefU8(lhs), Type::SliceRefU8(rhs)) => lhs == rhs,
+ (Type::SliceRef(lhs), Type::SliceRef(rhs)) => lhs == rhs,
(Type::Void(_), Type::Void(_)) => true,
(_, _) => false,
}
}
}
+impl Eq for Lifetimes {}
+
+impl PartialEq for Lifetimes {
+ fn eq(&self, other: &Self) -> bool {
+ let Lifetimes {
+ lt_token: _,
+ lifetimes,
+ gt_token: _,
+ } = self;
+ let Lifetimes {
+ lt_token: _,
+ lifetimes: lifetimes2,
+ gt_token: _,
+ } = other;
+ lifetimes.iter().eq(lifetimes2)
+ }
+}
+
+impl Hash for Lifetimes {
+ fn hash<H: Hasher>(&self, state: &mut H) {
+ let Lifetimes {
+ lt_token: _,
+ lifetimes,
+ gt_token: _,
+ } = self;
+ lifetimes.len().hash(state);
+ for lifetime in lifetimes {
+ lifetime.hash(state);
+ }
+ }
+}
+
impl Eq for Ty1 {}
impl PartialEq for Ty1 {
- fn eq(&self, other: &Ty1) -> bool {
+ fn eq(&self, other: &Self) -> bool {
let Ty1 {
name,
langle: _,
@@ -112,64 +149,167 @@ impl Hash for Ty1 {
impl Eq for Ref {}
impl PartialEq for Ref {
- fn eq(&self, other: &Ref) -> bool {
+ fn eq(&self, other: &Self) -> bool {
let Ref {
+ pinned,
ampersand: _,
lifetime,
- mutability,
+ mutable,
inner,
+ pin_tokens: _,
+ mutability: _,
} = self;
let Ref {
+ pinned: pinned2,
ampersand: _,
lifetime: lifetime2,
- mutability: mutability2,
+ mutable: mutable2,
inner: inner2,
+ pin_tokens: _,
+ mutability: _,
} = other;
- lifetime == lifetime2 && mutability.is_some() == mutability2.is_some() && inner == inner2
+ pinned == pinned2 && lifetime == lifetime2 && mutable == mutable2 && inner == inner2
}
}
impl Hash for Ref {
fn hash<H: Hasher>(&self, state: &mut H) {
let Ref {
+ pinned,
+ ampersand: _,
+ lifetime,
+ mutable,
+ inner,
+ pin_tokens: _,
+ mutability: _,
+ } = self;
+ pinned.hash(state);
+ lifetime.hash(state);
+ mutable.hash(state);
+ inner.hash(state);
+ }
+}
+
+impl Eq for Ptr {}
+
+impl PartialEq for Ptr {
+ fn eq(&self, other: &Ptr) -> bool {
+ let Ptr {
+ star: _,
+ mutable,
+ inner,
+ mutability: _,
+ constness: _,
+ } = self;
+ let Ptr {
+ star: _,
+ mutable: mutable2,
+ inner: inner2,
+ mutability: _,
+ constness: _,
+ } = other;
+ mutable == mutable2 && inner == inner2
+ }
+}
+
+impl Hash for Ptr {
+ fn hash<H: Hasher>(&self, state: &mut H) {
+ let Ptr {
+ star: _,
+ mutable,
+ inner,
+ mutability: _,
+ constness: _,
+ } = self;
+ mutable.hash(state);
+ inner.hash(state);
+ }
+}
+
+impl Eq for SliceRef {}
+
+impl PartialEq for SliceRef {
+ fn eq(&self, other: &Self) -> bool {
+ let SliceRef {
+ ampersand: _,
+ lifetime,
+ mutable,
+ bracket: _,
+ inner,
+ mutability: _,
+ } = self;
+ let SliceRef {
+ ampersand: _,
+ lifetime: lifetime2,
+ mutable: mutable2,
+ bracket: _,
+ inner: inner2,
+ mutability: _,
+ } = other;
+ lifetime == lifetime2 && mutable == mutable2 && inner == inner2
+ }
+}
+
+impl Hash for SliceRef {
+ fn hash<H: Hasher>(&self, state: &mut H) {
+ let SliceRef {
ampersand: _,
lifetime,
- mutability,
+ mutable,
+ bracket: _,
inner,
+ mutability: _,
} = self;
lifetime.hash(state);
- mutability.is_some().hash(state);
+ mutable.hash(state);
inner.hash(state);
}
}
-impl Eq for Slice {}
+impl Eq for Array {}
-impl PartialEq for Slice {
- fn eq(&self, other: &Slice) -> bool {
- let Slice { bracket: _, inner } = self;
- let Slice {
+impl PartialEq for Array {
+ fn eq(&self, other: &Self) -> bool {
+ let Array {
+ bracket: _,
+ inner,
+ semi_token: _,
+ len,
+ len_token: _,
+ } = self;
+ let Array {
bracket: _,
inner: inner2,
+ semi_token: _,
+ len: len2,
+ len_token: _,
} = other;
- inner == inner2
+ inner == inner2 && len == len2
}
}
-impl Hash for Slice {
+impl Hash for Array {
fn hash<H: Hasher>(&self, state: &mut H) {
- let Slice { bracket: _, inner } = self;
+ let Array {
+ bracket: _,
+ inner,
+ semi_token: _,
+ len,
+ len_token: _,
+ } = self;
inner.hash(state);
+ len.hash(state);
}
}
impl Eq for Signature {}
impl PartialEq for Signature {
- fn eq(&self, other: &Signature) -> bool {
+ fn eq(&self, other: &Self) -> bool {
let Signature {
unsafety,
fn_token: _,
+ generics: _,
receiver,
args,
ret,
@@ -180,6 +320,7 @@ impl PartialEq for Signature {
let Signature {
unsafety: unsafety2,
fn_token: _,
+ generics: _,
receiver: receiver2,
args: args2,
ret: ret2,
@@ -192,7 +333,23 @@ impl PartialEq for Signature {
&& ret == ret2
&& throws == throws2
&& args.len() == args2.len()
- && args.iter().zip(args2).all(|(arg, arg2)| arg == arg2)
+ && args.iter().zip(args2).all(|(arg, arg2)| {
+ let Var {
+ doc: _,
+ attrs: _,
+ visibility: _,
+ name: _,
+ ty,
+ } = arg;
+ let Var {
+ doc: _,
+ attrs: _,
+ visibility: _,
+ name: _,
+ ty: ty2,
+ } = arg2;
+ ty == ty2
+ })
}
}
@@ -201,6 +358,7 @@ impl Hash for Signature {
let Signature {
unsafety,
fn_token: _,
+ generics: _,
receiver,
args,
ret,
@@ -211,7 +369,14 @@ impl Hash for Signature {
unsafety.is_some().hash(state);
receiver.hash(state);
for arg in args {
- arg.hash(state);
+ let Var {
+ doc: _,
+ attrs: _,
+ visibility: _,
+ name: _,
+ ty,
+ } = arg;
+ ty.hash(state);
}
ret.hash(state);
throws.hash(state);
@@ -221,74 +386,49 @@ impl Hash for Signature {
impl Eq for Receiver {}
impl PartialEq for Receiver {
- fn eq(&self, other: &Receiver) -> bool {
+ fn eq(&self, other: &Self) -> bool {
let Receiver {
+ pinned,
ampersand: _,
lifetime,
- mutability,
+ mutable,
var: _,
ty,
shorthand: _,
+ pin_tokens: _,
+ mutability: _,
} = self;
let Receiver {
+ pinned: pinned2,
ampersand: _,
lifetime: lifetime2,
- mutability: mutability2,
+ mutable: mutable2,
var: _,
ty: ty2,
shorthand: _,
+ pin_tokens: _,
+ mutability: _,
} = other;
- lifetime == lifetime2 && mutability.is_some() == mutability2.is_some() && ty == ty2
+ pinned == pinned2 && lifetime == lifetime2 && mutable == mutable2 && ty == ty2
}
}
impl Hash for Receiver {
fn hash<H: Hasher>(&self, state: &mut H) {
let Receiver {
+ pinned,
ampersand: _,
lifetime,
- mutability,
+ mutable,
var: _,
ty,
shorthand: _,
+ pin_tokens: _,
+ mutability: _,
} = self;
+ pinned.hash(state);
lifetime.hash(state);
- mutability.is_some().hash(state);
- ty.hash(state);
- }
-}
-
-impl Hash for Impl {
- fn hash<H: Hasher>(&self, state: &mut H) {
- let Impl {
- impl_token: _,
- ty,
- brace_token: _,
- } = self;
+ mutable.hash(state);
ty.hash(state);
}
}
-
-impl Eq for Impl {}
-
-impl PartialEq for Impl {
- fn eq(&self, other: &Impl) -> bool {
- let Impl {
- impl_token: _,
- ty,
- brace_token: _,
- } = self;
- let Impl {
- impl_token: _,
- ty: ty2,
- brace_token: _,
- } = other;
- ty == ty2
- }
-}
-
-impl Borrow<Type> for &Impl {
- fn borrow(&self) -> &Type {
- &self.ty
- }
-}
diff --git a/syntax/improper.rs b/syntax/improper.rs
index 6fd31629..f19eb86a 100644
--- a/syntax/improper.rs
+++ b/syntax/improper.rs
@@ -19,7 +19,7 @@ impl<'a> Types<'a> {
} else if let Some(strct) = self.structs.get(ident) {
Depends(&strct.name.rust) // iterate to fixed-point
} else {
- Definite(self.rust.contains(ident))
+ Definite(self.rust.contains(ident) || self.aliases.contains_key(ident))
}
}
Type::RustBox(_)
@@ -27,10 +27,13 @@ impl<'a> Types<'a> {
| Type::Str(_)
| Type::Fn(_)
| Type::Void(_)
- | Type::Slice(_)
- | Type::SliceRefU8(_) => Definite(true),
- Type::UniquePtr(_) | Type::CxxVector(_) => Definite(false),
+ | Type::SliceRef(_) => Definite(true),
+ Type::UniquePtr(_) | Type::SharedPtr(_) | Type::WeakPtr(_) | Type::CxxVector(_) => {
+ Definite(false)
+ }
Type::Ref(ty) => self.determine_improper_ctype(&ty.inner),
+ Type::Ptr(ty) => self.determine_improper_ctype(&ty.inner),
+ Type::Array(ty) => self.determine_improper_ctype(&ty.inner),
}
}
}
diff --git a/syntax/instantiate.rs b/syntax/instantiate.rs
new file mode 100644
index 00000000..b6cbf24b
--- /dev/null
+++ b/syntax/instantiate.rs
@@ -0,0 +1,80 @@
+use crate::syntax::{NamedType, Ty1, Type};
+use proc_macro2::{Ident, Span};
+use std::hash::{Hash, Hasher};
+use syn::Token;
+
+#[derive(Copy, Clone, PartialEq, Eq, Hash)]
+pub enum ImplKey<'a> {
+ RustBox(NamedImplKey<'a>),
+ RustVec(NamedImplKey<'a>),
+ UniquePtr(NamedImplKey<'a>),
+ SharedPtr(NamedImplKey<'a>),
+ WeakPtr(NamedImplKey<'a>),
+ CxxVector(NamedImplKey<'a>),
+}
+
+#[derive(Copy, Clone)]
+pub struct NamedImplKey<'a> {
+ pub begin_span: Span,
+ pub rust: &'a Ident,
+ pub lt_token: Option<Token![<]>,
+ pub gt_token: Option<Token![>]>,
+ pub end_span: Span,
+}
+
+impl Type {
+ pub(crate) fn impl_key(&self) -> Option<ImplKey> {
+ if let Type::RustBox(ty) = self {
+ if let Type::Ident(ident) = &ty.inner {
+ return Some(ImplKey::RustBox(NamedImplKey::new(ty, ident)));
+ }
+ } else if let Type::RustVec(ty) = self {
+ if let Type::Ident(ident) = &ty.inner {
+ return Some(ImplKey::RustVec(NamedImplKey::new(ty, ident)));
+ }
+ } else if let Type::UniquePtr(ty) = self {
+ if let Type::Ident(ident) = &ty.inner {
+ return Some(ImplKey::UniquePtr(NamedImplKey::new(ty, ident)));
+ }
+ } else if let Type::SharedPtr(ty) = self {
+ if let Type::Ident(ident) = &ty.inner {
+ return Some(ImplKey::SharedPtr(NamedImplKey::new(ty, ident)));
+ }
+ } else if let Type::WeakPtr(ty) = self {
+ if let Type::Ident(ident) = &ty.inner {
+ return Some(ImplKey::WeakPtr(NamedImplKey::new(ty, ident)));
+ }
+ } else if let Type::CxxVector(ty) = self {
+ if let Type::Ident(ident) = &ty.inner {
+ return Some(ImplKey::CxxVector(NamedImplKey::new(ty, ident)));
+ }
+ }
+ None
+ }
+}
+
+impl<'a> PartialEq for NamedImplKey<'a> {
+ fn eq(&self, other: &Self) -> bool {
+ PartialEq::eq(self.rust, other.rust)
+ }
+}
+
+impl<'a> Eq for NamedImplKey<'a> {}
+
+impl<'a> Hash for NamedImplKey<'a> {
+ fn hash<H: Hasher>(&self, hasher: &mut H) {
+ self.rust.hash(hasher);
+ }
+}
+
+impl<'a> NamedImplKey<'a> {
+ fn new(outer: &Ty1, inner: &'a NamedType) -> Self {
+ NamedImplKey {
+ begin_span: outer.name.span(),
+ rust: &inner.rust,
+ lt_token: inner.generics.lt_token,
+ gt_token: inner.generics.gt_token,
+ end_span: outer.rangle.span,
+ }
+ }
+}
diff --git a/syntax/mangle.rs b/syntax/mangle.rs
index cc5115a9..287b4434 100644
--- a/syntax/mangle.rs
+++ b/syntax/mangle.rs
@@ -1,12 +1,86 @@
+// Mangled symbol arrangements:
+//
+// (a) One-off internal symbol.
+// pattern: {CXXBRIDGE} $ {NAME}
+// examples:
+// - cxxbridge1$exception
+// defining characteristics:
+// - 2 segments
+// - starts with cxxbridge
+//
+// (b) Behavior on a builtin binding without generic parameter.
+// pattern: {CXXBRIDGE} $ {TYPE} $ {NAME}
+// examples:
+// - cxxbridge1$string$len
+// defining characteristics:
+// - 3 segments
+// - starts with cxxbridge
+//
+// (c) Behavior on a builtin binding with generic parameter.
+// pattern: {CXXBRIDGE} $ {TYPE} $ {PARAM...} $ {NAME}
+// examples:
+// - cxxbridge1$box$org$rust$Struct$alloc
+// - cxxbridge1$unique_ptr$std$vector$u8$drop
+// defining characteristics:
+// - 4+ segments
+// - starts with cxxbridge
+//
+// (d) User-defined extern function.
+// pattern: {NAMESPACE...} $ {CXXBRIDGE} $ {NAME}
+// examples:
+// - cxxbridge1$new_client
+// - org$rust$cxxbridge1$new_client
+// defining characteristics:
+// - cxxbridge is second from end
+// FIXME: conflict with (a) if they collide with one of our one-off symbol names in the global namespace
+//
+// (e) User-defined extern member function.
+// pattern: {NAMESPACE...} $ {CXXBRIDGE} $ {TYPE} $ {NAME}
+// examples:
+// - org$cxxbridge1$Struct$get
+// defining characteristics:
+// - cxxbridge is third from end
+// FIXME: conflict with (b) if e.g. user binds a type in global namespace that collides with our builtin type names
+//
+// (f) Operator overload.
+// pattern: {NAMESPACE...} $ {CXXBRIDGE} $ {TYPE} $ operator $ {NAME}
+// examples:
+// - org$rust$cxxbridge1$Struct$operator$eq
+// defining characteristics:
+// - second segment from end is `operator` (not possible in type or namespace names)
+//
+// (g) Closure trampoline.
+// pattern: {NAMESPACE...} $ {CXXBRIDGE} $ {TYPE?} $ {NAME} $ {ARGUMENT} $ {DIRECTION}
+// examples:
+// - org$rust$cxxbridge1$Struct$invoke$f$0
+// defining characteristics:
+// - last symbol is `0` (C half) or `1` (Rust half) which are not legal identifiers on their own
+//
+//
+// Mangled preprocessor variable arrangements:
+//
+// (A) One-off internal variable.
+// pattern: {CXXBRIDGE} _ {NAME}
+// examples:
+// - CXXBRIDGE1_PANIC
+// - CXXBRIDGE1_RUST_STRING
+// defining characteristics:
+// - NAME does not begin with STRUCT or ENUM
+//
+// (B) Guard around user-defined type.
+// pattern: {CXXBRIDGE} _ {STRUCT or ENUM} _ {NAMESPACE...} $ {TYPE}
+// examples:
+// - CXXBRIDGE1_STRUCT_org$rust$Struct
+// - CXXBRIDGE1_ENUM_Enabled
+
use crate::syntax::symbol::{self, Symbol};
-use crate::syntax::{ExternFn, Types};
-use proc_macro2::Ident;
+use crate::syntax::{ExternFn, Pair, Types};
-const CXXBRIDGE: &str = "cxxbridge05";
+const CXXBRIDGE: &str = "cxxbridge1";
macro_rules! join {
- ($($segment:expr),*) => {
- symbol::join(&[$(&$segment),*])
+ ($($segment:expr),+ $(,)?) => {
+ symbol::join(&[$(&$segment),+])
};
}
@@ -17,20 +91,30 @@ pub fn extern_fn(efn: &ExternFn, types: &Types) -> Symbol {
join!(
efn.name.namespace,
CXXBRIDGE,
- receiver_ident.cxx,
- efn.name.rust
+ receiver_ident.name.cxx,
+ efn.name.rust,
)
}
None => join!(efn.name.namespace, CXXBRIDGE, efn.name.rust),
}
}
+pub fn operator(receiver: &Pair, operator: &'static str) -> Symbol {
+ join!(
+ receiver.namespace,
+ CXXBRIDGE,
+ receiver.cxx,
+ "operator",
+ operator,
+ )
+}
+
// The C half of a function pointer trampoline.
-pub fn c_trampoline(efn: &ExternFn, var: &Ident, types: &Types) -> Symbol {
- join!(extern_fn(efn, types), var, 0)
+pub fn c_trampoline(efn: &ExternFn, var: &Pair, types: &Types) -> Symbol {
+ join!(extern_fn(efn, types), var.rust, 0)
}
// The Rust half of a function pointer trampoline.
-pub fn r_trampoline(efn: &ExternFn, var: &Ident, types: &Types) -> Symbol {
- join!(extern_fn(efn, types), var, 1)
+pub fn r_trampoline(efn: &ExternFn, var: &Pair, types: &Types) -> Symbol {
+ join!(extern_fn(efn, types), var.rust, 1)
}
diff --git a/syntax/map.rs b/syntax/map.rs
new file mode 100644
index 00000000..4873409d
--- /dev/null
+++ b/syntax/map.rs
@@ -0,0 +1,179 @@
+use std::borrow::Borrow;
+use std::hash::Hash;
+use std::ops::Index;
+use std::slice;
+
+pub use self::ordered::OrderedMap;
+pub use self::unordered::UnorderedMap;
+pub use std::collections::hash_map::Entry;
+
+mod ordered {
+ use super::{Entry, Iter, UnorderedMap};
+ use std::borrow::Borrow;
+ use std::hash::Hash;
+ use std::mem;
+
+ pub struct OrderedMap<K, V> {
+ map: UnorderedMap<K, usize>,
+ vec: Vec<(K, V)>,
+ }
+
+ impl<K, V> OrderedMap<K, V> {
+ pub fn new() -> Self {
+ OrderedMap {
+ map: UnorderedMap::new(),
+ vec: Vec::new(),
+ }
+ }
+
+ pub fn iter(&self) -> Iter<K, V> {
+ Iter(self.vec.iter())
+ }
+
+ pub fn keys(&self) -> impl Iterator<Item = &K> {
+ self.vec.iter().map(|(k, _v)| k)
+ }
+ }
+
+ impl<K, V> OrderedMap<K, V>
+ where
+ K: Copy + Hash + Eq,
+ {
+ pub fn insert(&mut self, key: K, value: V) -> Option<V> {
+ match self.map.entry(key) {
+ Entry::Occupied(entry) => {
+ let i = &mut self.vec[*entry.get()];
+ Some(mem::replace(&mut i.1, value))
+ }
+ Entry::Vacant(entry) => {
+ entry.insert(self.vec.len());
+ self.vec.push((key, value));
+ None
+ }
+ }
+ }
+
+ pub fn contains_key<Q>(&self, key: &Q) -> bool
+ where
+ K: Borrow<Q>,
+ Q: ?Sized + Hash + Eq,
+ {
+ self.map.contains_key(key)
+ }
+
+ pub fn get<Q>(&self, key: &Q) -> Option<&V>
+ where
+ K: Borrow<Q>,
+ Q: ?Sized + Hash + Eq,
+ {
+ let i = *self.map.get(key)?;
+ Some(&self.vec[i].1)
+ }
+ }
+
+ impl<'a, K, V> IntoIterator for &'a OrderedMap<K, V> {
+ type Item = (&'a K, &'a V);
+ type IntoIter = Iter<'a, K, V>;
+ fn into_iter(self) -> Self::IntoIter {
+ self.iter()
+ }
+ }
+}
+
+mod unordered {
+ use crate::syntax::set::UnorderedSet;
+ use std::borrow::Borrow;
+ use std::collections::hash_map::{Entry, HashMap};
+ use std::hash::Hash;
+
+ // Wrapper prohibits accidentally introducing iteration over the map, which
+ // could lead to nondeterministic generated code.
+ pub struct UnorderedMap<K, V>(HashMap<K, V>);
+
+ impl<K, V> UnorderedMap<K, V> {
+ pub fn new() -> Self {
+ UnorderedMap(HashMap::new())
+ }
+ }
+
+ impl<K, V> UnorderedMap<K, V>
+ where
+ K: Hash + Eq,
+ {
+ pub fn insert(&mut self, key: K, value: V) -> Option<V> {
+ self.0.insert(key, value)
+ }
+
+ pub fn contains_key<Q>(&self, key: &Q) -> bool
+ where
+ K: Borrow<Q>,
+ Q: ?Sized + Hash + Eq,
+ {
+ self.0.contains_key(key)
+ }
+
+ pub fn get<Q>(&self, key: &Q) -> Option<&V>
+ where
+ K: Borrow<Q>,
+ Q: ?Sized + Hash + Eq,
+ {
+ self.0.get(key)
+ }
+
+ pub fn entry(&mut self, key: K) -> Entry<K, V> {
+ self.0.entry(key)
+ }
+
+ pub fn remove<Q>(&mut self, key: &Q) -> Option<V>
+ where
+ K: Borrow<Q>,
+ Q: ?Sized + Hash + Eq,
+ {
+ self.0.remove(key)
+ }
+
+ pub fn keys(&self) -> UnorderedSet<K>
+ where
+ K: Copy,
+ {
+ let mut set = UnorderedSet::new();
+ for key in self.0.keys() {
+ set.insert(*key);
+ }
+ set
+ }
+ }
+}
+
+pub struct Iter<'a, K, V>(slice::Iter<'a, (K, V)>);
+
+impl<'a, K, V> Iterator for Iter<'a, K, V> {
+ type Item = (&'a K, &'a V);
+
+ fn next(&mut self) -> Option<Self::Item> {
+ let (k, v) = self.0.next()?;
+ Some((k, v))
+ }
+
+ fn size_hint(&self) -> (usize, Option<usize>) {
+ self.0.size_hint()
+ }
+}
+
+impl<K, V> Default for UnorderedMap<K, V> {
+ fn default() -> Self {
+ UnorderedMap::new()
+ }
+}
+
+impl<Q, K, V> Index<&Q> for UnorderedMap<K, V>
+where
+ K: Borrow<Q> + Hash + Eq,
+ Q: ?Sized + Hash + Eq,
+{
+ type Output = V;
+
+ fn index(&self, key: &Q) -> &V {
+ self.get(key).unwrap()
+ }
+}
diff --git a/syntax/mod.rs b/syntax/mod.rs
index 33ed31cf..3d562931 100644
--- a/syntax/mod.rs
+++ b/syntax/mod.rs
@@ -1,9 +1,9 @@
// Functionality that is shared between the cxxbridge macro and the cmd.
pub mod atom;
-mod attrs;
+pub mod attrs;
pub mod check;
-mod derive;
+pub mod derive;
mod discriminant;
mod doc;
pub mod error;
@@ -11,18 +11,25 @@ pub mod file;
pub mod ident;
mod impls;
mod improper;
+pub mod instantiate;
pub mod mangle;
+pub mod map;
mod names;
pub mod namespace;
mod parse;
+mod pod;
pub mod qualified;
pub mod report;
+pub mod resolve;
pub mod set;
pub mod symbol;
mod tokens;
mod toposort;
+pub mod trivial;
pub mod types;
+mod visit;
+use self::attrs::OtherAttrs;
use self::discriminant::Discriminant;
use self::namespace::Namespace;
use self::parse::kw;
@@ -30,11 +37,12 @@ use self::symbol::Symbol;
use proc_macro2::{Ident, Span};
use syn::punctuated::Punctuated;
use syn::token::{Brace, Bracket, Paren};
-use syn::{Expr, Lifetime, Token, Type as RustType};
+use syn::{Expr, Generics, Lifetime, LitInt, Token, Type as RustType};
pub use self::atom::Atom;
-pub use self::derive::Derive;
+pub use self::derive::{Derive, Trait};
pub use self::doc::Doc;
+pub use self::names::ForeignName;
pub use self::parse::parse_items;
pub use self::types::Types;
@@ -67,9 +75,16 @@ pub enum IncludeKind {
}
pub struct ExternType {
+ pub lang: Lang,
pub doc: Doc,
+ pub derives: Vec<Derive>,
+ pub attrs: OtherAttrs,
+ pub visibility: Token![pub],
pub type_token: Token![type],
pub name: Pair,
+ pub generics: Lifetimes,
+ pub colon_token: Option<Token![:]>,
+ pub bounds: Vec<Derive>,
pub semi_token: Token![;],
pub trusted: bool,
}
@@ -77,33 +92,49 @@ pub struct ExternType {
pub struct Struct {
pub doc: Doc,
pub derives: Vec<Derive>,
+ pub attrs: OtherAttrs,
+ pub visibility: Token![pub],
pub struct_token: Token![struct],
pub name: Pair,
+ pub generics: Lifetimes,
pub brace_token: Brace,
pub fields: Vec<Var>,
}
pub struct Enum {
pub doc: Doc,
+ pub derives: Vec<Derive>,
+ pub attrs: OtherAttrs,
+ pub visibility: Token![pub],
pub enum_token: Token![enum],
pub name: Pair,
+ pub generics: Lifetimes,
pub brace_token: Brace,
pub variants: Vec<Variant>,
pub repr: Atom,
+ pub repr_type: Type,
+ pub explicit_repr: bool,
}
pub struct ExternFn {
pub lang: Lang,
pub doc: Doc,
+ pub attrs: OtherAttrs,
+ pub visibility: Token![pub],
pub name: Pair,
pub sig: Signature,
pub semi_token: Token![;],
+ pub trusted: bool,
}
pub struct TypeAlias {
pub doc: Doc,
+ pub derives: Vec<Derive>,
+ pub attrs: OtherAttrs,
+ pub visibility: Token![pub],
pub type_token: Token![type],
pub name: Pair,
+ pub generics: Lifetimes,
pub eq_token: Token![=],
pub ty: RustType,
pub semi_token: Token![;],
@@ -111,13 +142,25 @@ pub struct TypeAlias {
pub struct Impl {
pub impl_token: Token![impl],
+ pub impl_generics: Lifetimes,
+ pub negative: bool,
pub ty: Type,
+ pub ty_generics: Lifetimes,
pub brace_token: Brace,
+ pub negative_token: Option<Token![!]>,
+}
+
+#[derive(Clone, Default)]
+pub struct Lifetimes {
+ pub lt_token: Option<Token![<]>,
+ pub lifetimes: Punctuated<Lifetime, Token![,]>,
+ pub gt_token: Option<Token![>]>,
}
pub struct Signature {
pub unsafety: Option<Token![unsafe]>,
pub fn_token: Token![fn],
+ pub generics: Generics,
pub receiver: Option<Receiver>,
pub args: Punctuated<Var, Token![,]>,
pub ret: Option<Type>,
@@ -126,39 +169,49 @@ pub struct Signature {
pub throws_tokens: Option<(kw::Result, Token![<], Token![>])>,
}
-#[derive(Eq, PartialEq, Hash)]
pub struct Var {
- pub ident: Ident,
+ pub doc: Doc,
+ pub attrs: OtherAttrs,
+ pub visibility: Token![pub],
+ pub name: Pair,
pub ty: Type,
}
pub struct Receiver {
+ pub pinned: bool,
pub ampersand: Token![&],
pub lifetime: Option<Lifetime>,
- pub mutability: Option<Token![mut]>,
+ pub mutable: bool,
pub var: Token![self],
- pub ty: ResolvableName,
+ pub ty: NamedType,
pub shorthand: bool,
+ pub pin_tokens: Option<(kw::Pin, Token![<], Token![>])>,
+ pub mutability: Option<Token![mut]>,
}
pub struct Variant {
- pub ident: Ident,
+ pub doc: Doc,
+ pub attrs: OtherAttrs,
+ pub name: Pair,
pub discriminant: Discriminant,
pub expr: Option<Expr>,
}
pub enum Type {
- Ident(ResolvableName),
+ Ident(NamedType),
RustBox(Box<Ty1>),
RustVec(Box<Ty1>),
UniquePtr(Box<Ty1>),
+ SharedPtr(Box<Ty1>),
+ WeakPtr(Box<Ty1>),
Ref(Box<Ref>),
+ Ptr(Box<Ptr>),
Str(Box<Ref>),
CxxVector(Box<Ty1>),
Fn(Box<Signature>),
Void(Span),
- Slice(Box<Slice>),
- SliceRefU8(Box<Ref>),
+ SliceRef(Box<SliceRef>),
+ Array(Box<Array>),
}
pub struct Ty1 {
@@ -169,15 +222,38 @@ pub struct Ty1 {
}
pub struct Ref {
+ pub pinned: bool,
pub ampersand: Token![&],
pub lifetime: Option<Lifetime>,
+ pub mutable: bool,
+ pub inner: Type,
+ pub pin_tokens: Option<(kw::Pin, Token![<], Token![>])>,
+ pub mutability: Option<Token![mut]>,
+}
+
+pub struct Ptr {
+ pub star: Token![*],
+ pub mutable: bool,
+ pub inner: Type,
pub mutability: Option<Token![mut]>,
+ pub constness: Option<Token![const]>,
+}
+
+pub struct SliceRef {
+ pub ampersand: Token![&],
+ pub lifetime: Option<Lifetime>,
+ pub mutable: bool,
+ pub bracket: Bracket,
pub inner: Type,
+ pub mutability: Option<Token![mut]>,
}
-pub struct Slice {
+pub struct Array {
pub bracket: Bracket,
pub inner: Type,
+ pub semi_token: Token![;],
+ pub len: usize,
+ pub len_token: LitInt,
}
#[derive(Copy, Clone, PartialEq)]
@@ -191,13 +267,14 @@ pub enum Lang {
#[derive(Clone)]
pub struct Pair {
pub namespace: Namespace,
- pub cxx: Ident,
+ pub cxx: ForeignName,
pub rust: Ident,
}
// Wrapper for a type which needs to be resolved before it can be printed in
// C++.
-#[derive(Clone, PartialEq, Hash)]
-pub struct ResolvableName {
+#[derive(PartialEq, Eq, Hash)]
+pub struct NamedType {
pub rust: Ident,
+ pub generics: Lifetimes,
}
diff --git a/syntax/names.rs b/syntax/names.rs
index 25ae9f4a..cb6ace53 100644
--- a/syntax/names.rs
+++ b/syntax/names.rs
@@ -1,73 +1,70 @@
-use crate::syntax::{Namespace, Pair, ResolvableName, Symbol, Types};
+use crate::syntax::symbol::Segment;
+use crate::syntax::{Lifetimes, NamedType, Pair, Symbol};
use proc_macro2::{Ident, Span};
+use std::fmt::{self, Display};
use std::iter;
-use syn::Token;
+use syn::parse::{Error, Result};
+use syn::punctuated::Punctuated;
-impl Pair {
- // Use this constructor when the item can't have a different name in Rust
- // and C++.
- pub fn new(namespace: Namespace, ident: Ident) -> Self {
- Self {
- namespace,
- cxx: ident.clone(),
- rust: ident,
- }
- }
-
- // Use this constructor when attributes such as #[rust_name] can be used to
- // potentially give a different name in Rust vs C++.
- pub fn new_from_differing_names(
- namespace: Namespace,
- cxx_ident: Ident,
- rust_ident: Ident,
- ) -> Self {
- Self {
- namespace,
- cxx: cxx_ident,
- rust: rust_ident,
- }
- }
+#[derive(Clone)]
+pub struct ForeignName {
+ text: String,
+ span: Span,
+}
+impl Pair {
pub fn to_symbol(&self) -> Symbol {
- Symbol::from_idents(self.iter_all_segments())
+ let segments = self
+ .namespace
+ .iter()
+ .map(|ident| ident as &dyn Segment)
+ .chain(iter::once(&self.cxx as &dyn Segment));
+ Symbol::from_idents(segments)
}
pub fn to_fully_qualified(&self) -> String {
- format!("::{}", self.join("::"))
+ let mut fully_qualified = String::new();
+ for segment in &self.namespace {
+ fully_qualified += "::";
+ fully_qualified += &segment.to_string();
+ }
+ fully_qualified += "::";
+ fully_qualified += &self.cxx.to_string();
+ fully_qualified
}
+}
- fn iter_all_segments(&self) -> impl Iterator<Item = &Ident> {
- self.namespace.iter().chain(iter::once(&self.cxx))
+impl NamedType {
+ pub fn new(rust: Ident) -> Self {
+ let generics = Lifetimes {
+ lt_token: None,
+ lifetimes: Punctuated::new(),
+ gt_token: None,
+ };
+ NamedType { rust, generics }
}
- fn join(&self, sep: &str) -> String {
- self.iter_all_segments()
- .map(|s| s.to_string())
- .collect::<Vec<_>>()
- .join(sep)
+ pub fn span(&self) -> Span {
+ self.rust.span()
}
}
-impl ResolvableName {
- pub fn new(ident: Ident) -> Self {
- Self { rust: ident }
- }
-
- pub fn make_self(span: Span) -> Self {
- Self {
- rust: Token![Self](span).into(),
+impl ForeignName {
+ pub fn parse(text: &str, span: Span) -> Result<Self> {
+ // TODO: support C++ names containing whitespace (`unsigned int`) or
+ // non-alphanumeric characters (`operator++`).
+ match syn::parse_str::<Ident>(text) {
+ Ok(ident) => {
+ let text = ident.to_string();
+ Ok(ForeignName { text, span })
+ }
+ Err(err) => Err(Error::new(span, err)),
}
}
+}
- pub fn is_self(&self) -> bool {
- self.rust == "Self"
- }
-
- pub fn span(&self) -> Span {
- self.rust.span()
- }
-
- pub fn to_symbol(&self, types: &Types) -> Symbol {
- types.resolve(self).to_symbol()
+impl Display for ForeignName {
+ fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+ formatter.write_str(&self.text)
}
}
diff --git a/syntax/parse.rs b/syntax/parse.rs
index a5f8bd10..d7920761 100644
--- a/syntax/parse.rs
+++ b/syntax/parse.rs
@@ -1,23 +1,28 @@
+use crate::syntax::attrs::OtherAttrs;
use crate::syntax::discriminant::DiscriminantSet;
use crate::syntax::file::{Item, ItemForeignMod};
use crate::syntax::report::Errors;
use crate::syntax::Atom::*;
use crate::syntax::{
- attrs, error, Api, Doc, Enum, ExternFn, ExternType, Impl, Include, IncludeKind, Lang,
- Namespace, Pair, Receiver, Ref, ResolvableName, Signature, Slice, Struct, Ty1, Type, TypeAlias,
- Var, Variant,
+ 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,
};
-use proc_macro2::{Delimiter, Group, TokenStream, TokenTree};
+use proc_macro2::{Delimiter, Group, Span, TokenStream, TokenTree};
use quote::{format_ident, quote, quote_spanned};
+use std::mem;
use syn::parse::{ParseStream, Parser};
use syn::punctuated::Punctuated;
use syn::{
- Abi, Attribute, Error, Fields, FnArg, ForeignItem, ForeignItemFn, ForeignItemType,
- GenericArgument, Ident, ItemEnum, ItemImpl, ItemStruct, LitStr, Pat, PathArguments, Result,
- ReturnType, Token, Type as RustType, TypeBareFn, TypePath, TypeReference, TypeSlice,
+ Abi, Attribute, Error, Expr, Fields, FnArg, ForeignItem, ForeignItemFn, ForeignItemType,
+ GenericArgument, GenericParam, Generics, Ident, ItemEnum, ItemImpl, ItemStruct, Lit, LitStr,
+ Pat, PathArguments, Result, ReturnType, Signature as RustSignature, Token, TraitBound,
+ TraitBoundModifier, Type as RustType, TypeArray, TypeBareFn, TypeParamBound, TypePath, TypePtr,
+ TypeReference, Variant as RustVariant, Visibility,
};
pub mod kw {
+ syn::custom_keyword!(Pin);
syn::custom_keyword!(Result);
}
@@ -34,14 +39,11 @@ pub fn parse_items(
Ok(strct) => apis.push(strct),
Err(err) => cx.push(err),
},
- Item::Enum(item) => match parse_enum(cx, item, namespace) {
- Ok(enm) => apis.push(enm),
- Err(err) => cx.push(err),
- },
+ Item::Enum(item) => apis.push(parse_enum(cx, item, namespace)),
Item::ForeignMod(foreign_mod) => {
parse_foreign_mod(cx, foreign_mod, &mut apis, trusted, namespace)
}
- Item::Impl(item) => match parse_impl(item, namespace) {
+ Item::Impl(item) => match parse_impl(item) {
Ok(imp) => apis.push(imp),
Err(err) => cx.push(err),
},
@@ -52,29 +54,21 @@ pub fn parse_items(
apis
}
-fn parse_struct(cx: &mut Errors, item: ItemStruct, namespace: &Namespace) -> Result<Api> {
- let generics = &item.generics;
- if !generics.params.is_empty() || generics.where_clause.is_some() {
- let struct_token = item.struct_token;
- let ident = &item.ident;
- let where_clause = &generics.where_clause;
- let span = quote!(#struct_token #ident #generics #where_clause);
- return Err(Error::new_spanned(
- span,
- "struct with generic parameters is not supported yet",
- ));
- }
-
+fn parse_struct(cx: &mut Errors, mut item: ItemStruct, namespace: &Namespace) -> Result<Api> {
let mut doc = Doc::new();
let mut derives = Vec::new();
let mut namespace = namespace.clone();
- attrs::parse(
+ let mut cxx_name = None;
+ let mut rust_name = None;
+ let attrs = attrs::parse(
cx,
- &item.attrs,
+ mem::take(&mut item.attrs),
attrs::Parser {
doc: Some(&mut doc),
derives: Some(&mut derives),
namespace: Some(&mut namespace),
+ cxx_name: Some(&mut cxx_name),
+ rust_name: Some(&mut rust_name),
..Default::default()
},
);
@@ -87,87 +81,149 @@ fn parse_struct(cx: &mut Errors, item: ItemStruct, namespace: &Namespace) -> Res
}
};
- let fields = named_fields
- .named
- .into_iter()
- .map(|field| {
- Ok(Var {
- ident: field.ident.unwrap(),
- ty: parse_type(&field.ty, &namespace)?,
- })
- })
- .collect::<Result<_>>()?;
+ let mut lifetimes = Punctuated::new();
+ let mut has_unsupported_generic_param = false;
+ for pair in item.generics.params.into_pairs() {
+ let (param, punct) = pair.into_tuple();
+ match param {
+ GenericParam::Lifetime(param) => {
+ if !param.bounds.is_empty() && !has_unsupported_generic_param {
+ let msg = "lifetime parameter with bounds is not supported yet";
+ cx.error(&param, msg);
+ has_unsupported_generic_param = true;
+ }
+ lifetimes.push_value(param.lifetime);
+ if let Some(punct) = punct {
+ lifetimes.push_punct(punct);
+ }
+ }
+ GenericParam::Type(param) => {
+ if !has_unsupported_generic_param {
+ let msg = "struct with generic type parameter is not supported yet";
+ cx.error(&param, msg);
+ has_unsupported_generic_param = true;
+ }
+ }
+ GenericParam::Const(param) => {
+ if !has_unsupported_generic_param {
+ let msg = "struct with const generic parameter is not supported yet";
+ cx.error(&param, msg);
+ has_unsupported_generic_param = true;
+ }
+ }
+ }
+ }
+
+ if let Some(where_clause) = &item.generics.where_clause {
+ cx.error(
+ where_clause,
+ "struct with where-clause is not supported yet",
+ );
+ }
+
+ let mut fields = Vec::new();
+ for field in named_fields.named {
+ let ident = field.ident.unwrap();
+ let mut doc = Doc::new();
+ let mut cxx_name = None;
+ let mut rust_name = None;
+ let attrs = attrs::parse(
+ cx,
+ field.attrs,
+ attrs::Parser {
+ doc: Some(&mut doc),
+ cxx_name: Some(&mut cxx_name),
+ rust_name: Some(&mut rust_name),
+ ..Default::default()
+ },
+ );
+ let ty = match parse_type(&field.ty) {
+ Ok(ty) => ty,
+ Err(err) => {
+ cx.push(err);
+ continue;
+ }
+ };
+ let visibility = visibility_pub(&field.vis, ident.span());
+ let name = pair(Namespace::default(), &ident, cxx_name, rust_name);
+ fields.push(Var {
+ doc,
+ attrs,
+ visibility,
+ name,
+ ty,
+ });
+ }
+
+ let struct_token = item.struct_token;
+ let visibility = visibility_pub(&item.vis, struct_token.span);
+ let name = pair(namespace, &item.ident, cxx_name, rust_name);
+ let generics = Lifetimes {
+ lt_token: item.generics.lt_token,
+ lifetimes,
+ gt_token: item.generics.gt_token,
+ };
+ let brace_token = named_fields.brace_token;
Ok(Api::Struct(Struct {
doc,
derives,
- struct_token: item.struct_token,
- name: Pair::new(namespace, item.ident),
- brace_token: named_fields.brace_token,
+ attrs,
+ visibility,
+ struct_token,
+ name,
+ generics,
+ brace_token,
fields,
}))
}
-fn parse_enum(cx: &mut Errors, item: ItemEnum, namespace: &Namespace) -> Result<Api> {
- let generics = &item.generics;
- if !generics.params.is_empty() || generics.where_clause.is_some() {
- let enum_token = item.enum_token;
- let ident = &item.ident;
- let where_clause = &generics.where_clause;
- let span = quote!(#enum_token #ident #generics #where_clause);
- return Err(Error::new_spanned(
- span,
- "enums with generic parameters are not allowed",
- ));
- }
-
+fn parse_enum(cx: &mut Errors, item: ItemEnum, namespace: &Namespace) -> Api {
let mut doc = Doc::new();
+ let mut derives = Vec::new();
let mut repr = None;
let mut namespace = namespace.clone();
- attrs::parse(
+ let mut cxx_name = None;
+ let mut rust_name = None;
+ let attrs = attrs::parse(
cx,
- &item.attrs,
+ item.attrs,
attrs::Parser {
doc: Some(&mut doc),
+ derives: Some(&mut derives),
repr: Some(&mut repr),
namespace: Some(&mut namespace),
+ cxx_name: Some(&mut cxx_name),
+ rust_name: Some(&mut rust_name),
..Default::default()
},
);
+ if !item.generics.params.is_empty() {
+ let vis = &item.vis;
+ let enum_token = item.enum_token;
+ let ident = &item.ident;
+ let generics = &item.generics;
+ let span = quote!(#vis #enum_token #ident #generics);
+ cx.error(span, "enum with generic parameters is not supported");
+ } else if let Some(where_clause) = &item.generics.where_clause {
+ cx.error(where_clause, "enum with where-clause is not supported");
+ }
+
let mut variants = Vec::new();
let mut discriminants = DiscriminantSet::new(repr);
for variant in item.variants {
- match variant.fields {
- Fields::Unit => {}
- _ => {
- cx.error(variant, "enums with data are not supported yet");
- break;
- }
+ match parse_variant(cx, variant, &mut discriminants) {
+ Ok(variant) => variants.push(variant),
+ Err(err) => cx.push(err),
}
- let expr = variant.discriminant.as_ref().map(|(_, expr)| expr);
- let try_discriminant = match &expr {
- Some(lit) => discriminants.insert(lit),
- None => discriminants.insert_next(),
- };
- let discriminant = match try_discriminant {
- Ok(discriminant) => discriminant,
- Err(err) => {
- cx.error(variant, err);
- break;
- }
- };
- let expr = variant.discriminant.map(|(_, expr)| expr);
- variants.push(Variant {
- ident: variant.ident,
- discriminant,
- expr,
- });
}
let enum_token = item.enum_token;
+ let visibility = visibility_pub(&item.vis, enum_token.span);
let brace_token = item.brace_token;
+ let explicit_repr = repr.is_some();
let mut repr = U8;
match discriminants.inferred_repr() {
Ok(inferred) => repr = inferred,
@@ -178,14 +234,78 @@ fn parse_enum(cx: &mut Errors, item: ItemEnum, namespace: &Namespace) -> Result<
}
}
- Ok(Api::Enum(Enum {
+ 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 generics = Lifetimes {
+ lt_token: None,
+ lifetimes: Punctuated::new(),
+ gt_token: None,
+ };
+
+ Api::Enum(Enum {
doc,
+ derives,
+ attrs,
+ visibility,
enum_token,
- name: Pair::new(namespace, item.ident),
+ name,
+ generics,
brace_token,
variants,
repr,
- }))
+ repr_type,
+ explicit_repr,
+ })
+}
+
+fn parse_variant(
+ cx: &mut Errors,
+ mut variant: RustVariant,
+ discriminants: &mut DiscriminantSet,
+) -> Result<Variant> {
+ let mut doc = Doc::new();
+ let mut cxx_name = None;
+ let mut rust_name = None;
+ let attrs = attrs::parse(
+ cx,
+ mem::take(&mut variant.attrs),
+ attrs::Parser {
+ doc: Some(&mut doc),
+ cxx_name: Some(&mut cxx_name),
+ rust_name: Some(&mut rust_name),
+ ..Default::default()
+ },
+ );
+
+ match variant.fields {
+ Fields::Unit => {}
+ _ => {
+ let msg = "enums with data are not supported yet";
+ return Err(Error::new_spanned(variant, msg));
+ }
+ }
+
+ let expr = variant.discriminant.as_ref().map(|(_, expr)| expr);
+ let try_discriminant = match &expr {
+ Some(lit) => discriminants.insert(lit),
+ None => discriminants.insert_next(),
+ };
+ let discriminant = match try_discriminant {
+ Ok(discriminant) => discriminant,
+ Err(err) => return Err(Error::new_spanned(variant, err)),
+ };
+
+ let name = pair(Namespace::ROOT, &variant.ident, cxx_name, rust_name);
+ let expr = variant.discriminant.map(|(_, expr)| expr);
+
+ Ok(Variant {
+ doc,
+ attrs,
+ name,
+ discriminant,
+ expr,
+ })
}
fn parse_foreign_mod(
@@ -204,7 +324,7 @@ fn parse_foreign_mod(
Lang::Rust => {
if foreign_mod.unsafety.is_some() {
let unsafety = foreign_mod.unsafety;
- let abi = foreign_mod.abi;
+ let abi = &foreign_mod.abi;
let span = quote!(#unsafety #abi);
cx.error(span, "extern \"Rust\" block does not need to be unsafe");
}
@@ -217,7 +337,7 @@ fn parse_foreign_mod(
let mut namespace = namespace.clone();
attrs::parse(
cx,
- &foreign_mod.attrs,
+ foreign_mod.attrs,
attrs::Parser {
namespace: Some(&mut namespace),
..Default::default()
@@ -225,18 +345,18 @@ fn parse_foreign_mod(
);
let mut items = Vec::new();
- for foreign in &foreign_mod.items {
+ for foreign in foreign_mod.items {
match foreign {
ForeignItem::Type(foreign) => {
- match parse_extern_type(cx, foreign, lang, trusted, &namespace) {
- Ok(ety) => items.push(ety),
+ let ety = parse_extern_type(cx, foreign, lang, trusted, &namespace);
+ items.push(ety);
+ }
+ ForeignItem::Fn(foreign) => {
+ match parse_extern_fn(cx, foreign, lang, trusted, &namespace) {
+ Ok(efn) => items.push(efn),
Err(err) => cx.push(err),
}
}
- ForeignItem::Fn(foreign) => match parse_extern_fn(cx, foreign, lang, &namespace) {
- Ok(efn) => items.push(efn),
- Err(err) => cx.push(err),
- },
ForeignItem::Macro(foreign) if foreign.mac.path.is_ident("include") => {
match foreign.mac.parse_body_with(parse_include) {
Ok(include) => items.push(Api::Include(include)),
@@ -244,7 +364,7 @@ fn parse_foreign_mod(
}
}
ForeignItem::Verbatim(tokens) => {
- match parse_extern_verbatim(cx, tokens, lang, &namespace) {
+ match parse_extern_verbatim(cx, tokens, lang, trusted, &namespace) {
Ok(api) => items.push(api),
Err(err) => cx.push(err),
}
@@ -253,6 +373,18 @@ fn parse_foreign_mod(
}
}
+ if !trusted
+ && items.iter().any(|api| match api {
+ Api::CxxFunction(efn) => efn.unsafety.is_none(),
+ _ => false,
+ })
+ {
+ cx.error(
+ foreign_mod.abi,
+ "block must be declared `unsafe extern \"C++\"` if it contains any safe-to-call C++ functions",
+ );
+ }
+
let mut types = items.iter().filter_map(|item| match item {
Api::CxxType(ety) | Api::RustType(ety) => Some(&ety.name),
Api::TypeAlias(alias) => Some(&alias.name),
@@ -263,8 +395,8 @@ fn parse_foreign_mod(
for item in &mut items {
if let Api::CxxFunction(efn) | Api::RustFunction(efn) = item {
if let Some(receiver) = &mut efn.receiver {
- if receiver.ty.is_self() {
- receiver.ty = ResolvableName::new(single_type.rust.clone());
+ if receiver.ty.rust == "Self" {
+ receiver.ty.rust = single_type.rust.clone();
}
}
}
@@ -280,64 +412,113 @@ fn parse_lang(abi: &Abi) -> Result<Lang> {
None => {
return Err(Error::new_spanned(
abi,
- "ABI name is required, extern \"C\" or extern \"Rust\"",
+ "ABI name is required, extern \"C++\" or extern \"Rust\"",
));
}
};
+
match name.value().as_str() {
- "C" | "C++" => Ok(Lang::Cxx),
+ "C++" => Ok(Lang::Cxx),
"Rust" => Ok(Lang::Rust),
- _ => Err(Error::new_spanned(abi, "unrecognized ABI")),
+ _ => Err(Error::new_spanned(
+ abi,
+ "unrecognized ABI, requires either \"C++\" or \"Rust\"",
+ )),
}
}
fn parse_extern_type(
cx: &mut Errors,
- foreign_type: &ForeignItemType,
+ foreign_type: ForeignItemType,
lang: Lang,
trusted: bool,
namespace: &Namespace,
-) -> Result<Api> {
+) -> Api {
let mut doc = Doc::new();
+ let mut derives = Vec::new();
let mut namespace = namespace.clone();
- attrs::parse(
+ let mut cxx_name = None;
+ let mut rust_name = None;
+ let attrs = attrs::parse(
cx,
- &foreign_type.attrs,
+ foreign_type.attrs,
attrs::Parser {
doc: Some(&mut doc),
+ derives: Some(&mut derives),
namespace: Some(&mut namespace),
+ cxx_name: Some(&mut cxx_name),
+ rust_name: Some(&mut rust_name),
..Default::default()
},
);
+
let type_token = foreign_type.type_token;
- let ident = foreign_type.ident.clone();
+ let visibility = visibility_pub(&foreign_type.vis, type_token.span);
+ let name = pair(namespace, &foreign_type.ident, cxx_name, rust_name);
+ let generics = Lifetimes {
+ lt_token: None,
+ lifetimes: Punctuated::new(),
+ gt_token: None,
+ };
+ let colon_token = None;
+ let bounds = Vec::new();
let semi_token = foreign_type.semi_token;
- let api_type = match lang {
+
+ (match lang {
Lang::Cxx => Api::CxxType,
Lang::Rust => Api::RustType,
- };
- Ok(api_type(ExternType {
+ })(ExternType {
+ lang,
doc,
+ derives,
+ attrs,
+ visibility,
type_token,
- name: Pair::new(namespace, ident),
+ name,
+ generics,
+ colon_token,
+ bounds,
semi_token,
trusted,
- }))
+ })
}
fn parse_extern_fn(
cx: &mut Errors,
- foreign_fn: &ForeignItemFn,
+ mut foreign_fn: ForeignItemFn,
lang: Lang,
+ trusted: bool,
namespace: &Namespace,
) -> Result<Api> {
+ let mut doc = Doc::new();
+ let mut namespace = namespace.clone();
+ let mut cxx_name = None;
+ let mut rust_name = None;
+ let attrs = attrs::parse(
+ cx,
+ mem::take(&mut foreign_fn.attrs),
+ attrs::Parser {
+ doc: Some(&mut doc),
+ namespace: Some(&mut namespace),
+ cxx_name: Some(&mut cxx_name),
+ rust_name: Some(&mut rust_name),
+ ..Default::default()
+ },
+ );
+
let generics = &foreign_fn.sig.generics;
- if !generics.params.is_empty() || generics.where_clause.is_some() {
+ if generics.where_clause.is_some()
+ || generics.params.iter().any(|param| match param {
+ GenericParam::Lifetime(lifetime) => !lifetime.bounds.is_empty(),
+ GenericParam::Type(_) | GenericParam::Const(_) => true,
+ })
+ {
return Err(Error::new_spanned(
foreign_fn,
"extern function with generic parameters is not supported yet",
));
}
+
if let Some(variadic) = &foreign_fn.sig.variadic {
return Err(Error::new_spanned(
variadic,
@@ -345,21 +526,26 @@ fn parse_extern_fn(
));
}
- let mut doc = Doc::new();
- let mut cxx_name = None;
- let mut rust_name = None;
- let mut namespace = namespace.clone();
- attrs::parse(
- cx,
- &foreign_fn.attrs,
- attrs::Parser {
- doc: Some(&mut doc),
- cxx_name: Some(&mut cxx_name),
- rust_name: Some(&mut rust_name),
- namespace: Some(&mut namespace),
- ..Default::default()
- },
- );
+ if foreign_fn.sig.asyncness.is_some() {
+ return Err(Error::new_spanned(
+ foreign_fn,
+ "async function is not directly supported yet, but see https://cxx.rs/async.html for a working approach",
+ ));
+ }
+
+ if foreign_fn.sig.constness.is_some() {
+ return Err(Error::new_spanned(
+ foreign_fn,
+ "const extern function is not supported",
+ ));
+ }
+
+ if let Some(abi) = &foreign_fn.sig.abi {
+ return Err(Error::new_spanned(
+ abi,
+ "explicit ABI on extern function is not supported",
+ ));
+ }
let mut receiver = None;
let mut args = Punctuated::new();
@@ -369,12 +555,15 @@ fn parse_extern_fn(
FnArg::Receiver(arg) => {
if let Some((ampersand, lifetime)) = &arg.reference {
receiver = Some(Receiver {
+ pinned: false,
ampersand: *ampersand,
lifetime: lifetime.clone(),
- mutability: arg.mutability,
+ mutable: arg.mutability.is_some(),
var: arg.self_token,
- ty: ResolvableName::make_self(arg.self_token.span),
+ ty: NamedType::new(Ident::new("Self", arg.self_token.span)),
shorthand: true,
+ pin_tokens: None,
+ mutability: arg.mutability,
});
continue;
}
@@ -384,13 +573,23 @@ fn parse_extern_fn(
let ident = match arg.pat.as_ref() {
Pat::Ident(pat) => pat.ident.clone(),
Pat::Wild(pat) => {
- Ident::new(&format!("_{}", args.len()), pat.underscore_token.span)
+ Ident::new(&format!("arg{}", args.len()), pat.underscore_token.span)
}
_ => return Err(Error::new_spanned(arg, "unsupported signature")),
};
- let ty = parse_type(&arg.ty, &namespace)?;
+ let ty = parse_type(&arg.ty)?;
if ident != "self" {
- args.push_value(Var { ident, ty });
+ let doc = Doc::new();
+ let attrs = OtherAttrs::none();
+ let visibility = Token![pub](ident.span());
+ let name = pair(Namespace::default(), &ident, None, None);
+ args.push_value(Var {
+ doc,
+ attrs,
+ visibility,
+ name,
+ ty,
+ });
if let Some(comma) = comma {
args.push_punct(*comma);
}
@@ -399,12 +598,15 @@ fn parse_extern_fn(
if let Type::Ref(reference) = ty {
if let Type::Ident(ident) = reference.inner {
receiver = Some(Receiver {
+ pinned: reference.pinned,
ampersand: reference.ampersand,
lifetime: reference.lifetime,
- mutability: reference.mutability,
+ mutable: reference.mutable,
var: Token![self](ident.rust.span()),
ty: ident,
shorthand: false,
+ pin_tokens: reference.pin_tokens,
+ mutability: reference.mutability,
});
continue;
}
@@ -415,29 +617,30 @@ fn parse_extern_fn(
}
let mut throws_tokens = None;
- let ret = parse_return_type(&foreign_fn.sig.output, &mut throws_tokens, &namespace)?;
+ let ret = parse_return_type(&foreign_fn.sig.output, &mut throws_tokens)?;
let throws = throws_tokens.is_some();
let unsafety = foreign_fn.sig.unsafety;
let fn_token = foreign_fn.sig.fn_token;
- let name = Pair::new_from_differing_names(
- namespace,
- cxx_name.unwrap_or(foreign_fn.sig.ident.clone()),
- rust_name.unwrap_or(foreign_fn.sig.ident.clone()),
- );
+ let inherited_span = unsafety.map_or(fn_token.span, |unsafety| unsafety.span);
+ let visibility = visibility_pub(&foreign_fn.vis, inherited_span);
+ let name = pair(namespace, &foreign_fn.sig.ident, cxx_name, rust_name);
+ let generics = generics.clone();
let paren_token = foreign_fn.sig.paren_token;
let semi_token = foreign_fn.semi_token;
- let api_function = match lang {
+
+ Ok(match lang {
Lang::Cxx => Api::CxxFunction,
Lang::Rust => Api::RustFunction,
- };
-
- Ok(api_function(ExternFn {
+ }(ExternFn {
lang,
doc,
+ attrs,
+ visibility,
name,
sig: Signature {
unsafety,
fn_token,
+ generics,
receiver,
args,
ret,
@@ -446,72 +649,258 @@ fn parse_extern_fn(
throws_tokens,
},
semi_token,
+ trusted,
}))
}
fn parse_extern_verbatim(
cx: &mut Errors,
- tokens: &TokenStream,
+ tokens: TokenStream,
lang: Lang,
+ trusted: bool,
namespace: &Namespace,
) -> Result<Api> {
- // type Alias = crate::path::to::Type;
- let parse = |input: ParseStream| -> Result<TypeAlias> {
+ |input: ParseStream| -> Result<Api> {
let attrs = input.call(Attribute::parse_outer)?;
- let type_token: Token![type] = match input.parse()? {
- Some(type_token) => type_token,
- None => {
- let span = input.cursor().token_stream();
- return Err(Error::new_spanned(span, "unsupported foreign item"));
- }
- };
- let ident: Ident = input.parse()?;
- let eq_token: Token![=] = input.parse()?;
- let ty: RustType = input.parse()?;
- let semi_token: Token![;] = input.parse()?;
- let mut doc = Doc::new();
- let mut namespace = namespace.clone();
- attrs::parse(
- cx,
- &attrs,
- attrs::Parser {
- doc: Some(&mut doc),
- namespace: Some(&mut namespace),
- ..Default::default()
- },
- );
+ let visibility: Visibility = input.parse()?;
+ if input.peek(Token![type]) {
+ parse_extern_verbatim_type(cx, attrs, visibility, input, lang, trusted, namespace)
+ } else if input.peek(Token![fn]) {
+ parse_extern_verbatim_fn(input)
+ } else {
+ let span = input.cursor().token_stream();
+ Err(Error::new_spanned(
+ span,
+ "unsupported foreign item, expected `type` or `fn`",
+ ))
+ }
+ }
+ .parse2(tokens)
+}
- Ok(TypeAlias {
- doc,
- type_token,
- name: Pair::new(namespace, ident),
- eq_token,
- ty,
- semi_token,
- })
+fn parse_extern_verbatim_type(
+ cx: &mut Errors,
+ attrs: Vec<Attribute>,
+ visibility: Visibility,
+ input: ParseStream,
+ lang: Lang,
+ trusted: bool,
+ namespace: &Namespace,
+) -> Result<Api> {
+ let type_token: Token![type] = input.parse()?;
+ let ident: Ident = input.parse()?;
+ let generics: Generics = input.parse()?;
+ let mut lifetimes = Punctuated::new();
+ let mut has_unsupported_generic_param = false;
+ for pair in generics.params.into_pairs() {
+ let (param, punct) = pair.into_tuple();
+ match param {
+ GenericParam::Lifetime(param) => {
+ if !param.bounds.is_empty() && !has_unsupported_generic_param {
+ let msg = "lifetime parameter with bounds is not supported yet";
+ cx.error(&param, msg);
+ has_unsupported_generic_param = true;
+ }
+ lifetimes.push_value(param.lifetime);
+ if let Some(punct) = punct {
+ lifetimes.push_punct(punct);
+ }
+ }
+ GenericParam::Type(param) => {
+ if !has_unsupported_generic_param {
+ let msg = "extern type with generic type parameter is not supported yet";
+ cx.error(&param, msg);
+ has_unsupported_generic_param = true;
+ }
+ }
+ GenericParam::Const(param) => {
+ if !has_unsupported_generic_param {
+ let msg = "extern type with const generic parameter is not supported yet";
+ cx.error(&param, msg);
+ has_unsupported_generic_param = true;
+ }
+ }
+ }
+ }
+ let lifetimes = Lifetimes {
+ lt_token: generics.lt_token,
+ lifetimes,
+ gt_token: generics.gt_token,
};
+ let lookahead = input.lookahead1();
+ if lookahead.peek(Token![=]) {
+ // type Alias = crate::path::to::Type;
+ parse_type_alias(
+ cx, attrs, visibility, type_token, ident, lifetimes, input, lang, namespace,
+ )
+ } else if lookahead.peek(Token![:]) || lookahead.peek(Token![;]) {
+ // type Opaque: Bound2 + Bound2;
+ parse_extern_type_bounded(
+ cx, attrs, visibility, type_token, ident, lifetimes, input, lang, trusted, namespace,
+ )
+ } else {
+ Err(lookahead.error())
+ }
+}
- let type_alias = parse.parse2(tokens.clone())?;
- match lang {
- Lang::Cxx => Ok(Api::TypeAlias(type_alias)),
- Lang::Rust => {
- let (type_token, semi_token) = (type_alias.type_token, type_alias.semi_token);
- let span = quote!(#type_token #semi_token);
- let msg = "type alias in extern \"Rust\" block is not supported";
- Err(Error::new_spanned(span, msg))
+fn parse_extern_verbatim_fn(input: ParseStream) -> Result<Api> {
+ input.parse::<RustSignature>()?;
+ input.parse::<Token![;]>()?;
+ unreachable!()
+}
+
+fn parse_type_alias(
+ cx: &mut Errors,
+ attrs: Vec<Attribute>,
+ visibility: Visibility,
+ type_token: Token![type],
+ ident: Ident,
+ generics: Lifetimes,
+ input: ParseStream,
+ lang: Lang,
+ namespace: &Namespace,
+) -> Result<Api> {
+ let eq_token: Token![=] = input.parse()?;
+ let ty: RustType = input.parse()?;
+ let semi_token: Token![;] = input.parse()?;
+
+ let mut doc = Doc::new();
+ let mut derives = Vec::new();
+ let mut namespace = namespace.clone();
+ let mut cxx_name = None;
+ let mut rust_name = None;
+ let attrs = attrs::parse(
+ cx,
+ attrs,
+ attrs::Parser {
+ doc: Some(&mut doc),
+ derives: Some(&mut derives),
+ namespace: Some(&mut namespace),
+ cxx_name: Some(&mut cxx_name),
+ rust_name: Some(&mut rust_name),
+ ..Default::default()
+ },
+ );
+
+ if lang == Lang::Rust {
+ let span = quote!(#type_token #semi_token);
+ let msg = "type alias in extern \"Rust\" block is not supported";
+ return Err(Error::new_spanned(span, msg));
+ }
+
+ let visibility = visibility_pub(&visibility, type_token.span);
+ let name = pair(namespace, &ident, cxx_name, rust_name);
+
+ Ok(Api::TypeAlias(TypeAlias {
+ doc,
+ derives,
+ attrs,
+ visibility,
+ type_token,
+ name,
+ generics,
+ eq_token,
+ ty,
+ semi_token,
+ }))
+}
+
+fn parse_extern_type_bounded(
+ cx: &mut Errors,
+ attrs: Vec<Attribute>,
+ visibility: Visibility,
+ type_token: Token![type],
+ ident: Ident,
+ generics: Lifetimes,
+ input: ParseStream,
+ lang: Lang,
+ trusted: bool,
+ namespace: &Namespace,
+) -> Result<Api> {
+ let mut bounds = Vec::new();
+ let colon_token: Option<Token![:]> = input.parse()?;
+ if colon_token.is_some() {
+ loop {
+ match input.parse()? {
+ TypeParamBound::Trait(TraitBound {
+ paren_token: None,
+ modifier: TraitBoundModifier::None,
+ lifetimes: None,
+ path,
+ }) if if let Some(derive) = path.get_ident().and_then(Derive::from) {
+ bounds.push(derive);
+ true
+ } else {
+ false
+ } => {}
+ bound @ TypeParamBound::Trait(_) | bound @ TypeParamBound::Lifetime(_) => {
+ cx.error(bound, "unsupported trait");
+ }
+ }
+
+ let lookahead = input.lookahead1();
+ if lookahead.peek(Token![+]) {
+ input.parse::<Token![+]>()?;
+ } else if lookahead.peek(Token![;]) {
+ break;
+ } else {
+ return Err(lookahead.error());
+ }
}
}
+ let semi_token: Token![;] = input.parse()?;
+
+ let mut doc = Doc::new();
+ let mut derives = Vec::new();
+ let mut namespace = namespace.clone();
+ let mut cxx_name = None;
+ let mut rust_name = None;
+ let attrs = attrs::parse(
+ cx,
+ attrs,
+ attrs::Parser {
+ doc: Some(&mut doc),
+ derives: Some(&mut derives),
+ namespace: Some(&mut namespace),
+ cxx_name: Some(&mut cxx_name),
+ rust_name: Some(&mut rust_name),
+ ..Default::default()
+ },
+ );
+
+ let visibility = visibility_pub(&visibility, type_token.span);
+ let name = pair(namespace, &ident, cxx_name, rust_name);
+
+ Ok(match lang {
+ Lang::Cxx => Api::CxxType,
+ Lang::Rust => Api::RustType,
+ }(ExternType {
+ lang,
+ doc,
+ derives,
+ attrs,
+ visibility,
+ type_token,
+ name,
+ generics,
+ colon_token,
+ bounds,
+ semi_token,
+ trusted,
+ }))
}
-fn parse_impl(imp: ItemImpl, namespace: &Namespace) -> Result<Api> {
+fn parse_impl(imp: ItemImpl) -> Result<Api> {
+ let impl_token = imp.impl_token;
+
if !imp.items.is_empty() {
let mut span = Group::new(Delimiter::Brace, TokenStream::new());
span.set_span(imp.brace_token.span);
return Err(Error::new_spanned(span, "expected an empty impl block"));
}
- let self_ty = &imp.self_ty;
if let Some((bang, path, for_token)) = &imp.trait_ {
+ let self_ty = &imp.self_ty;
let span = quote!(#bang #path #for_token #self_ty);
return Err(Error::new_spanned(
span,
@@ -519,18 +908,83 @@ fn parse_impl(imp: ItemImpl, namespace: &Namespace) -> Result<Api> {
));
}
- let generics = &imp.generics;
- if !generics.params.is_empty() || generics.where_clause.is_some() {
+ if let Some(where_clause) = imp.generics.where_clause {
return Err(Error::new_spanned(
- imp,
- "generic parameters on an impl is not supported",
+ where_clause,
+ "where-clause on an impl is not supported yet",
));
}
+ let mut impl_generics = Lifetimes {
+ lt_token: imp.generics.lt_token,
+ lifetimes: Punctuated::new(),
+ gt_token: imp.generics.gt_token,
+ };
+ for pair in imp.generics.params.into_pairs() {
+ let (param, punct) = pair.into_tuple();
+ match param {
+ GenericParam::Lifetime(def) if def.bounds.is_empty() => {
+ impl_generics.lifetimes.push_value(def.lifetime);
+ if let Some(punct) = punct {
+ impl_generics.lifetimes.push_punct(punct);
+ }
+ }
+ _ => {
+ let span = quote!(#impl_token #impl_generics);
+ return Err(Error::new_spanned(
+ span,
+ "generic parameter on an impl is not supported yet",
+ ));
+ }
+ }
+ }
+
+ let mut negative_token = None;
+ let mut self_ty = *imp.self_ty;
+ if let RustType::Verbatim(ty) = &self_ty {
+ let mut iter = ty.clone().into_iter();
+ if let Some(TokenTree::Punct(punct)) = iter.next() {
+ if punct.as_char() == '!' {
+ let ty = iter.collect::<TokenStream>();
+ if !ty.is_empty() {
+ negative_token = Some(Token![!](punct.span()));
+ self_ty = syn::parse2(ty)?;
+ }
+ }
+ }
+ }
+
+ let ty = parse_type(&self_ty)?;
+ let ty_generics = match &ty {
+ Type::RustBox(ty)
+ | Type::RustVec(ty)
+ | Type::UniquePtr(ty)
+ | Type::SharedPtr(ty)
+ | Type::WeakPtr(ty)
+ | Type::CxxVector(ty) => match &ty.inner {
+ Type::Ident(ident) => ident.generics.clone(),
+ _ => Lifetimes::default(),
+ },
+ Type::Ident(_)
+ | Type::Ref(_)
+ | Type::Ptr(_)
+ | Type::Str(_)
+ | Type::Fn(_)
+ | Type::Void(_)
+ | Type::SliceRef(_)
+ | Type::Array(_) => Lifetimes::default(),
+ };
+
+ let negative = negative_token.is_some();
+ let brace_token = imp.brace_token;
Ok(Api::Impl(Impl {
- impl_token: imp.impl_token,
- ty: parse_type(&self_ty, namespace)?,
- brace_token: imp.brace_token,
+ impl_token,
+ impl_generics,
+ negative,
+ ty,
+ ty_generics,
+ brace_token,
+ negative_token,
}))
}
@@ -578,20 +1032,42 @@ fn parse_include(input: ParseStream) -> Result<Include> {
Err(input.error("expected \"quoted/path/to\" or <bracketed/path/to>"))
}
-fn parse_type(ty: &RustType, namespace: &Namespace) -> Result<Type> {
+fn parse_type(ty: &RustType) -> Result<Type> {
match ty {
- RustType::Reference(ty) => parse_type_reference(ty, namespace),
- RustType::Path(ty) => parse_type_path(ty, namespace),
- RustType::Slice(ty) => parse_type_slice(ty, namespace),
- RustType::BareFn(ty) => parse_type_fn(ty, namespace),
+ RustType::Reference(ty) => parse_type_reference(ty),
+ RustType::Ptr(ty) => parse_type_ptr(ty),
+ RustType::Path(ty) => parse_type_path(ty),
+ RustType::Array(ty) => parse_type_array(ty),
+ RustType::BareFn(ty) => parse_type_fn(ty),
RustType::Tuple(ty) if ty.elems.is_empty() => Ok(Type::Void(ty.paren_token.span)),
_ => Err(Error::new_spanned(ty, "unsupported type")),
}
}
-fn parse_type_reference(ty: &TypeReference, namespace: &Namespace) -> Result<Type> {
- let inner = parse_type(&ty.elem, namespace)?;
- let which = match &inner {
+fn parse_type_reference(ty: &TypeReference) -> Result<Type> {
+ let ampersand = ty.and_token;
+ let lifetime = ty.lifetime.clone();
+ let mutable = ty.mutability.is_some();
+ let mutability = ty.mutability;
+
+ if let RustType::Slice(slice) = ty.elem.as_ref() {
+ let inner = parse_type(&slice.elem)?;
+ let bracket = slice.bracket_token;
+ return Ok(Type::SliceRef(Box::new(SliceRef {
+ ampersand,
+ lifetime,
+ mutable,
+ bracket,
+ inner,
+ mutability,
+ })));
+ }
+
+ let inner = parse_type(&ty.elem)?;
+ let pinned = false;
+ let pin_tokens = None;
+
+ Ok(match &inner {
Type::Ident(ident) if ident.rust == "str" => {
if ty.mutability.is_some() {
return Err(Error::new_spanned(ty, "unsupported type"));
@@ -599,31 +1075,46 @@ fn parse_type_reference(ty: &TypeReference, namespace: &Namespace) -> Result<Typ
Type::Str
}
}
- Type::Slice(slice) => match &slice.inner {
- Type::Ident(ident) if ident.rust == U8 && ty.mutability.is_none() => Type::SliceRefU8,
- _ => Type::Ref,
- },
_ => Type::Ref,
- };
- Ok(which(Box::new(Ref {
- ampersand: ty.and_token,
- lifetime: ty.lifetime.clone(),
- mutability: ty.mutability,
+ }(Box::new(Ref {
+ pinned,
+ ampersand,
+ lifetime,
+ mutable,
inner,
+ pin_tokens,
+ mutability,
})))
}
-fn parse_type_path(ty: &TypePath, namespace: &Namespace) -> Result<Type> {
+fn parse_type_ptr(ty: &TypePtr) -> Result<Type> {
+ let star = ty.star_token;
+ let mutable = ty.mutability.is_some();
+ let constness = ty.const_token;
+ let mutability = ty.mutability;
+
+ let inner = parse_type(&ty.elem)?;
+
+ Ok(Type::Ptr(Box::new(Ptr {
+ star,
+ mutable,
+ inner,
+ mutability,
+ constness,
+ })))
+}
+
+fn parse_type_path(ty: &TypePath) -> Result<Type> {
let path = &ty.path;
if ty.qself.is_none() && path.leading_colon.is_none() && path.segments.len() == 1 {
let segment = &path.segments[0];
let ident = segment.ident.clone();
match &segment.arguments {
- PathArguments::None => return Ok(Type::Ident(ResolvableName::new(ident))),
+ PathArguments::None => return Ok(Type::Ident(NamedType::new(ident))),
PathArguments::AngleBracketed(generic) => {
if ident == "UniquePtr" && generic.args.len() == 1 {
if let GenericArgument::Type(arg) = &generic.args[0] {
- let inner = parse_type(arg, namespace)?;
+ let inner = parse_type(arg)?;
return Ok(Type::UniquePtr(Box::new(Ty1 {
name: ident,
langle: generic.lt_token,
@@ -631,9 +1122,29 @@ fn parse_type_path(ty: &TypePath, namespace: &Namespace) -> Result<Type> {
rangle: generic.gt_token,
})));
}
+ } else if ident == "SharedPtr" && generic.args.len() == 1 {
+ if let GenericArgument::Type(arg) = &generic.args[0] {
+ let inner = parse_type(arg)?;
+ return Ok(Type::SharedPtr(Box::new(Ty1 {
+ name: ident,
+ langle: generic.lt_token,
+ inner,
+ rangle: generic.gt_token,
+ })));
+ }
+ } else if ident == "WeakPtr" && generic.args.len() == 1 {
+ if let GenericArgument::Type(arg) = &generic.args[0] {
+ let inner = parse_type(arg)?;
+ return Ok(Type::WeakPtr(Box::new(Ty1 {
+ name: ident,
+ langle: generic.lt_token,
+ inner,
+ rangle: generic.gt_token,
+ })));
+ }
} else if ident == "CxxVector" && generic.args.len() == 1 {
if let GenericArgument::Type(arg) = &generic.args[0] {
- let inner = parse_type(arg, namespace)?;
+ let inner = parse_type(arg)?;
return Ok(Type::CxxVector(Box::new(Ty1 {
name: ident,
langle: generic.lt_token,
@@ -643,7 +1154,7 @@ fn parse_type_path(ty: &TypePath, namespace: &Namespace) -> Result<Type> {
}
} else if ident == "Box" && generic.args.len() == 1 {
if let GenericArgument::Type(arg) = &generic.args[0] {
- let inner = parse_type(arg, namespace)?;
+ let inner = parse_type(arg)?;
return Ok(Type::RustBox(Box::new(Ty1 {
name: ident,
langle: generic.lt_token,
@@ -653,7 +1164,7 @@ fn parse_type_path(ty: &TypePath, namespace: &Namespace) -> Result<Type> {
}
} else if ident == "Vec" && generic.args.len() == 1 {
if let GenericArgument::Type(arg) = &generic.args[0] {
- let inner = parse_type(arg, namespace)?;
+ let inner = parse_type(arg)?;
return Ok(Type::RustVec(Box::new(Ty1 {
name: ident,
langle: generic.lt_token,
@@ -661,59 +1172,144 @@ fn parse_type_path(ty: &TypePath, namespace: &Namespace) -> Result<Type> {
rangle: generic.gt_token,
})));
}
+ } else if ident == "Pin" && generic.args.len() == 1 {
+ if let GenericArgument::Type(arg) = &generic.args[0] {
+ let inner = parse_type(arg)?;
+ let pin_token = kw::Pin(ident.span());
+ if let Type::Ref(mut inner) = inner {
+ inner.pinned = true;
+ inner.pin_tokens =
+ Some((pin_token, generic.lt_token, generic.gt_token));
+ return Ok(Type::Ref(inner));
+ }
+ }
+ } else {
+ let mut lifetimes = Punctuated::new();
+ let mut only_lifetimes = true;
+ for pair in generic.args.pairs() {
+ let (param, punct) = pair.into_tuple();
+ if let GenericArgument::Lifetime(param) = param {
+ lifetimes.push_value(param.clone());
+ if let Some(punct) = punct {
+ lifetimes.push_punct(*punct);
+ }
+ } else {
+ only_lifetimes = false;
+ break;
+ }
+ }
+ if only_lifetimes {
+ return Ok(Type::Ident(NamedType {
+ rust: ident,
+ generics: Lifetimes {
+ lt_token: Some(generic.lt_token),
+ lifetimes,
+ gt_token: Some(generic.gt_token),
+ },
+ }));
+ }
}
}
PathArguments::Parenthesized(_) => {}
}
}
+
Err(Error::new_spanned(ty, "unsupported type"))
}
-fn parse_type_slice(ty: &TypeSlice, namespace: &Namespace) -> Result<Type> {
- let inner = parse_type(&ty.elem, namespace)?;
- Ok(Type::Slice(Box::new(Slice {
- bracket: ty.bracket_token,
+fn parse_type_array(ty: &TypeArray) -> Result<Type> {
+ let inner = parse_type(&ty.elem)?;
+
+ let len_expr = if let Expr::Lit(lit) = &ty.len {
+ lit
+ } else {
+ let msg = "unsupported expression, array length must be an integer literal";
+ return Err(Error::new_spanned(&ty.len, msg));
+ };
+
+ let len_token = if let Lit::Int(int) = &len_expr.lit {
+ int.clone()
+ } else {
+ let msg = "array length must be an integer literal";
+ return Err(Error::new_spanned(len_expr, msg));
+ };
+
+ let len = len_token.base10_parse::<usize>()?;
+ if len == 0 {
+ let msg = "array with zero size is not supported";
+ return Err(Error::new_spanned(ty, msg));
+ }
+
+ let bracket = ty.bracket_token;
+ let semi_token = ty.semi_token;
+
+ Ok(Type::Array(Box::new(Array {
+ bracket,
inner,
+ semi_token,
+ len,
+ len_token,
})))
}
-fn parse_type_fn(ty: &TypeBareFn, namespace: &Namespace) -> Result<Type> {
+fn parse_type_fn(ty: &TypeBareFn) -> Result<Type> {
if ty.lifetimes.is_some() {
return Err(Error::new_spanned(
ty,
"function pointer with lifetime parameters is not supported yet",
));
}
+
if ty.variadic.is_some() {
return Err(Error::new_spanned(
ty,
"variadic function pointer is not supported yet",
));
}
+
let args = ty
.inputs
.iter()
.enumerate()
.map(|(i, arg)| {
- let ty = parse_type(&arg.ty, namespace)?;
+ let ty = parse_type(&arg.ty)?;
let ident = match &arg.name {
Some(ident) => ident.0.clone(),
- None => format_ident!("_{}", i),
+ None => format_ident!("arg{}", i),
};
- Ok(Var { ident, ty })
+ let doc = Doc::new();
+ let attrs = OtherAttrs::none();
+ let visibility = Token![pub](ident.span());
+ let name = pair(Namespace::default(), &ident, None, None);
+ Ok(Var {
+ doc,
+ attrs,
+ visibility,
+ name,
+ ty,
+ })
})
.collect::<Result<_>>()?;
+
let mut throws_tokens = None;
- let ret = parse_return_type(&ty.output, &mut throws_tokens, namespace)?;
+ let ret = parse_return_type(&ty.output, &mut throws_tokens)?;
let throws = throws_tokens.is_some();
+
+ let unsafety = ty.unsafety;
+ let fn_token = ty.fn_token;
+ let generics = Generics::default();
+ let receiver = None;
+ let paren_token = ty.paren_token;
+
Ok(Type::Fn(Box::new(Signature {
- unsafety: ty.unsafety,
- fn_token: ty.fn_token,
- receiver: None,
+ unsafety,
+ fn_token,
+ generics,
+ receiver,
args,
ret,
throws,
- paren_token: ty.paren_token,
+ paren_token,
throws_tokens,
})))
}
@@ -721,12 +1317,12 @@ fn parse_type_fn(ty: &TypeBareFn, namespace: &Namespace) -> Result<Type> {
fn parse_return_type(
ty: &ReturnType,
throws_tokens: &mut Option<(kw::Result, Token![<], Token![>])>,
- namespace: &Namespace,
) -> Result<Option<Type>> {
let mut ret = match ty {
ReturnType::Default => return Ok(None),
ReturnType::Type(_, ret) => ret.as_ref(),
};
+
if let RustType::Path(ty) = ret {
let path = &ty.path;
if ty.qself.is_none() && path.leading_colon.is_none() && path.segments.len() == 1 {
@@ -743,8 +1339,32 @@ fn parse_return_type(
}
}
}
- match parse_type(ret, namespace)? {
+
+ match parse_type(ret)? {
Type::Void(_) => Ok(None),
ty => Ok(Some(ty)),
}
}
+
+fn visibility_pub(vis: &Visibility, inherited: Span) -> Token![pub] {
+ Token![pub](match vis {
+ Visibility::Public(vis) => vis.pub_token.span,
+ Visibility::Crate(vis) => vis.crate_token.span,
+ Visibility::Restricted(vis) => vis.pub_token.span,
+ Visibility::Inherited => inherited,
+ })
+}
+
+fn pair(
+ namespace: Namespace,
+ default: &Ident,
+ cxx: Option<ForeignName>,
+ rust: Option<Ident>,
+) -> Pair {
+ Pair {
+ namespace,
+ cxx: cxx
+ .unwrap_or_else(|| ForeignName::parse(&default.to_string(), default.span()).unwrap()),
+ rust: rust.unwrap_or_else(|| default.clone()),
+ }
+}
diff --git a/syntax/pod.rs b/syntax/pod.rs
new file mode 100644
index 00000000..0bf152ee
--- /dev/null
+++ b/syntax/pod.rs
@@ -0,0 +1,36 @@
+use crate::syntax::atom::Atom::{self, *};
+use crate::syntax::{derive, Trait, Type, Types};
+
+impl<'a> Types<'a> {
+ pub fn is_guaranteed_pod(&self, ty: &Type) -> bool {
+ match ty {
+ Type::Ident(ident) => {
+ let ident = &ident.rust;
+ if let Some(atom) = Atom::from(ident) {
+ match atom {
+ Bool | Char | U8 | U16 | U32 | U64 | Usize | I8 | I16 | I32 | I64
+ | Isize | F32 | F64 => true,
+ CxxString | RustString => false,
+ }
+ } else if let Some(strct) = self.structs.get(ident) {
+ derive::contains(&strct.derives, Trait::Copy)
+ || strct
+ .fields
+ .iter()
+ .all(|field| self.is_guaranteed_pod(&field.ty))
+ } else {
+ self.enums.contains_key(ident)
+ }
+ }
+ Type::RustBox(_)
+ | Type::RustVec(_)
+ | Type::UniquePtr(_)
+ | Type::SharedPtr(_)
+ | Type::WeakPtr(_)
+ | Type::CxxVector(_)
+ | Type::Void(_) => false,
+ Type::Ref(_) | Type::Str(_) | Type::Fn(_) | Type::SliceRef(_) | Type::Ptr(_) => true,
+ Type::Array(array) => self.is_guaranteed_pod(&array.inner),
+ }
+ }
+}
diff --git a/syntax/qualified.rs b/syntax/qualified.rs
index 5eefb8db..96f07c19 100644
--- a/syntax/qualified.rs
+++ b/syntax/qualified.rs
@@ -10,14 +10,14 @@ impl QualifiedName {
pub fn parse_unquoted(input: ParseStream) -> Result<Self> {
let mut segments = Vec::new();
let mut trailing_punct = true;
- input.parse::<Option<Token![::]>>()?;
+ let leading_colons: Option<Token![::]> = input.parse()?;
while trailing_punct && input.peek(Ident::peek_any) {
let ident = Ident::parse_any(input)?;
segments.push(ident);
let colons: Option<Token![::]> = input.parse()?;
trailing_punct = colons.is_some();
}
- if segments.is_empty() {
+ if segments.is_empty() && leading_colons.is_none() {
return Err(input.error("expected path"));
} else if trailing_punct {
return Err(input.error("expected path segment"));
@@ -28,7 +28,12 @@ impl QualifiedName {
pub fn parse_quoted_or_unquoted(input: ParseStream) -> Result<Self> {
if input.peek(LitStr) {
let lit: LitStr = input.parse()?;
- lit.parse_with(Self::parse_unquoted)
+ if lit.value().is_empty() {
+ let segments = Vec::new();
+ Ok(QualifiedName { segments })
+ } else {
+ lit.parse_with(Self::parse_unquoted)
+ }
} else {
Self::parse_unquoted(input)
}
diff --git a/syntax/resolve.rs b/syntax/resolve.rs
new file mode 100644
index 00000000..3a2635bd
--- /dev/null
+++ b/syntax/resolve.rs
@@ -0,0 +1,46 @@
+use crate::syntax::instantiate::NamedImplKey;
+use crate::syntax::{Lifetimes, NamedType, Pair, Types};
+use proc_macro2::Ident;
+
+#[derive(Copy, Clone)]
+pub struct Resolution<'a> {
+ pub name: &'a Pair,
+ pub generics: &'a Lifetimes,
+}
+
+impl<'a> Types<'a> {
+ pub fn resolve(&self, ident: &impl UnresolvedName) -> Resolution<'a> {
+ let ident = ident.ident();
+ match self.try_resolve(ident) {
+ Some(resolution) => resolution,
+ None => panic!("Unable to resolve type `{}`", ident),
+ }
+ }
+
+ pub fn try_resolve(&self, ident: &impl UnresolvedName) -> Option<Resolution<'a>> {
+ let ident = ident.ident();
+ self.resolutions.get(ident).copied()
+ }
+}
+
+pub trait UnresolvedName {
+ fn ident(&self) -> &Ident;
+}
+
+impl UnresolvedName for Ident {
+ fn ident(&self) -> &Ident {
+ self
+ }
+}
+
+impl UnresolvedName for NamedType {
+ fn ident(&self) -> &Ident {
+ &self.rust
+ }
+}
+
+impl<'a> UnresolvedName for NamedImplKey<'a> {
+ fn ident(&self) -> &Ident {
+ self.rust
+ }
+}
diff --git a/syntax/set.rs b/syntax/set.rs
index 6508f556..ca0c43e0 100644
--- a/syntax/set.rs
+++ b/syntax/set.rs
@@ -50,11 +50,21 @@ mod ordered {
}
}
+ impl<'a, T> OrderedSet<&'a T> {
+ pub fn is_empty(&self) -> bool {
+ self.vec.is_empty()
+ }
+
+ pub fn iter(&self) -> Iter<'_, 'a, T> {
+ Iter(self.vec.iter())
+ }
+ }
+
impl<'s, 'a, T> IntoIterator for &'s OrderedSet<&'a T> {
type Item = &'a T;
type IntoIter = Iter<'s, 'a, T>;
fn into_iter(self) -> Self::IntoIter {
- Iter(self.vec.iter())
+ self.iter()
}
}
}
@@ -95,6 +105,10 @@ mod unordered {
{
self.0.get(value)
}
+
+ pub fn retain(&mut self, f: impl FnMut(&T) -> bool) {
+ self.0.retain(f);
+ }
}
}
@@ -102,9 +116,14 @@ pub struct Iter<'s, 'a, T>(slice::Iter<'s, &'a T>);
impl<'s, 'a, T> Iterator for Iter<'s, 'a, T> {
type Item = &'a T;
+
fn next(&mut self) -> Option<Self::Item> {
self.0.next().copied()
}
+
+ fn size_hint(&self) -> (usize, Option<usize>) {
+ self.0.size_hint()
+ }
}
impl<'a, T> Debug for OrderedSet<&'a T>
diff --git a/syntax/symbol.rs b/syntax/symbol.rs
index 253f57dc..a13a4f3c 100644
--- a/syntax/symbol.rs
+++ b/syntax/symbol.rs
@@ -1,11 +1,11 @@
use crate::syntax::namespace::Namespace;
-use crate::syntax::Pair;
+use crate::syntax::{ForeignName, Pair};
use proc_macro2::{Ident, TokenStream};
use quote::ToTokens;
use std::fmt::{self, Display, Write};
// A mangled symbol consisting of segments separated by '$'.
-// For example: cxxbridge05$string$new
+// For example: cxxbridge1$string$new
pub struct Symbol(String);
impl Display for Symbol {
@@ -30,7 +30,7 @@ impl Symbol {
assert!(self.0.len() > len_before);
}
- pub fn from_idents<'a, T: Iterator<Item = &'a Ident>>(it: T) -> Self {
+ pub fn from_idents<'a>(it: impl Iterator<Item = &'a dyn Segment>) -> Self {
let mut symbol = Symbol(String::new());
for segment in it {
segment.write(&mut symbol);
@@ -55,16 +55,19 @@ impl Segment for str {
symbol.push(&self);
}
}
+
impl Segment for usize {
fn write(&self, symbol: &mut Symbol) {
symbol.push(&self);
}
}
+
impl Segment for Ident {
fn write(&self, symbol: &mut Symbol) {
symbol.push(&self);
}
}
+
impl Segment for Symbol {
fn write(&self, symbol: &mut Symbol) {
symbol.push(&self);
@@ -86,6 +89,14 @@ impl Segment for Pair {
}
}
+impl Segment for ForeignName {
+ fn write(&self, symbol: &mut Symbol) {
+ // TODO: support C++ names containing whitespace (`unsigned int`) or
+ // non-alphanumeric characters (`operator++`).
+ self.to_string().write(symbol);
+ }
+}
+
impl<T> Segment for &'_ T
where
T: ?Sized + Segment + Display,
diff --git a/syntax/tokens.rs b/syntax/tokens.rs
index 500ea0b3..c1a06a21 100644
--- a/syntax/tokens.rs
+++ b/syntax/tokens.rs
@@ -1,77 +1,155 @@
use crate::syntax::atom::Atom::*;
use crate::syntax::{
- Atom, Derive, Enum, ExternFn, ExternType, Impl, Receiver, Ref, ResolvableName, Signature,
- Slice, Struct, Ty1, Type, TypeAlias, Var,
+ Array, Atom, Derive, Enum, 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};
-use syn::Token;
+use syn::{token, Token};
impl ToTokens for Type {
fn to_tokens(&self, tokens: &mut TokenStream) {
match self {
Type::Ident(ident) => {
- if ident.rust == CxxString {
+ if ident.rust == Char {
+ let span = ident.rust.span();
+ tokens.extend(quote_spanned!(span=> ::std::os::raw::));
+ } else if ident.rust == CxxString {
let span = ident.rust.span();
tokens.extend(quote_spanned!(span=> ::cxx::));
}
- ident.rust.to_tokens(tokens);
- }
- Type::RustBox(ty) | Type::UniquePtr(ty) | Type::CxxVector(ty) | Type::RustVec(ty) => {
- ty.to_tokens(tokens)
+ ident.to_tokens(tokens);
}
- Type::Ref(r) | Type::Str(r) | Type::SliceRefU8(r) => r.to_tokens(tokens),
- Type::Slice(s) => s.to_tokens(tokens),
+ Type::RustBox(ty)
+ | Type::UniquePtr(ty)
+ | Type::SharedPtr(ty)
+ | Type::WeakPtr(ty)
+ | Type::CxxVector(ty)
+ | Type::RustVec(ty) => ty.to_tokens(tokens),
+ Type::Ref(r) | Type::Str(r) => r.to_tokens(tokens),
+ Type::Ptr(p) => p.to_tokens(tokens),
+ Type::Array(a) => a.to_tokens(tokens),
Type::Fn(f) => f.to_tokens(tokens),
Type::Void(span) => tokens.extend(quote_spanned!(*span=> ())),
+ Type::SliceRef(r) => r.to_tokens(tokens),
}
}
}
impl ToTokens for Var {
fn to_tokens(&self, tokens: &mut TokenStream) {
- self.ident.to_tokens(tokens);
- Token![:](self.ident.span()).to_tokens(tokens);
- self.ty.to_tokens(tokens);
+ let Var {
+ doc: _,
+ attrs: _,
+ visibility: _,
+ name,
+ ty,
+ } = self;
+ name.rust.to_tokens(tokens);
+ Token![:](name.rust.span()).to_tokens(tokens);
+ ty.to_tokens(tokens);
}
}
impl ToTokens for Ty1 {
fn to_tokens(&self, tokens: &mut TokenStream) {
- let span = self.name.span();
- let name = self.name.to_string();
- if let "UniquePtr" | "CxxVector" = name.as_str() {
- tokens.extend(quote_spanned!(span=> ::cxx::));
- } else if name == "Vec" {
- tokens.extend(quote_spanned!(span=> ::std::vec::));
+ let Ty1 {
+ name,
+ langle,
+ inner,
+ rangle,
+ } = self;
+ let span = name.span();
+ match name.to_string().as_str() {
+ "UniquePtr" | "SharedPtr" | "WeakPtr" | "CxxVector" => {
+ tokens.extend(quote_spanned!(span=> ::cxx::));
+ }
+ "Vec" => {
+ tokens.extend(quote_spanned!(span=> ::std::vec::));
+ }
+ _ => {}
}
- self.name.to_tokens(tokens);
- self.langle.to_tokens(tokens);
- self.inner.to_tokens(tokens);
- self.rangle.to_tokens(tokens);
+ name.to_tokens(tokens);
+ langle.to_tokens(tokens);
+ inner.to_tokens(tokens);
+ rangle.to_tokens(tokens);
}
}
impl ToTokens for Ref {
fn to_tokens(&self, tokens: &mut TokenStream) {
- self.ampersand.to_tokens(tokens);
- self.lifetime.to_tokens(tokens);
- self.mutability.to_tokens(tokens);
- self.inner.to_tokens(tokens);
+ let Ref {
+ pinned: _,
+ ampersand,
+ lifetime,
+ mutable: _,
+ inner,
+ pin_tokens,
+ mutability,
+ } = self;
+ if let Some((pin, langle, _rangle)) = pin_tokens {
+ tokens.extend(quote_spanned!(pin.span=> ::std::pin::Pin));
+ langle.to_tokens(tokens);
+ }
+ ampersand.to_tokens(tokens);
+ lifetime.to_tokens(tokens);
+ mutability.to_tokens(tokens);
+ inner.to_tokens(tokens);
+ if let Some((_pin, _langle, rangle)) = pin_tokens {
+ rangle.to_tokens(tokens);
+ }
+ }
+}
+
+impl ToTokens for Ptr {
+ fn to_tokens(&self, tokens: &mut TokenStream) {
+ let Ptr {
+ star,
+ mutable: _,
+ inner,
+ mutability,
+ constness,
+ } = self;
+ star.to_tokens(tokens);
+ mutability.to_tokens(tokens);
+ constness.to_tokens(tokens);
+ inner.to_tokens(tokens);
}
}
-impl ToTokens for Slice {
+impl ToTokens for SliceRef {
fn to_tokens(&self, tokens: &mut TokenStream) {
- self.bracket.surround(tokens, |tokens| {
- self.inner.to_tokens(tokens);
+ let SliceRef {
+ ampersand,
+ lifetime,
+ mutable: _,
+ bracket,
+ inner,
+ mutability,
+ } = self;
+ ampersand.to_tokens(tokens);
+ lifetime.to_tokens(tokens);
+ mutability.to_tokens(tokens);
+ bracket.surround(tokens, |tokens| {
+ inner.to_tokens(tokens);
});
}
}
-impl ToTokens for Derive {
+impl ToTokens for Array {
fn to_tokens(&self, tokens: &mut TokenStream) {
- Ident::new(self.as_ref(), Span::call_site()).to_tokens(tokens);
+ let Array {
+ bracket,
+ inner,
+ semi_token,
+ len: _,
+ len_token,
+ } = self;
+ bracket.surround(tokens, |tokens| {
+ inner.to_tokens(tokens);
+ semi_token.to_tokens(tokens);
+ len_token.to_tokens(tokens);
+ });
}
}
@@ -81,11 +159,18 @@ impl ToTokens for Atom {
}
}
+impl ToTokens for Derive {
+ fn to_tokens(&self, tokens: &mut TokenStream) {
+ Ident::new(self.what.as_ref(), self.span).to_tokens(tokens);
+ }
+}
+
impl ToTokens for ExternType {
fn to_tokens(&self, tokens: &mut TokenStream) {
// Notional token range for error reporting purposes.
self.type_token.to_tokens(tokens);
self.name.rust.to_tokens(tokens);
+ self.generics.to_tokens(tokens);
}
}
@@ -94,6 +179,7 @@ impl ToTokens for TypeAlias {
// Notional token range for error reporting purposes.
self.type_token.to_tokens(tokens);
self.name.rust.to_tokens(tokens);
+ self.generics.to_tokens(tokens);
}
}
@@ -102,6 +188,7 @@ impl ToTokens for Struct {
// Notional token range for error reporting purposes.
self.struct_token.to_tokens(tokens);
self.name.rust.to_tokens(tokens);
+ self.generics.to_tokens(tokens);
}
}
@@ -110,12 +197,14 @@ impl ToTokens for Enum {
// Notional token range for error reporting purposes.
self.enum_token.to_tokens(tokens);
self.name.rust.to_tokens(tokens);
+ self.generics.to_tokens(tokens);
}
}
impl ToTokens for ExternFn {
fn to_tokens(&self, tokens: &mut TokenStream) {
// Notional token range for error reporting purposes.
+ self.unsafety.to_tokens(tokens);
self.sig.fn_token.to_tokens(tokens);
self.semi_token.to_tokens(tokens);
}
@@ -123,21 +212,56 @@ impl ToTokens for ExternFn {
impl ToTokens for Impl {
fn to_tokens(&self, tokens: &mut TokenStream) {
- self.impl_token.to_tokens(tokens);
- self.ty.to_tokens(tokens);
- self.brace_token.surround(tokens, |_tokens| {});
+ let Impl {
+ impl_token,
+ impl_generics,
+ negative: _,
+ ty,
+ ty_generics: _,
+ brace_token,
+ negative_token,
+ } = self;
+ impl_token.to_tokens(tokens);
+ impl_generics.to_tokens(tokens);
+ negative_token.to_tokens(tokens);
+ ty.to_tokens(tokens);
+ brace_token.surround(tokens, |_tokens| {});
+ }
+}
+
+impl ToTokens for Lifetimes {
+ fn to_tokens(&self, tokens: &mut TokenStream) {
+ let Lifetimes {
+ lt_token,
+ lifetimes,
+ gt_token,
+ } = self;
+ lt_token.to_tokens(tokens);
+ lifetimes.to_tokens(tokens);
+ gt_token.to_tokens(tokens);
}
}
impl ToTokens for Signature {
fn to_tokens(&self, tokens: &mut TokenStream) {
- self.fn_token.to_tokens(tokens);
- self.paren_token.surround(tokens, |tokens| {
- self.args.to_tokens(tokens);
+ let Signature {
+ unsafety: _,
+ fn_token,
+ generics: _,
+ receiver: _,
+ args,
+ ret,
+ throws: _,
+ paren_token,
+ throws_tokens,
+ } = self;
+ fn_token.to_tokens(tokens);
+ paren_token.surround(tokens, |tokens| {
+ args.to_tokens(tokens);
});
- if let Some(ret) = &self.ret {
- Token![->](self.paren_token.span).to_tokens(tokens);
- if let Some((result, langle, rangle)) = self.throws_tokens {
+ if let Some(ret) = ret {
+ Token![->](paren_token.span).to_tokens(tokens);
+ if let Some((result, langle, rangle)) = throws_tokens {
result.to_tokens(tokens);
langle.to_tokens(tokens);
ret.to_tokens(tokens);
@@ -145,30 +269,89 @@ impl ToTokens for Signature {
} else {
ret.to_tokens(tokens);
}
+ } else if let Some((result, langle, rangle)) = throws_tokens {
+ Token![->](paren_token.span).to_tokens(tokens);
+ result.to_tokens(tokens);
+ langle.to_tokens(tokens);
+ token::Paren(langle.span).surround(tokens, |_| ());
+ rangle.to_tokens(tokens);
}
}
}
-impl ToTokens for ResolvableName {
+impl ToTokens for NamedType {
fn to_tokens(&self, tokens: &mut TokenStream) {
- self.rust.to_tokens(tokens);
+ let NamedType { rust, generics } = self;
+ rust.to_tokens(tokens);
+ generics.to_tokens(tokens);
}
}
pub struct ReceiverType<'a>(&'a Receiver);
+pub struct ReceiverTypeSelf<'a>(&'a Receiver);
impl Receiver {
// &TheType
pub fn ty(&self) -> ReceiverType {
ReceiverType(self)
}
+
+ // &Self
+ pub fn ty_self(&self) -> ReceiverTypeSelf {
+ ReceiverTypeSelf(self)
+ }
}
impl ToTokens for ReceiverType<'_> {
fn to_tokens(&self, tokens: &mut TokenStream) {
- self.0.ampersand.to_tokens(tokens);
- self.0.lifetime.to_tokens(tokens);
- self.0.mutability.to_tokens(tokens);
- self.0.ty.to_tokens(tokens);
+ let Receiver {
+ pinned: _,
+ ampersand,
+ lifetime,
+ mutable: _,
+ var: _,
+ ty,
+ shorthand: _,
+ pin_tokens,
+ mutability,
+ } = &self.0;
+ if let Some((pin, langle, _rangle)) = pin_tokens {
+ tokens.extend(quote_spanned!(pin.span=> ::std::pin::Pin));
+ langle.to_tokens(tokens);
+ }
+ ampersand.to_tokens(tokens);
+ lifetime.to_tokens(tokens);
+ mutability.to_tokens(tokens);
+ ty.to_tokens(tokens);
+ if let Some((_pin, _langle, rangle)) = pin_tokens {
+ rangle.to_tokens(tokens);
+ }
+ }
+}
+
+impl ToTokens for ReceiverTypeSelf<'_> {
+ fn to_tokens(&self, tokens: &mut TokenStream) {
+ let Receiver {
+ pinned: _,
+ ampersand,
+ lifetime,
+ mutable: _,
+ var: _,
+ ty,
+ shorthand: _,
+ pin_tokens,
+ mutability,
+ } = &self.0;
+ if let Some((pin, langle, _rangle)) = pin_tokens {
+ tokens.extend(quote_spanned!(pin.span=> ::std::pin::Pin));
+ langle.to_tokens(tokens);
+ }
+ ampersand.to_tokens(tokens);
+ lifetime.to_tokens(tokens);
+ mutability.to_tokens(tokens);
+ Token![Self](ty.rust.span()).to_tokens(tokens);
+ if let Some((_pin, _langle, rangle)) = pin_tokens {
+ rangle.to_tokens(tokens);
+ }
}
}
diff --git a/syntax/toposort.rs b/syntax/toposort.rs
index 876f9ad1..8fe55b8b 100644
--- a/syntax/toposort.rs
+++ b/syntax/toposort.rs
@@ -1,6 +1,6 @@
+use crate::syntax::map::{Entry, UnorderedMap as Map};
use crate::syntax::report::Errors;
use crate::syntax::{Api, Struct, Type, Types};
-use std::collections::btree_map::{BTreeMap as Map, Entry};
enum Mark {
Visiting,
diff --git a/syntax/trivial.rs b/syntax/trivial.rs
new file mode 100644
index 00000000..fe95e2b7
--- /dev/null
+++ b/syntax/trivial.rs
@@ -0,0 +1,257 @@
+use crate::syntax::map::UnorderedMap;
+use crate::syntax::set::{OrderedSet as Set, UnorderedSet};
+use crate::syntax::{Api, Enum, ExternFn, NamedType, Pair, Struct, Type};
+use proc_macro2::Ident;
+use std::fmt::{self, Display};
+
+#[derive(Copy, Clone)]
+pub enum TrivialReason<'a> {
+ StructField(&'a Struct),
+ FunctionArgument(&'a ExternFn),
+ FunctionReturn(&'a ExternFn),
+ BoxTarget,
+ VecElement,
+ UnpinnedMut(&'a ExternFn),
+}
+
+pub fn required_trivial_reasons<'a>(
+ apis: &'a [Api],
+ all: &Set<&'a Type>,
+ structs: &UnorderedMap<&'a Ident, &'a Struct>,
+ enums: &UnorderedMap<&'a Ident, &'a Enum>,
+ cxx: &UnorderedSet<&'a Ident>,
+) -> UnorderedMap<&'a Ident, Vec<TrivialReason<'a>>> {
+ let mut required_trivial = UnorderedMap::new();
+
+ let mut insist_extern_types_are_trivial = |ident: &'a NamedType, reason| {
+ if cxx.contains(&ident.rust)
+ && !structs.contains_key(&ident.rust)
+ && !enums.contains_key(&ident.rust)
+ {
+ required_trivial
+ .entry(&ident.rust)
+ .or_insert_with(Vec::new)
+ .push(reason);
+ }
+ };
+
+ for api in apis {
+ match api {
+ Api::Struct(strct) => {
+ for field in &strct.fields {
+ if let Type::Ident(ident) = &field.ty {
+ let reason = TrivialReason::StructField(strct);
+ insist_extern_types_are_trivial(ident, reason);
+ }
+ }
+ }
+ Api::CxxFunction(efn) | Api::RustFunction(efn) => {
+ if let Some(receiver) = &efn.receiver {
+ if receiver.mutable && !receiver.pinned {
+ let reason = TrivialReason::UnpinnedMut(efn);
+ insist_extern_types_are_trivial(&receiver.ty, reason);
+ }
+ }
+ for arg in &efn.args {
+ match &arg.ty {
+ Type::Ident(ident) => {
+ let reason = TrivialReason::FunctionArgument(efn);
+ insist_extern_types_are_trivial(ident, reason);
+ }
+ Type::Ref(ty) => {
+ if ty.mutable && !ty.pinned {
+ if let Type::Ident(ident) = &ty.inner {
+ let reason = TrivialReason::UnpinnedMut(efn);
+ insist_extern_types_are_trivial(ident, reason);
+ }
+ }
+ }
+ _ => {}
+ }
+ }
+ if let Some(ret) = &efn.ret {
+ match ret {
+ Type::Ident(ident) => {
+ let reason = TrivialReason::FunctionReturn(efn);
+ insist_extern_types_are_trivial(ident, reason);
+ }
+ Type::Ref(ty) => {
+ if ty.mutable && !ty.pinned {
+ if let Type::Ident(ident) = &ty.inner {
+ let reason = TrivialReason::UnpinnedMut(efn);
+ insist_extern_types_are_trivial(ident, reason);
+ }
+ }
+ }
+ _ => {}
+ }
+ }
+ }
+ _ => {}
+ }
+ }
+
+ for ty in all {
+ match ty {
+ Type::RustBox(ty) => {
+ if let Type::Ident(ident) = &ty.inner {
+ let reason = TrivialReason::BoxTarget;
+ insist_extern_types_are_trivial(ident, reason);
+ }
+ }
+ Type::RustVec(ty) => {
+ if let Type::Ident(ident) = &ty.inner {
+ let reason = TrivialReason::VecElement;
+ insist_extern_types_are_trivial(ident, reason);
+ }
+ }
+ _ => {}
+ }
+ }
+
+ required_trivial
+}
+
+// Context:
+// "type {type} should be trivially move constructible and trivially destructible in C++ to be used as {what} in Rust"
+// "needs a cxx::ExternType impl in order to be used as {what}"
+pub fn as_what<'a>(name: &'a Pair, reasons: &'a [TrivialReason]) -> impl Display + 'a {
+ struct Description<'a> {
+ name: &'a Pair,
+ reasons: &'a [TrivialReason<'a>],
+ }
+
+ impl<'a> Display for Description<'a> {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ let mut field_of = Set::new();
+ let mut argument_of = Set::new();
+ let mut return_of = Set::new();
+ let mut box_target = false;
+ let mut vec_element = false;
+ let mut unpinned_mut = Set::new();
+
+ for reason in self.reasons {
+ match reason {
+ TrivialReason::StructField(strct) => {
+ field_of.insert(&strct.name.rust);
+ }
+ TrivialReason::FunctionArgument(efn) => {
+ argument_of.insert(&efn.name.rust);
+ }
+ TrivialReason::FunctionReturn(efn) => {
+ return_of.insert(&efn.name.rust);
+ }
+ TrivialReason::BoxTarget => box_target = true,
+ TrivialReason::VecElement => vec_element = true,
+ TrivialReason::UnpinnedMut(efn) => {
+ unpinned_mut.insert(&efn.name.rust);
+ }
+ }
+ }
+
+ let mut clauses = Vec::new();
+ if !field_of.is_empty() {
+ clauses.push(Clause::Set {
+ article: "a",
+ desc: "field of",
+ set: &field_of,
+ });
+ }
+ if !argument_of.is_empty() {
+ clauses.push(Clause::Set {
+ article: "an",
+ desc: "argument of",
+ set: &argument_of,
+ });
+ }
+ if !return_of.is_empty() {
+ clauses.push(Clause::Set {
+ article: "a",
+ desc: "return value of",
+ set: &return_of,
+ });
+ }
+ if box_target {
+ clauses.push(Clause::Ty1 {
+ article: "type",
+ desc: "Box",
+ param: self.name,
+ });
+ }
+ if vec_element {
+ clauses.push(Clause::Ty1 {
+ article: "a",
+ desc: "vector element in Vec",
+ param: self.name,
+ });
+ }
+ if !unpinned_mut.is_empty() {
+ clauses.push(Clause::Set {
+ article: "a",
+ desc: "non-pinned mutable reference in signature of",
+ set: &unpinned_mut,
+ });
+ }
+
+ for (i, clause) in clauses.iter().enumerate() {
+ if i == 0 {
+ write!(f, "{} ", clause.article())?;
+ } else if i + 1 < clauses.len() {
+ write!(f, ", ")?;
+ } else {
+ write!(f, " or ")?;
+ }
+ clause.fmt(f)?;
+ }
+
+ Ok(())
+ }
+ }
+
+ enum Clause<'a> {
+ Set {
+ article: &'a str,
+ desc: &'a str,
+ set: &'a Set<&'a Ident>,
+ },
+ Ty1 {
+ article: &'a str,
+ desc: &'a str,
+ param: &'a Pair,
+ },
+ }
+
+ impl<'a> Clause<'a> {
+ fn article(&self) -> &'a str {
+ match self {
+ Clause::Set { article, .. } | Clause::Ty1 { article, .. } => article,
+ }
+ }
+
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ Clause::Set {
+ article: _,
+ desc,
+ set,
+ } => {
+ write!(f, "{} ", desc)?;
+ for (i, ident) in set.iter().take(3).enumerate() {
+ if i > 0 {
+ write!(f, ", ")?;
+ }
+ write!(f, "`{}`", ident)?;
+ }
+ Ok(())
+ }
+ Clause::Ty1 {
+ article: _,
+ desc,
+ param,
+ } => write!(f, "{}<{}>", desc, param.rust),
+ }
+ }
+ }
+
+ Description { name, reasons }
+}
diff --git a/syntax/types.rs b/syntax/types.rs
index 90a8221f..af7916d7 100644
--- a/syntax/types.rs
+++ b/syntax/types.rs
@@ -1,67 +1,61 @@
-use crate::syntax::atom::Atom::{self, *};
use crate::syntax::improper::ImproperCtype;
+use crate::syntax::instantiate::ImplKey;
+use crate::syntax::map::{OrderedMap, UnorderedMap};
use crate::syntax::report::Errors;
-use crate::syntax::set::{OrderedSet as Set, UnorderedSet};
+use crate::syntax::resolve::Resolution;
+use crate::syntax::set::{OrderedSet, UnorderedSet};
+use crate::syntax::trivial::{self, TrivialReason};
+use crate::syntax::visit::{self, Visit};
use crate::syntax::{
- toposort, Api, Derive, Enum, ExternFn, ExternType, Impl, Pair, ResolvableName, Struct, Type,
- TypeAlias,
+ toposort, Api, Atom, Enum, ExternType, Impl, Lifetimes, Pair, Struct, Type, TypeAlias,
};
use proc_macro2::Ident;
use quote::ToTokens;
-use std::collections::BTreeMap as Map;
pub struct Types<'a> {
- pub all: Set<&'a Type>,
- pub structs: Map<&'a Ident, &'a Struct>,
- pub enums: Map<&'a Ident, &'a Enum>,
- pub cxx: Set<&'a Ident>,
- pub rust: Set<&'a Ident>,
- pub aliases: Map<&'a Ident, &'a TypeAlias>,
- pub untrusted: Map<&'a Ident, &'a ExternType>,
- pub required_trivial: Map<&'a Ident, TrivialReason<'a>>,
- pub explicit_impls: Set<&'a Impl>,
- pub resolutions: Map<&'a Ident, &'a Pair>,
+ pub all: OrderedSet<&'a Type>,
+ pub structs: UnorderedMap<&'a Ident, &'a Struct>,
+ pub enums: UnorderedMap<&'a Ident, &'a Enum>,
+ pub cxx: UnorderedSet<&'a Ident>,
+ pub rust: UnorderedSet<&'a Ident>,
+ pub aliases: UnorderedMap<&'a Ident, &'a TypeAlias>,
+ pub untrusted: UnorderedMap<&'a Ident, &'a ExternType>,
+ pub required_trivial: UnorderedMap<&'a Ident, Vec<TrivialReason<'a>>>,
+ pub impls: OrderedMap<ImplKey<'a>, Option<&'a Impl>>,
+ pub resolutions: UnorderedMap<&'a Ident, Resolution<'a>>,
pub struct_improper_ctypes: UnorderedSet<&'a Ident>,
pub toposorted_structs: Vec<&'a Struct>,
}
impl<'a> Types<'a> {
pub fn collect(cx: &mut Errors, apis: &'a [Api]) -> Self {
- let mut all = Set::new();
- let mut structs = Map::new();
- let mut enums = Map::new();
- let mut cxx = Set::new();
- let mut rust = Set::new();
- let mut aliases = Map::new();
- let mut untrusted = Map::new();
- let mut explicit_impls = Set::new();
- let mut resolutions = Map::new();
+ let mut all = OrderedSet::new();
+ let mut structs = UnorderedMap::new();
+ let mut enums = UnorderedMap::new();
+ let mut cxx = UnorderedSet::new();
+ let mut rust = UnorderedSet::new();
+ let mut aliases = UnorderedMap::new();
+ let mut untrusted = UnorderedMap::new();
+ let mut impls = OrderedMap::new();
+ let mut resolutions = UnorderedMap::new();
let struct_improper_ctypes = UnorderedSet::new();
let toposorted_structs = Vec::new();
- fn visit<'a>(all: &mut Set<&'a Type>, ty: &'a Type) {
- all.insert(ty);
- match ty {
- Type::Ident(_) | Type::Str(_) | Type::Void(_) | Type::SliceRefU8(_) => {}
- Type::RustBox(ty)
- | Type::UniquePtr(ty)
- | Type::CxxVector(ty)
- | Type::RustVec(ty) => visit(all, &ty.inner),
- Type::Ref(r) => visit(all, &r.inner),
- Type::Slice(s) => visit(all, &s.inner),
- Type::Fn(f) => {
- if let Some(ret) = &f.ret {
- visit(all, ret);
- }
- for arg in &f.args {
- visit(all, &arg.ty);
- }
+ fn visit<'a>(all: &mut OrderedSet<&'a Type>, ty: &'a Type) {
+ struct CollectTypes<'s, 'a>(&'s mut OrderedSet<&'a Type>);
+
+ impl<'s, 'a> Visit<'a> for CollectTypes<'s, 'a> {
+ fn visit_type(&mut self, ty: &'a Type) {
+ self.0.insert(ty);
+ visit::visit_type(self, ty);
}
}
+
+ CollectTypes(all).visit_type(ty);
}
- let mut add_resolution = |pair: &'a Pair| {
- resolutions.insert(&pair.rust, pair);
+ let mut add_resolution = |name: &'a Pair, generics: &'a Lifetimes| {
+ resolutions.insert(&name.rust, Resolution { name, generics });
};
let mut type_names = UnorderedSet::new();
@@ -91,9 +85,10 @@ impl<'a> Types<'a> {
for field in &strct.fields {
visit(&mut all, &field.ty);
}
- add_resolution(&strct.name);
+ add_resolution(&strct.name, &strct.generics);
}
Api::Enum(enm) => {
+ all.insert(&enm.repr_type);
let ident = &enm.name.rust;
if !type_names.insert(ident)
&& (!cxx.contains(ident)
@@ -106,7 +101,7 @@ impl<'a> Types<'a> {
duplicate_name(cx, enm, ident);
}
enums.insert(ident, enm);
- add_resolution(&enm.name);
+ add_resolution(&enm.name, &enm.generics);
}
Api::CxxType(ety) => {
let ident = &ety.name.rust;
@@ -123,7 +118,7 @@ impl<'a> Types<'a> {
if !ety.trusted {
untrusted.insert(ident, ety);
}
- add_resolution(&ety.name);
+ add_resolution(&ety.name, &ety.generics);
}
Api::RustType(ety) => {
let ident = &ety.name.rust;
@@ -131,7 +126,7 @@ impl<'a> Types<'a> {
duplicate_name(cx, ety, ident);
}
rust.insert(ident);
- add_resolution(&ety.name);
+ add_resolution(&ety.name, &ety.generics);
}
Api::CxxFunction(efn) | Api::RustFunction(efn) => {
// Note: duplication of the C++ name is fine because C++ has
@@ -153,12 +148,34 @@ impl<'a> Types<'a> {
}
cxx.insert(ident);
aliases.insert(ident, alias);
- add_resolution(&alias.name);
+ add_resolution(&alias.name, &alias.generics);
}
Api::Impl(imp) => {
visit(&mut all, &imp.ty);
- explicit_impls.insert(imp);
+ if let Some(key) = imp.ty.impl_key() {
+ impls.insert(key, Some(imp));
+ }
+ }
+ }
+ }
+
+ for ty in &all {
+ let impl_key = match ty.impl_key() {
+ Some(impl_key) => impl_key,
+ None => continue,
+ };
+ let implicit_impl = match impl_key {
+ ImplKey::RustBox(ident)
+ | ImplKey::RustVec(ident)
+ | ImplKey::UniquePtr(ident)
+ | ImplKey::SharedPtr(ident)
+ | ImplKey::WeakPtr(ident)
+ | ImplKey::CxxVector(ident) => {
+ Atom::from(ident.rust).is_none() && !aliases.contains_key(ident.rust)
}
+ };
+ if implicit_impl && !impls.contains_key(&impl_key) {
+ impls.insert(impl_key, None);
}
}
@@ -166,35 +183,8 @@ impl<'a> Types<'a> {
// we check that this is permissible. We do this _after_ scanning all
// the APIs above, in case some function or struct references a type
// which is declared subsequently.
- let mut required_trivial = Map::new();
- let mut insist_alias_types_are_trivial = |ty: &'a Type, reason| {
- if let Type::Ident(ident) = ty {
- if cxx.contains(&ident.rust) {
- required_trivial.entry(&ident.rust).or_insert(reason);
- }
- }
- };
- for api in apis {
- match api {
- Api::Struct(strct) => {
- let reason = TrivialReason::StructField(strct);
- for field in &strct.fields {
- insist_alias_types_are_trivial(&field.ty, reason);
- }
- }
- Api::CxxFunction(efn) | Api::RustFunction(efn) => {
- let reason = TrivialReason::FunctionArgument(efn);
- for arg in &efn.args {
- insist_alias_types_are_trivial(&arg.ty, reason);
- }
- if let Some(ret) = &efn.ret {
- let reason = TrivialReason::FunctionReturn(efn);
- insist_alias_types_are_trivial(&ret, reason);
- }
- }
- _ => {}
- }
- }
+ let required_trivial =
+ trivial::required_trivial_reasons(apis, &all, &structs, &enums, &cxx);
let mut types = Types {
all,
@@ -205,7 +195,7 @@ impl<'a> Types<'a> {
aliases,
untrusted,
required_trivial,
- explicit_impls,
+ impls,
resolutions,
struct_improper_ctypes,
toposorted_structs,
@@ -213,7 +203,7 @@ impl<'a> Types<'a> {
types.toposorted_structs = toposort::sort(cx, apis, &types);
- let mut unresolved_structs: Vec<&Ident> = types.structs.keys().copied().collect();
+ let mut unresolved_structs = types.structs.keys();
let mut new_information = true;
while new_information {
new_information = false;
@@ -242,25 +232,10 @@ impl<'a> Types<'a> {
pub fn needs_indirect_abi(&self, ty: &Type) -> bool {
match ty {
- Type::Ident(ident) => {
- if let Some(strct) = self.structs.get(&ident.rust) {
- !self.is_pod(strct)
- } else {
- Atom::from(&ident.rust) == Some(RustString)
- }
- }
- Type::RustVec(_) => true,
- _ => false,
- }
- }
-
- pub fn is_pod(&self, strct: &Struct) -> bool {
- for derive in &strct.derives {
- if *derive == Derive::Copy {
- return true;
- }
+ Type::RustBox(_) | Type::UniquePtr(_) => false,
+ Type::Array(_) => true,
+ _ => !self.is_guaranteed_pod(ty),
}
- false
}
// Types that trigger rustc's default #[warn(improper_ctypes)] lint, even if
@@ -275,12 +250,6 @@ impl<'a> Types<'a> {
ImproperCtype::Depends(ident) => self.struct_improper_ctypes.contains(ident),
}
}
-
- pub fn resolve(&self, ident: &ResolvableName) -> &Pair {
- self.resolutions
- .get(&ident.rust)
- .expect("Unable to resolve type")
- }
}
impl<'t, 'a> IntoIterator for &'t Types<'a> {
@@ -291,13 +260,6 @@ impl<'t, 'a> IntoIterator for &'t Types<'a> {
}
}
-#[derive(Copy, Clone)]
-pub enum TrivialReason<'a> {
- StructField(&'a Struct),
- FunctionArgument(&'a ExternFn),
- FunctionReturn(&'a ExternFn),
-}
-
fn duplicate_name(cx: &mut Errors, sp: impl ToTokens, ident: &Ident) {
let msg = format!("the name `{}` is defined multiple times", ident);
cx.error(sp, msg);
diff --git a/syntax/visit.rs b/syntax/visit.rs
new file mode 100644
index 00000000..2f31378f
--- /dev/null
+++ b/syntax/visit.rs
@@ -0,0 +1,34 @@
+use crate::syntax::Type;
+
+pub trait Visit<'a> {
+ fn visit_type(&mut self, ty: &'a Type) {
+ visit_type(self, ty);
+ }
+}
+
+pub fn visit_type<'a, V>(visitor: &mut V, ty: &'a Type)
+where
+ V: Visit<'a> + ?Sized,
+{
+ match ty {
+ Type::Ident(_) | Type::Str(_) | Type::Void(_) => {}
+ Type::RustBox(ty)
+ | Type::UniquePtr(ty)
+ | Type::SharedPtr(ty)
+ | Type::WeakPtr(ty)
+ | Type::CxxVector(ty)
+ | Type::RustVec(ty) => visitor.visit_type(&ty.inner),
+ Type::Ref(r) => visitor.visit_type(&r.inner),
+ Type::Ptr(p) => visitor.visit_type(&p.inner),
+ Type::Array(a) => visitor.visit_type(&a.inner),
+ Type::SliceRef(s) => visitor.visit_type(&s.inner),
+ Type::Fn(fun) => {
+ if let Some(ret) = &fun.ret {
+ visitor.visit_type(ret);
+ }
+ for arg in &fun.args {
+ visitor.visit_type(&arg.ty);
+ }
+ }
+ }
+}
diff --git a/tests/BUCK b/tests/BUCK
index 5bbe5009..d131ddb9 100644
--- a/tests/BUCK
+++ b/tests/BUCK
@@ -12,7 +12,7 @@ rust_test(
rust_library(
name = "ffi",
srcs = [
- "ffi/extra.rs",
+ "ffi/cast.rs",
"ffi/lib.rs",
"ffi/module.rs",
],
@@ -28,11 +28,11 @@ cxx_library(
srcs = [
"ffi/tests.cc",
":bridge/source",
- ":extra/source",
":module/source",
],
headers = {
"ffi/lib.rs.h": ":bridge/header",
+ "ffi/module.rs.h": ":module/header",
"ffi/tests.h": "ffi/tests.h",
},
deps = ["//:core"],
@@ -44,11 +44,6 @@ rust_cxx_bridge(
)
rust_cxx_bridge(
- name = "extra",
- src = "ffi/extra.rs",
-)
-
-rust_cxx_bridge(
name = "module",
src = "ffi/module.rs",
)
diff --git a/tests/BUILD b/tests/BUILD
index 57ffab99..7886e4fa 100644
--- a/tests/BUILD
+++ b/tests/BUILD
@@ -15,7 +15,7 @@ rust_test(
rust_library(
name = "cxx_test_suite",
srcs = [
- "ffi/extra.rs",
+ "ffi/cast.rs",
"ffi/lib.rs",
"ffi/module.rs",
],
@@ -30,12 +30,12 @@ cc_library(
srcs = [
"ffi/tests.cc",
":bridge/source",
- ":extra/source",
":module/source",
],
hdrs = ["ffi/tests.h"],
deps = [
":bridge/include",
+ ":module/include",
"//:core",
],
)
@@ -47,12 +47,6 @@ rust_cxx_bridge(
)
rust_cxx_bridge(
- name = "extra",
- src = "ffi/extra.rs",
- deps = [":impl"],
-)
-
-rust_cxx_bridge(
name = "module",
src = "ffi/module.rs",
deps = [":impl"],
diff --git a/tests/cxx_gen.rs b/tests/cxx_gen.rs
index 06d7bc7b..e91675d9 100644
--- a/tests/cxx_gen.rs
+++ b/tests/cxx_gen.rs
@@ -6,7 +6,7 @@ use std::str;
const BRIDGE0: &str = r#"
#[cxx::bridge]
mod ffi {
- extern "C" {
+ unsafe extern "C++" {
pub fn do_cpp_thing(foo: &str);
}
}
@@ -20,7 +20,7 @@ fn test_extern_c_function() {
let output = str::from_utf8(&generated.implementation).unwrap();
// To avoid continual breakage we won't test every byte.
// Let's look for the major features.
- assert!(output.contains("void cxxbridge05$do_cpp_thing(::rust::repr::PtrLen foo)"));
+ assert!(output.contains("void cxxbridge1$do_cpp_thing(::rust::Str foo)"));
}
#[test]
@@ -30,5 +30,5 @@ fn test_impl_annotation() {
let source = BRIDGE0.parse().unwrap();
let generated = generate_header_and_cc(source, &opt).unwrap();
let output = str::from_utf8(&generated.implementation).unwrap();
- assert!(output.contains("ANNOTATION void cxxbridge05$do_cpp_thing(::rust::repr::PtrLen foo)"));
+ assert!(output.contains("ANNOTATION void cxxbridge1$do_cpp_thing(::rust::Str foo)"));
}
diff --git a/tests/cxx_string.rs b/tests/cxx_string.rs
new file mode 100644
index 00000000..be693cd2
--- /dev/null
+++ b/tests/cxx_string.rs
@@ -0,0 +1,15 @@
+use cxx::{let_cxx_string, CxxString};
+
+#[test]
+fn test_async_cxx_string() {
+ async fn f() {
+ let_cxx_string!(s = "...");
+
+ async fn g(_: &CxxString) {}
+ g(&s).await;
+ }
+
+ // https://github.com/dtolnay/cxx/issues/693
+ fn assert_send(_: impl Send) {}
+ assert_send(f());
+}
diff --git a/tests/ffi/build.rs b/tests/ffi/build.rs
index 4b2cbdfe..86f8cd3a 100644
--- a/tests/ffi/build.rs
+++ b/tests/ffi/build.rs
@@ -6,9 +6,13 @@ fn main() {
}
CFG.include_prefix = "tests/ffi";
- let sources = vec!["lib.rs", "extra.rs", "module.rs"];
- cxx_build::bridges(sources)
- .file("tests.cc")
- .flag_if_supported(cxxbridge_flags::STD)
- .compile("cxx-test-suite");
+ let sources = vec!["lib.rs", "module.rs"];
+ let mut build = cxx_build::bridges(sources);
+ build.file("tests.cc");
+ build.flag_if_supported(cxxbridge_flags::STD);
+ build.warnings_into_errors(cfg!(deny_warnings));
+ if cfg!(not(target_env = "msvc")) {
+ build.define("CXX_TEST_INSTANTIATIONS", None);
+ }
+ build.compile("cxx-test-suite");
}
diff --git a/tests/ffi/cast.rs b/tests/ffi/cast.rs
new file mode 100644
index 00000000..30ecb6a8
--- /dev/null
+++ b/tests/ffi/cast.rs
@@ -0,0 +1,14 @@
+use std::os::raw::c_char;
+use std::slice;
+
+pub fn c_char_to_unsigned(slice: &[c_char]) -> &[u8] {
+ let ptr = slice.as_ptr().cast::<u8>();
+ let len = slice.len();
+ unsafe { slice::from_raw_parts(ptr, len) }
+}
+
+pub fn unsigned_to_c_char(slice: &[u8]) -> &[c_char] {
+ let ptr = slice.as_ptr().cast::<c_char>();
+ let len = slice.len();
+ unsafe { slice::from_raw_parts(ptr, len) }
+}
diff --git a/tests/ffi/extra.rs b/tests/ffi/extra.rs
deleted file mode 100644
index cd76a7d7..00000000
--- a/tests/ffi/extra.rs
+++ /dev/null
@@ -1,63 +0,0 @@
-// Separate mod so that &self in the lib.rs mod has an unambiguous receiver. At
-// the moment, the cxx C++ codegen can't convert more than one cxx::bridge mod
-// per file, so that's why we need to put this outside of lib.rs. All of this
-// could go into module.rs instead, but for now its purpose is narrowly scoped
-// for testing aliasing between cxx::bridge mods, so we'll keep it that way and
-// start a new mod here.
-
-// Rustfmt mangles the extern type alias.
-// https://github.com/rust-lang/rustfmt/issues/4159
-#[rustfmt::skip]
-#[cxx::bridge(namespace = "tests")]
-pub mod ffi2 {
- impl UniquePtr<D> {}
- impl UniquePtr<E> {}
- impl UniquePtr<F> {}
- impl UniquePtr<G> {}
-
- extern "C" {
- include!("tests/ffi/tests.h");
-
- type D = crate::other::D;
- type E = crate::other::E;
- #[namespace = "F"]
- type F = crate::other::f::F;
- #[namespace = "G"]
- type G = crate::other::G;
-
- #[namespace = "H"]
- type H;
-
- fn c_take_trivial_ptr(d: UniquePtr<D>);
- fn c_take_trivial_ref(d: &D);
- fn c_take_trivial(d: D);
- fn c_take_trivial_ns_ptr(g: UniquePtr<G>);
- fn c_take_trivial_ns_ref(g: &G);
- fn c_take_trivial_ns(g: G);
- fn c_take_opaque_ptr(e: UniquePtr<E>);
- fn c_take_opaque_ref(e: &E);
- fn c_take_opaque_ns_ptr(e: UniquePtr<F>);
- fn c_take_opaque_ns_ref(e: &F);
- fn c_return_trivial_ptr() -> UniquePtr<D>;
- fn c_return_trivial() -> D;
- fn c_return_trivial_ns_ptr() -> UniquePtr<G>;
- fn c_return_trivial_ns() -> G;
- fn c_return_opaque_ptr() -> UniquePtr<E>;
- fn c_return_ns_opaque_ptr() -> UniquePtr<F>;
- fn c_return_ns_unique_ptr() -> UniquePtr<H>;
- fn c_take_ref_ns_c(h: &H);
-
- #[namespace = "other"]
- fn ns_c_take_trivial(d: D);
- #[namespace = "other"]
- fn ns_c_return_trivial() -> D;
-
- #[namespace = "I"]
- type I;
-
- fn get(self: &I) -> u32;
-
- #[namespace = "I"]
- fn ns_c_return_unique_ptr_ns() -> UniquePtr<I>;
- }
-}
diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs
index 4c7cbe4d..fcbe1530 100644
--- a/tests/ffi/lib.rs
+++ b/tests/ffi/lib.rs
@@ -1,87 +1,47 @@
#![allow(
clippy::boxed_local,
clippy::just_underscores_and_digits,
+ clippy::let_underscore_drop,
+ clippy::missing_safety_doc,
+ clippy::must_use_candidate,
+ clippy::needless_lifetimes,
+ clippy::needless_pass_by_value,
clippy::ptr_arg,
- clippy::trivially_copy_pass_by_ref
+ clippy::trivially_copy_pass_by_ref,
+ clippy::unnecessary_wraps,
+ clippy::unused_self
)]
-pub mod extra;
+pub mod cast;
pub mod module;
-use cxx::{CxxString, CxxVector, UniquePtr};
+use cxx::{CxxString, CxxVector, SharedPtr, UniquePtr};
use std::fmt::{self, Display};
-
-mod other {
- use cxx::kind::{Opaque, Trivial};
- use cxx::{type_id, CxxString, ExternType};
-
- #[repr(C)]
- pub struct D {
- pub d: u64,
- }
-
- #[repr(C)]
- pub struct E {
- e: u64,
- e_str: CxxString,
- }
-
- pub mod f {
- use cxx::kind::Opaque;
- use cxx::{type_id, CxxString, ExternType};
-
- #[repr(C)]
- pub struct F {
- e: u64,
- e_str: CxxString,
- }
-
- unsafe impl ExternType for F {
- type Id = type_id!("F::F");
- type Kind = Opaque;
- }
- }
-
- #[repr(C)]
- pub struct G {
- pub g: u64,
- }
-
- unsafe impl ExternType for G {
- type Id = type_id!("G::G");
- type Kind = Trivial;
- }
-
- unsafe impl ExternType for D {
- type Id = type_id!("tests::D");
- type Kind = Trivial;
- }
-
- unsafe impl ExternType for E {
- type Id = type_id!("tests::E");
- type Kind = Opaque;
- }
-}
+use std::mem::MaybeUninit;
+use std::os::raw::c_char;
#[cxx::bridge(namespace = "tests")]
pub mod ffi {
- #[derive(Clone)]
+ #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
struct Shared {
z: usize,
}
+ #[derive(PartialEq, PartialOrd)]
struct SharedString {
msg: String,
}
+ #[derive(Debug, Hash, PartialOrd, Ord)]
enum Enum {
AVal,
BVal = 2020,
- CVal,
+ #[cxx_name = "CVal"]
+ LastVal,
}
#[namespace = "A"]
- #[derive(Clone)]
+ #[derive(Copy, Clone, Default)]
struct AShared {
z: usize,
}
@@ -112,11 +72,22 @@ pub mod ffi {
}
#[namespace = "second"]
+ #[derive(Hash)]
struct Second {
i: i32,
+ e: COwnedEnum,
}
- extern "C" {
+ pub struct Array {
+ a: [i32; 4],
+ }
+
+ #[derive(Copy, Clone, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
+ pub struct StructWithLifetime<'a> {
+ s: &'a str,
+ }
+
+ unsafe extern "C++" {
include!("tests/ffi/tests.h");
type C;
@@ -125,10 +96,12 @@ pub mod ffi {
fn c_return_shared() -> Shared;
fn c_return_box() -> Box<R>;
fn c_return_unique_ptr() -> UniquePtr<C>;
+ fn c_return_shared_ptr() -> SharedPtr<C>;
fn c_return_ref(shared: &Shared) -> &usize;
fn c_return_mut(shared: &mut Shared) -> &mut usize;
fn c_return_str(shared: &Shared) -> &str;
- fn c_return_sliceu8(shared: &Shared) -> &[u8];
+ fn c_return_slice_char(shared: &Shared) -> &[c_char];
+ fn c_return_mutsliceu8(slice: &mut [u8]) -> &mut [u8];
fn c_return_rust_string() -> String;
fn c_return_unique_ptr_string() -> UniquePtr<CxxString>;
fn c_return_unique_ptr_vector_u8() -> UniquePtr<CxxVector<u8>>;
@@ -137,10 +110,10 @@ pub mod ffi {
fn c_return_unique_ptr_vector_shared() -> UniquePtr<CxxVector<Shared>>;
fn c_return_unique_ptr_vector_opaque() -> UniquePtr<CxxVector<C>>;
fn c_return_ref_vector(c: &C) -> &CxxVector<u8>;
- fn c_return_mut_vector(c: &mut C) -> &mut CxxVector<u8>;
+ fn c_return_mut_vector(c: Pin<&mut C>) -> Pin<&mut CxxVector<u8>>;
fn c_return_rust_vec() -> Vec<u8>;
fn c_return_ref_rust_vec(c: &C) -> &Vec<u8>;
- fn c_return_mut_rust_vec(c: &mut C) -> &mut Vec<u8>;
+ fn c_return_mut_rust_vec(c: Pin<&mut C>) -> &mut Vec<u8>;
fn c_return_rust_vec_string() -> Vec<String>;
fn c_return_identity(_: usize) -> usize;
fn c_return_sum(_: usize, _: usize) -> usize;
@@ -149,6 +122,8 @@ pub mod ffi {
fn c_return_nested_ns_ref(shared: &ABShared) -> &usize;
fn c_return_ns_enum(n: u16) -> AEnum;
fn c_return_nested_ns_enum(n: u16) -> ABEnum;
+ fn c_return_const_ptr(n: usize) -> *const C;
+ fn c_return_mut_ptr(n: usize) -> *mut C;
fn c_take_primitive(n: usize);
fn c_take_shared(shared: Shared);
@@ -156,7 +131,11 @@ pub mod ffi {
fn c_take_ref_r(r: &R);
fn c_take_ref_c(c: &C);
fn c_take_str(s: &str);
- fn c_take_sliceu8(s: &[u8]);
+ fn c_take_slice_char(s: &[c_char]);
+ fn c_take_slice_shared(s: &[Shared]);
+ fn c_take_slice_shared_sort(s: &mut [Shared]);
+ fn c_take_slice_r(s: &[R]);
+ fn c_take_slice_r_sort(s: &mut [R]);
fn c_take_rust_string(s: String);
fn c_take_unique_ptr_string(s: UniquePtr<CxxString>);
fn c_take_unique_ptr_vector_u8(v: UniquePtr<CxxVector<u8>>);
@@ -171,6 +150,7 @@ pub mod ffi {
fn c_take_rust_vec_shared_index(v: Vec<Shared>);
fn c_take_rust_vec_shared_push(v: Vec<Shared>);
fn c_take_rust_vec_shared_forward_iterator(v: Vec<Shared>);
+ fn c_take_rust_vec_shared_sort(v: Vec<Shared>);
fn c_take_ref_rust_vec(v: &Vec<u8>);
fn c_take_ref_rust_vec_string(v: &Vec<String>);
fn c_take_ref_rust_vec_index(v: &Vec<u8>);
@@ -184,6 +164,8 @@ pub mod ffi {
fn c_take_nested_ns_shared(shared: ABShared);
fn c_take_rust_vec_ns_shared(v: Vec<AShared>);
fn c_take_rust_vec_nested_ns_shared(v: Vec<ABShared>);
+ unsafe fn c_take_const_ptr(c: *const C) -> usize;
+ unsafe fn c_take_mut_ptr(c: *mut C) -> usize;
fn c_try_return_void() -> Result<()>;
fn c_try_return_primitive() -> Result<usize>;
@@ -192,6 +174,7 @@ pub mod ffi {
fn c_try_return_ref(s: &String) -> Result<&String>;
fn c_try_return_str(s: &str) -> Result<&str>;
fn c_try_return_sliceu8(s: &[u8]) -> Result<&[u8]>;
+ fn c_try_return_mutsliceu8(s: &mut [u8]) -> Result<&mut [u8]>;
fn c_try_return_rust_string() -> Result<String>;
fn c_try_return_unique_ptr_string() -> Result<UniquePtr<CxxString>>;
fn c_try_return_rust_vec() -> Result<Vec<u8>>;
@@ -199,12 +182,18 @@ pub mod ffi {
fn c_try_return_ref_rust_vec(c: &C) -> Result<&Vec<u8>>;
fn get(self: &C) -> usize;
- fn set(self: &mut C, n: usize) -> usize;
+ fn set(self: Pin<&mut C>, n: usize) -> usize;
fn get2(&self) -> usize;
- fn set2(&mut self, n: usize) -> usize;
- fn set_succeed(&mut self, n: usize) -> Result<usize>;
- fn get_fail(&mut self) -> Result<usize>;
+ fn getRef(self: &C) -> &usize;
+ fn getMut(self: Pin<&mut C>) -> &mut usize;
+ fn set_succeed(self: Pin<&mut C>, n: usize) -> Result<usize>;
+ fn get_fail(self: Pin<&mut C>) -> Result<usize>;
fn c_method_on_shared(self: &Shared) -> usize;
+ fn c_method_ref_on_shared(self: &Shared) -> &usize;
+ fn c_method_mut_on_shared(self: &mut Shared) -> &mut usize;
+ fn c_set_array(self: &mut Array, value: i32);
+
+ fn c_get_use_count(weak: &WeakPtr<C>) -> usize;
#[rust_name = "i32_overloaded_method"]
fn cOverloadedMethod(&self, x: i32) -> String;
@@ -219,31 +208,57 @@ pub mod ffi {
fn ns_c_take_ns_shared(shared: AShared);
}
- extern "C" {
+ extern "C++" {
+ include!("tests/ffi/module.rs.h");
+
type COwnedEnum;
+ type Job = crate::module::ffi::Job;
+ }
+
+ extern "Rust" {
+ #[derive(ExternType)]
+ type Reference<'a>;
+ }
+
+ unsafe extern "C++" {
+ type Borrow<'a>;
+
+ fn c_return_borrow<'a>(s: &'a CxxString) -> UniquePtr<Borrow<'a>>;
+
+ #[rust_name = "c_return_borrow_elided"]
+ fn c_return_borrow(s: &CxxString) -> UniquePtr<Borrow>;
+
+ fn const_member(self: &Borrow);
+ fn nonconst_member(self: Pin<&mut Borrow>);
}
#[repr(u32)]
+ #[derive(Hash)]
enum COwnedEnum {
+ #[cxx_name = "CVAL1"]
CVal1,
+ #[cxx_name = "CVAL2"]
CVal2,
}
extern "Rust" {
type R;
- type R2;
fn r_return_primitive() -> usize;
fn r_return_shared() -> Shared;
fn r_return_box() -> Box<R>;
fn r_return_unique_ptr() -> UniquePtr<C>;
+ fn r_return_shared_ptr() -> SharedPtr<C>;
fn r_return_ref(shared: &Shared) -> &usize;
fn r_return_mut(shared: &mut Shared) -> &mut usize;
fn r_return_str(shared: &Shared) -> &str;
+ fn r_return_sliceu8(shared: &Shared) -> &[u8];
+ fn r_return_mutsliceu8(slice: &mut [u8]) -> &mut [u8];
fn r_return_rust_string() -> String;
fn r_return_unique_ptr_string() -> UniquePtr<CxxString>;
fn r_return_rust_vec() -> Vec<u8>;
fn r_return_rust_vec_string() -> Vec<String>;
+ fn r_return_rust_vec_extern_struct() -> Vec<Job>;
fn r_return_ref_rust_vec(shared: &Shared) -> &Vec<u8>;
fn r_return_mut_rust_vec(shared: &mut Shared) -> &mut Vec<u8>;
fn r_return_identity(_: usize) -> usize;
@@ -254,10 +269,11 @@ pub mod ffi {
fn r_take_shared(shared: Shared);
fn r_take_box(r: Box<R>);
fn r_take_unique_ptr(c: UniquePtr<C>);
+ fn r_take_shared_ptr(c: SharedPtr<C>);
fn r_take_ref_r(r: &R);
fn r_take_ref_c(c: &C);
fn r_take_str(s: &str);
- fn r_take_sliceu8(s: &[u8]);
+ fn r_take_slice_char(s: &[c_char]);
fn r_take_rust_string(s: String);
fn r_take_unique_ptr_string(s: UniquePtr<CxxString>);
fn r_take_ref_vector(v: &CxxVector<u8>);
@@ -272,11 +288,13 @@ pub mod ffi {
fn r_try_return_primitive() -> Result<usize>;
fn r_try_return_box() -> Result<Box<R>>;
fn r_fail_return_primitive() -> Result<usize>;
+ fn r_try_return_sliceu8(s: &[u8]) -> Result<&[u8]>;
+ fn r_try_return_mutsliceu8(s: &mut [u8]) -> Result<&mut [u8]>;
- fn r_return_r2(n: usize) -> Box<R2>;
- fn get(self: &R2) -> usize;
- fn set(self: &mut R2, n: usize) -> usize;
+ fn get(self: &R) -> usize;
+ fn set(self: &mut R, n: usize) -> usize;
fn r_method_on_shared(self: &Shared) -> String;
+ fn r_get_array_sum(self: &Array) -> i32;
#[cxx_name = "rAliasedFunction"]
fn r_aliased_function(x: i32) -> String;
@@ -302,13 +320,66 @@ pub mod ffi {
struct Dag4 {
dag0: Dag0,
}
+
+ impl Box<Shared> {}
}
-pub type R = usize;
+mod other {
+ use cxx::kind::{Opaque, Trivial};
+ use cxx::{type_id, CxxString, ExternType};
+
+ #[repr(C)]
+ pub struct D {
+ pub d: u64,
+ }
+
+ #[repr(C)]
+ pub struct E {
+ e: u64,
+ e_str: CxxString,
+ }
+
+ pub mod f {
+ use cxx::kind::Opaque;
+ use cxx::{type_id, CxxString, ExternType};
+
+ #[repr(C)]
+ pub struct F {
+ e: u64,
+ e_str: CxxString,
+ }
+
+ unsafe impl ExternType for F {
+ type Id = type_id!("F::F");
+ type Kind = Opaque;
+ }
+ }
+
+ #[repr(C)]
+ pub struct G {
+ pub g: u64,
+ }
+
+ unsafe impl ExternType for G {
+ type Id = type_id!("G::G");
+ type Kind = Trivial;
+ }
+
+ unsafe impl ExternType for D {
+ type Id = type_id!("tests::D");
+ type Kind = Trivial;
+ }
+
+ unsafe impl ExternType for E {
+ type Id = type_id!("tests::E");
+ type Kind = Opaque;
+ }
+}
-pub struct R2(usize);
+#[derive(PartialEq, Debug)]
+pub struct R(pub usize);
-impl R2 {
+impl R {
fn get(&self) -> usize {
self.0
}
@@ -319,12 +390,20 @@ impl R2 {
}
}
+pub struct Reference<'a>(&'a String);
+
impl ffi::Shared {
fn r_method_on_shared(&self) -> String {
"2020".to_owned()
}
}
+impl ffi::Array {
+ pub fn r_get_array_sum(&self) -> i32 {
+ self.a.iter().sum()
+ }
+}
+
#[derive(Debug)]
struct Error;
@@ -345,7 +424,7 @@ fn r_return_shared() -> ffi::Shared {
}
fn r_return_box() -> Box<R> {
- Box::new(2020)
+ Box::new(R(2020))
}
fn r_return_unique_ptr() -> UniquePtr<ffi::C> {
@@ -355,6 +434,18 @@ fn r_return_unique_ptr() -> UniquePtr<ffi::C> {
unsafe { UniquePtr::from_raw(cxx_test_suite_get_unique_ptr()) }
}
+fn r_return_shared_ptr() -> SharedPtr<ffi::C> {
+ extern "C" {
+ fn cxx_test_suite_get_shared_ptr(repr: *mut SharedPtr<ffi::C>);
+ }
+ let mut shared_ptr = MaybeUninit::<SharedPtr<ffi::C>>::uninit();
+ let repr = shared_ptr.as_mut_ptr();
+ unsafe {
+ cxx_test_suite_get_shared_ptr(repr);
+ shared_ptr.assume_init()
+ }
+}
+
fn r_return_ref(shared: &ffi::Shared) -> &usize {
&shared.z
}
@@ -368,6 +459,15 @@ fn r_return_str(shared: &ffi::Shared) -> &str {
"2020"
}
+fn r_return_sliceu8(shared: &ffi::Shared) -> &[u8] {
+ let _ = shared;
+ b"2020"
+}
+
+fn r_return_mutsliceu8(slice: &mut [u8]) -> &mut [u8] {
+ slice
+}
+
fn r_return_rust_string() -> String {
"2020".to_owned()
}
@@ -387,6 +487,10 @@ fn r_return_rust_vec_string() -> Vec<String> {
Vec::new()
}
+fn r_return_rust_vec_extern_struct() -> Vec<ffi::Job> {
+ Vec::new()
+}
+
fn r_return_ref_rust_vec(shared: &ffi::Shared) -> &Vec<u8> {
let _ = shared;
unimplemented!()
@@ -411,7 +515,7 @@ fn r_return_enum(n: u32) -> ffi::Enum {
} else if n <= 2020 {
ffi::Enum::BVal
} else {
- ffi::Enum::CVal
+ ffi::Enum::LastVal
}
}
@@ -431,6 +535,10 @@ fn r_take_unique_ptr(c: UniquePtr<ffi::C>) {
let _ = c;
}
+fn r_take_shared_ptr(c: SharedPtr<ffi::C>) {
+ let _ = c;
+}
+
fn r_take_ref_r(r: &R) {
let _ = r;
}
@@ -447,8 +555,9 @@ fn r_take_rust_string(s: String) {
assert_eq!(s, "2020");
}
-fn r_take_sliceu8(s: &[u8]) {
+fn r_take_slice_char(s: &[c_char]) {
assert_eq!(s.len(), 5);
+ let s = cast::c_char_to_unsigned(s);
assert_eq!(std::str::from_utf8(s).unwrap(), "2020\0");
}
@@ -495,15 +604,19 @@ fn r_try_return_primitive() -> Result<usize, Error> {
}
fn r_try_return_box() -> Result<Box<R>, Error> {
- Ok(Box::new(2020))
+ Ok(Box::new(R(2020)))
}
fn r_fail_return_primitive() -> Result<usize, Error> {
Err(Error)
}
-fn r_return_r2(n: usize) -> Box<R2> {
- Box::new(R2(n))
+fn r_try_return_sliceu8(slice: &[u8]) -> Result<&[u8], Error> {
+ Ok(slice)
+}
+
+fn r_try_return_mutsliceu8(slice: &mut [u8]) -> Result<&mut [u8], Error> {
+ Ok(slice)
}
fn r_aliased_function(x: i32) -> String {
diff --git a/tests/ffi/module.rs b/tests/ffi/module.rs
index 899d45b0..21a86206 100644
--- a/tests/ffi/module.rs
+++ b/tests/ffi/module.rs
@@ -1,13 +1,78 @@
-// Rustfmt mangles the extern type alias.
-// https://github.com/rust-lang/rustfmt/issues/4159
-#[rustfmt::skip]
#[cxx::bridge(namespace = "tests")]
pub mod ffi {
- extern "C" {
+ struct Job {
+ raw: u32,
+ }
+
+ unsafe extern "C++" {
include!("tests/ffi/tests.h");
type C = crate::ffi::C;
fn c_take_unique_ptr(c: UniquePtr<C>);
}
+
+ impl Vec<Job> {}
+}
+
+#[cxx::bridge(namespace = "tests")]
+pub mod ffi2 {
+ unsafe extern "C++" {
+ include!("tests/ffi/tests.h");
+
+ type D = crate::other::D;
+ type E = crate::other::E;
+ #[namespace = "F"]
+ type F = crate::other::f::F;
+ #[namespace = "G"]
+ type G = crate::other::G;
+
+ #[namespace = "H"]
+ type H;
+
+ fn c_take_trivial_ptr(d: UniquePtr<D>);
+ fn c_take_trivial_ref(d: &D);
+ fn c_take_trivial_mut_ref(d: &mut D);
+ fn c_take_trivial_pin_ref(d: Pin<&D>);
+ fn c_take_trivial_pin_mut_ref(d: Pin<&mut D>);
+ fn c_take_trivial_ref_method(self: &D);
+ fn c_take_trivial_mut_ref_method(self: &mut D);
+ fn c_take_trivial(d: D);
+ fn c_take_trivial_ns_ptr(g: UniquePtr<G>);
+ fn c_take_trivial_ns_ref(g: &G);
+ fn c_take_trivial_ns(g: G);
+ fn c_take_opaque_ptr(e: UniquePtr<E>);
+ fn c_take_opaque_ref(e: &E);
+ fn c_take_opaque_ref_method(self: &E);
+ fn c_take_opaque_mut_ref_method(self: Pin<&mut E>);
+ fn c_take_opaque_ns_ptr(e: UniquePtr<F>);
+ fn c_take_opaque_ns_ref(e: &F);
+ fn c_return_trivial_ptr() -> UniquePtr<D>;
+ fn c_return_trivial() -> D;
+ fn c_return_trivial_ns_ptr() -> UniquePtr<G>;
+ fn c_return_trivial_ns() -> G;
+ fn c_return_opaque_ptr() -> UniquePtr<E>;
+ fn c_return_opaque_mut_pin(e: Pin<&mut E>) -> Pin<&mut E>;
+ fn c_return_ns_opaque_ptr() -> UniquePtr<F>;
+ fn c_return_ns_unique_ptr() -> UniquePtr<H>;
+ fn c_take_ref_ns_c(h: &H);
+
+ #[namespace = "other"]
+ fn ns_c_take_trivial(d: D);
+ #[namespace = "other"]
+ fn ns_c_return_trivial() -> D;
+
+ #[namespace = "I"]
+ type I;
+
+ fn get(self: &I) -> u32;
+
+ #[namespace = "I"]
+ fn ns_c_return_unique_ptr_ns() -> UniquePtr<I>;
+ }
+
+ impl UniquePtr<D> {}
+ impl UniquePtr<E> {}
+ impl UniquePtr<F> {}
+ impl UniquePtr<G> {}
}
diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc
index 2305766c..ba12ed45 100644
--- a/tests/ffi/tests.cc
+++ b/tests/ffi/tests.cc
@@ -1,11 +1,13 @@
#include "tests/ffi/tests.h"
#include "tests/ffi/lib.rs.h"
+#include <cstdlib>
#include <cstring>
#include <iterator>
#include <memory>
#include <numeric>
#include <stdexcept>
#include <string>
+#include <tuple>
extern "C" void cxx_test_suite_set_correct() noexcept;
extern "C" tests::R *cxx_test_suite_get_box() noexcept;
@@ -21,22 +23,31 @@ size_t C::get() const { return this->n; }
size_t C::get2() const { return this->n; }
-size_t C::set(size_t n) {
- this->n = n;
- return this->n;
-}
+const size_t &C::getRef() const { return this->n; }
+
+size_t &C::getMut() { return this->n; }
-size_t C::set2(size_t n) {
+size_t C::set(size_t n) {
this->n = n;
return this->n;
}
-size_t C::set_succeed(size_t n) { return this->set2(n); }
+size_t C::set_succeed(size_t n) { return this->set(n); }
size_t C::get_fail() { throw std::runtime_error("unimplemented"); }
size_t Shared::c_method_on_shared() const noexcept { return 2021; }
+const size_t &Shared::c_method_ref_on_shared() const noexcept {
+ return this->z;
+}
+
+size_t &Shared::c_method_mut_on_shared() noexcept { return this->z; }
+
+void Array::c_set_array(int32_t val) noexcept {
+ this->a = {val, val, val, val};
+}
+
const std::vector<uint8_t> &C::get_v() const { return this->v; }
std::vector<uint8_t> &C::get_v() { return this->v; }
@@ -50,6 +61,14 @@ Shared c_return_shared() { return Shared{2020}; }
::A::B::ABShared c_return_nested_ns_shared() { return ::A::B::ABShared{2020}; }
rust::Box<R> c_return_box() {
+ Shared shared{0};
+ rust::Box<Shared> box{shared}; // explicit constructor from const T&
+ rust::Box<Shared> other{std::move(shared)}; // explicit constructor from T&&
+ box = std::move(other); // move assignment
+ rust::Box<Shared> box2(*box); // copy from another Box
+ rust::Box<Shared> other2(std::move(other)); // move constructor
+ rust::Box<Shared>::in_place(shared.z); // placement static factory
+ rust::Box<Shared>::in_place<size_t>(0);
return rust::Box<R>::from_raw(cxx_test_suite_get_box());
}
@@ -57,6 +76,10 @@ std::unique_ptr<C> c_return_unique_ptr() {
return std::unique_ptr<C>(new C{2020});
}
+std::shared_ptr<C> c_return_shared_ptr() {
+ return std::shared_ptr<C>(new C{2020});
+}
+
std::unique_ptr<::H::H> c_return_ns_unique_ptr() {
return std::unique_ptr<::H::H>(new ::H::H{"hello"});
}
@@ -76,10 +99,13 @@ rust::Str c_return_str(const Shared &shared) {
return "2020";
}
-rust::Slice<uint8_t> c_return_sliceu8(const Shared &shared) {
+rust::Slice<const char> c_return_slice_char(const Shared &shared) {
(void)shared;
- return rust::Slice<uint8_t>(reinterpret_cast<const uint8_t *>(SLICE_DATA),
- sizeof(SLICE_DATA));
+ return rust::Slice<const char>(SLICE_DATA, sizeof(SLICE_DATA));
+}
+
+rust::Slice<uint8_t> c_return_mutsliceu8(rust::Slice<uint8_t> slice) {
+ return slice;
}
rust::String c_return_rust_string() { return "2020"; }
@@ -129,7 +155,8 @@ const std::vector<uint8_t> &c_return_ref_vector(const C &c) {
std::vector<uint8_t> &c_return_mut_vector(C &c) { return c.get_v(); }
rust::Vec<uint8_t> c_return_rust_vec() {
- throw std::runtime_error("unimplemented");
+ rust::Vec<uint8_t> vec{2, 0, 2, 0};
+ return vec;
}
const rust::Vec<uint8_t> &c_return_ref_rust_vec(const C &c) {
@@ -143,7 +170,7 @@ rust::Vec<uint8_t> &c_return_mut_rust_vec(C &c) {
}
rust::Vec<rust::String> c_return_rust_vec_string() {
- throw std::runtime_error("unimplemented");
+ return {"2", "0", "2", "0"};
}
size_t c_return_identity(size_t n) { return n; }
@@ -180,6 +207,20 @@ Enum c_return_enum(uint16_t n) {
}
}
+const C *c_return_const_ptr(size_t c) { return new C(c); }
+
+C *c_return_mut_ptr(size_t c) { return new C(c); }
+
+Borrow::Borrow(const std::string &s) : s(s) {}
+
+void Borrow::const_member() const {}
+
+void Borrow::nonconst_member() {}
+
+std::unique_ptr<Borrow> c_return_borrow(const std::string &s) {
+ return std::unique_ptr<Borrow>(new Borrow(s));
+}
+
void c_take_primitive(size_t n) {
if (n == 2020) {
cxx_test_suite_set_correct();
@@ -240,9 +281,44 @@ void c_take_str(rust::Str s) {
}
}
-void c_take_sliceu8(rust::Slice<uint8_t> s) {
- if (std::string(reinterpret_cast<const char *>(s.data()), s.size()) ==
- "2020") {
+void c_take_slice_char(rust::Slice<const char> s) {
+ if (std::string(s.data(), s.size()) == "2020") {
+ cxx_test_suite_set_correct();
+ }
+}
+
+void c_take_slice_shared(rust::Slice<const Shared> s) {
+ if (s.size() == 2 && s.data()->z == 2020 && s[1].z == 2021 &&
+ s.at(1).z == 2021 && s.front().z == 2020 && s.back().z == 2021) {
+ cxx_test_suite_set_correct();
+ }
+}
+
+void c_take_slice_shared_sort(rust::Slice<Shared> s) {
+ // Exercise requirements of RandomAccessIterator.
+ // https://en.cppreference.com/w/cpp/named_req/RandomAccessIterator
+ std::sort(s.begin(), s.end());
+ if (s[0].z == 0 && s[1].z == 2 && s[2].z == 4 && s[3].z == 7) {
+ cxx_test_suite_set_correct();
+ }
+}
+
+void c_take_slice_r(rust::Slice<const R> s) {
+ if (s.size() == 3 && s[0].get() == 2020 && s[1].get() == 2050) {
+ cxx_test_suite_set_correct();
+ }
+}
+
+bool operator<(const R &a, const R &b) noexcept { return a.get() < b.get(); }
+
+void c_take_slice_r_sort(rust::Slice<R> s) {
+ std::qsort(s.data(), s.size(), rust::size_of<decltype(s)::value_type>(),
+ [](const void *fst, const void *snd) {
+ auto &a = *static_cast<const R *>(fst);
+ auto &b = *static_cast<const R *>(snd);
+ return a < b ? -1 : b < a ? 1 : 0;
+ });
+ if (s[0].get() == 2020 && s[1].get() == 2021 && s[2].get() == 2050) {
cxx_test_suite_set_correct();
}
}
@@ -340,11 +416,23 @@ void c_take_rust_vec_string(rust::Vec<rust::String> v) {
void c_take_rust_vec_shared_forward_iterator(rust::Vec<Shared> v) {
// Exercise requirements of ForwardIterator
// https://en.cppreference.com/w/cpp/named_req/ForwardIterator
- uint32_t sum = 0;
+ uint32_t sum = 0, csum = 0;
for (auto it = v.begin(), it_end = v.end(); it != it_end; it++) {
sum += it->z;
}
- if (sum == 2021) {
+ for (auto it = v.cbegin(), it_end = v.cend(); it != it_end; it++) {
+ csum += it->z;
+ }
+ if (sum == 2021 && csum == 2021) {
+ cxx_test_suite_set_correct();
+ }
+}
+
+void c_take_rust_vec_shared_sort(rust::Vec<Shared> v) {
+ // Exercise requirements of RandomAccessIterator.
+ // https://en.cppreference.com/w/cpp/named_req/RandomAccessIterator
+ std::sort(v.begin(), v.end());
+ if (v[0].z == 0 && v[1].z == 2 && v[2].z == 4 && v[3].z == 7) {
cxx_test_suite_set_correct();
}
}
@@ -424,6 +512,14 @@ void c_take_nested_ns_enum(::A::B::ABEnum e) {
}
}
+size_t c_take_const_ptr(const C *c) { return c->get(); }
+
+size_t c_take_mut_ptr(C *c) {
+ size_t result = c->get();
+ delete c;
+ return result;
+}
+
void c_try_return_void() {}
size_t c_try_return_primitive() { return 2020; }
@@ -436,7 +532,13 @@ const rust::String &c_try_return_ref(const rust::String &s) { return s; }
rust::Str c_try_return_str(rust::Str s) { return s; }
-rust::Slice<uint8_t> c_try_return_sliceu8(rust::Slice<uint8_t> s) { return s; }
+rust::Slice<const uint8_t> c_try_return_sliceu8(rust::Slice<const uint8_t> s) {
+ return s;
+}
+
+rust::Slice<uint8_t> c_try_return_mutsliceu8(rust::Slice<uint8_t> s) {
+ return s;
+}
rust::String c_try_return_rust_string() { return c_return_rust_string(); }
@@ -457,10 +559,19 @@ const rust::Vec<uint8_t> &c_try_return_ref_rust_vec(const C &c) {
throw std::runtime_error("unimplemented");
}
+size_t c_get_use_count(const std::weak_ptr<C> &weak) noexcept {
+ return weak.use_count();
+}
+
extern "C" C *cxx_test_suite_get_unique_ptr() noexcept {
return std::unique_ptr<C>(new C{2020}).release();
}
+extern "C" void
+cxx_test_suite_get_shared_ptr(std::shared_ptr<C> *repr) noexcept {
+ new (repr) std::shared_ptr<C>(new C{2020});
+}
+
extern "C" std::string *cxx_test_suite_get_unique_ptr_string() noexcept {
return std::unique_ptr<std::string>(new std::string("2020")).release();
}
@@ -493,6 +604,24 @@ void c_take_trivial_ref(const D &d) {
}
}
+void c_take_trivial_mut_ref(D &d) { (void)d; }
+
+void c_take_trivial_pin_ref(const D &d) { (void)d; }
+
+void c_take_trivial_pin_mut_ref(D &d) { (void)d; }
+
+void D::c_take_trivial_ref_method() const {
+ if (d == 30) {
+ cxx_test_suite_set_correct();
+ }
+}
+
+void D::c_take_trivial_mut_ref_method() {
+ if (d == 30) {
+ cxx_test_suite_set_correct();
+ }
+}
+
void c_take_trivial(D d) {
if (d.d == 30) {
cxx_test_suite_set_correct();
@@ -535,6 +664,18 @@ void c_take_opaque_ref(const E &e) {
}
}
+void E::c_take_opaque_ref_method() const {
+ if (e == 40 && e_str == "hello") {
+ cxx_test_suite_set_correct();
+ }
+}
+
+void E::c_take_opaque_mut_ref_method() {
+ if (e == 40 && e_str == "hello") {
+ cxx_test_suite_set_correct();
+ }
+}
+
void c_take_opaque_ns_ref(const ::F::F &f) {
if (f.f == 40 && f.f_str == "hello") {
cxx_test_suite_set_correct();
@@ -572,6 +713,8 @@ std::unique_ptr<E> c_return_opaque_ptr() {
return e;
}
+E &c_return_opaque_mut_pin(E &e) { return e; }
+
std::unique_ptr<::F::F> c_return_ns_opaque_ptr() {
auto f = std::unique_ptr<::F::F>(new ::F::F());
f->f = 40;
@@ -589,10 +732,16 @@ extern "C" const char *cxx_run_test() noexcept {
} \
} while (false)
+ ASSERT(rust::size_of<R>() == sizeof(size_t));
+ ASSERT(rust::align_of<R>() == alignof(size_t));
+ ASSERT(rust::size_of<size_t>() == sizeof(size_t));
+ ASSERT(rust::align_of<size_t>() == alignof(size_t));
+
ASSERT(r_return_primitive() == 2020);
ASSERT(r_return_shared().z == 2020);
ASSERT(cxx_test_suite_r_is_correct(&*r_return_box()));
ASSERT(r_return_unique_ptr()->get() == 2020);
+ ASSERT(r_return_shared_ptr()->get() == 2020);
ASSERT(r_return_ref(Shared{2020}) == 2020);
ASSERT(std::string(r_return_str(Shared{2020})) == "2020");
ASSERT(std::string(r_return_rust_string()) == "2020");
@@ -606,10 +755,10 @@ extern "C" const char *cxx_run_test() noexcept {
r_take_primitive(2020);
r_take_shared(Shared{2020});
r_take_unique_ptr(std::unique_ptr<C>(new C{2020}));
+ r_take_shared_ptr(std::shared_ptr<C>(new C{2020}));
r_take_ref_c(C{2020});
r_take_str(rust::Str("2020"));
- r_take_sliceu8(rust::Slice<uint8_t>(
- reinterpret_cast<const uint8_t *>(SLICE_DATA), sizeof(SLICE_DATA)));
+ r_take_slice_char(rust::Slice<const char>(SLICE_DATA, sizeof(SLICE_DATA)));
r_take_rust_string(rust::String("2020"));
r_take_unique_ptr_string(
std::unique_ptr<std::string>(new std::string("2020")));
@@ -628,16 +777,74 @@ extern "C" const char *cxx_run_test() noexcept {
ASSERT(std::strcmp(e.what(), "rust error") == 0);
}
- auto r2 = r_return_r2(2020);
- ASSERT(r2->get() == 2020);
- ASSERT(r2->set(2021) == 2021);
+ auto r = r_return_box();
+ ASSERT(r->get() == 2020);
+ ASSERT(r->set(2021) == 2021);
+ ASSERT(r->get() == 2021);
+
+ using std::swap;
+ auto r2 = r_return_box();
+ swap(r, r2);
+ ASSERT(r->get() == 2020);
ASSERT(r2->get() == 2021);
- ASSERT(r2->set(2020) == 2020);
- ASSERT(r2->get() == 2020);
+
ASSERT(std::string(Shared{0}.r_method_on_shared()) == "2020");
ASSERT(std::string(rAliasedFunction(2020)) == "2020");
+ ASSERT(Shared{1} == Shared{1});
+ ASSERT(Shared{1} != Shared{2});
+
+ rust::String first = "first", second = "second", sec = "sec";
+ bool (rust::String::*cmp)(const rust::String &) const;
+ bool first_first, first_second, sec_second, second_sec;
+ for (auto test : {
+ std::tuple<decltype(cmp), bool, bool, bool, bool>{
+ &rust::String::operator==, true, false, false, false},
+ {&rust::String::operator!=, false, true, true, true},
+ {&rust::String::operator<, false, true, true, false},
+ {&rust::String::operator<=, true, true, true, false},
+ {&rust::String::operator>, false, false, false, true},
+ {&rust::String::operator>=, true, false, false, true},
+ }) {
+ std::tie(cmp, first_first, first_second, sec_second, second_sec) = test;
+ ASSERT((first.*cmp)(first) == first_first);
+ ASSERT((first.*cmp)(second) == first_second);
+ ASSERT((sec.*cmp)(second) == sec_second);
+ ASSERT((second.*cmp)(sec) == second_sec);
+ }
+
+ rust::String cstring = "test";
+ ASSERT(cstring.length() == 4);
+ ASSERT(strncmp(cstring.data(), "test", 4) == 0);
+ ASSERT(strncmp(cstring.c_str(), "test", 5) == 0);
+ ASSERT(cstring.length() == 4);
+
+ rust::String other_cstring = "foo";
+ swap(cstring, other_cstring);
+ ASSERT(cstring == "foo");
+ ASSERT(other_cstring == "test");
+
+ rust::Str cstr = "test";
+ rust::Str other_cstr = "foo";
+ swap(cstr, other_cstr);
+ ASSERT(cstr == "foo");
+ ASSERT(other_cstr == "test");
+
+ rust::Vec<int> vec1{1, 2};
+ rust::Vec<int> vec2{3, 4};
+ swap(vec1, vec2);
+ ASSERT(vec1[0] == 3 && vec1[1] == 4);
+ ASSERT(vec2[0] == 1 && vec2[1] == 2);
+
+ // Test Vec<usize> and Vec<isize>. These are weird because on Linux and
+ // Windows size_t is exactly the same C++ type as one of the sized integer
+ // types (typically uint64_t, both of which are defined as unsigned long),
+ // while on macOS it is a distinct type.
+ // https://github.com/dtolnay/cxx/issues/705
+ (void)rust::Vec<size_t>();
+ (void)rust::Vec<rust::isize>();
+
cxx_test_suite_set_correct();
return nullptr;
}
@@ -671,3 +878,25 @@ std::unique_ptr<I> ns_c_return_unique_ptr_ns() {
return std::unique_ptr<I>(new I());
}
} // namespace I
+
+// Instantiate any remaining class member functions not already covered above.
+// This is an easy way to at least typecheck anything missed by unit tests.
+// https://en.cppreference.com/w/cpp/language/class_template#Explicit_instantiation
+// > When an explicit instantiation names a class template specialization, it
+// > serves as an explicit instantiation of the same kind (declaration or
+// > definition) of each of its non-inherited non-template members that has not
+// > been previously explicitly specialized in the translation unit.
+#if defined(CXX_TEST_INSTANTIATIONS)
+template class rust::Box<tests::Shared>;
+template class rust::Slice<const char>;
+template class rust::Slice<const uint8_t>;
+template class rust::Slice<uint8_t>;
+template class rust::Slice<const tests::Shared>;
+template class rust::Slice<tests::Shared>;
+template class rust::Slice<const tests::R>;
+template class rust::Slice<tests::R>;
+template class rust::Vec<uint8_t>;
+template class rust::Vec<rust::String>;
+template class rust::Vec<tests::Shared>;
+template class rust::Fn<size_t(rust::String)>;
+#endif
diff --git a/tests/ffi/tests.h b/tests/ffi/tests.h
index 2217c361..74acbf4f 100644
--- a/tests/ffi/tests.h
+++ b/tests/ffi/tests.h
@@ -45,7 +45,8 @@ public:
size_t get() const;
size_t set(size_t n);
size_t get2() const;
- size_t set2(size_t n);
+ const size_t &getRef() const;
+ size_t &getMut();
size_t set_succeed(size_t n);
size_t get_fail();
const std::vector<uint8_t> &get_v() const;
@@ -60,16 +61,27 @@ private:
struct D {
uint64_t d;
+ void c_take_trivial_ref_method() const;
+ void c_take_trivial_mut_ref_method();
};
struct E {
uint64_t e;
std::string e_str;
+ void c_take_opaque_ref_method() const;
+ void c_take_opaque_mut_ref_method();
};
enum COwnedEnum {
- CVal1,
- CVal2,
+ CVAL1,
+ CVAL2,
+};
+
+struct Borrow {
+ Borrow(const std::string &s);
+ void const_member() const;
+ void nonconst_member();
+ const std::string &s;
};
size_t c_return_primitive();
@@ -78,13 +90,15 @@ Shared c_return_shared();
::A::B::ABShared c_return_nested_ns_shared();
rust::Box<R> c_return_box();
std::unique_ptr<C> c_return_unique_ptr();
+std::shared_ptr<C> c_return_shared_ptr();
std::unique_ptr<::H::H> c_return_ns_unique_ptr();
const size_t &c_return_ref(const Shared &shared);
const size_t &c_return_ns_ref(const ::A::AShared &shared);
const size_t &c_return_nested_ns_ref(const ::A::B::ABShared &shared);
size_t &c_return_mut(Shared &shared);
rust::Str c_return_str(const Shared &shared);
-rust::Slice<uint8_t> c_return_sliceu8(const Shared &shared);
+rust::Slice<const char> c_return_slice_char(const Shared &shared);
+rust::Slice<uint8_t> c_return_mutsliceu8(rust::Slice<uint8_t> slice);
rust::String c_return_rust_string();
std::unique_ptr<std::string> c_return_unique_ptr_string();
std::unique_ptr<std::vector<uint8_t>> c_return_unique_ptr_vector_u8();
@@ -103,6 +117,9 @@ size_t c_return_sum(size_t n1, size_t n2);
Enum c_return_enum(uint16_t n);
::A::AEnum c_return_ns_enum(uint16_t n);
::A::B::ABEnum c_return_nested_ns_enum(uint16_t n);
+std::unique_ptr<Borrow> c_return_borrow(const std::string &s);
+const C *c_return_const_ptr(size_t n);
+C *c_return_mut_ptr(size_t n);
void c_take_primitive(size_t n);
void c_take_shared(Shared shared);
@@ -114,7 +131,11 @@ void c_take_ref_r(const R &r);
void c_take_ref_c(const C &c);
void c_take_ref_ns_c(const ::H::H &h);
void c_take_str(rust::Str s);
-void c_take_sliceu8(rust::Slice<uint8_t> s);
+void c_take_slice_char(rust::Slice<const char> s);
+void c_take_slice_shared(rust::Slice<const Shared> s);
+void c_take_slice_shared_sort(rust::Slice<Shared> s);
+void c_take_slice_r(rust::Slice<const R> s);
+void c_take_slice_r_sort(rust::Slice<R> s);
void c_take_rust_string(rust::String s);
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);
@@ -132,6 +153,7 @@ void c_take_rust_vec_string(rust::Vec<rust::String> v);
void c_take_rust_vec_shared_index(rust::Vec<Shared> v);
void c_take_rust_vec_shared_push(rust::Vec<Shared> v);
void c_take_rust_vec_shared_forward_iterator(rust::Vec<Shared> v);
+void c_take_rust_vec_shared_sort(rust::Vec<Shared> v);
void c_take_ref_rust_vec(const rust::Vec<uint8_t> &v);
void c_take_ref_rust_vec_string(const rust::Vec<rust::String> &v);
void c_take_ref_rust_vec_index(const rust::Vec<uint8_t> &v);
@@ -141,6 +163,8 @@ void c_take_callback(rust::Fn<size_t(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);
+size_t c_take_const_ptr(const C *c);
+size_t c_take_mut_ptr(C *c);
void c_try_return_void();
size_t c_try_return_primitive();
@@ -148,15 +172,21 @@ size_t c_fail_return_primitive();
rust::Box<R> c_try_return_box();
const rust::String &c_try_return_ref(const rust::String &);
rust::Str c_try_return_str(rust::Str);
-rust::Slice<uint8_t> c_try_return_sliceu8(rust::Slice<uint8_t>);
+rust::Slice<const uint8_t> c_try_return_sliceu8(rust::Slice<const uint8_t>);
+rust::Slice<uint8_t> c_try_return_mutsliceu8(rust::Slice<uint8_t>);
rust::String c_try_return_rust_string();
std::unique_ptr<std::string> c_try_return_unique_ptr_string();
rust::Vec<uint8_t> c_try_return_rust_vec();
rust::Vec<rust::String> c_try_return_rust_vec_string();
const rust::Vec<uint8_t> &c_try_return_ref_rust_vec(const C &c);
+size_t c_get_use_count(const std::weak_ptr<C> &weak) noexcept;
+
void c_take_trivial_ptr(std::unique_ptr<D> d);
void c_take_trivial_ref(const D &d);
+void c_take_trivial_mut_ref(D &d);
+void c_take_trivial_pin_ref(const D &d);
+void c_take_trivial_pin_mut_ref(D &d);
void c_take_trivial(D d);
void c_take_trivial_ns_ptr(std::unique_ptr<::G::G> g);
@@ -171,6 +201,7 @@ D c_return_trivial();
std::unique_ptr<::G::G> c_return_trivial_ns_ptr();
::G::G c_return_trivial_ns();
std::unique_ptr<E> c_return_opaque_ptr();
+E &c_return_opaque_mut_pin(E &e);
std::unique_ptr<::F::F> c_return_ns_opaque_ptr();
rust::String cOverloadedFunction(int32_t x);
diff --git a/tests/test.rs b/tests/test.rs
index 2bae4d85..32b8e832 100644
--- a/tests/test.rs
+++ b/tests/test.rs
@@ -1,7 +1,16 @@
-#![allow(clippy::assertions_on_constants, clippy::float_cmp, clippy::unit_cmp)]
+#![allow(
+ clippy::assertions_on_constants,
+ clippy::cast_possible_truncation,
+ clippy::cast_possible_wrap,
+ clippy::float_cmp,
+ clippy::needless_pass_by_value,
+ clippy::unit_cmp,
+ clippy::unseparated_literal_suffix
+)]
-use cxx_test_suite::extra::ffi2;
-use cxx_test_suite::ffi;
+use cxx::SharedPtr;
+use cxx_test_suite::module::ffi2;
+use cxx_test_suite::{cast, ffi, R};
use std::cell::Cell;
use std::ffi::CStr;
@@ -18,7 +27,7 @@ macro_rules! check {
($run:expr) => {{
CORRECT.with(|correct| correct.set(false));
$run;
- assert!(CORRECT.with(|correct| correct.get()), stringify!($run));
+ assert!(CORRECT.with(Cell::get), "{}", stringify!($run));
}};
}
@@ -30,14 +39,17 @@ fn test_c_return() {
assert_eq!(2020, ffi::c_return_primitive());
assert_eq!(2020, ffi::c_return_shared().z);
- assert_eq!(2020, *ffi::c_return_box());
+ assert_eq!(2020, ffi::c_return_box().0);
ffi::c_return_unique_ptr();
ffi2::c_return_ns_unique_ptr();
assert_eq!(2020, *ffi::c_return_ref(&shared));
assert_eq!(2020, *ffi::c_return_ns_ref(&ns_shared));
assert_eq!(2020, *ffi::c_return_nested_ns_ref(&nested_ns_shared));
assert_eq!("2020", ffi::c_return_str(&shared));
- assert_eq!(b"2020\0", ffi::c_return_sliceu8(&shared));
+ assert_eq!(
+ b"2020\0",
+ cast::c_char_to_unsigned(ffi::c_return_slice_char(&shared)),
+ );
assert_eq!("2020", ffi::c_return_rust_string());
assert_eq!("2020", ffi::c_return_unique_ptr_string().to_str().unwrap());
assert_eq!(4, ffi::c_return_unique_ptr_vector_u8().len());
@@ -68,7 +80,7 @@ fn test_c_return() {
_ => assert!(false),
}
match ffi::c_return_enum(2021) {
- enm @ ffi::Enum::CVal => assert_eq!(2021, enm.repr),
+ enm @ ffi::Enum::LastVal => assert_eq!(2021, enm.repr),
_ => assert!(false),
}
match ffi::c_return_ns_enum(0) {
@@ -89,7 +101,7 @@ fn test_c_try_return() {
"logic error",
ffi::c_fail_return_primitive().unwrap_err().what(),
);
- assert_eq!(2020, *ffi::c_try_return_box().unwrap());
+ assert_eq!(2020, ffi::c_try_return_box().unwrap().0);
assert_eq!("2020", *ffi::c_try_return_ref(&"2020".to_owned()).unwrap());
assert_eq!("2020", ffi::c_try_return_str("2020").unwrap());
assert_eq!(b"2020", ffi::c_try_return_sliceu8(b"2020").unwrap());
@@ -107,12 +119,33 @@ fn test_c_take() {
check!(ffi::c_take_ns_shared(ffi::AShared { z: 2020 }));
check!(ffi::ns_c_take_ns_shared(ffi::AShared { z: 2020 }));
check!(ffi::c_take_nested_ns_shared(ffi::ABShared { z: 2020 }));
- check!(ffi::c_take_box(Box::new(2020)));
+ check!(ffi::c_take_box(Box::new(R(2020))));
check!(ffi::c_take_ref_c(&unique_ptr));
check!(ffi2::c_take_ref_ns_c(&unique_ptr_ns));
check!(cxx_test_suite::module::ffi::c_take_unique_ptr(unique_ptr));
check!(ffi::c_take_str("2020"));
- check!(ffi::c_take_sliceu8(b"2020"));
+ check!(ffi::c_take_slice_char(cast::unsigned_to_c_char(b"2020")));
+ check!(ffi::c_take_slice_shared(&[
+ ffi::Shared { z: 2020 },
+ ffi::Shared { z: 2021 },
+ ]));
+ let shared_sort_slice = &mut [
+ ffi::Shared { z: 2 },
+ ffi::Shared { z: 0 },
+ ffi::Shared { z: 7 },
+ ffi::Shared { z: 4 },
+ ];
+ check!(ffi::c_take_slice_shared_sort(shared_sort_slice));
+ assert_eq!(shared_sort_slice[0].z, 0);
+ assert_eq!(shared_sort_slice[1].z, 2);
+ assert_eq!(shared_sort_slice[2].z, 4);
+ assert_eq!(shared_sort_slice[3].z, 7);
+ let r_sort_slice = &mut [R(2020), R(2050), R(2021)];
+ check!(ffi::c_take_slice_r(r_sort_slice));
+ check!(ffi::c_take_slice_r_sort(r_sort_slice));
+ assert_eq!(r_sort_slice[0].0, 2020);
+ assert_eq!(r_sort_slice[1].0, 2021);
+ assert_eq!(r_sort_slice[2].0, 2050);
check!(ffi::c_take_rust_string("2020".to_owned()));
check!(ffi::c_take_unique_ptr_string(
ffi::c_return_unique_ptr_string()
@@ -137,6 +170,13 @@ fn test_c_take() {
check!(ffi::c_take_rust_vec_shared_forward_iterator(
shared_test_vec,
));
+ let shared_sort_vec = vec![
+ ffi::Shared { z: 2 },
+ ffi::Shared { z: 0 },
+ ffi::Shared { z: 7 },
+ ffi::Shared { z: 4 },
+ ];
+ check!(ffi::c_take_rust_vec_shared_sort(shared_sort_vec));
check!(ffi::c_take_ref_rust_vec(&test_vec));
check!(ffi::c_take_ref_rust_vec_index(&test_vec));
check!(ffi::c_take_ref_rust_vec_copy(&test_vec));
@@ -188,13 +228,35 @@ fn test_c_method_calls() {
let old_value = unique_ptr.get();
assert_eq!(2020, old_value);
- assert_eq!(2021, unique_ptr.set(2021));
+ assert_eq!(2021, unique_ptr.pin_mut().set(2021));
assert_eq!(2021, unique_ptr.get());
- assert_eq!(old_value, unique_ptr.set2(old_value));
- assert_eq!(old_value, unique_ptr.get2());
- assert_eq!(2022, unique_ptr.set_succeed(2022).unwrap());
- assert!(unique_ptr.get_fail().is_err());
+ assert_eq!(2021, unique_ptr.get2());
+ assert_eq!(2021, *unique_ptr.getRef());
+ assert_eq!(2021, *unique_ptr.pin_mut().getMut());
+ assert_eq!(2022, unique_ptr.pin_mut().set_succeed(2022).unwrap());
+ assert!(unique_ptr.pin_mut().get_fail().is_err());
assert_eq!(2021, ffi::Shared { z: 0 }.c_method_on_shared());
+ assert_eq!(2022, *ffi::Shared { z: 2022 }.c_method_ref_on_shared());
+ assert_eq!(2023, *ffi::Shared { z: 2023 }.c_method_mut_on_shared());
+
+ let val = 42;
+ let mut array = ffi::Array { a: [0, 0, 0, 0] };
+ array.c_set_array(val);
+ assert_eq!(array.a.len() as i32 * val, array.r_get_array_sum());
+}
+
+#[test]
+fn test_shared_ptr_weak_ptr() {
+ let shared_ptr = ffi::c_return_shared_ptr();
+ let weak_ptr = SharedPtr::downgrade(&shared_ptr);
+ assert_eq!(1, ffi::c_get_use_count(&weak_ptr));
+
+ assert!(!weak_ptr.upgrade().is_null());
+ assert_eq!(1, ffi::c_get_use_count(&weak_ptr));
+
+ drop(shared_ptr);
+ assert_eq!(0, ffi::c_get_use_count(&weak_ptr));
+ assert!(weak_ptr.upgrade().is_null());
}
#[test]
@@ -209,17 +271,24 @@ fn test_c_ns_method_calls() {
fn test_enum_representations() {
assert_eq!(0, ffi::Enum::AVal.repr);
assert_eq!(2020, ffi::Enum::BVal.repr);
- assert_eq!(2021, ffi::Enum::CVal.repr);
+ assert_eq!(2021, ffi::Enum::LastVal.repr);
+}
+
+#[test]
+fn test_debug() {
+ assert_eq!("Shared { z: 1 }", format!("{:?}", ffi::Shared { z: 1 }));
+ assert_eq!("BVal", format!("{:?}", ffi::Enum::BVal));
+ assert_eq!("Enum(9)", format!("{:?}", ffi::Enum { repr: 9 }));
}
#[no_mangle]
-extern "C" fn cxx_test_suite_get_box() -> *mut cxx_test_suite::R {
- Box::into_raw(Box::new(2020usize))
+extern "C" fn cxx_test_suite_get_box() -> *mut R {
+ Box::into_raw(Box::new(R(2020usize)))
}
#[no_mangle]
-unsafe extern "C" fn cxx_test_suite_r_is_correct(r: *const cxx_test_suite::R) -> bool {
- *r == 2020
+unsafe extern "C" fn cxx_test_suite_r_is_correct(r: *const R) -> bool {
+ (*r).0 == 2020
}
#[test]
@@ -233,10 +302,14 @@ fn test_rust_name_attribute() {
#[test]
fn test_extern_trivial() {
- let d = ffi2::c_return_trivial();
+ let mut d = ffi2::c_return_trivial();
check!(ffi2::c_take_trivial_ref(&d));
+ check!(d.c_take_trivial_ref_method());
+ check!(d.c_take_trivial_mut_ref_method());
check!(ffi2::c_take_trivial(d));
- let d = ffi2::c_return_trivial_ptr();
+ let mut d = ffi2::c_return_trivial_ptr();
+ check!(d.c_take_trivial_ref_method());
+ check!(d.c_take_trivial_mut_ref_method());
check!(ffi2::c_take_trivial_ptr(d));
cxx::UniquePtr::new(ffi2::D { d: 42 });
let d = ffi2::ns_c_return_trivial();
@@ -252,11 +325,29 @@ fn test_extern_trivial() {
#[test]
fn test_extern_opaque() {
- let e = ffi2::c_return_opaque_ptr();
+ let mut e = ffi2::c_return_opaque_ptr();
check!(ffi2::c_take_opaque_ref(e.as_ref().unwrap()));
+ check!(e.c_take_opaque_ref_method());
+ check!(e.pin_mut().c_take_opaque_mut_ref_method());
check!(ffi2::c_take_opaque_ptr(e));
let f = ffi2::c_return_ns_opaque_ptr();
check!(ffi2::c_take_opaque_ns_ref(f.as_ref().unwrap()));
check!(ffi2::c_take_opaque_ns_ptr(f));
}
+
+#[test]
+fn test_raw_ptr() {
+ let c = ffi::c_return_mut_ptr(2023);
+ let mut c_unique = unsafe { cxx::UniquePtr::from_raw(c) };
+ assert_eq!(2023, c_unique.pin_mut().set_succeed(2023).unwrap());
+ // c will be dropped as it's now in a UniquePtr
+
+ let c2 = ffi::c_return_mut_ptr(2024);
+ assert_eq!(2024, unsafe { ffi::c_take_const_ptr(c2) });
+ assert_eq!(2024, unsafe { ffi::c_take_mut_ptr(c2) }); // deletes c2
+
+ let c3 = ffi::c_return_const_ptr(2025);
+ assert_eq!(2025, unsafe { ffi::c_take_const_ptr(c3) });
+ assert_eq!(2025, unsafe { ffi::c_take_mut_ptr(c3 as *mut ffi::C) }); // deletes c3
+}
diff --git a/tests/ui/array_len_expr.rs b/tests/ui/array_len_expr.rs
new file mode 100644
index 00000000..73da57c8
--- /dev/null
+++ b/tests/ui/array_len_expr.rs
@@ -0,0 +1,10 @@
+#[cxx::bridge]
+mod ffi {
+ struct Shared {
+ arraystr: [String; "13"],
+ arraysub: [String; 15 - 1],
+ arrayzero: [String; 0],
+ }
+}
+
+fn main() {}
diff --git a/tests/ui/array_len_expr.stderr b/tests/ui/array_len_expr.stderr
new file mode 100644
index 00000000..4b45a0b9
--- /dev/null
+++ b/tests/ui/array_len_expr.stderr
@@ -0,0 +1,17 @@
+error: array length must be an integer literal
+ --> $DIR/array_len_expr.rs:4:28
+ |
+4 | arraystr: [String; "13"],
+ | ^^^^
+
+error: unsupported expression, array length must be an integer literal
+ --> $DIR/array_len_expr.rs:5:28
+ |
+5 | arraysub: [String; 15 - 1],
+ | ^^^^^^
+
+error: array with zero size is not supported
+ --> $DIR/array_len_expr.rs:6:20
+ |
+6 | arrayzero: [String; 0],
+ | ^^^^^^^^^^^
diff --git a/tests/ui/array_len_suffix.rs b/tests/ui/array_len_suffix.rs
new file mode 100644
index 00000000..1e9d514f
--- /dev/null
+++ b/tests/ui/array_len_suffix.rs
@@ -0,0 +1,8 @@
+#[cxx::bridge]
+mod ffi {
+ unsafe extern "C++" {
+ fn array() -> [String; 12u16];
+ }
+}
+
+fn main() {}
diff --git a/tests/ui/array_len_suffix.stderr b/tests/ui/array_len_suffix.stderr
new file mode 100644
index 00000000..b72fc022
--- /dev/null
+++ b/tests/ui/array_len_suffix.stderr
@@ -0,0 +1,10 @@
+error[E0308]: mismatched types
+ --> $DIR/array_len_suffix.rs:4:32
+ |
+4 | fn array() -> [String; 12u16];
+ | ^^^^^ expected `usize`, found `u16`
+ |
+help: change the type of the numeric literal from `u16` to `usize`
+ |
+4 | fn array() -> [String; 12usize];
+ | ^^^^^^^
diff --git a/tests/ui/async_fn.rs b/tests/ui/async_fn.rs
new file mode 100644
index 00000000..69f26421
--- /dev/null
+++ b/tests/ui/async_fn.rs
@@ -0,0 +1,10 @@
+#[cxx::bridge]
+mod ffi {
+ extern "Rust" {
+ async fn f();
+ }
+}
+
+async fn f() {}
+
+fn main() {}
diff --git a/tests/ui/async_fn.stderr b/tests/ui/async_fn.stderr
new file mode 100644
index 00000000..dedec7bb
--- /dev/null
+++ b/tests/ui/async_fn.stderr
@@ -0,0 +1,5 @@
+error: async function is not directly supported yet, but see https://cxx.rs/async.html for a working approach
+ --> $DIR/async_fn.rs:4:9
+ |
+4 | async fn f();
+ | ^^^^^^^^^^^^^
diff --git a/tests/ui/by_value_not_supported.rs b/tests/ui/by_value_not_supported.rs
index 3ff950a8..62bf8d4f 100644
--- a/tests/ui/by_value_not_supported.rs
+++ b/tests/ui/by_value_not_supported.rs
@@ -6,7 +6,7 @@ mod ffi {
s: CxxString,
}
- extern "C" {
+ extern "C++" {
type C;
}
diff --git a/tests/ui/by_value_not_supported.stderr b/tests/ui/by_value_not_supported.stderr
index 0a56dd48..7288c93b 100644
--- a/tests/ui/by_value_not_supported.stderr
+++ b/tests/ui/by_value_not_supported.stderr
@@ -16,7 +16,7 @@ error: using C++ string by value is not supported
6 | s: CxxString,
| ^^^^^^^^^^^^
-error: needs a cxx::ExternType impl in order to be used as a field of `S`
+error: needs a cxx::ExternType impl in order to be used as a field of `S`, argument of `f` or return value of `f`
--> $DIR/by_value_not_supported.rs:10:9
|
10 | type C;
diff --git a/tests/ui/const_fn.rs b/tests/ui/const_fn.rs
new file mode 100644
index 00000000..1ad48941
--- /dev/null
+++ b/tests/ui/const_fn.rs
@@ -0,0 +1,10 @@
+#[cxx::bridge]
+mod ffi {
+ extern "Rust" {
+ const fn f();
+ }
+}
+
+const fn f() {}
+
+fn main() {}
diff --git a/tests/ui/const_fn.stderr b/tests/ui/const_fn.stderr
new file mode 100644
index 00000000..a62ca837
--- /dev/null
+++ b/tests/ui/const_fn.stderr
@@ -0,0 +1,5 @@
+error: const extern function is not supported
+ --> $DIR/const_fn.rs:4:9
+ |
+4 | const fn f();
+ | ^^^^^^^^^^^^^
diff --git a/tests/ui/deny_missing_docs.rs b/tests/ui/deny_missing_docs.rs
new file mode 100644
index 00000000..037894bb
--- /dev/null
+++ b/tests/ui/deny_missing_docs.rs
@@ -0,0 +1,94 @@
+// TODO: More work is needed so that the missing_docs lints produced by rustc
+// are properly positioned inside of the bridge.
+
+//! ...
+
+#![deny(missing_docs)]
+
+/// ...
+#[cxx::bridge]
+pub mod ffi {
+ pub struct UndocumentedStruct {
+ pub undocumented_field: u8,
+ }
+
+ /// ...
+ pub struct DocumentedStruct {
+ /// ...
+ pub documented_field: u8,
+ }
+
+ pub enum UndocumentedEnum {
+ UndocumentedVariant = 0,
+ }
+
+ /// ...
+ pub enum DocumentedEnum {
+ /// ...
+ DocumentedVariant = 0,
+ }
+
+ extern "Rust" {
+ pub type UndocumentedRustType;
+
+ /// ...
+ pub type DocumentedRustType;
+
+ pub fn undocumented_rust_fn() -> u8;
+
+ /// ...
+ pub fn documented_rust_fn() -> u8;
+ }
+
+ unsafe extern "C++" {
+ pub type UndocumentedForeignType;
+
+ /// ...
+ pub type DocumentedForeignType;
+
+ pub type UndocumentedTypeAlias = crate::bindgen::UndocumentedTypeAlias;
+
+ /// ...
+ pub type DocumentedTypeAlias = crate::bindgen::DocumentedTypeAlias;
+
+ pub fn undocumented_foreign_fn() -> u8;
+
+ /// ...
+ pub fn documented_foreign_fn() -> u8;
+ }
+
+ #[allow(missing_docs)]
+ pub struct SuppressUndocumentedStruct {
+ pub undocumented_field: u8,
+ }
+}
+
+struct UndocumentedRustType;
+struct DocumentedRustType;
+
+mod bindgen {
+ use cxx::{type_id, ExternType};
+
+ pub struct UndocumentedTypeAlias;
+ pub struct DocumentedTypeAlias;
+
+ unsafe impl ExternType for UndocumentedTypeAlias {
+ type Id = type_id!("UndocumentedTypeAlias");
+ type Kind = cxx::kind::Opaque;
+ }
+
+ unsafe impl ExternType for DocumentedTypeAlias {
+ type Id = type_id!("DocumentedTypeAlias");
+ type Kind = cxx::kind::Opaque;
+ }
+}
+
+fn undocumented_rust_fn() -> u8 {
+ 0
+}
+
+fn documented_rust_fn() -> u8 {
+ 0
+}
+
+fn main() {}
diff --git a/tests/ui/deny_missing_docs.stderr b/tests/ui/deny_missing_docs.stderr
new file mode 100644
index 00000000..e7aadfbe
--- /dev/null
+++ b/tests/ui/deny_missing_docs.stderr
@@ -0,0 +1,47 @@
+error: missing documentation for a struct
+ --> $DIR/deny_missing_docs.rs:11:5
+ |
+11 | pub struct UndocumentedStruct {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+note: the lint level is defined here
+ --> $DIR/deny_missing_docs.rs:6:9
+ |
+6 | #![deny(missing_docs)]
+ | ^^^^^^^^^^^^
+
+error: missing documentation for a struct field
+ --> $DIR/deny_missing_docs.rs:12:9
+ |
+12 | pub undocumented_field: u8,
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: missing documentation for a struct
+ --> $DIR/deny_missing_docs.rs:21:5
+ |
+21 | pub enum UndocumentedEnum {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: missing documentation for an associated constant
+ --> $DIR/deny_missing_docs.rs:22:9
+ |
+22 | UndocumentedVariant = 0,
+ | ^^^^^^^^^^^^^^^^^^^
+
+error: missing documentation for a struct
+ --> $DIR/deny_missing_docs.rs:44:9
+ |
+44 | pub type UndocumentedForeignType;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: missing documentation for a type alias
+ --> $DIR/deny_missing_docs.rs:49:9
+ |
+49 | pub type UndocumentedTypeAlias = crate::bindgen::UndocumentedTypeAlias;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: missing documentation for a function
+ --> $DIR/deny_missing_docs.rs:54:9
+ |
+54 | pub fn undocumented_foreign_fn() -> u8;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/tests/ui/derive_duplicate.rs b/tests/ui/derive_duplicate.rs
new file mode 100644
index 00000000..3061f765
--- /dev/null
+++ b/tests/ui/derive_duplicate.rs
@@ -0,0 +1,9 @@
+#[cxx::bridge]
+mod ffi {
+ #[derive(Clone, Clone)]
+ struct Struct {
+ x: usize,
+ }
+}
+
+fn main() {}
diff --git a/tests/ui/derive_duplicate.stderr b/tests/ui/derive_duplicate.stderr
new file mode 100644
index 00000000..ce271632
--- /dev/null
+++ b/tests/ui/derive_duplicate.stderr
@@ -0,0 +1,7 @@
+error[E0119]: conflicting implementations of trait `std::clone::Clone` for type `ffi::Struct`
+ --> $DIR/derive_duplicate.rs:3:21
+ |
+3 | #[derive(Clone, Clone)]
+ | ----- ^^^^^ conflicting implementation for `ffi::Struct`
+ | |
+ | first implementation here
diff --git a/tests/ui/derive_nonclone.rs b/tests/ui/derive_nonclone.rs
new file mode 100644
index 00000000..58110289
--- /dev/null
+++ b/tests/ui/derive_nonclone.rs
@@ -0,0 +1,13 @@
+#[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
new file mode 100644
index 00000000..04eae4fd
--- /dev/null
+++ b/tests/ui/derive_nonclone.stderr
@@ -0,0 +1,7 @@
+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/derive_noncopy.rs b/tests/ui/derive_noncopy.rs
new file mode 100644
index 00000000..ae705fd0
--- /dev/null
+++ b/tests/ui/derive_noncopy.rs
@@ -0,0 +1,13 @@
+#[cxx::bridge]
+mod ffi {
+ #[derive(Copy)]
+ struct TryCopy {
+ other: Other,
+ }
+
+ struct Other {
+ x: usize,
+ }
+}
+
+fn main() {}
diff --git a/tests/ui/derive_noncopy.stderr b/tests/ui/derive_noncopy.stderr
new file mode 100644
index 00000000..d6410280
--- /dev/null
+++ b/tests/ui/derive_noncopy.stderr
@@ -0,0 +1,8 @@
+error[E0204]: the trait `Copy` may not be implemented for this type
+ --> $DIR/derive_noncopy.rs:3:14
+ |
+3 | #[derive(Copy)]
+ | ^^^^
+4 | struct TryCopy {
+5 | other: Other,
+ | ------------ this field does not implement `Copy`
diff --git a/tests/ui/disallow_lifetime.rs b/tests/ui/disallow_lifetime.rs
deleted file mode 100644
index ee39fef9..00000000
--- a/tests/ui/disallow_lifetime.rs
+++ /dev/null
@@ -1,13 +0,0 @@
-#[cxx::bridge]
-mod ffi {
- extern "C" {
- type C;
- fn f(&'static self);
- }
-
- extern "Rust" {
- fn f(string: &'a String);
- }
-}
-
-fn main() {}
diff --git a/tests/ui/disallow_lifetime.stderr b/tests/ui/disallow_lifetime.stderr
deleted file mode 100644
index d7c42e4b..00000000
--- a/tests/ui/disallow_lifetime.stderr
+++ /dev/null
@@ -1,11 +0,0 @@
-error: references with explicit lifetimes are not supported
- --> $DIR/disallow_lifetime.rs:9:22
- |
-9 | fn f(string: &'a String);
- | ^^^^^^^^^^
-
-error: references with explicit lifetimes are not supported
- --> $DIR/disallow_lifetime.rs:5:14
- |
-5 | fn f(&'static self);
- | ^^^^^^^^^^^^^
diff --git a/tests/ui/drop_shared.rs b/tests/ui/drop_shared.rs
new file mode 100644
index 00000000..5e3de4a1
--- /dev/null
+++ b/tests/ui/drop_shared.rs
@@ -0,0 +1,14 @@
+#[cxx::bridge]
+mod ffi {
+ struct Shared {
+ fd: i32,
+ }
+}
+
+impl Drop for ffi::Shared {
+ fn drop(&mut self) {
+ println!("close({})", self.fd);
+ }
+}
+
+fn main() {}
diff --git a/tests/ui/drop_shared.stderr b/tests/ui/drop_shared.stderr
new file mode 100644
index 00000000..f2724e35
--- /dev/null
+++ b/tests/ui/drop_shared.stderr
@@ -0,0 +1,8 @@
+error[E0119]: conflicting implementations of trait `ffi::_::forbid::Drop` for type `ffi::Shared`
+ --> $DIR/drop_shared.rs:3:5
+ |
+1 | #[cxx::bridge]
+ | -------------- first implementation here
+2 | mod ffi {
+3 | struct Shared {
+ | ^^^^^^^^^^^^^ conflicting implementation for `ffi::Shared`
diff --git a/tests/ui/duplicate_enum_discriminants.rs b/tests/ui/duplicate_enum_discriminants.rs
deleted file mode 100644
index ec3d61a8..00000000
--- a/tests/ui/duplicate_enum_discriminants.rs
+++ /dev/null
@@ -1,15 +0,0 @@
-#[cxx::bridge]
-mod ffi {
- enum A {
- V1 = 10,
- V2 = 10,
- }
-
- enum B {
- V1 = 10,
- V2,
- V3 = 11,
- }
-}
-
-fn main() {}
diff --git a/tests/ui/duplicate_enum_discriminants.stderr b/tests/ui/duplicate_enum_discriminants.stderr
deleted file mode 100644
index 14505e32..00000000
--- a/tests/ui/duplicate_enum_discriminants.stderr
+++ /dev/null
@@ -1,11 +0,0 @@
-error: discriminant value `10` already exists
- --> $DIR/duplicate_enum_discriminants.rs:5:9
- |
-5 | V2 = 10,
- | ^^^^^^^
-
-error: discriminant value `11` already exists
- --> $DIR/duplicate_enum_discriminants.rs:11:9
- |
-11 | V3 = 11,
- | ^^^^^^^
diff --git a/tests/ui/empty_enum.stderr b/tests/ui/empty_enum.stderr
index 7f350191..0556da9a 100644
--- a/tests/ui/empty_enum.stderr
+++ b/tests/ui/empty_enum.stderr
@@ -1,4 +1,4 @@
-error: enums without any variants are not supported
+error: explicit #[repr(...)] is required for enum without any variants
--> $DIR/empty_enum.rs:3:5
|
3 | enum A {}
diff --git a/tests/ui/enum_match_without_wildcard.stderr b/tests/ui/enum_match_without_wildcard.stderr
index 85cc0c55..f43678f7 100644
--- a/tests/ui/enum_match_without_wildcard.stderr
+++ b/tests/ui/enum_match_without_wildcard.stderr
@@ -1,8 +1,8 @@
error[E0004]: non-exhaustive patterns: `A { repr: 2_u8..=u8::MAX }` not covered
--> $DIR/enum_match_without_wildcard.rs:12:11
|
-1 | #[cxx::bridge]
- | -------------- `ffi::A` defined here
+3 | enum A {
+ | ------ `ffi::A` defined here
...
12 | match a {
| ^ pattern `A { repr: 2_u8..=u8::MAX }` not covered
diff --git a/tests/ui/enum_receiver.rs b/tests/ui/enum_receiver.rs
new file mode 100644
index 00000000..a27f2746
--- /dev/null
+++ b/tests/ui/enum_receiver.rs
@@ -0,0 +1,11 @@
+#[cxx::bridge]
+mod ffi {
+ enum Enum {
+ Variant,
+ }
+ extern "Rust" {
+ fn f(self: &Enum);
+ }
+}
+
+fn main() {}
diff --git a/tests/ui/enum_receiver.stderr b/tests/ui/enum_receiver.stderr
new file mode 100644
index 00000000..e09b4e39
--- /dev/null
+++ b/tests/ui/enum_receiver.stderr
@@ -0,0 +1,5 @@
+error: unsupported receiver type; C++ does not allow member functions on enums
+ --> $DIR/enum_receiver.rs:7:20
+ |
+7 | fn f(self: &Enum);
+ | ^^^^^
diff --git a/tests/ui/expected_named.rs b/tests/ui/expected_named.rs
new file mode 100644
index 00000000..31626d1f
--- /dev/null
+++ b/tests/ui/expected_named.rs
@@ -0,0 +1,9 @@
+#[cxx::bridge]
+mod ffi {
+ unsafe extern "C++" {
+ type Borrowed<'a>;
+ fn borrowed() -> UniquePtr<Borrowed>;
+ }
+}
+
+fn main() {}
diff --git a/tests/ui/expected_named.stderr b/tests/ui/expected_named.stderr
new file mode 100644
index 00000000..46764014
--- /dev/null
+++ b/tests/ui/expected_named.stderr
@@ -0,0 +1,11 @@
+error[E0106]: missing lifetime specifier
+ --> $DIR/expected_named.rs:5:36
+ |
+5 | fn borrowed() -> UniquePtr<Borrowed>;
+ | ^^^^^^^^ expected named lifetime parameter
+ |
+ = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from
+help: consider using the `'static` lifetime
+ |
+5 | fn borrowed() -> UniquePtr<Borrowed<'static>>;
+ | ^^^^^^^^^^^^^^^^^
diff --git a/tests/ui/extern_fn_abi.rs b/tests/ui/extern_fn_abi.rs
new file mode 100644
index 00000000..1f933388
--- /dev/null
+++ b/tests/ui/extern_fn_abi.rs
@@ -0,0 +1,8 @@
+#[cxx::bridge]
+mod ffi {
+ extern "C++" {
+ extern "Java" fn f();
+ }
+}
+
+fn main() {}
diff --git a/tests/ui/extern_fn_abi.stderr b/tests/ui/extern_fn_abi.stderr
new file mode 100644
index 00000000..3abf47ab
--- /dev/null
+++ b/tests/ui/extern_fn_abi.stderr
@@ -0,0 +1,5 @@
+error: explicit ABI on extern function is not supported
+ --> $DIR/extern_fn_abi.rs:4:9
+ |
+4 | extern "Java" fn f();
+ | ^^^^^^^^^^^^^
diff --git a/tests/ui/extern_type_bound.rs b/tests/ui/extern_type_bound.rs
new file mode 100644
index 00000000..958accd3
--- /dev/null
+++ b/tests/ui/extern_type_bound.rs
@@ -0,0 +1,15 @@
+#[cxx::bridge]
+mod ffi {
+ extern "C++" {
+ type Opaque: PartialEq + PartialOrd;
+ }
+}
+
+#[cxx::bridge]
+mod ffi {
+ extern "C++" {
+ type Opaque: for<'de> Deserialize<'de>;
+ }
+}
+
+fn main() {}
diff --git a/tests/ui/extern_type_bound.stderr b/tests/ui/extern_type_bound.stderr
new file mode 100644
index 00000000..ca07ef7f
--- /dev/null
+++ b/tests/ui/extern_type_bound.stderr
@@ -0,0 +1,11 @@
+error: extern type bounds are not implemented yet
+ --> $DIR/extern_type_bound.rs:4:22
+ |
+4 | type Opaque: PartialEq + PartialOrd;
+ | ^^^^^^^^^^^^^^^^^^^^^^
+
+error: unsupported trait
+ --> $DIR/extern_type_bound.rs:11:22
+ |
+11 | type Opaque: for<'de> Deserialize<'de>;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/tests/ui/extern_type_generic.rs b/tests/ui/extern_type_generic.rs
new file mode 100644
index 00000000..4de2c981
--- /dev/null
+++ b/tests/ui/extern_type_generic.rs
@@ -0,0 +1,8 @@
+#[cxx::bridge]
+mod ffi {
+ extern "C++" {
+ type Generic<T>;
+ }
+}
+
+fn main() {}
diff --git a/tests/ui/extern_type_generic.stderr b/tests/ui/extern_type_generic.stderr
new file mode 100644
index 00000000..95faec6c
--- /dev/null
+++ b/tests/ui/extern_type_generic.stderr
@@ -0,0 +1,5 @@
+error: extern type with generic type parameter is not supported yet
+ --> $DIR/extern_type_generic.rs:4:22
+ |
+4 | type Generic<T>;
+ | ^
diff --git a/tests/ui/extern_type_lifetime_bound.rs b/tests/ui/extern_type_lifetime_bound.rs
new file mode 100644
index 00000000..ad581818
--- /dev/null
+++ b/tests/ui/extern_type_lifetime_bound.rs
@@ -0,0 +1,8 @@
+#[cxx::bridge]
+mod ffi {
+ extern "C++" {
+ type Complex<'a, 'b: 'a>;
+ }
+}
+
+fn main() {}
diff --git a/tests/ui/extern_type_lifetime_bound.stderr b/tests/ui/extern_type_lifetime_bound.stderr
new file mode 100644
index 00000000..8c58050a
--- /dev/null
+++ b/tests/ui/extern_type_lifetime_bound.stderr
@@ -0,0 +1,5 @@
+error: lifetime parameter with bounds is not supported yet
+ --> $DIR/extern_type_lifetime_bound.rs:4:26
+ |
+4 | type Complex<'a, 'b: 'a>;
+ | ^^^^^^
diff --git a/tests/ui/fallible_fnptr.rs b/tests/ui/fallible_fnptr.rs
new file mode 100644
index 00000000..c45813de
--- /dev/null
+++ b/tests/ui/fallible_fnptr.rs
@@ -0,0 +1,8 @@
+#[cxx::bridge]
+mod ffi {
+ unsafe extern "C++" {
+ fn f(callback: fn() -> Result<()>);
+ }
+}
+
+fn main() {}
diff --git a/tests/ui/fallible_fnptr.stderr b/tests/ui/fallible_fnptr.stderr
new file mode 100644
index 00000000..1d3fbe42
--- /dev/null
+++ b/tests/ui/fallible_fnptr.stderr
@@ -0,0 +1,5 @@
+error: function pointer returning Result is not supported yet
+ --> $DIR/fallible_fnptr.rs:4:24
+ |
+4 | fn f(callback: fn() -> Result<()>);
+ | ^^^^^^^^^^^^^^^^^^
diff --git a/tests/ui/function_with_body.rs b/tests/ui/function_with_body.rs
new file mode 100644
index 00000000..52be79af
--- /dev/null
+++ b/tests/ui/function_with_body.rs
@@ -0,0 +1,8 @@
+#[cxx::bridge]
+mod ffi {
+ unsafe extern "C++" {
+ fn f() {}
+ }
+}
+
+fn main() {}
diff --git a/tests/ui/function_with_body.stderr b/tests/ui/function_with_body.stderr
new file mode 100644
index 00000000..c5641db8
--- /dev/null
+++ b/tests/ui/function_with_body.stderr
@@ -0,0 +1,5 @@
+error: expected `;`
+ --> $DIR/function_with_body.rs:4:16
+ |
+4 | fn f() {}
+ | ^
diff --git a/tests/ui/generic_enum.rs b/tests/ui/generic_enum.rs
index 808856eb..feeb94be 100644
--- a/tests/ui/generic_enum.rs
+++ b/tests/ui/generic_enum.rs
@@ -3,6 +3,14 @@ mod ffi {
enum A<T> {
Field,
}
+
+ enum B<T> where T: Copy {
+ Field,
+ }
+
+ enum C where void: Copy {
+ Field,
+ }
}
fn main() {}
diff --git a/tests/ui/generic_enum.stderr b/tests/ui/generic_enum.stderr
index bf267f74..ea83d7c8 100644
--- a/tests/ui/generic_enum.stderr
+++ b/tests/ui/generic_enum.stderr
@@ -1,5 +1,17 @@
-error: enums with generic parameters are not allowed
+error: enum with generic parameters is not supported
--> $DIR/generic_enum.rs:3:5
|
3 | enum A<T> {
| ^^^^^^^^^
+
+error: enum with generic parameters is not supported
+ --> $DIR/generic_enum.rs:7:5
+ |
+7 | enum B<T> where T: Copy {
+ | ^^^^^^^^^
+
+error: enum with where-clause is not supported
+ --> $DIR/generic_enum.rs:11:12
+ |
+11 | enum C where void: Copy {
+ | ^^^^^^^^^^^^^^^^
diff --git a/tests/ui/include.rs b/tests/ui/include.rs
index 82fa8deb..3e848b58 100644
--- a/tests/ui/include.rs
+++ b/tests/ui/include.rs
@@ -1,6 +1,6 @@
#[cxx::bridge]
mod ffi {
- extern "C" {
+ extern "C++" {
include!("path/to" what);
include!(<path/to> what);
include!(<path/to);
diff --git a/tests/ui/lifetime_extern_cxx.rs b/tests/ui/lifetime_extern_cxx.rs
new file mode 100644
index 00000000..e85b3392
--- /dev/null
+++ b/tests/ui/lifetime_extern_cxx.rs
@@ -0,0 +1,9 @@
+#[cxx::bridge]
+mod ffi {
+ extern "C++" {
+ type Opaque;
+ unsafe fn f<'a>(&'a self, arg: &str) -> &'a str;
+ }
+}
+
+fn main() {}
diff --git a/tests/ui/lifetime_extern_cxx.stderr b/tests/ui/lifetime_extern_cxx.stderr
new file mode 100644
index 00000000..099c6ce4
--- /dev/null
+++ b/tests/ui/lifetime_extern_cxx.stderr
@@ -0,0 +1,5 @@
+error: extern C++ function with lifetimes must be declared in `unsafe extern "C++"` block
+ --> $DIR/lifetime_extern_cxx.rs:5:9
+ |
+5 | unsafe fn f<'a>(&'a self, arg: &str) -> &'a str;
+ | ^^^^^^^^^^^^^^^
diff --git a/tests/ui/lifetime_extern_rust.rs b/tests/ui/lifetime_extern_rust.rs
new file mode 100644
index 00000000..72c37cba
--- /dev/null
+++ b/tests/ui/lifetime_extern_rust.rs
@@ -0,0 +1,17 @@
+#[cxx::bridge]
+mod ffi {
+ extern "Rust" {
+ type Opaque;
+ fn f<'a>(&'a self, arg: &str) -> &'a str;
+ }
+}
+
+pub struct Opaque;
+
+impl Opaque {
+ fn f(&self, _arg: &str) -> &str {
+ ""
+ }
+}
+
+fn main() {}
diff --git a/tests/ui/lifetime_extern_rust.stderr b/tests/ui/lifetime_extern_rust.stderr
new file mode 100644
index 00000000..0fc806ad
--- /dev/null
+++ b/tests/ui/lifetime_extern_rust.stderr
@@ -0,0 +1,5 @@
+error: must be `unsafe fn f` in order to expose explicit lifetimes to C++
+ --> $DIR/lifetime_extern_rust.rs:5:9
+ |
+5 | fn f<'a>(&'a self, arg: &str) -> &'a str;
+ | ^^^^^^^^
diff --git a/tests/ui/multiple_parse_error.stderr b/tests/ui/multiple_parse_error.stderr
index 41895071..f736fd1f 100644
--- a/tests/ui/multiple_parse_error.stderr
+++ b/tests/ui/multiple_parse_error.stderr
@@ -1,10 +1,10 @@
-error: struct with generic parameters is not supported yet
+error: unit structs are not supported
--> $DIR/multiple_parse_error.rs:3:5
|
3 | struct Monad<T>;
- | ^^^^^^^^^^^^^^^
+ | ^^^^^^^^^^^^^^^^
-error: unrecognized ABI
+error: unrecognized ABI, requires either "C++" or "Rust"
--> $DIR/multiple_parse_error.rs:5:5
|
5 | extern "Haskell" {}
diff --git a/tests/ui/mut_return.rs b/tests/ui/mut_return.rs
new file mode 100644
index 00000000..f30988b8
--- /dev/null
+++ b/tests/ui/mut_return.rs
@@ -0,0 +1,18 @@
+#[cxx::bridge]
+mod ffi {
+ extern "Rust" {
+ type Mut<'a>;
+ }
+
+ unsafe extern "C++" {
+ type Thing;
+
+ fn f(t: &Thing) -> Pin<&mut CxxString>;
+ unsafe fn g(t: &Thing) -> Pin<&mut CxxString>;
+ fn h(t: Box<Mut>) -> Pin<&mut CxxString>;
+ fn i<'a>(t: Box<Mut<'a>>) -> Pin<&'a mut CxxString>;
+ fn j(t: &Thing) -> &mut [u8];
+ }
+}
+
+fn main() {}
diff --git a/tests/ui/mut_return.stderr b/tests/ui/mut_return.stderr
new file mode 100644
index 00000000..e33393d8
--- /dev/null
+++ b/tests/ui/mut_return.stderr
@@ -0,0 +1,11 @@
+error: &mut return type is not allowed unless there is a &mut argument
+ --> $DIR/mut_return.rs:10:9
+ |
+10 | fn f(t: &Thing) -> Pin<&mut CxxString>;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: &mut return type is not allowed unless there is a &mut argument
+ --> $DIR/mut_return.rs:14:9
+ |
+14 | fn j(t: &Thing) -> &mut [u8];
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/tests/ui/nonlocal_rust_type.rs b/tests/ui/nonlocal_rust_type.rs
new file mode 100644
index 00000000..d8a238ae
--- /dev/null
+++ b/tests/ui/nonlocal_rust_type.rs
@@ -0,0 +1,18 @@
+pub struct MyBuilder<'a> {
+ _s: &'a str,
+}
+
+type OptBuilder<'a> = Option<MyBuilder<'a>>;
+
+#[cxx::bridge]
+mod ffi {
+ extern "Rust" {
+ type OptBuilder<'a>;
+ }
+
+ struct MyBuilder<'a> {
+ rs: Box<OptBuilder<'a>>,
+ }
+}
+
+fn main() {}
diff --git a/tests/ui/nonlocal_rust_type.stderr b/tests/ui/nonlocal_rust_type.stderr
new file mode 100644
index 00000000..d266353c
--- /dev/null
+++ b/tests/ui/nonlocal_rust_type.stderr
@@ -0,0 +1,21 @@
+error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
+ --> $DIR/nonlocal_rust_type.rs:10:9
+ |
+10 | type OptBuilder<'a>;
+ | ^^^^^--------------
+ | | |
+ | | `Option` is not defined in the current crate
+ | impl doesn't use only types from inside the current crate
+ |
+ = note: define and implement a trait or new type instead
+
+error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
+ --> $DIR/nonlocal_rust_type.rs:14:13
+ |
+14 | rs: Box<OptBuilder<'a>>,
+ | ^^^^---------------
+ | | |
+ | | `Option` is not defined in the current crate
+ | impl doesn't use only types from inside the current crate
+ |
+ = note: define and implement a trait or new type instead
diff --git a/tests/ui/opaque_autotraits.rs b/tests/ui/opaque_autotraits.rs
new file mode 100644
index 00000000..99406d74
--- /dev/null
+++ b/tests/ui/opaque_autotraits.rs
@@ -0,0 +1,16 @@
+#[cxx::bridge]
+mod ffi {
+ extern "C++" {
+ type Opaque;
+ }
+}
+
+fn assert_send<T: Send>() {}
+fn assert_sync<T: Sync>() {}
+fn assert_unpin<T: Unpin>() {}
+
+fn main() {
+ assert_send::<ffi::Opaque>();
+ assert_sync::<ffi::Opaque>();
+ assert_unpin::<ffi::Opaque>();
+}
diff --git a/tests/ui/opaque_autotraits.stderr b/tests/ui/opaque_autotraits.stderr
new file mode 100644
index 00000000..4f469eff
--- /dev/null
+++ b/tests/ui/opaque_autotraits.stderr
@@ -0,0 +1,40 @@
+error[E0277]: `*const u8` 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
+ |
+ = help: within `ffi::Opaque`, the trait `Send` is not implemented for `*const u8`
+ = note: required because it appears within the type `[*const u8; 0]`
+ = note: required because it appears within the type `cxx::private::Opaque`
+ = note: required because it appears within the type `ffi::Opaque`
+
+error[E0277]: `*const u8` 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
+ |
+ = help: within `ffi::Opaque`, the trait `Sync` is not implemented for `*const u8`
+ = note: required because it appears within the type `[*const u8; 0]`
+ = note: required because it appears within the type `cxx::private::Opaque`
+ = note: required because it appears within the type `ffi::Opaque`
+
+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: 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`
diff --git a/tests/ui/pin_mut_opaque.rs b/tests/ui/pin_mut_opaque.rs
new file mode 100644
index 00000000..ac1ca43a
--- /dev/null
+++ b/tests/ui/pin_mut_opaque.rs
@@ -0,0 +1,14 @@
+#[cxx::bridge]
+mod ffi {
+ unsafe extern "C++" {
+ type Opaque;
+ fn f(arg: &mut Opaque);
+ fn g(&mut self);
+ fn h(self: &mut Opaque);
+ fn s(s: &mut CxxString);
+ fn v(v: &mut CxxVector<u8>);
+ }
+
+}
+
+fn main() {}
diff --git a/tests/ui/pin_mut_opaque.stderr b/tests/ui/pin_mut_opaque.stderr
new file mode 100644
index 00000000..95af3cd9
--- /dev/null
+++ b/tests/ui/pin_mut_opaque.stderr
@@ -0,0 +1,35 @@
+error: mutable reference to C++ type requires a pin -- use Pin<&mut Opaque>
+ --> $DIR/pin_mut_opaque.rs:5:19
+ |
+5 | fn f(arg: &mut Opaque);
+ | ^^^^^^^^^^^
+
+error: mutable reference to C++ type requires a pin -- use Pin<&mut CxxString>
+ --> $DIR/pin_mut_opaque.rs:8:17
+ |
+8 | fn s(s: &mut CxxString);
+ | ^^^^^^^^^^^^^^
+
+error: mutable reference to C++ type requires a pin -- use Pin<&mut CxxVector<...>>
+ --> $DIR/pin_mut_opaque.rs:9:17
+ |
+9 | fn v(v: &mut CxxVector<u8>);
+ | ^^^^^^^^^^^^^^^^^^
+
+error: needs a cxx::ExternType impl in order to be used as a non-pinned mutable reference in signature of `f`, `g`, `h`
+ --> $DIR/pin_mut_opaque.rs:4:9
+ |
+4 | type Opaque;
+ | ^^^^^^^^^^^
+
+error: mutable reference to opaque C++ type requires a pin -- use `self: Pin<&mut Opaque>`
+ --> $DIR/pin_mut_opaque.rs:6:14
+ |
+6 | fn g(&mut self);
+ | ^^^^^^^^^
+
+error: mutable reference to opaque C++ type requires a pin -- use `self: Pin<&mut Opaque>`
+ --> $DIR/pin_mut_opaque.rs:7:20
+ |
+7 | fn h(self: &mut Opaque);
+ | ^^^^^^^^^^^
diff --git a/tests/ui/ptr_in_fnptr.rs b/tests/ui/ptr_in_fnptr.rs
new file mode 100644
index 00000000..73c97c66
--- /dev/null
+++ b/tests/ui/ptr_in_fnptr.rs
@@ -0,0 +1,8 @@
+#[cxx::bridge]
+mod ffi {
+ unsafe extern "C++" {
+ fn f(callback: fn(p: *const u8));
+ }
+}
+
+fn main() {}
diff --git a/tests/ui/ptr_in_fnptr.stderr b/tests/ui/ptr_in_fnptr.stderr
new file mode 100644
index 00000000..372d4cd9
--- /dev/null
+++ b/tests/ui/ptr_in_fnptr.stderr
@@ -0,0 +1,5 @@
+error: pointer argument requires that the function pointer be marked unsafe
+ --> $DIR/ptr_in_fnptr.rs:4:27
+ |
+4 | fn f(callback: fn(p: *const u8));
+ | ^^^^^^^^^^^^
diff --git a/tests/ui/ptr_missing_unsafe.rs b/tests/ui/ptr_missing_unsafe.rs
new file mode 100644
index 00000000..56f0007c
--- /dev/null
+++ b/tests/ui/ptr_missing_unsafe.rs
@@ -0,0 +1,10 @@
+#[cxx::bridge]
+mod ffi {
+ unsafe extern "C++" {
+ type C;
+
+ fn not_unsafe_ptr(c: *mut C);
+ }
+}
+
+fn main() {}
diff --git a/tests/ui/ptr_missing_unsafe.stderr b/tests/ui/ptr_missing_unsafe.stderr
new file mode 100644
index 00000000..65f53a98
--- /dev/null
+++ b/tests/ui/ptr_missing_unsafe.stderr
@@ -0,0 +1,5 @@
+error: pointer argument requires that the function be marked unsafe
+ --> $DIR/ptr_missing_unsafe.rs:6:27
+ |
+6 | fn not_unsafe_ptr(c: *mut C);
+ | ^^^^^^^^^
diff --git a/tests/ui/ptr_no_const_mut.rs b/tests/ui/ptr_no_const_mut.rs
new file mode 100644
index 00000000..eecec3fb
--- /dev/null
+++ b/tests/ui/ptr_no_const_mut.rs
@@ -0,0 +1,10 @@
+#[cxx::bridge]
+mod ffi {
+ unsafe extern "C++" {
+ type C;
+
+ fn get_neither_const_nor_mut() -> *C;
+ }
+}
+
+fn main() {}
diff --git a/tests/ui/ptr_no_const_mut.stderr b/tests/ui/ptr_no_const_mut.stderr
new file mode 100644
index 00000000..8a391852
--- /dev/null
+++ b/tests/ui/ptr_no_const_mut.stderr
@@ -0,0 +1,13 @@
+error: expected mut or const in raw pointer type
+ --> $DIR/ptr_no_const_mut.rs:6:43
+ |
+6 | fn get_neither_const_nor_mut() -> *C;
+ | ^ expected mut or const in raw pointer type
+ |
+ = help: use `*mut T` or `*const T` as appropriate
+
+error: expected `const` or `mut`
+ --> $DIR/ptr_no_const_mut.rs:6:44
+ |
+6 | fn get_neither_const_nor_mut() -> *C;
+ | ^
diff --git a/tests/ui/ptr_unsupported.rs b/tests/ui/ptr_unsupported.rs
new file mode 100644
index 00000000..9d59c033
--- /dev/null
+++ b/tests/ui/ptr_unsupported.rs
@@ -0,0 +1,12 @@
+#[cxx::bridge]
+mod ffi {
+ unsafe extern "C++" {
+ type C;
+
+ fn get_ptr_to_reference() -> *mut &C;
+ fn get_uniqueptr_to_ptr() -> UniquePtr<*mut C>;
+ fn get_vector_of_ptr() -> UniquePtr<CxxVector<*mut C>>;
+ }
+}
+
+fn main() {}
diff --git a/tests/ui/ptr_unsupported.stderr b/tests/ui/ptr_unsupported.stderr
new file mode 100644
index 00000000..a3e93434
--- /dev/null
+++ b/tests/ui/ptr_unsupported.stderr
@@ -0,0 +1,17 @@
+error: C++ does not allow pointer to reference as a type
+ --> $DIR/ptr_unsupported.rs:6:38
+ |
+6 | fn get_ptr_to_reference() -> *mut &C;
+ | ^^^^^^^
+
+error: unsupported unique_ptr target type
+ --> $DIR/ptr_unsupported.rs:7:38
+ |
+7 | fn get_uniqueptr_to_ptr() -> UniquePtr<*mut C>;
+ | ^^^^^^^^^^^^^^^^^
+
+error: unsupported vector element type
+ --> $DIR/ptr_unsupported.rs:8:45
+ |
+8 | fn get_vector_of_ptr() -> UniquePtr<CxxVector<*mut C>>;
+ | ^^^^^^^^^^^^^^^^^^
diff --git a/tests/ui/reference_to_reference.rs b/tests/ui/reference_to_reference.rs
index 526d0363..91fe1600 100644
--- a/tests/ui/reference_to_reference.rs
+++ b/tests/ui/reference_to_reference.rs
@@ -1,6 +1,6 @@
#[cxx::bridge]
mod ffi {
- extern "C" {
+ unsafe extern "C++" {
type ThingC;
fn repro_c(t: &&ThingC);
}
diff --git a/tests/ui/reserved_name.rs b/tests/ui/reserved_name.rs
index 27acca5e..409e67c0 100644
--- a/tests/ui/reserved_name.rs
+++ b/tests/ui/reserved_name.rs
@@ -4,7 +4,7 @@ mod ffi {
val: usize,
}
- extern "C" {
+ extern "C++" {
type Box;
}
diff --git a/tests/ui/root_namespace.rs b/tests/ui/root_namespace.rs
new file mode 100644
index 00000000..886fbd94
--- /dev/null
+++ b/tests/ui/root_namespace.rs
@@ -0,0 +1,13 @@
+#[cxx::bridge]
+mod ffi {
+ #[namespace = "::"]
+ extern "Rust" {}
+
+ #[namespace = ""]
+ extern "Rust" {}
+
+ #[namespace = ]
+ extern "Rust" {}
+}
+
+fn main() {}
diff --git a/tests/ui/root_namespace.stderr b/tests/ui/root_namespace.stderr
new file mode 100644
index 00000000..a9c01a2e
--- /dev/null
+++ b/tests/ui/root_namespace.stderr
@@ -0,0 +1,5 @@
+error: expected expression, found `]`
+ --> $DIR/root_namespace.rs:9:19
+ |
+9 | #[namespace = ]
+ | ^ expected expression
diff --git a/tests/ui/rust_pinned.rs b/tests/ui/rust_pinned.rs
new file mode 100644
index 00000000..34ca7e33
--- /dev/null
+++ b/tests/ui/rust_pinned.rs
@@ -0,0 +1,14 @@
+use std::marker::PhantomPinned;
+
+#[cxx::bridge]
+mod ffi {
+ extern "Rust" {
+ type Pinned;
+ }
+}
+
+pub struct Pinned {
+ _pinned: PhantomPinned,
+}
+
+fn main() {}
diff --git a/tests/ui/rust_pinned.stderr b/tests/ui/rust_pinned.stderr
new file mode 100644
index 00000000..f16f9d55
--- /dev/null
+++ b/tests/ui/rust_pinned.stderr
@@ -0,0 +1,10 @@
+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`
diff --git a/tests/ui/slice_unsupported.rs b/tests/ui/slice_unsupported.rs
new file mode 100644
index 00000000..7a148dd7
--- /dev/null
+++ b/tests/ui/slice_unsupported.rs
@@ -0,0 +1,10 @@
+#[cxx::bridge]
+mod ffi {
+ unsafe extern "C++" {
+ type Opaque;
+
+ fn f(_: &mut [Opaque]);
+ }
+}
+
+fn main() {}
diff --git a/tests/ui/slice_unsupported.stderr b/tests/ui/slice_unsupported.stderr
new file mode 100644
index 00000000..787076fb
--- /dev/null
+++ b/tests/ui/slice_unsupported.stderr
@@ -0,0 +1,5 @@
+error: unsupported &mut [T] element type: opaque C++ type is not supported yet
+ --> $DIR/slice_unsupported.rs:6:17
+ |
+6 | fn f(_: &mut [Opaque]);
+ | ^^^^^^^^^^^^^
diff --git a/tests/ui/unique_ptr_as_mut.rs b/tests/ui/unique_ptr_as_mut.rs
new file mode 100644
index 00000000..d2c758a9
--- /dev/null
+++ b/tests/ui/unique_ptr_as_mut.rs
@@ -0,0 +1,23 @@
+use cxx::UniquePtr;
+
+#[cxx::bridge]
+mod ffi {
+ struct Shared {
+ x: i32,
+ }
+
+ extern "C++" {
+ type Opaque;
+ }
+
+ impl UniquePtr<Shared> {}
+ impl UniquePtr<Opaque> {}
+}
+
+fn main() {
+ let mut shared = UniquePtr::<ffi::Shared>::null();
+ let _: &mut ffi::Shared = &mut shared;
+
+ let mut opaque = UniquePtr::<ffi::Opaque>::null();
+ let _: &mut ffi::Opaque = &mut opaque;
+}
diff --git a/tests/ui/unique_ptr_as_mut.stderr b/tests/ui/unique_ptr_as_mut.stderr
new file mode 100644
index 00000000..a41f716a
--- /dev/null
+++ b/tests/ui/unique_ptr_as_mut.stderr
@@ -0,0 +1,7 @@
+error[E0596]: cannot borrow data in a dereference of `UniquePtr<ffi::Opaque>` as mutable
+ --> $DIR/unique_ptr_as_mut.rs:22:31
+ |
+22 | let _: &mut ffi::Opaque = &mut opaque;
+ | ^^^^^^^^^^^ cannot borrow as mutable
+ |
+ = help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `UniquePtr<ffi::Opaque>`
diff --git a/tests/ui/unique_ptr_to_opaque.rs b/tests/ui/unique_ptr_to_opaque.rs
index 720ca691..5226ca81 100644
--- a/tests/ui/unique_ptr_to_opaque.rs
+++ b/tests/ui/unique_ptr_to_opaque.rs
@@ -11,7 +11,7 @@ mod outside {
#[cxx::bridge]
mod ffi {
- extern "C" {
+ extern "C++" {
type C = crate::outside::C;
}
diff --git a/tests/ui/unique_ptr_twice.rs b/tests/ui/unique_ptr_twice.rs
index b6cb4d42..14c6f62a 100644
--- a/tests/ui/unique_ptr_twice.rs
+++ b/tests/ui/unique_ptr_twice.rs
@@ -1,6 +1,6 @@
#[cxx::bridge]
mod here {
- extern "C" {
+ extern "C++" {
type C;
}
@@ -9,7 +9,7 @@ mod here {
#[cxx::bridge]
mod there {
- extern "C" {
+ extern "C++" {
type C = crate::here::C;
}
diff --git a/tests/ui/unique_ptr_twice.stderr b/tests/ui/unique_ptr_twice.stderr
index 5686cf14..ee26b646 100644
--- a/tests/ui/unique_ptr_twice.stderr
+++ b/tests/ui/unique_ptr_twice.stderr
@@ -1,4 +1,4 @@
-error[E0119]: conflicting implementations of trait `cxx::private::UniquePtrTarget` for type `here::C`:
+error[E0119]: conflicting implementations of trait `cxx::memory::UniquePtrTarget` for type `here::C`
--> $DIR/unique_ptr_twice.rs:16:5
|
7 | impl UniquePtr<C> {}
diff --git a/tests/ui/unnamed_receiver.rs b/tests/ui/unnamed_receiver.rs
index 917e991f..5f53a0a5 100644
--- a/tests/ui/unnamed_receiver.rs
+++ b/tests/ui/unnamed_receiver.rs
@@ -1,6 +1,6 @@
#[cxx::bridge]
mod ffi {
- extern "C" {
+ unsafe extern "C++" {
type One;
type Two;
fn f(&mut self);
diff --git a/tests/ui/unpin_impl.rs b/tests/ui/unpin_impl.rs
new file mode 100644
index 00000000..129fcb11
--- /dev/null
+++ b/tests/ui/unpin_impl.rs
@@ -0,0 +1,10 @@
+#[cxx::bridge]
+mod ffi {
+ extern "C++" {
+ type Opaque;
+ }
+}
+
+impl Unpin for ffi::Opaque {}
+
+fn main() {}
diff --git a/tests/ui/unpin_impl.stderr b/tests/ui/unpin_impl.stderr
new file mode 100644
index 00000000..01ee19c1
--- /dev/null
+++ b/tests/ui/unpin_impl.stderr
@@ -0,0 +1,8 @@
+error[E0282]: type annotations needed for `fn()`
+ --> $DIR/unpin_impl.rs:4:14
+ |
+1 | #[cxx::bridge]
+ | -------------- consider giving this pattern the explicit type `fn()`, with the type parameters specified
+...
+4 | type Opaque;
+ | ^^^^^^ cannot infer type
diff --git a/tests/ui/unrecognized_receiver.rs b/tests/ui/unrecognized_receiver.rs
index 823bfcf3..eee82593 100644
--- a/tests/ui/unrecognized_receiver.rs
+++ b/tests/ui/unrecognized_receiver.rs
@@ -1,6 +1,6 @@
#[cxx::bridge]
mod ffi {
- extern "C" {
+ unsafe extern "C++" {
fn f(self: &Unrecognized);
}
}
diff --git a/tests/ui/unsupported_elided.stderr b/tests/ui/unsupported_elided.stderr
index 5136390c..932076d9 100644
--- a/tests/ui/unsupported_elided.stderr
+++ b/tests/ui/unsupported_elided.stderr
@@ -1,3 +1,9 @@
+error[E0726]: implicit elided lifetime not allowed here
+ --> $DIR/unsupported_elided.rs:6:14
+ |
+6 | type T;
+ | ^- help: indicate the anonymous lifetime: `<'_>`
+
error[E0106]: missing lifetime specifier
--> $DIR/unsupported_elided.rs:8:24
|
diff --git a/tests/ui/vec_opaque.rs b/tests/ui/vec_opaque.rs
new file mode 100644
index 00000000..d0b279cf
--- /dev/null
+++ b/tests/ui/vec_opaque.rs
@@ -0,0 +1,34 @@
+#[cxx::bridge]
+mod handle {
+ extern "C++" {
+ type Job;
+ }
+}
+
+#[cxx::bridge]
+mod ffi1 {
+ extern "C++" {
+ type Job;
+ }
+
+ extern "Rust" {
+ fn f() -> Vec<Job>;
+ }
+}
+
+#[cxx::bridge]
+mod ffi2 {
+ extern "C++" {
+ type Job = crate::handle::Job;
+ }
+
+ extern "Rust" {
+ fn f() -> Vec<Job>;
+ }
+}
+
+fn f() -> Vec<handle::Job> {
+ unimplemented!()
+}
+
+fn main() {}
diff --git a/tests/ui/vec_opaque.stderr b/tests/ui/vec_opaque.stderr
new file mode 100644
index 00000000..b8dad916
--- /dev/null
+++ b/tests/ui/vec_opaque.stderr
@@ -0,0 +1,22 @@
+error: Rust Vec containing C++ type is not supported yet
+ --> $DIR/vec_opaque.rs:15:19
+ |
+15 | fn f() -> Vec<Job>;
+ | ^^^^^^^^
+
+error: needs a cxx::ExternType impl in order to be used as a vector element in Vec<Job>
+ --> $DIR/vec_opaque.rs:11:9
+ |
+11 | type Job;
+ | ^^^^^^^^
+
+error[E0271]: type mismatch resolving `<handle::Job as ExternType>::Kind == Trivial`
+ --> $DIR/vec_opaque.rs:22:9
+ |
+22 | type Job = crate::handle::Job;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected enum `Trivial`, found enum `cxx::kind::Opaque`
+ |
+ ::: $WORKSPACE/src/extern_type.rs
+ |
+ | 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/wrong_type_id.rs b/tests/ui/wrong_type_id.rs
index e3d13807..9137e516 100644
--- a/tests/ui/wrong_type_id.rs
+++ b/tests/ui/wrong_type_id.rs
@@ -1,13 +1,13 @@
#[cxx::bridge(namespace = "folly")]
mod here {
- extern "C" {
+ extern "C++" {
type StringPiece;
}
}
#[cxx::bridge(namespace = "folly")]
mod there {
- extern "C" {
+ extern "C++" {
type ByteRange = crate::here::StringPiece;
}
}
diff --git a/tests/unique_ptr.rs b/tests/unique_ptr.rs
index e5eb66bc..bbaa3164 100644
--- a/tests/unique_ptr.rs
+++ b/tests/unique_ptr.rs
@@ -1,3 +1,5 @@
+#![allow(clippy::let_underscore_drop)]
+
use cxx::{CxxString, UniquePtr};
#[test]
diff --git a/third-party/BUCK b/third-party/BUCK
index a9a39132..84a5e18b 100644
--- a/third-party/BUCK
+++ b/third-party/BUCK
@@ -7,7 +7,7 @@ rust_library(
rust_library(
name = "cc",
- srcs = glob(["vendor/cc-1.0.62/src/**"]),
+ srcs = glob(["vendor/cc-1.0.67/src/**"]),
visibility = ["PUBLIC"],
)
@@ -25,7 +25,7 @@ rust_library(
rust_library(
name = "codespan-reporting",
- srcs = glob(["vendor/codespan-reporting-0.9.5/src/**"]),
+ srcs = glob(["vendor/codespan-reporting-0.11.1/src/**"]),
visibility = ["PUBLIC"],
deps = [
":termcolor",
@@ -41,7 +41,7 @@ rust_library(
rust_library(
name = "proc-macro2",
- srcs = glob(["vendor/proc-macro2-1.0.24/src/**"]),
+ srcs = glob(["vendor/proc-macro2-1.0.26/src/**"]),
visibility = ["PUBLIC"],
features = [
"proc-macro",
@@ -57,7 +57,7 @@ rust_library(
rust_library(
name = "quote",
- srcs = glob(["vendor/quote-1.0.7/src/**"]),
+ srcs = glob(["vendor/quote-1.0.9/src/**"]),
visibility = ["PUBLIC"],
features = ["proc-macro"],
deps = [":proc-macro2"],
@@ -72,7 +72,7 @@ rust_library(
rust_library(
name = "syn",
- srcs = glob(["vendor/syn-1.0.48/src/**"]),
+ srcs = glob(["vendor/syn-1.0.68/src/**"]),
visibility = ["PUBLIC"],
features = [
"clone-impls",
@@ -91,7 +91,7 @@ rust_library(
rust_library(
name = "termcolor",
- srcs = glob(["vendor/termcolor-1.1.0/src/**"]),
+ srcs = glob(["vendor/termcolor-1.1.2/src/**"]),
)
rust_library(
diff --git a/third-party/BUILD b/third-party/BUILD
index d72b0835..cf1a2883 100644
--- a/third-party/BUILD
+++ b/third-party/BUILD
@@ -1,10 +1,8 @@
load(
"//tools/bazel:rust.bzl",
glob = "third_party_glob",
- rust_binary = "third_party_rust_binary",
rust_library = "third_party_rust_library",
)
-load("@bazel_tools//tools/build_defs/pkg:pkg.bzl", "pkg_tar")
rust_library(
name = "bitflags",
@@ -13,7 +11,7 @@ rust_library(
rust_library(
name = "cc",
- srcs = glob(["vendor/cc-1.0.62/src/**"]),
+ srcs = glob(["vendor/cc-1.0.67/src/**"]),
visibility = ["//visibility:public"],
)
@@ -31,7 +29,7 @@ rust_library(
rust_library(
name = "codespan-reporting",
- srcs = glob(["vendor/codespan-reporting-0.9.5/src/**"]),
+ srcs = glob(["vendor/codespan-reporting-0.11.1/src/**"]),
visibility = ["//visibility:public"],
deps = [
":termcolor",
@@ -47,7 +45,7 @@ rust_library(
rust_library(
name = "proc-macro2",
- srcs = glob(["vendor/proc-macro2-1.0.24/src/**"]),
+ srcs = glob(["vendor/proc-macro2-1.0.26/src/**"]),
crate_features = [
"proc-macro",
"span-locations",
@@ -63,7 +61,7 @@ rust_library(
rust_library(
name = "quote",
- srcs = glob(["vendor/quote-1.0.7/src/**"]),
+ srcs = glob(["vendor/quote-1.0.9/src/**"]),
crate_features = ["proc-macro"],
visibility = ["//visibility:public"],
deps = [":proc-macro2"],
@@ -78,7 +76,7 @@ rust_library(
rust_library(
name = "syn",
- srcs = glob(["vendor/syn-1.0.48/src/**"]),
+ srcs = glob(["vendor/syn-1.0.68/src/**"]),
crate_features = [
"clone-impls",
"derive",
@@ -97,7 +95,7 @@ rust_library(
rust_library(
name = "termcolor",
- srcs = glob(["vendor/termcolor-1.1.0/src/**"]),
+ srcs = glob(["vendor/termcolor-1.1.2/src/**"]),
)
rust_library(
diff --git a/third-party/Cargo.lock b/third-party/Cargo.lock
index 76b95f0e..a95e737b 100644
--- a/third-party/Cargo.lock
+++ b/third-party/Cargo.lock
@@ -1,5 +1,7 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
+version = 3
+
[[package]]
name = "ansi_term"
version = "0.11.0"
@@ -28,9 +30,12 @@ checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
[[package]]
name = "cc"
-version = "1.0.62"
+version = "1.0.67"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f1770ced377336a88a67c473594ccc14eca6f4559217c34f64aac8f83d641b40"
+checksum = "e3c69b077ad434294d3ce9f1f6143a2a4b89a8a2d54ef813d85003a4fd1137fd"
+dependencies = [
+ "jobserver",
+]
[[package]]
name = "clap"
@@ -49,9 +54,9 @@ dependencies = [
[[package]]
name = "codespan-reporting"
-version = "0.9.5"
+version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6e0762455306b1ed42bc651ef6a2197aabda5e1d4a43c34d5eab5c1a3634e81d"
+checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e"
dependencies = [
"termcolor",
"unicode-width",
@@ -59,7 +64,7 @@ dependencies = [
[[package]]
name = "cxx"
-version = "0.5.9"
+version = "1.0.42"
dependencies = [
"cc",
"cxx-build",
@@ -74,7 +79,7 @@ dependencies = [
[[package]]
name = "cxx-build"
-version = "0.5.9"
+version = "1.0.42"
dependencies = [
"cc",
"codespan-reporting",
@@ -89,7 +94,7 @@ dependencies = [
[[package]]
name = "cxx-gen"
-version = "0.6.6"
+version = "0.7.42"
dependencies = [
"cc",
"codespan-reporting",
@@ -109,7 +114,7 @@ dependencies = [
[[package]]
name = "cxxbridge-cmd"
-version = "0.5.9"
+version = "1.0.42"
dependencies = [
"clap",
"codespan-reporting",
@@ -120,11 +125,11 @@ dependencies = [
[[package]]
name = "cxxbridge-flags"
-version = "0.5.9"
+version = "1.0.42"
[[package]]
name = "cxxbridge-macro"
-version = "0.5.9"
+version = "1.0.42"
dependencies = [
"cxx",
"proc-macro2",
@@ -154,18 +159,27 @@ checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
[[package]]
name = "hermit-abi"
-version = "0.1.17"
+version = "0.1.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5aca5565f760fb5b220e499d72710ed156fdb74e631659e99377d9ebfbd13ae8"
+checksum = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c"
dependencies = [
"libc",
]
[[package]]
name = "itoa"
-version = "0.4.6"
+version = "0.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dc6f3ad7b9d11a0c00842ff8de1b60ee58661048eb8049ed33c73594f359d7e6"
+checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736"
+
+[[package]]
+name = "jobserver"
+version = "0.1.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c71313ebb9439f74b00d9d2dcec36440beaf57a6aa0623068441dd7cd81a7f2"
+dependencies = [
+ "libc",
+]
[[package]]
name = "lazy_static"
@@ -175,15 +189,15 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
-version = "0.2.80"
+version = "0.2.92"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4d58d1b70b004888f764dfbf6a26a3b0342a1632d33968e4a179d8011c760614"
+checksum = "56d855069fafbb9b344c0f962150cd2c1187975cb1c22c1522c240d8c4986714"
[[package]]
name = "link-cplusplus"
-version = "1.0.4"
+version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f96aa785c87218ec773df6c510af203872b34e2df2cf47d6e908e5f36231e354"
+checksum = "8f1becd27d473556dc610b8afa1636ef90747b574a84553bc11e82371d5ef2d1"
dependencies = [
"cc",
]
@@ -196,18 +210,18 @@ checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c"
[[package]]
name = "proc-macro2"
-version = "1.0.24"
+version = "1.0.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71"
+checksum = "a152013215dca273577e18d2bf00fa862b89b24169fb78c4c95aeb07992c9cec"
dependencies = [
"unicode-xid",
]
[[package]]
name = "quote"
-version = "1.0.7"
+version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37"
+checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7"
dependencies = [
"proc-macro2",
]
@@ -232,18 +246,18 @@ checksum = "7e114536316b51a5aa7a0e59fc49661fd263c5507dd08bd28de052e57626ce69"
[[package]]
name = "serde"
-version = "1.0.117"
+version = "1.0.125"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b88fa983de7720629c9387e9f517353ed404164b1e482c970a90c1a4aaf7dc1a"
+checksum = "558dc50e1a5a5fa7112ca2ce4effcb321b0300c0d4ccf0776a9f60cd89031171"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
-version = "1.0.117"
+version = "1.0.125"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cbd1ae72adb44aab48f325a02444a5fc079349a8d804c1fc922aed3f7454c74e"
+checksum = "b093b7a2bb58203b5da3056c05b4ec1fed827dcfdb37347a8841695263b3d06d"
dependencies = [
"proc-macro2",
"quote",
@@ -252,9 +266,9 @@ dependencies = [
[[package]]
name = "serde_json"
-version = "1.0.59"
+version = "1.0.64"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dcac07dbffa1c65e7f816ab9eba78eb142c6d44410f4eeba1e26e4f5dfa56b95"
+checksum = "799e97dc9fdae36a5c8b8f2cae9ce2ee9fdce2058c57a93e6099d919fd982f79"
dependencies = [
"itoa",
"ryu",
@@ -269,9 +283,9 @@ checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
[[package]]
name = "syn"
-version = "1.0.48"
+version = "1.0.68"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cc371affeffc477f42a221a1e4297aedcea33d47d19b61455588bd9d8f6b19ac"
+checksum = "3ce15dd3ed8aa2f8eeac4716d6ef5ab58b6b9256db41d7e1a0224c2788e8fd87"
dependencies = [
"proc-macro2",
"quote",
@@ -280,9 +294,9 @@ dependencies = [
[[package]]
name = "termcolor"
-version = "1.1.0"
+version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bb6bfa289a4d7c5766392812c0a1f4c1ba45afa1ad47803c11e1f407d846d75f"
+checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4"
dependencies = [
"winapi-util",
]
@@ -298,18 +312,18 @@ dependencies = [
[[package]]
name = "toml"
-version = "0.5.7"
+version = "0.5.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "75cf45bb0bef80604d001caaec0d09da99611b3c0fd39d3080468875cdb65645"
+checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa"
dependencies = [
"serde",
]
[[package]]
name = "trybuild"
-version = "1.0.35"
+version = "1.0.41"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b7d30fe369fd650072b352b1a9cb9587669de6b89be3b8225544012c1c45292d"
+checksum = "99471a206425fba51842a9186315f32d91c56eadc21ea4c21f847b59cf778f8b"
dependencies = [
"dissimilar",
"glob",
diff --git a/tools/bazel/BUILD b/tools/bazel/BUILD
index e69de29b..d42fc71c 100644
--- a/tools/bazel/BUILD
+++ b/tools/bazel/BUILD
@@ -0,0 +1,7 @@
+load("@bazel_skylib//:bzl_library.bzl", "bzl_library")
+
+bzl_library(
+ name = "bzl_srcs",
+ srcs = glob(["**/*.bzl"]),
+ visibility = ["//visibility:public"],
+)
diff --git a/tools/bazel/rust.bzl b/tools/bazel/rust.bzl
index b7b23e55..9f71923a 100644
--- a/tools/bazel/rust.bzl
+++ b/tools/bazel/rust.bzl
@@ -1,5 +1,7 @@
+"""A module wrapping the core rules of `rules_rust`"""
+
load(
- "@io_bazel_rules_rust//rust:rust.bzl",
+ "@rules_rust//rust:rust.bzl",
_rust_binary = "rust_binary",
_rust_library = "rust_library",
_rust_test = "rust_test",
diff --git a/tools/bazel/rust_cxx_bridge.bzl b/tools/bazel/rust_cxx_bridge.bzl
index 534f6b5a..c7d07e8a 100644
--- a/tools/bazel/rust_cxx_bridge.bzl
+++ b/tools/bazel/rust_cxx_bridge.bzl
@@ -1,7 +1,15 @@
+# buildifier: disable=module-docstring
load("@bazel_skylib//rules:run_binary.bzl", "run_binary")
load("@rules_cc//cc:defs.bzl", "cc_library")
def rust_cxx_bridge(name, src, deps = []):
+ """A macro defining a cxx bridge library
+
+ Args:
+ name (string): The name of the new target
+ src (string): The rust source file to generate a bridge for
+ deps (list, optional): A list of dependencies for the underlying cc_library. Defaults to [].
+ """
native.alias(
name = "%s/header" % name,
actual = src + ".h",
@@ -26,7 +34,7 @@ def rust_cxx_bridge(name, src, deps = []):
"-o",
"$(location %s.cc)" % src,
],
- tool = "//:codegen",
+ tool = "@cxx.rs//:codegen",
)
cc_library(
diff --git a/tools/bazel/vendor.bzl b/tools/bazel/vendor.bzl
index e9f10ac6..e404ad99 100644
--- a/tools/bazel/vendor.bzl
+++ b/tools/bazel/vendor.bzl
@@ -1,3 +1,7 @@
+"""A module defining a repository rule for vendoring the dependencies
+of a crate in the current workspace.
+"""
+
def _impl(repository_ctx):
# Link cxx repository into @third-party.
lockfile = repository_ctx.path(repository_ctx.attr.lockfile)
@@ -48,7 +52,12 @@ def _log_cargo_vendor(repository_ctx, result):
repository_ctx.execute(print, quiet = False)
vendor = repository_rule(
- attrs = {"lockfile": attr.label()},
+ doc = "A rule used to vendor the dependencies of a crate in the current workspace",
+ attrs = {
+ "lockfile": attr.label(
+ doc = "A lockfile providing the set of crates to vendor",
+ ),
+ },
local = True,
implementation = _impl,
)
diff --git a/tools/cargo/build.rs b/tools/cargo/build.rs
index 6c9d22d8..401c7418 100644
--- a/tools/cargo/build.rs
+++ b/tools/cargo/build.rs
@@ -58,11 +58,11 @@ fn main() {
#[cfg(windows)]
if let Some(out_dir) = env::var_os("OUT_DIR") {
let parent_dir = Path::new(&out_dir).join("symlink");
- let from_dir = parent_dir.join("from");
- let to_dir = parent_dir.join("to");
- if fs::create_dir_all(&from_dir).is_ok()
- && fs::remove_dir(&to_dir).is_ok()
- && windows::symlink_dir(&from_dir, &to_dir).is_err()
+ let original_dir = parent_dir.join("original");
+ let link_dir = parent_dir.join("link");
+ if fs::create_dir_all(&original_dir).is_ok()
+ && (!link_dir.exists() || fs::remove_dir(&link_dir).is_ok())
+ && windows::symlink_dir(&original_dir, &link_dir).is_err()
{
message = DENIED;
}