Import cxx 1.0.157 upstream upstream/1.0.157
authorDongHun Kwak <dh0128.kwak@samsung.com>
Tue, 22 Apr 2025 05:00:48 +0000 (14:00 +0900)
committerDongHun Kwak <dh0128.kwak@samsung.com>
Tue, 22 Apr 2025 05:00:48 +0000 (14:00 +0900)
268 files changed:
.bazelignore [new file with mode: 0644]
.bazelrc [new file with mode: 0644]
.bcr/README.md [new file with mode: 0644]
.bcr/config.yml [new file with mode: 0644]
.bcr/metadata.template.json [new file with mode: 0644]
.bcr/presubmit.yml [new file with mode: 0644]
.bcr/source.template.json [new file with mode: 0644]
.buckconfig [new file with mode: 0644]
.buckroot [new file with mode: 0644]
.cargo_vcs_info.json [new file with mode: 0644]
.clang-format [new file with mode: 0644]
.clang-tidy [new file with mode: 0644]
.devcontainer/Dockerfile [new file with mode: 0644]
.devcontainer/README.md [new file with mode: 0644]
.devcontainer/build.Dockerfile [new file with mode: 0644]
.devcontainer/devcontainer.json [new file with mode: 0644]
.gitattributes [new file with mode: 0644]
.github/FUNDING.yml [new file with mode: 0644]
.github/workflows/buck2.yml [new file with mode: 0644]
.github/workflows/ci.yml [new file with mode: 0644]
.github/workflows/install.yml [new file with mode: 0644]
.github/workflows/release.yml [new file with mode: 0644]
.github/workflows/site.yml [new file with mode: 0644]
.gitignore [new file with mode: 0644]
.vscode/README.md [new file with mode: 0644]
.vscode/launch.json [new file with mode: 0644]
.vscode/settings.json [new file with mode: 0644]
.vscode/tasks.json [new file with mode: 0644]
.watchmanconfig [new file with mode: 0644]
BUCK [new file with mode: 0644]
BUILD.bazel [new file with mode: 0644]
Cargo.toml [new file with mode: 0644]
Cargo.toml.orig [new file with mode: 0644]
LICENSE-APACHE [new file with mode: 0644]
LICENSE-MIT [new file with mode: 0644]
MODULE.bazel [new file with mode: 0644]
MODULE.bazel.lock [new file with mode: 0644]
README.md [new file with mode: 0644]
book/.gitignore [new file with mode: 0644]
book/README.md [new file with mode: 0644]
book/book.toml [new file with mode: 0644]
book/build.js [new file with mode: 0755]
book/build.sh [new file with mode: 0755]
book/css/cxx.css [new file with mode: 0644]
book/diagram/.gitignore [new file with mode: 0644]
book/diagram/Makefile [new file with mode: 0644]
book/diagram/overview.tex [new file with mode: 0644]
book/eslint.config.mjs [new file with mode: 0644]
book/package-lock.json [new file with mode: 0644]
book/package.json [new file with mode: 0644]
book/src/404.md [new file with mode: 0644]
book/src/SUMMARY.md [new file with mode: 0644]
book/src/async.md [new file with mode: 0644]
book/src/attributes.md [new file with mode: 0644]
book/src/binding/box.md [new file with mode: 0644]
book/src/binding/cxxstring.md [new file with mode: 0644]
book/src/binding/cxxvector.md [new file with mode: 0644]
book/src/binding/fn.md [new file with mode: 0644]
book/src/binding/rawptr.md [new file with mode: 0644]
book/src/binding/result.md [new file with mode: 0644]
book/src/binding/sharedptr.md [new file with mode: 0644]
book/src/binding/slice.md [new file with mode: 0644]
book/src/binding/str.md [new file with mode: 0644]
book/src/binding/string.md [new file with mode: 0644]
book/src/binding/uniqueptr.md [new file with mode: 0644]
book/src/binding/vec.md [new file with mode: 0644]
book/src/bindings.md [new file with mode: 0644]
book/src/build/bazel.md [new file with mode: 0644]
book/src/build/cargo.md [new file with mode: 0644]
book/src/build/cmake.md [new file with mode: 0644]
book/src/build/other.md [new file with mode: 0644]
book/src/building.md [new file with mode: 0644]
book/src/concepts.md [new file with mode: 0644]
book/src/context.md [new file with mode: 0644]
book/src/cxx.png [new file with mode: 0644]
book/src/extern-c++.md [new file with mode: 0644]
book/src/extern-rust.md [new file with mode: 0644]
book/src/index.md [new file with mode: 0644]
book/src/overview.svg [new file with mode: 0644]
book/src/reference.md [new file with mode: 0644]
book/src/shared.md [new file with mode: 0644]
book/src/tutorial.md [new file with mode: 0644]
book/theme/head.hbs [new file with mode: 0644]
build.rs [new file with mode: 0644]
compile_flags.txt [new file with mode: 0644]
include/cxx.h [new file with mode: 0644]
reindeer.toml [new file with mode: 0644]
rust-toolchain.toml [new file with mode: 0644]
src/cxx.cc [new file with mode: 0644]
src/cxx_string.rs [new file with mode: 0644]
src/cxx_vector.rs [new file with mode: 0644]
src/exception.rs [new file with mode: 0644]
src/extern_type.rs [new file with mode: 0644]
src/fmt.rs [new file with mode: 0644]
src/function.rs [new file with mode: 0644]
src/hash.rs [new file with mode: 0644]
src/lib.rs [new file with mode: 0644]
src/lossy.rs [new file with mode: 0644]
src/macros/assert.rs [new file with mode: 0644]
src/macros/mod.rs [new file with mode: 0644]
src/memory.rs [new file with mode: 0644]
src/opaque.rs [new file with mode: 0644]
src/result.rs [new file with mode: 0644]
src/rust_slice.rs [new file with mode: 0644]
src/rust_str.rs [new file with mode: 0644]
src/rust_string.rs [new file with mode: 0644]
src/rust_type.rs [new file with mode: 0644]
src/rust_vec.rs [new file with mode: 0644]
src/shared_ptr.rs [new file with mode: 0644]
src/symbols/exception.rs [new file with mode: 0644]
src/symbols/mod.rs [new file with mode: 0644]
src/symbols/rust_slice.rs [new file with mode: 0644]
src/symbols/rust_str.rs [new file with mode: 0644]
src/symbols/rust_string.rs [new file with mode: 0644]
src/symbols/rust_vec.rs [new file with mode: 0644]
src/type_id.rs [new file with mode: 0644]
src/unique_ptr.rs [new file with mode: 0644]
src/unwind.rs [new file with mode: 0644]
src/vector.rs [new file with mode: 0644]
src/weak_ptr.rs [new file with mode: 0644]
tests/BUCK [new file with mode: 0644]
tests/BUILD.bazel [new file with mode: 0644]
tests/compiletest.rs [new file with mode: 0644]
tests/cxx_gen.rs [new file with mode: 0644]
tests/cxx_string.rs [new file with mode: 0644]
tests/cxx_vector.rs [new file with mode: 0644]
tests/test.rs [new file with mode: 0644]
tests/ui/array_len_expr.rs [new file with mode: 0644]
tests/ui/array_len_expr.stderr [new file with mode: 0644]
tests/ui/array_len_suffix.rs [new file with mode: 0644]
tests/ui/array_len_suffix.stderr [new file with mode: 0644]
tests/ui/async_fn.rs [new file with mode: 0644]
tests/ui/async_fn.stderr [new file with mode: 0644]
tests/ui/bad_explicit_impl.rs [new file with mode: 0644]
tests/ui/bad_explicit_impl.stderr [new file with mode: 0644]
tests/ui/by_value_not_supported.rs [new file with mode: 0644]
tests/ui/by_value_not_supported.stderr [new file with mode: 0644]
tests/ui/const_fn.rs [new file with mode: 0644]
tests/ui/const_fn.stderr [new file with mode: 0644]
tests/ui/cxx_crate_name_qualified_cxx_string.rs [new file with mode: 0644]
tests/ui/cxx_crate_name_qualified_cxx_string.stderr [new file with mode: 0644]
tests/ui/data_enums.rs [new file with mode: 0644]
tests/ui/data_enums.stderr [new file with mode: 0644]
tests/ui/deny_elided_lifetimes.rs [new file with mode: 0644]
tests/ui/deny_elided_lifetimes.stderr [new file with mode: 0644]
tests/ui/deny_missing_docs.rs [new file with mode: 0644]
tests/ui/deny_missing_docs.stderr [new file with mode: 0644]
tests/ui/derive_duplicate.rs [new file with mode: 0644]
tests/ui/derive_duplicate.stderr [new file with mode: 0644]
tests/ui/derive_noncopy.rs [new file with mode: 0644]
tests/ui/derive_noncopy.stderr [new file with mode: 0644]
tests/ui/drop_shared.rs [new file with mode: 0644]
tests/ui/drop_shared.stderr [new file with mode: 0644]
tests/ui/empty_enum.rs [new file with mode: 0644]
tests/ui/empty_enum.stderr [new file with mode: 0644]
tests/ui/empty_struct.rs [new file with mode: 0644]
tests/ui/empty_struct.stderr [new file with mode: 0644]
tests/ui/enum_inconsistent.rs [new file with mode: 0644]
tests/ui/enum_inconsistent.stderr [new file with mode: 0644]
tests/ui/enum_match_without_wildcard.rs [new file with mode: 0644]
tests/ui/enum_match_without_wildcard.stderr [new file with mode: 0644]
tests/ui/enum_out_of_bounds.rs [new file with mode: 0644]
tests/ui/enum_out_of_bounds.stderr [new file with mode: 0644]
tests/ui/enum_overflows.rs [new file with mode: 0644]
tests/ui/enum_overflows.stderr [new file with mode: 0644]
tests/ui/enum_receiver.rs [new file with mode: 0644]
tests/ui/enum_receiver.stderr [new file with mode: 0644]
tests/ui/enum_unsatisfiable.rs [new file with mode: 0644]
tests/ui/enum_unsatisfiable.stderr [new file with mode: 0644]
tests/ui/expected_named.rs [new file with mode: 0644]
tests/ui/expected_named.stderr [new file with mode: 0644]
tests/ui/extern_fn_abi.rs [new file with mode: 0644]
tests/ui/extern_fn_abi.stderr [new file with mode: 0644]
tests/ui/extern_type_bound.rs [new file with mode: 0644]
tests/ui/extern_type_bound.stderr [new file with mode: 0644]
tests/ui/extern_type_generic.rs [new file with mode: 0644]
tests/ui/extern_type_generic.stderr [new file with mode: 0644]
tests/ui/extern_type_lifetime_bound.rs [new file with mode: 0644]
tests/ui/extern_type_lifetime_bound.stderr [new file with mode: 0644]
tests/ui/fallible_fnptr.rs [new file with mode: 0644]
tests/ui/fallible_fnptr.stderr [new file with mode: 0644]
tests/ui/function_with_body.rs [new file with mode: 0644]
tests/ui/function_with_body.stderr [new file with mode: 0644]
tests/ui/generic_enum.rs [new file with mode: 0644]
tests/ui/generic_enum.stderr [new file with mode: 0644]
tests/ui/impl_trait_for_type.rs [new file with mode: 0644]
tests/ui/impl_trait_for_type.stderr [new file with mode: 0644]
tests/ui/include.rs [new file with mode: 0644]
tests/ui/include.stderr [new file with mode: 0644]
tests/ui/lifetime_extern_cxx.rs [new file with mode: 0644]
tests/ui/lifetime_extern_cxx.stderr [new file with mode: 0644]
tests/ui/lifetime_extern_rust.rs [new file with mode: 0644]
tests/ui/lifetime_extern_rust.stderr [new file with mode: 0644]
tests/ui/missing_unsafe.rs [new file with mode: 0644]
tests/ui/missing_unsafe.stderr [new file with mode: 0644]
tests/ui/multiple_parse_error.rs [new file with mode: 0644]
tests/ui/multiple_parse_error.stderr [new file with mode: 0644]
tests/ui/mut_return.rs [new file with mode: 0644]
tests/ui/mut_return.stderr [new file with mode: 0644]
tests/ui/non_integer_discriminant_enum.rs [new file with mode: 0644]
tests/ui/non_integer_discriminant_enum.stderr [new file with mode: 0644]
tests/ui/nonempty_impl_block.rs [new file with mode: 0644]
tests/ui/nonempty_impl_block.stderr [new file with mode: 0644]
tests/ui/nonlocal_rust_type.rs [new file with mode: 0644]
tests/ui/nonlocal_rust_type.stderr [new file with mode: 0644]
tests/ui/opaque_autotraits.rs [new file with mode: 0644]
tests/ui/opaque_autotraits.stderr [new file with mode: 0644]
tests/ui/opaque_not_sized.rs [new file with mode: 0644]
tests/ui/opaque_not_sized.stderr [new file with mode: 0644]
tests/ui/pin_mut_opaque.rs [new file with mode: 0644]
tests/ui/pin_mut_opaque.stderr [new file with mode: 0644]
tests/ui/ptr_in_fnptr.rs [new file with mode: 0644]
tests/ui/ptr_in_fnptr.stderr [new file with mode: 0644]
tests/ui/ptr_missing_unsafe.rs [new file with mode: 0644]
tests/ui/ptr_missing_unsafe.stderr [new file with mode: 0644]
tests/ui/ptr_no_const_mut.rs [new file with mode: 0644]
tests/ui/ptr_no_const_mut.stderr [new file with mode: 0644]
tests/ui/ptr_unsupported.rs [new file with mode: 0644]
tests/ui/ptr_unsupported.stderr [new file with mode: 0644]
tests/ui/raw_ident_namespace.rs [new file with mode: 0644]
tests/ui/raw_ident_namespace.stderr [new file with mode: 0644]
tests/ui/reference_to_reference.rs [new file with mode: 0644]
tests/ui/reference_to_reference.stderr [new file with mode: 0644]
tests/ui/reserved_lifetime.rs [new file with mode: 0644]
tests/ui/reserved_lifetime.stderr [new file with mode: 0644]
tests/ui/reserved_name.rs [new file with mode: 0644]
tests/ui/reserved_name.stderr [new file with mode: 0644]
tests/ui/result_no_display.rs [new file with mode: 0644]
tests/ui/result_no_display.stderr [new file with mode: 0644]
tests/ui/root_namespace.rs [new file with mode: 0644]
tests/ui/root_namespace.stderr [new file with mode: 0644]
tests/ui/rust_pinned.rs [new file with mode: 0644]
tests/ui/rust_pinned.stderr [new file with mode: 0644]
tests/ui/slice_of_type_alias.rs [new file with mode: 0644]
tests/ui/slice_of_type_alias.stderr [new file with mode: 0644]
tests/ui/slice_unsupported.rs [new file with mode: 0644]
tests/ui/slice_unsupported.stderr [new file with mode: 0644]
tests/ui/struct_cycle.rs [new file with mode: 0644]
tests/ui/struct_cycle.stderr [new file with mode: 0644]
tests/ui/type_alias_rust.rs [new file with mode: 0644]
tests/ui/type_alias_rust.stderr [new file with mode: 0644]
tests/ui/unique_ptr_as_mut.rs [new file with mode: 0644]
tests/ui/unique_ptr_as_mut.stderr [new file with mode: 0644]
tests/ui/unique_ptr_to_opaque.rs [new file with mode: 0644]
tests/ui/unique_ptr_to_opaque.stderr [new file with mode: 0644]
tests/ui/unique_ptr_twice.rs [new file with mode: 0644]
tests/ui/unique_ptr_twice.stderr [new file with mode: 0644]
tests/ui/unnamed_receiver.rs [new file with mode: 0644]
tests/ui/unnamed_receiver.stderr [new file with mode: 0644]
tests/ui/unpin_impl.rs [new file with mode: 0644]
tests/ui/unpin_impl.stderr [new file with mode: 0644]
tests/ui/unrecognized_receiver.rs [new file with mode: 0644]
tests/ui/unrecognized_receiver.stderr [new file with mode: 0644]
tests/ui/unsupported_elided.rs [new file with mode: 0644]
tests/ui/unsupported_elided.stderr [new file with mode: 0644]
tests/ui/vec_opaque.rs [new file with mode: 0644]
tests/ui/vec_opaque.stderr [new file with mode: 0644]
tests/ui/vector_autotraits.rs [new file with mode: 0644]
tests/ui/vector_autotraits.stderr [new file with mode: 0644]
tests/ui/wrong_type_id.rs [new file with mode: 0644]
tests/ui/wrong_type_id.stderr [new file with mode: 0644]
tests/unique_ptr.rs [new file with mode: 0644]
tools/bazel/BUILD.bazel [new file with mode: 0644]
tools/bazel/extension.bzl [new file with mode: 0644]
tools/bazel/rust_cxx_bridge.bzl [new file with mode: 0644]
tools/buck/rust_cxx_bridge.bzl [new file with mode: 0644]
tools/buck/toolchains/BUCK [new file with mode: 0644]
tools/cargo/build.rs [new file with mode: 0644]

diff --git a/.bazelignore b/.bazelignore
new file mode 100644 (file)
index 0000000..c42dab3
--- /dev/null
@@ -0,0 +1,2 @@
+target/
+tools/buck/buck2/
diff --git a/.bazelrc b/.bazelrc
new file mode 100644 (file)
index 0000000..09d078e
--- /dev/null
+++ b/.bazelrc
@@ -0,0 +1,21 @@
+###############################################################################
+## Bazel Configuration Flags
+##
+## `.bazelrc` is a Bazel configuration file.
+## https://bazel.build/docs/best-practices#bazelrc-file
+###############################################################################
+
+build --enable_platform_specific_config
+build:linux --@rules_rust//:extra_rustc_flags=-Clink-arg=-fuse-ld=lld
+build:linux --cxxopt=-std=c++17
+build:macos --cxxopt=-std=c++17
+
+###############################################################################
+## Custom user flags
+##
+## This should always be the last thing in the `.bazelrc` file to ensure
+## consistent behavior when setting flags in that file as `.bazelrc` files are
+## evaluated top to bottom.
+###############################################################################
+
+try-import %workspace%/user.bazelrc
diff --git a/.bcr/README.md b/.bcr/README.md
new file mode 100644 (file)
index 0000000..44ae7fe
--- /dev/null
@@ -0,0 +1,9 @@
+# Bazel Central Registry
+
+When the ruleset is released, we want it to be published to the
+Bazel Central Registry automatically:
+<https://registry.bazel.build>
+
+This folder contains configuration files to automate the publish step.
+See <https://github.com/bazel-contrib/publish-to-bcr/blob/main/templates/README.md>
+for authoritative documentation about these files.
diff --git a/.bcr/config.yml b/.bcr/config.yml
new file mode 100644 (file)
index 0000000..8531afc
--- /dev/null
@@ -0,0 +1,3 @@
+fixedReleaser:
+  login: dtolnay
+  email: dtolnay@gmail.com
diff --git a/.bcr/metadata.template.json b/.bcr/metadata.template.json
new file mode 100644 (file)
index 0000000..0982309
--- /dev/null
@@ -0,0 +1,16 @@
+{
+  "homepage": "https://cxx.rs",
+  "maintainers": [
+    {
+      "github": "dtolnay",
+      "github_user_id": 1940490,
+      "email": "dtolnay@gmail.com",
+      "name": "David Tolnay"
+    }
+  ],
+  "repository": [
+    "github:dtolnay/cxx"
+  ],
+  "versions": [],
+  "yanked_versions": {}
+}
diff --git a/.bcr/presubmit.yml b/.bcr/presubmit.yml
new file mode 100644 (file)
index 0000000..b5083f5
--- /dev/null
@@ -0,0 +1,15 @@
+matrix:
+  platform:
+    - macos_arm64
+    - ubuntu2404
+    - windows
+  bazel: [7.x, 8.x]
+tasks:
+  verify_targets:
+    name: Verify build targets
+    platform: ${{ platform }}
+    bazel: ${{ bazel }}
+    build_targets:
+      - '@cxx.rs//...'
+    test_targets:
+      - '@cxx.rs//...'
diff --git a/.bcr/source.template.json b/.bcr/source.template.json
new file mode 100644 (file)
index 0000000..902c238
--- /dev/null
@@ -0,0 +1,5 @@
+{
+  "integrity": "",
+  "strip_prefix": "{REPO}-{VERSION}",
+  "url": "https://github.com/{OWNER}/{REPO}/releases/download/{TAG}/{REPO}-{VERSION}.tar.gz"
+}
diff --git a/.buckconfig b/.buckconfig
new file mode 100644 (file)
index 0000000..1878b58
--- /dev/null
@@ -0,0 +1,30 @@
+[cells]
+root = .
+prelude = tools/buck/prelude
+toolchains = tools/buck/toolchains
+none = none
+
+[external_cells]
+prelude = bundled
+
+[cell_aliases]
+config = prelude
+fbcode = none
+fbsource = none
+
+[project]
+# Hide BUCK files under target/package/ from `buck build ...`. Otherwise:
+#   $ buck build ...
+#   //target/package/cxx-0.3.0/tests:ffi references non-existing file or directory 'target/package/cxx-0.3.0/tests/ffi/lib.rs'
+#
+# Also hide some Bazel-managed directories that contain symlinks to the repo root.
+ignore = \
+    .git, \
+    bazel-bin, \
+    bazel-cxx, \
+    bazel-out, \
+    bazel-testlogs, \
+    target
+
+[parser]
+target_platform_detector_spec = target:root//...->prelude//platforms:default
diff --git a/.buckroot b/.buckroot
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json
new file mode 100644 (file)
index 0000000..76a464f
--- /dev/null
@@ -0,0 +1,6 @@
+{
+  "git": {
+    "sha1": "f9d547b60324bc02d9983622159973a75d06ea10"
+  },
+  "path_in_vcs": ""
+}
\ No newline at end of file
diff --git a/.clang-format b/.clang-format
new file mode 100644 (file)
index 0000000..8ea286f
--- /dev/null
@@ -0,0 +1,3 @@
+AlwaysBreakTemplateDeclarations: true
+MaxEmptyLinesToKeep: 3
+ReflowComments: false
diff --git a/.clang-tidy b/.clang-tidy
new file mode 100644 (file)
index 0000000..671d539
--- /dev/null
@@ -0,0 +1,20 @@
+Checks:
+  clang-analyzer-*,
+  clang-diagnostic-*,
+  cppcoreguidelines-*,
+  modernize-*,
+  -cppcoreguidelines-avoid-const-or-ref-data-members,
+  -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-return-braced-init-list,
+  -modernize-use-default-member-init,
+  -modernize-use-equals-default,
+  -modernize-use-trailing-return-type,
+HeaderFilterRegex: cxx\.h
diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile
new file mode 100644 (file)
index 0000000..93cfc05
--- /dev/null
@@ -0,0 +1 @@
+FROM dtolnay/devcontainer:latest
diff --git a/.devcontainer/README.md b/.devcontainer/README.md
new file mode 100644 (file)
index 0000000..b30aebc
--- /dev/null
@@ -0,0 +1,4 @@
+This directory contains the container setup used when developing CXX inside of
+GitHub [Codespaces].
+
+[Codespaces]: https://github.com/features/codespaces
diff --git a/.devcontainer/build.Dockerfile b/.devcontainer/build.Dockerfile
new file mode 100644 (file)
index 0000000..74ddd7b
--- /dev/null
@@ -0,0 +1,14 @@
+FROM mcr.microsoft.com/devcontainers/rust:bookworm
+
+RUN apt-get update \
+    && export DEBIAN_FRONTEND=noninteractive \
+    && apt-get -y install --no-install-recommends clang lld zstd \
+    && apt-get clean \
+    && rm -rf /var/lib/apt/lists/* \
+    && wget -q -O /usr/local/bin/bazel https://github.com/bazelbuild/bazelisk/releases/latest/download/bazelisk-linux-amd64 \
+    && wget -q -O /tmp/buck.zst https://github.com/facebook/buck2/releases/download/latest/buck2-x86_64-unknown-linux-gnu.zst \
+    && wget -q -O /usr/local/bin/buildifier https://github.com/bazelbuild/buildtools/releases/latest/download/buildifier-linux-amd64 \
+    && unzstd /tmp/buck.zst -o /usr/local/bin/buck \
+    && chmod +x /usr/local/bin/bazel /usr/local/bin/buck /usr/local/bin/buildifier \
+    && rm /tmp/buck.zst \
+    && rustup component add rust-analyzer rust-src
diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json
new file mode 100644 (file)
index 0000000..b5b2911
--- /dev/null
@@ -0,0 +1,20 @@
+{
+    "name": "Rust",
+    "build": {
+        "dockerfile": "Dockerfile"
+    },
+    "runArgs": ["--cap-add=SYS_PTRACE", "--security-opt", "seccomp=unconfined"],
+    "settings": {
+        "terminal.integrated.shell.linux": "/bin/bash",
+        "lldb.executable": "/usr/bin/lldb",
+        "files.watcherExclude": {
+            "**/target/**": true
+        }
+    },
+    "extensions": [
+        "BazelBuild.vscode-bazel",
+        "ms-vscode.cpptools",
+        "rust-lang.rust-analyzer",
+        "vadimcn.vscode-lldb"
+    ]
+}
diff --git a/.gitattributes b/.gitattributes
new file mode 100644 (file)
index 0000000..1cdc71c
--- /dev/null
@@ -0,0 +1,3 @@
+MODULE.bazel.lock linguist-generated
+third-party/BUCK linguist-generated
+third-party/bazel/** linguist-generated
diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
new file mode 100644 (file)
index 0000000..7507077
--- /dev/null
@@ -0,0 +1 @@
+github: dtolnay
diff --git a/.github/workflows/buck2.yml b/.github/workflows/buck2.yml
new file mode 100644 (file)
index 0000000..9c3c54a
--- /dev/null
@@ -0,0 +1,28 @@
+name: Buck2
+
+on:
+  push:
+  workflow_dispatch:
+  schedule: [cron: "40 1,13 * * *"]
+
+permissions:
+  contents: read
+
+jobs:
+  buck2:
+    name: Buck2 on ${{matrix.os == 'ubuntu' && 'Linux' || matrix.os == 'macos' && 'macOS' || matrix.os == 'windows' && 'Windows' || '???'}}
+    runs-on: ${{matrix.os}}-latest
+    strategy:
+      fail-fast: false
+      matrix:
+        os: [ubuntu, macos, windows]
+    timeout-minutes: 45
+    steps:
+      - uses: actions/checkout@v4
+      - uses: dtolnay/rust-toolchain@stable
+        with:
+          components: rust-src
+      - uses: dtolnay/install-buck2@latest
+      - run: buck2 run demo
+      - run: buck2 build ...
+      - run: buck2 test ...
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644 (file)
index 0000000..b52099e
--- /dev/null
@@ -0,0 +1,224 @@
+name: CI
+
+on:
+  push:
+  pull_request:
+  workflow_dispatch:
+  schedule: [cron: "40 1 * * *"]
+
+permissions:
+  contents: read
+
+jobs:
+  pre_ci:
+    uses: dtolnay/.github/.github/workflows/pre_ci.yml@master
+
+  test:
+    name: ${{matrix.name || format('Rust {0}', matrix.rust)}}
+    needs: pre_ci
+    if: needs.pre_ci.outputs.continue
+    runs-on: ${{matrix.os}}-latest
+    strategy:
+      fail-fast: false
+      matrix:
+        rust: [nightly, beta, stable, 1.82.0, 1.80.0, 1.77.0, 1.74.0, 1.73.0]
+        os: [ubuntu]
+        cc: ['']
+        flags: ['']
+        include:
+          - name: Cargo on macOS
+            rust: nightly
+            os: macos
+          - name: Cargo on Windows (msvc)
+            rust: nightly-x86_64-pc-windows-msvc
+            os: windows
+            flags: /EHsc
+          - name: Clang
+            rust: nightly
+            cc: clang++
+            os: ubuntu
+            flags: -std=c++20 -Werror -Wall
+          - name: C++14
+            rust: nightly
+            os: ubuntu
+            flags: -std=c++14 -Werror -Wall
+          - name: C++17
+            rust: nightly
+            os: ubuntu
+            flags: -std=c++17 -Werror -Wall
+          - name: C++20
+            rust: nightly
+            os: ubuntu
+            flags: -std=c++20 -Werror -Wall
+    env:
+      CXX: ${{matrix.cc}}
+      CXXFLAGS: ${{matrix.flags}}
+      RUSTFLAGS: --cfg deny_warnings -Dwarnings
+    timeout-minutes: 45
+    steps:
+      - name: Enable symlinks (windows)
+        if: matrix.os == 'windows'
+        run: git config --global core.symlinks true
+      - uses: actions/checkout@v4
+      - uses: dtolnay/rust-toolchain@master
+        with:
+          toolchain: ${{matrix.rust}}
+          components: rust-src
+      - name: Determine test suite subset
+        # Our Windows and macOS jobs are the longest running, so exclude the
+        # relatively slow compiletest from them to speed up end-to-end CI time,
+        # except during cron builds when no human is presumably waiting on the
+        # build. The extra coverage is not particularly valuable and we can
+        # still ensure the test is kept passing on the basis of the scheduled
+        # builds.
+        run: |
+          echo RUSTFLAGS=$RUSTFLAGS >> $GITHUB_ENV
+          echo exclude=--exclude cxx-test-suite ${{matrix.rust == '1.73.0' && '--exclude cxxbridge-cmd' || ''}} >> $GITHUB_OUTPUT
+        env:
+          RUSTFLAGS: ${{env.RUSTFLAGS}} ${{matrix.os != 'ubuntu' && github.event_name != 'schedule' && '--cfg skip_ui_tests' || ''}}
+        id: testsuite
+        shell: bash
+      - name: Ignore macOS linker warning
+        run: echo RUSTFLAGS=${RUSTFLAGS}\ -Alinker_messages >> $GITHUB_ENV
+        if: matrix.os == 'macos'
+      - run: cargo run --manifest-path demo/Cargo.toml
+      - run: cargo test --workspace ${{steps.testsuite.outputs.exclude}}
+        if: matrix.rust != '1.74.0' && matrix.rust != '1.73.0'
+      - run: cargo check --no-default-features --features alloc
+        env:
+          RUSTFLAGS: --cfg compile_error_if_std ${{env.RUSTFLAGS}}
+      - run: cargo check --no-default-features
+        env:
+          RUSTFLAGS: --cfg compile_error_if_alloc --cfg cxx_experimental_no_alloc ${{env.RUSTFLAGS}}
+      - uses: actions/upload-artifact@v4
+        if: matrix.os == 'ubuntu' && matrix.rust == 'nightly' && always()
+        with:
+          name: Cargo.lock
+          path: Cargo.lock
+        continue-on-error: true
+
+  reindeer:
+    name: Reindeer
+    runs-on: ubuntu-latest
+    if: github.event_name != 'pull_request'
+    timeout-minutes: 45
+    steps:
+      - uses: actions/checkout@v4
+      - uses: dtolnay/rust-toolchain@stable
+        with:
+          components: rust-src
+      - uses: dtolnay/install@reindeer
+      - run: reindeer buckify
+        working-directory: third-party
+      - name: Check reindeer-generated BUCK file up to date
+        run: git diff --exit-code
+
+  bazel:
+    name: Bazel on ${{matrix.os == 'ubuntu' && 'Linux' || matrix.os == 'macos' && 'macOS' || matrix.os == 'windows' && 'Windows' || '???'}}
+    runs-on: ${{matrix.os}}-latest
+    if: github.event_name != 'pull_request'
+    strategy:
+      fail-fast: false
+      matrix:
+        os: [ubuntu, macos, windows]
+    timeout-minutes: 45
+    steps:
+      - uses: actions/checkout@v4
+      - name: Install lld
+        run: sudo apt-get install lld
+        if: matrix.os == 'ubuntu'
+      - name: Set bazelrc for Windows
+        run: echo "startup --output_user_root=D:/bzl" > user.bazelrc
+        if: matrix.os == 'windows'
+      - run: bazel --version
+      - run: bazel run demo --verbose_failures --noshow_progress ${{matrix.os == 'macos' && '--xcode_version_config=tools/bazel:github_actions_xcodes' || ''}}
+      - run: bazel test ... --verbose_failures --noshow_progress ${{matrix.os == 'macos' && '--xcode_version_config=tools/bazel:github_actions_xcodes' || ''}}
+      - name: Check MODULE.bazel.lock up to date
+        run: git diff --exit-code
+      - run: bazel run //third-party:vendor
+        if: matrix.os == 'ubuntu' || matrix.os == 'macos'
+      - name: Check third-party/bazel up to date
+        run: git diff --exit-code
+        if: matrix.os == 'ubuntu' || matrix.os == 'macos'
+
+  minimal:
+    name: Minimal versions
+    needs: pre_ci
+    if: needs.pre_ci.outputs.continue
+    runs-on: ubuntu-latest
+    timeout-minutes: 45
+    steps:
+      - uses: actions/checkout@v4
+      - uses: dtolnay/rust-toolchain@nightly
+      - run: cargo generate-lockfile -Z minimal-versions
+      - run: cargo check --locked --workspace
+
+  doc:
+    name: Documentation
+    needs: pre_ci
+    if: needs.pre_ci.outputs.continue
+    runs-on: ubuntu-latest
+    timeout-minutes: 45
+    env:
+      RUSTDOCFLAGS: -Dwarnings
+    steps:
+      - uses: actions/checkout@v4
+      - uses: dtolnay/rust-toolchain@nightly
+        with:
+          components: rust-src
+      - uses: dtolnay/install@cargo-docs-rs
+      - run: cargo docs-rs
+      - run: cargo docs-rs -p cxx-build
+      - run: cargo docs-rs -p cxx-gen
+      - run: cargo docs-rs -p cxxbridge-flags
+      - run: cargo docs-rs -p cxxbridge-macro
+
+  clippy:
+    name: Clippy
+    runs-on: ubuntu-latest
+    if: github.event_name != 'pull_request'
+    timeout-minutes: 45
+    env:
+      RUSTFLAGS: -Dwarnings
+    steps:
+      - uses: actions/checkout@v4
+      - uses: dtolnay/rust-toolchain@nightly
+        with:
+          components: clippy, rust-src
+      - run: cargo clippy --workspace --tests --exclude demo -- -Dclippy::all -Dclippy::pedantic
+      - run: cargo clippy --manifest-path demo/Cargo.toml -- -Dclippy::all
+
+  clang-tidy:
+    name: Clang Tidy
+    runs-on: ubuntu-latest
+    if: github.event_name != 'pull_request'
+    timeout-minutes: 45
+    steps:
+      - uses: actions/checkout@v4
+      - name: Install clang-tidy
+        run: sudo apt-get install clang-tidy-18
+      - name: Run clang-tidy
+        run: clang-tidy-18 src/cxx.cc --warnings-as-errors=*
+
+  eslint:
+    name: ESLint
+    runs-on: ubuntu-latest
+    if: github.event_name != 'pull_request'
+    timeout-minutes: 45
+    steps:
+      - uses: actions/checkout@v4
+      - run: npm install
+        working-directory: book
+      - run: npx eslint
+        working-directory: book
+
+  outdated:
+    name: Outdated
+    runs-on: ubuntu-latest
+    if: github.event_name != 'pull_request'
+    timeout-minutes: 45
+    steps:
+      - uses: actions/checkout@v4
+      - uses: dtolnay/rust-toolchain@stable
+      - uses: dtolnay/install@cargo-outdated
+      - run: cargo outdated --workspace --exit-code 1
diff --git a/.github/workflows/install.yml b/.github/workflows/install.yml
new file mode 100644 (file)
index 0000000..025fe23
--- /dev/null
@@ -0,0 +1,18 @@
+name: Install
+
+on:
+  workflow_dispatch:
+  schedule: [cron: "40 1 * * *"]
+  push: {tags: ['*']}
+
+permissions: {}
+
+env:
+  RUSTFLAGS: -Dwarnings
+
+jobs:
+  install:
+    name: Install
+    uses: dtolnay/.github/.github/workflows/check_install.yml@master
+    with:
+      crate: cxxbridge-cmd
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
new file mode 100644 (file)
index 0000000..196e389
--- /dev/null
@@ -0,0 +1,23 @@
+name: Release
+
+on:
+  release:
+    types: [released]
+
+permissions:
+  attestations: write
+  contents: write
+  id-token: write
+
+jobs:
+  upload:
+    uses: dtolnay/.github/.github/workflows/release_tgz.yml@master
+
+  publish-to-bcr:
+    needs: upload
+    uses: bazel-contrib/publish-to-bcr/.github/workflows/publish.yaml@47913235f61615d02c989d652c4d10c45c0c4f0b
+    with:
+      tag_name: ${{github.event.release.tag_name}}
+      registry_fork: dtolnay-contrib/bazel-central-registry
+    secrets:
+      publish_token: ${{secrets.PUBLISH_TOKEN}}
diff --git a/.github/workflows/site.yml b/.github/workflows/site.yml
new file mode 100644 (file)
index 0000000..555be19
--- /dev/null
@@ -0,0 +1,37 @@
+name: Deploy
+
+on:
+  push:
+    branches:
+      - master
+    paths:
+      - book/**
+      - .github/workflows/site.yml
+  workflow_dispatch:
+
+jobs:
+  deploy:
+    name: Deploy
+    runs-on: ubuntu-latest
+    permissions:
+      contents: write
+    timeout-minutes: 30
+    steps:
+      - uses: actions/checkout@v4
+      - uses: dtolnay/install@mdbook
+      - run: mdbook --version
+
+      - name: Build
+        run: book/build.sh
+
+      - name: Push to gh-pages
+        working-directory: book/build
+        run: |
+          REV=$(git rev-parse --short HEAD)
+          git init
+          git remote add upstream https://x-access-token:${{secrets.GITHUB_TOKEN}}@github.com/dtolnay/cxx
+          git config user.name "CXX"
+          git config user.email "dtolnay+cxx@gmail.com"
+          git add -A .
+          git commit -qm "Website @ ${{github.repository}}@${REV}"
+          git push -q upstream HEAD:refs/heads/gh-pages --force
diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..6b6f5c6
--- /dev/null
@@ -0,0 +1,11 @@
+/.buckd
+/bazel-bin
+/bazel-cxx
+/bazel-out
+/bazel-testlogs
+/user.bazelrc
+/buck-out
+/expand.cc
+/expand.rs
+/target/
+/Cargo.lock
diff --git a/.vscode/README.md b/.vscode/README.md
new file mode 100644 (file)
index 0000000..5ed5b27
--- /dev/null
@@ -0,0 +1,4 @@
+VS Code actions and configuration. Applicable when developing CXX inside of
+GitHub [Codespaces].
+
+[Codespaces]: https://github.com/features/codespaces
diff --git a/.vscode/launch.json b/.vscode/launch.json
new file mode 100644 (file)
index 0000000..0218f47
--- /dev/null
@@ -0,0 +1,29 @@
+{
+    "version": "0.2.0",
+    "configurations": [
+        {
+            "name": "Run cxx demo",
+            "type": "lldb",
+            "request": "launch",
+            "cargo": {
+                "args": ["build", "--manifest-path", "demo/Cargo.toml"],
+                "filter": {
+                    "name": "demo",
+                    "kind": "bin"
+                }
+            }
+        },
+        {
+            "name": "Debug cargo tests",
+            "type": "lldb",
+            "request": "launch",
+            "cargo": {
+                "args": ["test", "--no-run"],
+                "filter": {
+                    "name": "test",
+                    "kind": "test"
+                }
+            }
+        }
+    ]
+}
diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644 (file)
index 0000000..8a1c2c1
--- /dev/null
@@ -0,0 +1,5 @@
+{
+    "search.exclude": {
+        "**/target": true
+    }
+}
diff --git a/.vscode/tasks.json b/.vscode/tasks.json
new file mode 100644 (file)
index 0000000..44a2ab7
--- /dev/null
@@ -0,0 +1,30 @@
+{
+    "version": "2.0.0",
+    "tasks": [
+        {
+            "label": "Cargo test",
+            "type": "shell",
+            "command": "cargo test",
+            "group": "test"
+        },
+        {
+            "label": "Bazel test",
+            "type": "shell",
+            "command": "bazel test ...",
+            "group": "test",
+            "dependsOn": ["Vendor"]
+        },
+        {
+            "label": "Buck test",
+            "type": "shell",
+            "command": "buck test ...",
+            "group": "test",
+            "dependsOn": ["Vendor"]
+        },
+        {
+            "label": "Vendor",
+            "type": "shell",
+            "command": "cp third-party/Cargo.lock . && cargo vendor --versioned-dirs --locked third-party/vendor"
+        }
+    ]
+}
diff --git a/.watchmanconfig b/.watchmanconfig
new file mode 100644 (file)
index 0000000..d93f308
--- /dev/null
@@ -0,0 +1,3 @@
+{
+    "ignore_dirs": ["buck-out"]
+}
diff --git a/BUCK b/BUCK
new file mode 100644 (file)
index 0000000..dccc386
--- /dev/null
+++ b/BUCK
@@ -0,0 +1,100 @@
+rust_library(
+    name = "cxx",
+    srcs = glob(["src/**/*.rs"]),
+    doc_deps = [
+        ":cxx-build",
+    ],
+    edition = "2021",
+    features = [
+        "alloc",
+        "std",
+    ],
+    visibility = ["PUBLIC"],
+    deps = [
+        ":core",
+        ":cxxbridge-macro",
+        "//third-party:foldhash",
+    ],
+)
+
+alias(
+    name = "codegen",
+    actual = ":cxxbridge",
+    visibility = ["PUBLIC"],
+)
+
+rust_binary(
+    name = "cxxbridge",
+    srcs = glob(["gen/cmd/src/**/*.rs"]) + [
+        "gen/cmd/src/gen",
+        "gen/cmd/src/syntax",
+    ],
+    edition = "2021",
+    deps = [
+        "//third-party:clap",
+        "//third-party:codespan-reporting",
+        "//third-party:proc-macro2",
+        "//third-party:quote",
+        "//third-party:syn",
+    ],
+)
+
+cxx_library(
+    name = "core",
+    srcs = ["src/cxx.cc"],
+    exported_headers = {
+        "cxx.h": "include/cxx.h",
+    },
+    header_namespace = "rust",
+    preferred_linkage = "static",
+    visibility = ["PUBLIC"],
+)
+
+rust_library(
+    name = "cxxbridge-macro",
+    srcs = glob(["macro/src/**/*.rs"]) + ["macro/src/syntax"],
+    doctests = False,
+    edition = "2021",
+    proc_macro = True,
+    deps = [
+        "//third-party:proc-macro2",
+        "//third-party:quote",
+        "//third-party:rustversion",
+        "//third-party:syn",
+    ],
+)
+
+rust_library(
+    name = "cxx-build",
+    srcs = glob(["gen/build/src/**/*.rs"]) + [
+        "gen/build/src/gen",
+        "gen/build/src/syntax",
+    ],
+    doctests = False,
+    edition = "2021",
+    deps = [
+        "//third-party:cc",
+        "//third-party:codespan-reporting",
+        "//third-party:proc-macro2",
+        "//third-party:quote",
+        "//third-party:scratch",
+        "//third-party:syn",
+    ],
+)
+
+rust_library(
+    name = "cxx-gen",
+    srcs = glob(["gen/lib/src/**/*.rs"]) + [
+        "gen/lib/src/gen",
+        "gen/lib/src/syntax",
+    ],
+    edition = "2021",
+    visibility = ["PUBLIC"],
+    deps = [
+        "//third-party:cc",
+        "//third-party:codespan-reporting",
+        "//third-party:proc-macro2",
+        "//third-party:quote",
+        "//third-party:syn",
+    ],
+)
diff --git a/BUILD.bazel b/BUILD.bazel
new file mode 100644 (file)
index 0000000..f1e2f92
--- /dev/null
@@ -0,0 +1,99 @@
+load("@rules_cc//cc:defs.bzl", "cc_library")
+load("@rules_rust//rust:defs.bzl", "rust_binary", "rust_library", "rust_proc_macro")
+
+rust_library(
+    name = "cxx",
+    srcs = glob(["src/**/*.rs"]),
+    crate_features = [
+        "alloc",
+        "std",
+    ],
+    edition = "2021",
+    proc_macro_deps = [
+        ":cxxbridge-macro",
+    ],
+    visibility = ["//visibility:public"],
+    deps = [
+        ":core-lib",
+        "@crates.io//:foldhash",
+    ],
+)
+
+alias(
+    name = "codegen",
+    actual = ":cxxbridge",
+    visibility = ["//visibility:public"],
+)
+
+rust_binary(
+    name = "cxxbridge",
+    srcs = glob(["gen/cmd/src/**/*.rs"]),
+    compile_data = ["gen/cmd/src/gen/include/cxx.h"],
+    edition = "2021",
+    deps = [
+        "@crates.io//:clap",
+        "@crates.io//:codespan-reporting",
+        "@crates.io//:proc-macro2",
+        "@crates.io//:quote",
+        "@crates.io//:syn",
+    ],
+)
+
+cc_library(
+    name = "core",
+    hdrs = ["include/cxx.h"],
+    include_prefix = "rust",
+    strip_include_prefix = "include",
+    visibility = ["//visibility:public"],
+)
+
+cc_library(
+    name = "core-lib",
+    srcs = ["src/cxx.cc"],
+    hdrs = ["include/cxx.h"],
+    linkstatic = True,
+)
+
+rust_proc_macro(
+    name = "cxxbridge-macro",
+    srcs = glob(["macro/src/**/*.rs"]),
+    edition = "2021",
+    proc_macro_deps = [
+        "@crates.io//:rustversion",
+    ],
+    deps = [
+        "@crates.io//:proc-macro2",
+        "@crates.io//:quote",
+        "@crates.io//:syn",
+    ],
+)
+
+rust_library(
+    name = "cxx-build",
+    srcs = glob(["gen/build/src/**/*.rs"]),
+    compile_data = ["gen/build/src/gen/include/cxx.h"],
+    edition = "2021",
+    deps = [
+        "@crates.io//:cc",
+        "@crates.io//:codespan-reporting",
+        "@crates.io//:proc-macro2",
+        "@crates.io//:quote",
+        "@crates.io//:scratch",
+        "@crates.io//:syn",
+    ],
+)
+
+rust_library(
+    name = "cxx-gen",
+    srcs = glob(["gen/lib/src/**/*.rs"]),
+    compile_data = ["gen/lib/src/gen/include/cxx.h"],
+    edition = "2021",
+    visibility = ["//visibility:public"],
+    deps = [
+        "@crates.io//:cc",
+        "@crates.io//:codespan-reporting",
+        "@crates.io//:proc-macro2",
+        "@crates.io//:quote",
+        "@crates.io//:syn",
+    ],
+)
diff --git a/Cargo.toml b/Cargo.toml
new file mode 100644 (file)
index 0000000..9400c2e
--- /dev/null
@@ -0,0 +1,152 @@
+# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
+#
+# When uploading crates to the registry Cargo will automatically
+# "normalize" Cargo.toml files for maximal compatibility
+# with all versions of Cargo and also rewrite `path` dependencies
+# to registry (e.g., crates.io) dependencies.
+#
+# If you are reading this file be aware that the original Cargo.toml
+# will likely look very different (and much more reasonable).
+# See Cargo.toml.orig for the original contents.
+
+[package]
+edition = "2021"
+rust-version = "1.73"
+name = "cxx"
+version = "1.0.157"
+authors = ["David Tolnay <dtolnay@gmail.com>"]
+build = "build.rs"
+links = "cxxbridge1"
+exclude = [
+    "/demo",
+    "/gen",
+    "/syntax",
+    "/third-party",
+    "/tools/buck/prelude",
+]
+autolib = false
+autobins = false
+autoexamples = false
+autotests = false
+autobenches = false
+description = "Safe interop between Rust and C++"
+homepage = "https://cxx.rs"
+documentation = "https://docs.rs/cxx"
+readme = "README.md"
+keywords = [
+    "ffi",
+    "c++",
+]
+categories = [
+    "development-tools::ffi",
+    "api-bindings",
+    "no-std",
+]
+license = "MIT OR Apache-2.0"
+repository = "https://github.com/dtolnay/cxx"
+
+[package.metadata.bazel]
+additive_build_file_content = '''
+cc_library(
+    name = "cxx_cc",
+    srcs = ["src/cxx.cc"],
+    hdrs = ["include/cxx.h"],
+    include_prefix = "rust",
+    includes = ["include"],
+    linkstatic = True,
+    strip_include_prefix = "include",
+    visibility = ["//visibility:public"],
+)
+'''
+deps = [":cxx_cc"]
+gen_build_script = false
+
+[package.metadata.bazel.extra_aliased_targets]
+cxx_cc = "cxx_cc"
+
+[package.metadata.docs.rs]
+rustdoc-args = [
+    "--generate-link-to-definition",
+    "--extern-html-root-url=core=https://doc.rust-lang.org",
+    "--extern-html-root-url=alloc=https://doc.rust-lang.org",
+    "--extern-html-root-url=std=https://doc.rust-lang.org",
+]
+targets = ["x86_64-unknown-linux-gnu"]
+
+[features]
+alloc = []
+"c++14" = ["cxxbridge-flags/c++14"]
+"c++17" = ["cxxbridge-flags/c++17"]
+"c++20" = ["cxxbridge-flags/c++20"]
+default = [
+    "std",
+    "cxxbridge-flags/default",
+]
+std = [
+    "alloc",
+    "foldhash/std",
+]
+
+[lib]
+name = "cxx"
+path = "src/lib.rs"
+
+[[test]]
+name = "compiletest"
+path = "tests/compiletest.rs"
+
+[[test]]
+name = "cxx_gen"
+path = "tests/cxx_gen.rs"
+
+[[test]]
+name = "cxx_string"
+path = "tests/cxx_string.rs"
+
+[[test]]
+name = "cxx_vector"
+path = "tests/cxx_vector.rs"
+
+[[test]]
+name = "test"
+path = "tests/test.rs"
+
+[[test]]
+name = "unique_ptr"
+path = "tests/unique_ptr.rs"
+
+[dependencies.cxxbridge-macro]
+version = "=1.0.157"
+
+[dependencies.foldhash]
+version = "0.1"
+default-features = false
+
+[dependencies.link-cplusplus]
+version = "1.0.9"
+
+[dev-dependencies.cxx-build]
+version = "=1.0.157"
+
+[dev-dependencies.cxx-gen]
+version = "0.7"
+
+[dev-dependencies.cxx-test-suite]
+version = "0"
+
+[dev-dependencies.rustversion]
+version = "1.0.13"
+
+[dev-dependencies.trybuild]
+version = "1.0.81"
+features = ["diff"]
+
+[build-dependencies.cc]
+version = "1.0.83"
+
+[build-dependencies.cxxbridge-flags]
+version = "=1.0.157"
+default-features = false
+
+[target."cfg(any())".build-dependencies.cxxbridge-cmd]
+version = "=1.0.157"
diff --git a/Cargo.toml.orig b/Cargo.toml.orig
new file mode 100644 (file)
index 0000000..c67f252
--- /dev/null
@@ -0,0 +1,76 @@
+[package]
+name = "cxx"
+version = "1.0.157"
+authors = ["David Tolnay <dtolnay@gmail.com>"]
+categories = ["development-tools::ffi", "api-bindings", "no-std"]
+description = "Safe interop between Rust and C++"
+documentation = "https://docs.rs/cxx"
+edition = "2021"
+exclude = ["/demo", "/gen", "/syntax", "/third-party", "/tools/buck/prelude"]
+homepage = "https://cxx.rs"
+keywords = ["ffi", "c++"]
+license = "MIT OR Apache-2.0"
+links = "cxxbridge1"
+repository = "https://github.com/dtolnay/cxx"
+rust-version = "1.73"
+
+[features]
+default = ["std", "cxxbridge-flags/default"] # c++11
+"c++14" = ["cxxbridge-flags/c++14"]
+"c++17" = ["cxxbridge-flags/c++17"]
+"c++20" = ["cxxbridge-flags/c++20"]
+alloc = []
+std = ["alloc", "foldhash/std"]
+
+[dependencies]
+cxxbridge-macro = { version = "=1.0.157", path = "macro" }
+foldhash = { version = "0.1", default-features = false }
+link-cplusplus = "1.0.9"
+
+[build-dependencies]
+cc = "1.0.83"
+cxxbridge-flags = { version = "=1.0.157", path = "flags", default-features = false }
+
+[dev-dependencies]
+cxx-build = { version = "=1.0.157", path = "gen/build" }
+cxx-gen = { version = "0.7", path = "gen/lib" }
+cxx-test-suite = { version = "0", path = "tests/ffi" }
+rustversion = "1.0.13"
+trybuild = { version = "1.0.81", features = ["diff"] }
+
+# Disallow incompatible cxxbridge-cmd version appearing in the same lockfile.
+[target.'cfg(any())'.build-dependencies]
+cxxbridge-cmd = { version = "=1.0.157", path = "gen/cmd" }
+
+[workspace]
+members = ["demo", "flags", "gen/build", "gen/cmd", "gen/lib", "macro", "tests/ffi"]
+
+[package.metadata.docs.rs]
+targets = ["x86_64-unknown-linux-gnu"]
+rustdoc-args = [
+    "--generate-link-to-definition",
+    "--extern-html-root-url=core=https://doc.rust-lang.org",
+    "--extern-html-root-url=alloc=https://doc.rust-lang.org",
+    "--extern-html-root-url=std=https://doc.rust-lang.org",
+]
+
+[package.metadata.bazel]
+additive_build_file_content = """
+cc_library(
+    name = "cxx_cc",
+    srcs = ["src/cxx.cc"],
+    hdrs = ["include/cxx.h"],
+    include_prefix = "rust",
+    includes = ["include"],
+    linkstatic = True,
+    strip_include_prefix = "include",
+    visibility = ["//visibility:public"],
+)
+"""
+deps = [":cxx_cc"]
+extra_aliased_targets = { cxx_cc = "cxx_cc" }
+gen_build_script = false
+
+[patch.crates-io]
+cxx = { path = "." }
+cxx-build = { path = "gen/build" }
diff --git a/LICENSE-APACHE b/LICENSE-APACHE
new file mode 100644 (file)
index 0000000..1b5ec8b
--- /dev/null
@@ -0,0 +1,176 @@
+                              Apache License
+                        Version 2.0, January 2004
+                     http://www.apache.org/licenses/
+
+TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+1. Definitions.
+
+   "License" shall mean the terms and conditions for use, reproduction,
+   and distribution as defined by Sections 1 through 9 of this document.
+
+   "Licensor" shall mean the copyright owner or entity authorized by
+   the copyright owner that is granting the License.
+
+   "Legal Entity" shall mean the union of the acting entity and all
+   other entities that control, are controlled by, or are under common
+   control with that entity. For the purposes of this definition,
+   "control" means (i) the power, direct or indirect, to cause the
+   direction or management of such entity, whether by contract or
+   otherwise, or (ii) ownership of fifty percent (50%) or more of the
+   outstanding shares, or (iii) beneficial ownership of such entity.
+
+   "You" (or "Your") shall mean an individual or Legal Entity
+   exercising permissions granted by this License.
+
+   "Source" form shall mean the preferred form for making modifications,
+   including but not limited to software source code, documentation
+   source, and configuration files.
+
+   "Object" form shall mean any form resulting from mechanical
+   transformation or translation of a Source form, including but
+   not limited to compiled object code, generated documentation,
+   and conversions to other media types.
+
+   "Work" shall mean the work of authorship, whether in Source or
+   Object form, made available under the License, as indicated by a
+   copyright notice that is included in or attached to the work
+   (an example is provided in the Appendix below).
+
+   "Derivative Works" shall mean any work, whether in Source or Object
+   form, that is based on (or derived from) the Work and for which the
+   editorial revisions, annotations, elaborations, or other modifications
+   represent, as a whole, an original work of authorship. For the purposes
+   of this License, Derivative Works shall not include works that remain
+   separable from, or merely link (or bind by name) to the interfaces of,
+   the Work and Derivative Works thereof.
+
+   "Contribution" shall mean any work of authorship, including
+   the original version of the Work and any modifications or additions
+   to that Work or Derivative Works thereof, that is intentionally
+   submitted to Licensor for inclusion in the Work by the copyright owner
+   or by an individual or Legal Entity authorized to submit on behalf of
+   the copyright owner. For the purposes of this definition, "submitted"
+   means any form of electronic, verbal, or written communication sent
+   to the Licensor or its representatives, including but not limited to
+   communication on electronic mailing lists, source code control systems,
+   and issue tracking systems that are managed by, or on behalf of, the
+   Licensor for the purpose of discussing and improving the Work, but
+   excluding communication that is conspicuously marked or otherwise
+   designated in writing by the copyright owner as "Not a Contribution."
+
+   "Contributor" shall mean Licensor and any individual or Legal Entity
+   on behalf of whom a Contribution has been received by Licensor and
+   subsequently incorporated within the Work.
+
+2. Grant of Copyright License. Subject to the terms and conditions of
+   this License, each Contributor hereby grants to You a perpetual,
+   worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+   copyright license to reproduce, prepare Derivative Works of,
+   publicly display, publicly perform, sublicense, and distribute the
+   Work and such Derivative Works in Source or Object form.
+
+3. Grant of Patent License. Subject to the terms and conditions of
+   this License, each Contributor hereby grants to You a perpetual,
+   worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+   (except as stated in this section) patent license to make, have made,
+   use, offer to sell, sell, import, and otherwise transfer the Work,
+   where such license applies only to those patent claims licensable
+   by such Contributor that are necessarily infringed by their
+   Contribution(s) alone or by combination of their Contribution(s)
+   with the Work to which such Contribution(s) was submitted. If You
+   institute patent litigation against any entity (including a
+   cross-claim or counterclaim in a lawsuit) alleging that the Work
+   or a Contribution incorporated within the Work constitutes direct
+   or contributory patent infringement, then any patent licenses
+   granted to You under this License for that Work shall terminate
+   as of the date such litigation is filed.
+
+4. Redistribution. You may reproduce and distribute copies of the
+   Work or Derivative Works thereof in any medium, with or without
+   modifications, and in Source or Object form, provided that You
+   meet the following conditions:
+
+   (a) You must give any other recipients of the Work or
+       Derivative Works a copy of this License; and
+
+   (b) You must cause any modified files to carry prominent notices
+       stating that You changed the files; and
+
+   (c) You must retain, in the Source form of any Derivative Works
+       that You distribute, all copyright, patent, trademark, and
+       attribution notices from the Source form of the Work,
+       excluding those notices that do not pertain to any part of
+       the Derivative Works; and
+
+   (d) If the Work includes a "NOTICE" text file as part of its
+       distribution, then any Derivative Works that You distribute must
+       include a readable copy of the attribution notices contained
+       within such NOTICE file, excluding those notices that do not
+       pertain to any part of the Derivative Works, in at least one
+       of the following places: within a NOTICE text file distributed
+       as part of the Derivative Works; within the Source form or
+       documentation, if provided along with the Derivative Works; or,
+       within a display generated by the Derivative Works, if and
+       wherever such third-party notices normally appear. The contents
+       of the NOTICE file are for informational purposes only and
+       do not modify the License. You may add Your own attribution
+       notices within Derivative Works that You distribute, alongside
+       or as an addendum to the NOTICE text from the Work, provided
+       that such additional attribution notices cannot be construed
+       as modifying the License.
+
+   You may add Your own copyright statement to Your modifications and
+   may provide additional or different license terms and conditions
+   for use, reproduction, or distribution of Your modifications, or
+   for any such Derivative Works as a whole, provided Your use,
+   reproduction, and distribution of the Work otherwise complies with
+   the conditions stated in this License.
+
+5. Submission of Contributions. Unless You explicitly state otherwise,
+   any Contribution intentionally submitted for inclusion in the Work
+   by You to the Licensor shall be under the terms and conditions of
+   this License, without any additional terms or conditions.
+   Notwithstanding the above, nothing herein shall supersede or modify
+   the terms of any separate license agreement you may have executed
+   with Licensor regarding such Contributions.
+
+6. Trademarks. This License does not grant permission to use the trade
+   names, trademarks, service marks, or product names of the Licensor,
+   except as required for reasonable and customary use in describing the
+   origin of the Work and reproducing the content of the NOTICE file.
+
+7. Disclaimer of Warranty. Unless required by applicable law or
+   agreed to in writing, Licensor provides the Work (and each
+   Contributor provides its Contributions) on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+   implied, including, without limitation, any warranties or conditions
+   of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+   PARTICULAR PURPOSE. You are solely responsible for determining the
+   appropriateness of using or redistributing the Work and assume any
+   risks associated with Your exercise of permissions under this License.
+
+8. Limitation of Liability. In no event and under no legal theory,
+   whether in tort (including negligence), contract, or otherwise,
+   unless required by applicable law (such as deliberate and grossly
+   negligent acts) or agreed to in writing, shall any Contributor be
+   liable to You for damages, including any direct, indirect, special,
+   incidental, or consequential damages of any character arising as a
+   result of this License or out of the use or inability to use the
+   Work (including but not limited to damages for loss of goodwill,
+   work stoppage, computer failure or malfunction, or any and all
+   other commercial damages or losses), even if such Contributor
+   has been advised of the possibility of such damages.
+
+9. Accepting Warranty or Additional Liability. While redistributing
+   the Work or Derivative Works thereof, You may choose to offer,
+   and charge a fee for, acceptance of support, warranty, indemnity,
+   or other liability obligations and/or rights consistent with this
+   License. However, in accepting such obligations, You may act only
+   on Your own behalf and on Your sole responsibility, not on behalf
+   of any other Contributor, and only if You agree to indemnify,
+   defend, and hold each Contributor harmless for any liability
+   incurred by, or claims asserted against, such Contributor by reason
+   of your accepting any such warranty or additional liability.
+
+END OF TERMS AND CONDITIONS
diff --git a/LICENSE-MIT b/LICENSE-MIT
new file mode 100644 (file)
index 0000000..31aa793
--- /dev/null
@@ -0,0 +1,23 @@
+Permission is hereby granted, free of charge, to any
+person obtaining a copy of this software and associated
+documentation files (the "Software"), to deal in the
+Software without restriction, including without
+limitation the rights to use, copy, modify, merge,
+publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software
+is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice
+shall be included in all copies or substantial portions
+of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
+ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
+TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
+SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
+IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
diff --git a/MODULE.bazel b/MODULE.bazel
new file mode 100644 (file)
index 0000000..c14a5c9
--- /dev/null
@@ -0,0 +1,21 @@
+module(
+    name = "cxx.rs",
+    version = "0.0.0",
+    bazel_compatibility = [">=7.2.1"],
+    compatibility_level = 1,
+)
+
+bazel_dep(name = "bazel_features", version = "1.21.0")
+bazel_dep(name = "bazel_skylib", version = "1.7.1")
+bazel_dep(name = "platforms", version = "0.0.11")
+bazel_dep(name = "rules_cc", version = "0.1.1")
+bazel_dep(name = "rules_rust", version = "0.60.0")
+
+rust = use_extension("@rules_rust//rust:extensions.bzl", "rust")
+rust.toolchain(versions = ["1.86.0"])
+use_repo(rust, "rust_toolchains")
+
+register_toolchains("@rust_toolchains//:all")
+
+crate_repositories = use_extension("//tools/bazel:extension.bzl", "crate_repositories")
+use_repo(crate_repositories, "crates.io", "vendor")
diff --git a/MODULE.bazel.lock b/MODULE.bazel.lock
new file mode 100644 (file)
index 0000000..e84b165
--- /dev/null
@@ -0,0 +1,243 @@
+{
+  "lockFileVersion": 18,
+  "registryFileHashes": {
+    "https://bcr.bazel.build/bazel_registry.json": "8a28e4aff06ee60aed2a8c281907fb8bcbf3b753c91fb5a5c57da3215d5b3497",
+    "https://bcr.bazel.build/modules/abseil-cpp/20210324.2/MODULE.bazel": "7cd0312e064fde87c8d1cd79ba06c876bd23630c83466e9500321be55c96ace2",
+    "https://bcr.bazel.build/modules/abseil-cpp/20211102.0/MODULE.bazel": "70390338f7a5106231d20620712f7cccb659cd0e9d073d1991c038eb9fc57589",
+    "https://bcr.bazel.build/modules/abseil-cpp/20230125.1/MODULE.bazel": "89047429cb0207707b2dface14ba7f8df85273d484c2572755be4bab7ce9c3a0",
+    "https://bcr.bazel.build/modules/abseil-cpp/20230802.0.bcr.1/MODULE.bazel": "1c8cec495288dccd14fdae6e3f95f772c1c91857047a098fad772034264cc8cb",
+    "https://bcr.bazel.build/modules/abseil-cpp/20230802.0/MODULE.bazel": "d253ae36a8bd9ee3c5955384096ccb6baf16a1b1e93e858370da0a3b94f77c16",
+    "https://bcr.bazel.build/modules/abseil-cpp/20230802.1/MODULE.bazel": "fa92e2eb41a04df73cdabeec37107316f7e5272650f81d6cc096418fe647b915",
+    "https://bcr.bazel.build/modules/abseil-cpp/20240116.1/MODULE.bazel": "37bcdb4440fbb61df6a1c296ae01b327f19e9bb521f9b8e26ec854b6f97309ed",
+    "https://bcr.bazel.build/modules/abseil-cpp/20240116.1/source.json": "9be551b8d4e3ef76875c0d744b5d6a504a27e3ae67bc6b28f46415fd2d2957da",
+    "https://bcr.bazel.build/modules/apple_support/1.17.1/MODULE.bazel": "655c922ab1209978a94ef6ca7d9d43e940cd97d9c172fb55f94d91ac53f8610b",
+    "https://bcr.bazel.build/modules/apple_support/1.17.1/source.json": "6b2b8c74d14e8d485528a938e44bdb72a5ba17632b9e14ef6e68a5ee96c8347f",
+    "https://bcr.bazel.build/modules/bazel_features/1.1.1/MODULE.bazel": "27b8c79ef57efe08efccbd9dd6ef70d61b4798320b8d3c134fd571f78963dbcd",
+    "https://bcr.bazel.build/modules/bazel_features/1.10.0/MODULE.bazel": "f75e8807570484a99be90abcd52b5e1f390362c258bcb73106f4544957a48101",
+    "https://bcr.bazel.build/modules/bazel_features/1.11.0/MODULE.bazel": "f9382337dd5a474c3b7d334c2f83e50b6eaedc284253334cf823044a26de03e8",
+    "https://bcr.bazel.build/modules/bazel_features/1.15.0/MODULE.bazel": "d38ff6e517149dc509406aca0db3ad1efdd890a85e049585b7234d04238e2a4d",
+    "https://bcr.bazel.build/modules/bazel_features/1.17.0/MODULE.bazel": "039de32d21b816b47bd42c778e0454217e9c9caac4a3cf8e15c7231ee3ddee4d",
+    "https://bcr.bazel.build/modules/bazel_features/1.18.0/MODULE.bazel": "1be0ae2557ab3a72a57aeb31b29be347bcdc5d2b1eb1e70f39e3851a7e97041a",
+    "https://bcr.bazel.build/modules/bazel_features/1.19.0/MODULE.bazel": "59adcdf28230d220f0067b1f435b8537dd033bfff8db21335ef9217919c7fb58",
+    "https://bcr.bazel.build/modules/bazel_features/1.21.0/MODULE.bazel": "675642261665d8eea09989aa3b8afb5c37627f1be178382c320d1b46afba5e3b",
+    "https://bcr.bazel.build/modules/bazel_features/1.21.0/source.json": "3e8379efaaef53ce35b7b8ba419df829315a880cb0a030e5bb45c96d6d5ecb5f",
+    "https://bcr.bazel.build/modules/bazel_features/1.4.1/MODULE.bazel": "e45b6bb2350aff3e442ae1111c555e27eac1d915e77775f6fdc4b351b758b5d7",
+    "https://bcr.bazel.build/modules/bazel_features/1.9.1/MODULE.bazel": "8f679097876a9b609ad1f60249c49d68bfab783dd9be012faf9d82547b14815a",
+    "https://bcr.bazel.build/modules/bazel_skylib/1.0.3/MODULE.bazel": "bcb0fd896384802d1ad283b4e4eb4d718eebd8cb820b0a2c3a347fb971afd9d8",
+    "https://bcr.bazel.build/modules/bazel_skylib/1.1.1/MODULE.bazel": "1add3e7d93ff2e6998f9e118022c84d163917d912f5afafb3058e3d2f1545b5e",
+    "https://bcr.bazel.build/modules/bazel_skylib/1.2.0/MODULE.bazel": "44fe84260e454ed94ad326352a698422dbe372b21a1ac9f3eab76eb531223686",
+    "https://bcr.bazel.build/modules/bazel_skylib/1.2.1/MODULE.bazel": "f35baf9da0efe45fa3da1696ae906eea3d615ad41e2e3def4aeb4e8bc0ef9a7a",
+    "https://bcr.bazel.build/modules/bazel_skylib/1.3.0/MODULE.bazel": "20228b92868bf5cfc41bda7afc8a8ba2a543201851de39d990ec957b513579c5",
+    "https://bcr.bazel.build/modules/bazel_skylib/1.4.1/MODULE.bazel": "a0dcb779424be33100dcae821e9e27e4f2901d9dfd5333efe5ac6a8d7ab75e1d",
+    "https://bcr.bazel.build/modules/bazel_skylib/1.4.2/MODULE.bazel": "3bd40978e7a1fac911d5989e6b09d8f64921865a45822d8b09e815eaa726a651",
+    "https://bcr.bazel.build/modules/bazel_skylib/1.5.0/MODULE.bazel": "32880f5e2945ce6a03d1fbd588e9198c0a959bb42297b2cfaf1685b7bc32e138",
+    "https://bcr.bazel.build/modules/bazel_skylib/1.6.1/MODULE.bazel": "8fdee2dbaace6c252131c00e1de4b165dc65af02ea278476187765e1a617b917",
+    "https://bcr.bazel.build/modules/bazel_skylib/1.7.0/MODULE.bazel": "0db596f4563de7938de764cc8deeabec291f55e8ec15299718b93c4423e9796d",
+    "https://bcr.bazel.build/modules/bazel_skylib/1.7.1/MODULE.bazel": "3120d80c5861aa616222ec015332e5f8d3171e062e3e804a2a0253e1be26e59b",
+    "https://bcr.bazel.build/modules/bazel_skylib/1.7.1/source.json": "f121b43eeefc7c29efbd51b83d08631e2347297c95aac9764a701f2a6a2bb953",
+    "https://bcr.bazel.build/modules/buildozer/7.1.2/MODULE.bazel": "2e8dd40ede9c454042645fd8d8d0cd1527966aa5c919de86661e62953cd73d84",
+    "https://bcr.bazel.build/modules/buildozer/7.1.2/source.json": "c9028a501d2db85793a6996205c8de120944f50a0d570438fcae0457a5f9d1f8",
+    "https://bcr.bazel.build/modules/google_benchmark/1.8.2/MODULE.bazel": "a70cf1bba851000ba93b58ae2f6d76490a9feb74192e57ab8e8ff13c34ec50cb",
+    "https://bcr.bazel.build/modules/googletest/1.11.0/MODULE.bazel": "3a83f095183f66345ca86aa13c58b59f9f94a2f81999c093d4eeaa2d262d12f4",
+    "https://bcr.bazel.build/modules/googletest/1.14.0.bcr.1/MODULE.bazel": "22c31a561553727960057361aa33bf20fb2e98584bc4fec007906e27053f80c6",
+    "https://bcr.bazel.build/modules/googletest/1.14.0.bcr.1/source.json": "41e9e129f80d8c8bf103a7acc337b76e54fad1214ac0a7084bf24f4cd924b8b4",
+    "https://bcr.bazel.build/modules/googletest/1.14.0/MODULE.bazel": "cfbcbf3e6eac06ef9d85900f64424708cc08687d1b527f0ef65aa7517af8118f",
+    "https://bcr.bazel.build/modules/jsoncpp/1.9.5/MODULE.bazel": "31271aedc59e815656f5736f282bb7509a97c7ecb43e927ac1a37966e0578075",
+    "https://bcr.bazel.build/modules/jsoncpp/1.9.5/source.json": "4108ee5085dd2885a341c7fab149429db457b3169b86eb081fa245eadf69169d",
+    "https://bcr.bazel.build/modules/libpfm/4.11.0/MODULE.bazel": "45061ff025b301940f1e30d2c16bea596c25b176c8b6b3087e92615adbd52902",
+    "https://bcr.bazel.build/modules/platforms/0.0.10/MODULE.bazel": "8cb8efaf200bdeb2150d93e162c40f388529a25852b332cec879373771e48ed5",
+    "https://bcr.bazel.build/modules/platforms/0.0.11/MODULE.bazel": "0daefc49732e227caa8bfa834d65dc52e8cc18a2faf80df25e8caea151a9413f",
+    "https://bcr.bazel.build/modules/platforms/0.0.11/source.json": "f7e188b79ebedebfe75e9e1d098b8845226c7992b307e28e1496f23112e8fc29",
+    "https://bcr.bazel.build/modules/platforms/0.0.4/MODULE.bazel": "9b328e31ee156f53f3c416a64f8491f7eb731742655a47c9eec4703a71644aee",
+    "https://bcr.bazel.build/modules/platforms/0.0.5/MODULE.bazel": "5733b54ea419d5eaf7997054bb55f6a1d0b5ff8aedf0176fef9eea44f3acda37",
+    "https://bcr.bazel.build/modules/platforms/0.0.6/MODULE.bazel": "ad6eeef431dc52aefd2d77ed20a4b353f8ebf0f4ecdd26a807d2da5aa8cd0615",
+    "https://bcr.bazel.build/modules/platforms/0.0.7/MODULE.bazel": "72fd4a0ede9ee5c021f6a8dd92b503e089f46c227ba2813ff183b71616034814",
+    "https://bcr.bazel.build/modules/platforms/0.0.8/MODULE.bazel": "9f142c03e348f6d263719f5074b21ef3adf0b139ee4c5133e2aa35664da9eb2d",
+    "https://bcr.bazel.build/modules/platforms/0.0.9/MODULE.bazel": "4a87a60c927b56ddd67db50c89acaa62f4ce2a1d2149ccb63ffd871d5ce29ebc",
+    "https://bcr.bazel.build/modules/protobuf/21.7/MODULE.bazel": "a5a29bb89544f9b97edce05642fac225a808b5b7be74038ea3640fae2f8e66a7",
+    "https://bcr.bazel.build/modules/protobuf/27.0/MODULE.bazel": "7873b60be88844a0a1d8f80b9d5d20cfbd8495a689b8763e76c6372998d3f64c",
+    "https://bcr.bazel.build/modules/protobuf/27.1/MODULE.bazel": "703a7b614728bb06647f965264967a8ef1c39e09e8f167b3ca0bb1fd80449c0d",
+    "https://bcr.bazel.build/modules/protobuf/29.0-rc2/MODULE.bazel": "6241d35983510143049943fc0d57937937122baf1b287862f9dc8590fc4c37df",
+    "https://bcr.bazel.build/modules/protobuf/29.0/MODULE.bazel": "319dc8bf4c679ff87e71b1ccfb5a6e90a6dbc4693501d471f48662ac46d04e4e",
+    "https://bcr.bazel.build/modules/protobuf/29.0/source.json": "b857f93c796750eef95f0d61ee378f3420d00ee1dd38627b27193aa482f4f981",
+    "https://bcr.bazel.build/modules/protobuf/3.19.0/MODULE.bazel": "6b5fbb433f760a99a22b18b6850ed5784ef0e9928a72668b66e4d7ccd47db9b0",
+    "https://bcr.bazel.build/modules/pybind11_bazel/2.11.1/MODULE.bazel": "88af1c246226d87e65be78ed49ecd1e6f5e98648558c14ce99176da041dc378e",
+    "https://bcr.bazel.build/modules/pybind11_bazel/2.11.1/source.json": "be4789e951dd5301282729fe3d4938995dc4c1a81c2ff150afc9f1b0504c6022",
+    "https://bcr.bazel.build/modules/re2/2023-09-01/MODULE.bazel": "cb3d511531b16cfc78a225a9e2136007a48cf8a677e4264baeab57fe78a80206",
+    "https://bcr.bazel.build/modules/re2/2023-09-01/source.json": "e044ce89c2883cd957a2969a43e79f7752f9656f6b20050b62f90ede21ec6eb4",
+    "https://bcr.bazel.build/modules/rules_android/0.1.1/MODULE.bazel": "48809ab0091b07ad0182defb787c4c5328bd3a278938415c00a7b69b50c4d3a8",
+    "https://bcr.bazel.build/modules/rules_android/0.1.1/source.json": "e6986b41626ee10bdc864937ffb6d6bf275bb5b9c65120e6137d56e6331f089e",
+    "https://bcr.bazel.build/modules/rules_cc/0.0.1/MODULE.bazel": "cb2aa0747f84c6c3a78dad4e2049c154f08ab9d166b1273835a8174940365647",
+    "https://bcr.bazel.build/modules/rules_cc/0.0.10/MODULE.bazel": "ec1705118f7eaedd6e118508d3d26deba2a4e76476ada7e0e3965211be012002",
+    "https://bcr.bazel.build/modules/rules_cc/0.0.13/MODULE.bazel": "0e8529ed7b323dad0775ff924d2ae5af7640b23553dfcd4d34344c7e7a867191",
+    "https://bcr.bazel.build/modules/rules_cc/0.0.14/MODULE.bazel": "5e343a3aac88b8d7af3b1b6d2093b55c347b8eefc2e7d1442f7a02dc8fea48ac",
+    "https://bcr.bazel.build/modules/rules_cc/0.0.15/MODULE.bazel": "6704c35f7b4a72502ee81f61bf88706b54f06b3cbe5558ac17e2e14666cd5dcc",
+    "https://bcr.bazel.build/modules/rules_cc/0.0.16/MODULE.bazel": "7661303b8fc1b4d7f532e54e9d6565771fea666fbdf839e0a86affcd02defe87",
+    "https://bcr.bazel.build/modules/rules_cc/0.0.17/MODULE.bazel": "2ae1d8f4238ec67d7185d8861cb0a2cdf4bc608697c331b95bf990e69b62e64a",
+    "https://bcr.bazel.build/modules/rules_cc/0.0.2/MODULE.bazel": "6915987c90970493ab97393024c156ea8fb9f3bea953b2f3ec05c34f19b5695c",
+    "https://bcr.bazel.build/modules/rules_cc/0.0.6/MODULE.bazel": "abf360251023dfe3efcef65ab9d56beefa8394d4176dd29529750e1c57eaa33f",
+    "https://bcr.bazel.build/modules/rules_cc/0.0.8/MODULE.bazel": "964c85c82cfeb6f3855e6a07054fdb159aced38e99a5eecf7bce9d53990afa3e",
+    "https://bcr.bazel.build/modules/rules_cc/0.0.9/MODULE.bazel": "836e76439f354b89afe6a911a7adf59a6b2518fafb174483ad78a2a2fde7b1c5",
+    "https://bcr.bazel.build/modules/rules_cc/0.1.1/MODULE.bazel": "2f0222a6f229f0bf44cd711dc13c858dad98c62d52bd51d8fc3a764a83125513",
+    "https://bcr.bazel.build/modules/rules_cc/0.1.1/source.json": "d61627377bd7dd1da4652063e368d9366fc9a73920bfa396798ad92172cf645c",
+    "https://bcr.bazel.build/modules/rules_foreign_cc/0.9.0/MODULE.bazel": "c9e8c682bf75b0e7c704166d79b599f93b72cfca5ad7477df596947891feeef6",
+    "https://bcr.bazel.build/modules/rules_fuzzing/0.5.2/MODULE.bazel": "40c97d1144356f52905566c55811f13b299453a14ac7769dfba2ac38192337a8",
+    "https://bcr.bazel.build/modules/rules_fuzzing/0.5.2/source.json": "c8b1e2c717646f1702290959a3302a178fb639d987ab61d548105019f11e527e",
+    "https://bcr.bazel.build/modules/rules_java/4.0.0/MODULE.bazel": "5a78a7ae82cd1a33cef56dc578c7d2a46ed0dca12643ee45edbb8417899e6f74",
+    "https://bcr.bazel.build/modules/rules_java/5.3.5/MODULE.bazel": "a4ec4f2db570171e3e5eb753276ee4b389bae16b96207e9d3230895c99644b86",
+    "https://bcr.bazel.build/modules/rules_java/6.0.0/MODULE.bazel": "8a43b7df601a7ec1af61d79345c17b31ea1fedc6711fd4abfd013ea612978e39",
+    "https://bcr.bazel.build/modules/rules_java/6.4.0/MODULE.bazel": "e986a9fe25aeaa84ac17ca093ef13a4637f6107375f64667a15999f77db6c8f6",
+    "https://bcr.bazel.build/modules/rules_java/6.5.2/MODULE.bazel": "1d440d262d0e08453fa0c4d8f699ba81609ed0e9a9a0f02cd10b3e7942e61e31",
+    "https://bcr.bazel.build/modules/rules_java/7.10.0/MODULE.bazel": "530c3beb3067e870561739f1144329a21c851ff771cd752a49e06e3dc9c2e71a",
+    "https://bcr.bazel.build/modules/rules_java/7.12.2/MODULE.bazel": "579c505165ee757a4280ef83cda0150eea193eed3bef50b1004ba88b99da6de6",
+    "https://bcr.bazel.build/modules/rules_java/7.2.0/MODULE.bazel": "06c0334c9be61e6cef2c8c84a7800cef502063269a5af25ceb100b192453d4ab",
+    "https://bcr.bazel.build/modules/rules_java/7.3.2/MODULE.bazel": "50dece891cfdf1741ea230d001aa9c14398062f2b7c066470accace78e412bc2",
+    "https://bcr.bazel.build/modules/rules_java/7.6.1/MODULE.bazel": "2f14b7e8a1aa2f67ae92bc69d1ec0fa8d9f827c4e17ff5e5f02e91caa3b2d0fe",
+    "https://bcr.bazel.build/modules/rules_java/8.11.0/MODULE.bazel": "c3d280bc5ff1038dcb3bacb95d3f6b83da8dd27bba57820ec89ea4085da767ad",
+    "https://bcr.bazel.build/modules/rules_java/8.11.0/source.json": "302b52a39259a85aa06ca3addb9787864ca3e03b432a5f964ea68244397e7544",
+    "https://bcr.bazel.build/modules/rules_jvm_external/4.4.2/MODULE.bazel": "a56b85e418c83eb1839819f0b515c431010160383306d13ec21959ac412d2fe7",
+    "https://bcr.bazel.build/modules/rules_jvm_external/5.1/MODULE.bazel": "33f6f999e03183f7d088c9be518a63467dfd0be94a11d0055fe2d210f89aa909",
+    "https://bcr.bazel.build/modules/rules_jvm_external/5.2/MODULE.bazel": "d9351ba35217ad0de03816ef3ed63f89d411349353077348a45348b096615036",
+    "https://bcr.bazel.build/modules/rules_jvm_external/5.3/MODULE.bazel": "bf93870767689637164657731849fb887ad086739bd5d360d90007a581d5527d",
+    "https://bcr.bazel.build/modules/rules_jvm_external/6.1/MODULE.bazel": "75b5fec090dbd46cf9b7d8ea08cf84a0472d92ba3585b476f44c326eda8059c4",
+    "https://bcr.bazel.build/modules/rules_jvm_external/6.3/MODULE.bazel": "c998e060b85f71e00de5ec552019347c8bca255062c990ac02d051bb80a38df0",
+    "https://bcr.bazel.build/modules/rules_jvm_external/6.3/source.json": "6f5f5a5a4419ae4e37c35a5bb0a6ae657ed40b7abc5a5189111b47fcebe43197",
+    "https://bcr.bazel.build/modules/rules_kotlin/1.9.0/MODULE.bazel": "ef85697305025e5a61f395d4eaede272a5393cee479ace6686dba707de804d59",
+    "https://bcr.bazel.build/modules/rules_kotlin/1.9.6/MODULE.bazel": "d269a01a18ee74d0335450b10f62c9ed81f2321d7958a2934e44272fe82dcef3",
+    "https://bcr.bazel.build/modules/rules_kotlin/1.9.6/source.json": "2faa4794364282db7c06600b7e5e34867a564ae91bda7cae7c29c64e9466b7d5",
+    "https://bcr.bazel.build/modules/rules_license/0.0.3/MODULE.bazel": "627e9ab0247f7d1e05736b59dbb1b6871373de5ad31c3011880b4133cafd4bd0",
+    "https://bcr.bazel.build/modules/rules_license/0.0.7/MODULE.bazel": "088fbeb0b6a419005b89cf93fe62d9517c0a2b8bb56af3244af65ecfe37e7d5d",
+    "https://bcr.bazel.build/modules/rules_license/1.0.0/MODULE.bazel": "a7fda60eefdf3d8c827262ba499957e4df06f659330bbe6cdbdb975b768bb65c",
+    "https://bcr.bazel.build/modules/rules_license/1.0.0/source.json": "a52c89e54cc311196e478f8382df91c15f7a2bfdf4c6cd0e2675cc2ff0b56efb",
+    "https://bcr.bazel.build/modules/rules_pkg/0.7.0/MODULE.bazel": "df99f03fc7934a4737122518bb87e667e62d780b610910f0447665a7e2be62dc",
+    "https://bcr.bazel.build/modules/rules_pkg/1.0.1/MODULE.bazel": "5b1df97dbc29623bccdf2b0dcd0f5cb08e2f2c9050aab1092fd39a41e82686ff",
+    "https://bcr.bazel.build/modules/rules_pkg/1.0.1/source.json": "bd82e5d7b9ce2d31e380dd9f50c111d678c3bdaca190cb76b0e1c71b05e1ba8a",
+    "https://bcr.bazel.build/modules/rules_proto/4.0.0/MODULE.bazel": "a7a7b6ce9bee418c1a760b3d84f83a299ad6952f9903c67f19e4edd964894e06",
+    "https://bcr.bazel.build/modules/rules_proto/5.3.0-21.7/MODULE.bazel": "e8dff86b0971688790ae75528fe1813f71809b5afd57facb44dad9e8eca631b7",
+    "https://bcr.bazel.build/modules/rules_proto/6.0.2/MODULE.bazel": "ce916b775a62b90b61888052a416ccdda405212b6aaeb39522f7dc53431a5e73",
+    "https://bcr.bazel.build/modules/rules_proto/7.0.2/MODULE.bazel": "bf81793bd6d2ad89a37a40693e56c61b0ee30f7a7fdbaf3eabbf5f39de47dea2",
+    "https://bcr.bazel.build/modules/rules_proto/7.0.2/source.json": "1e5e7260ae32ef4f2b52fd1d0de8d03b606a44c91b694d2f1afb1d3b28a48ce1",
+    "https://bcr.bazel.build/modules/rules_python/0.10.2/MODULE.bazel": "cc82bc96f2997baa545ab3ce73f196d040ffb8756fd2d66125a530031cd90e5f",
+    "https://bcr.bazel.build/modules/rules_python/0.23.1/MODULE.bazel": "49ffccf0511cb8414de28321f5fcf2a31312b47c40cc21577144b7447f2bf300",
+    "https://bcr.bazel.build/modules/rules_python/0.25.0/MODULE.bazel": "72f1506841c920a1afec76975b35312410eea3aa7b63267436bfb1dd91d2d382",
+    "https://bcr.bazel.build/modules/rules_python/0.28.0/MODULE.bazel": "cba2573d870babc976664a912539b320cbaa7114cd3e8f053c720171cde331ed",
+    "https://bcr.bazel.build/modules/rules_python/0.31.0/MODULE.bazel": "93a43dc47ee570e6ec9f5779b2e64c1476a6ce921c48cc9a1678a91dd5f8fd58",
+    "https://bcr.bazel.build/modules/rules_python/0.4.0/MODULE.bazel": "9208ee05fd48bf09ac60ed269791cf17fb343db56c8226a720fbb1cdf467166c",
+    "https://bcr.bazel.build/modules/rules_python/0.40.0/MODULE.bazel": "9d1a3cd88ed7d8e39583d9ffe56ae8a244f67783ae89b60caafc9f5cf318ada7",
+    "https://bcr.bazel.build/modules/rules_python/0.40.0/source.json": "939d4bd2e3110f27bfb360292986bb79fd8dcefb874358ccd6cdaa7bda029320",
+    "https://bcr.bazel.build/modules/rules_rust/0.60.0/MODULE.bazel": "911ff2a12d01ac574fd6dfec0b05fa976ff8693d8c2420db637a9f98f697b0ae",
+    "https://bcr.bazel.build/modules/rules_rust/0.60.0/source.json": "2b17f77e27489aa1b86b765a141642a1966a2a35fed0207277f3327fd09ef3d4",
+    "https://bcr.bazel.build/modules/rules_shell/0.2.0/MODULE.bazel": "fda8a652ab3c7d8fee214de05e7a9916d8b28082234e8d2c0094505c5268ed3c",
+    "https://bcr.bazel.build/modules/rules_shell/0.3.0/MODULE.bazel": "de4402cd12f4cc8fda2354fce179fdb068c0b9ca1ec2d2b17b3e21b24c1a937b",
+    "https://bcr.bazel.build/modules/rules_shell/0.3.0/source.json": "c55ed591aa5009401ddf80ded9762ac32c358d2517ee7820be981e2de9756cf3",
+    "https://bcr.bazel.build/modules/stardoc/0.5.1/MODULE.bazel": "1a05d92974d0c122f5ccf09291442580317cdd859f07a8655f1db9a60374f9f8",
+    "https://bcr.bazel.build/modules/stardoc/0.5.3/MODULE.bazel": "c7f6948dae6999bf0db32c1858ae345f112cacf98f174c7a8bb707e41b974f1c",
+    "https://bcr.bazel.build/modules/stardoc/0.5.6/MODULE.bazel": "c43dabc564990eeab55e25ed61c07a1aadafe9ece96a4efabb3f8bf9063b71ef",
+    "https://bcr.bazel.build/modules/stardoc/0.7.0/MODULE.bazel": "05e3d6d30c099b6770e97da986c53bd31844d7f13d41412480ea265ac9e8079c",
+    "https://bcr.bazel.build/modules/stardoc/0.7.1/MODULE.bazel": "3548faea4ee5dda5580f9af150e79d0f6aea934fc60c1cc50f4efdd9420759e7",
+    "https://bcr.bazel.build/modules/stardoc/0.7.1/source.json": "b6500ffcd7b48cd72c29bb67bcac781e12701cc0d6d55d266a652583cfcdab01",
+    "https://bcr.bazel.build/modules/upb/0.0.0-20220923-a547704/MODULE.bazel": "7298990c00040a0e2f121f6c32544bab27d4452f80d9ce51349b1a28f3005c43",
+    "https://bcr.bazel.build/modules/zlib/1.2.11/MODULE.bazel": "07b389abc85fdbca459b69e2ec656ae5622873af3f845e1c9d80fe179f3effa0",
+    "https://bcr.bazel.build/modules/zlib/1.3.1.bcr.3/MODULE.bazel": "af322bc08976524477c79d1e45e241b6efbeb918c497e8840b8ab116802dda79",
+    "https://bcr.bazel.build/modules/zlib/1.3.1.bcr.3/source.json": "2be409ac3c7601245958cd4fcdff4288be79ed23bd690b4b951f500d54ee6e7d",
+    "https://bcr.bazel.build/modules/zlib/1.3.1/MODULE.bazel": "751c9940dcfe869f5f7274e1295422a34623555916eb98c174c1e945594bf198"
+  },
+  "selectedYankedVersions": {},
+  "moduleExtensions": {
+    "@@apple_support+//crosstool:setup.bzl%apple_cc_configure_extension": {
+      "general": {
+        "bzlTransitiveDigest": "xcBTf2+GaloFpg7YEh/Bv+1yAczRkiCt3DGws4K7kSk=",
+        "usagesDigest": "3L+PK6aRnliv0iIS8m3kdo+LjmvjJWoFCm3qZcPSg+8=",
+        "recordedFileInputs": {},
+        "recordedDirentsInputs": {},
+        "envVariables": {},
+        "generatedRepoSpecs": {
+          "local_config_apple_cc_toolchains": {
+            "repoRuleId": "@@apple_support+//crosstool:setup.bzl%_apple_cc_autoconf_toolchains",
+            "attributes": {}
+          },
+          "local_config_apple_cc": {
+            "repoRuleId": "@@apple_support+//crosstool:setup.bzl%_apple_cc_autoconf",
+            "attributes": {}
+          }
+        },
+        "recordedRepoMappingEntries": [
+          [
+            "apple_support+",
+            "bazel_tools",
+            "bazel_tools"
+          ],
+          [
+            "bazel_tools",
+            "rules_cc",
+            "rules_cc+"
+          ]
+        ]
+      }
+    },
+    "@@rules_kotlin+//src/main/starlark/core/repositories:bzlmod_setup.bzl%rules_kotlin_extensions": {
+      "general": {
+        "bzlTransitiveDigest": "sFhcgPbDQehmbD1EOXzX4H1q/CD5df8zwG4kp4jbvr8=",
+        "usagesDigest": "QI2z8ZUR+mqtbwsf2fLqYdJAkPOHdOV+tF2yVAUgRzw=",
+        "recordedFileInputs": {},
+        "recordedDirentsInputs": {},
+        "envVariables": {},
+        "generatedRepoSpecs": {
+          "com_github_jetbrains_kotlin_git": {
+            "repoRuleId": "@@rules_kotlin+//src/main/starlark/core/repositories:compiler.bzl%kotlin_compiler_git_repository",
+            "attributes": {
+              "urls": [
+                "https://github.com/JetBrains/kotlin/releases/download/v1.9.23/kotlin-compiler-1.9.23.zip"
+              ],
+              "sha256": "93137d3aab9afa9b27cb06a824c2324195c6b6f6179d8a8653f440f5bd58be88"
+            }
+          },
+          "com_github_jetbrains_kotlin": {
+            "repoRuleId": "@@rules_kotlin+//src/main/starlark/core/repositories:compiler.bzl%kotlin_capabilities_repository",
+            "attributes": {
+              "git_repository_name": "com_github_jetbrains_kotlin_git",
+              "compiler_version": "1.9.23"
+            }
+          },
+          "com_github_google_ksp": {
+            "repoRuleId": "@@rules_kotlin+//src/main/starlark/core/repositories:ksp.bzl%ksp_compiler_plugin_repository",
+            "attributes": {
+              "urls": [
+                "https://github.com/google/ksp/releases/download/1.9.23-1.0.20/artifacts.zip"
+              ],
+              "sha256": "ee0618755913ef7fd6511288a232e8fad24838b9af6ea73972a76e81053c8c2d",
+              "strip_version": "1.9.23-1.0.20"
+            }
+          },
+          "com_github_pinterest_ktlint": {
+            "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_file",
+            "attributes": {
+              "sha256": "01b2e0ef893383a50dbeb13970fe7fa3be36ca3e83259e01649945b09d736985",
+              "urls": [
+                "https://github.com/pinterest/ktlint/releases/download/1.3.0/ktlint"
+              ],
+              "executable": true
+            }
+          },
+          "rules_android": {
+            "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+            "attributes": {
+              "sha256": "cd06d15dd8bb59926e4d65f9003bfc20f9da4b2519985c27e190cddc8b7a7806",
+              "strip_prefix": "rules_android-0.1.1",
+              "urls": [
+                "https://github.com/bazelbuild/rules_android/archive/v0.1.1.zip"
+              ]
+            }
+          }
+        },
+        "recordedRepoMappingEntries": [
+          [
+            "rules_kotlin+",
+            "bazel_tools",
+            "bazel_tools"
+          ]
+        ]
+      }
+    }
+  }
+}
diff --git a/README.md b/README.md
new file mode 100644 (file)
index 0000000..9c6dab1
--- /dev/null
+++ b/README.md
@@ -0,0 +1,388 @@
+CXX &mdash; safe FFI between Rust and C++
+=========================================
+
+[<img alt="github" src="https://img.shields.io/badge/github-dtolnay/cxx-8da0cb?style=for-the-badge&labelColor=555555&logo=github" height="20">](https://github.com/dtolnay/cxx)
+[<img alt="crates.io" src="https://img.shields.io/crates/v/cxx.svg?style=for-the-badge&color=fc8d62&logo=rust" height="20">](https://crates.io/crates/cxx)
+[<img alt="docs.rs" src="https://img.shields.io/badge/docs.rs-cxx-66c2a5?style=for-the-badge&labelColor=555555&logo=docs.rs" height="20">](https://docs.rs/cxx)
+[<img alt="build status" src="https://img.shields.io/github/actions/workflow/status/dtolnay/cxx/ci.yml?branch=master&style=for-the-badge" height="20">](https://github.com/dtolnay/cxx/actions?query=branch%3Amaster)
+
+This library provides a **safe** mechanism for calling C++ code from Rust and
+Rust code from C++, not subject to the many ways that things can go wrong when
+using bindgen or cbindgen to generate unsafe C-style bindings.
+
+This doesn't change the fact that 100% of C++ code is unsafe. When auditing a
+project, you would be on the hook for auditing all the unsafe Rust code and
+*all* the C++ code. The core safety claim under this new model is that auditing
+just the C++ side would be sufficient to catch all problems, i.e. the Rust side
+can be 100% safe.
+
+```toml
+[dependencies]
+cxx = "1.0"
+
+[build-dependencies]
+cxx-build = "1.0"
+```
+
+*Compiler support: requires rustc 1.73+ 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
+embedded together in one Rust module (the next section shows an example). From
+this, CXX receives a complete picture of the boundary to perform static analyses
+against the types and function signatures to uphold both Rust's and C++'s
+invariants and requirements.
+
+If everything checks out statically, then CXX uses a pair of code generators to
+emit the relevant `extern "C"` signatures on both sides together with any
+necessary static assertions for later in the build process to verify
+correctness. On the Rust side this code generator is simply an attribute
+procedural macro. On the C++ side it can be a small Cargo build script if your
+build is managed by Cargo, or for other build systems like Bazel or Buck we
+provide a command line tool which generates the header and source file and
+should be easy to integrate.
+
+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 types from whichever side they please,
+such as Rust's `String` or C++'s `std::string`, Rust's `Box` or C++'s
+`std::unique_ptr`, Rust's `Vec` or C++'s `std::vector`, etc in any combination.
+CXX guarantees an ABI-compatible signature that both sides understand, based on
+builtin bindings for key standard library types to expose an idiomatic API on
+those types to the other language. For example when manipulating a C++ string
+from Rust, its `len()` method becomes a call of the `size()` member function
+defined by C++; when manipulating a Rust string from C++, its `size()` member
+function calls Rust's `len()`.
+
+<br>
+
+## Example
+
+In this example we are writing a Rust application that wishes to take advantage
+of an existing C++ client for 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 chunks, or fragments of a file spread across memory for some other reason.
+
+A runnable version of this example is provided under the *demo* directory of
+this repo. To try it out, run `cargo run` from that directory.
+
+```rust
+#[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. 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;
+    }
+}
+```
+
+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.
+
+Here are links to the complete set of source files involved in the demo:
+
+- [demo/src/main.rs](demo/src/main.rs)
+- [demo/build.rs](demo/build.rs)
+- [demo/include/blobstore.h](demo/include/blobstore.h)
+- [demo/src/blobstore.cc](demo/src/blobstore.cc)
+
+To look at the code generated in both languages for the example by the CXX code
+generators:
+
+```console
+   # run Rust code generator and print to stdout
+   # (requires https://github.com/dtolnay/cargo-expand)
+$ cargo expand --manifest-path demo/Cargo.toml
+
+   # run C++ code generator and print to stdout
+$ cargo run --manifest-path gen/cmd/Cargo.toml -- demo/src/main.rs
+```
+
+<br>
+
+## Details
+
+As seen in the example, the language of the FFI boundary involves 3 kinds of
+items:
+
+- **Shared structs** &mdash; their fields are made visible to both languages.
+  The definition written within cxx::bridge is the single 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 `UniquePtr`. 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.
+
+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 right shims
+where necessary to make it all work.
+
+<br>
+
+## Comparison vs bindgen and cbindgen
+
+Notice that with CXX there is repetition of all the function signatures: they
+are typed out once where the implementation is defined (in C++ or Rust) and
+again inside the cxx::bridge module, though compile-time assertions guarantee
+these are kept in sync. This is different from [bindgen] and [cbindgen] where
+function signatures are typed by a human once and the tool consumes them in one
+language and emits them in the other language.
+
+[bindgen]: https://github.com/rust-lang/rust-bindgen
+[cbindgen]: https://github.com/eqrion/cbindgen/
+
+This is because CXX fills a somewhat different role. It is a lower level tool
+than bindgen or cbindgen in a sense; you can think of it as being a replacement
+for the concept of `extern "C"` signatures as we know them, rather than a
+replacement for a bindgen. It would be reasonable to build a higher level
+bindgen-like tool on top of CXX which consumes a C++ header and/or Rust module
+(and/or IDL like Thrift) as source of truth and generates the cxx::bridge,
+eliminating the repetition while leveraging the static analysis safety
+guarantees of CXX.
+
+But note in other ways CXX is higher level than the bindgens, with rich support
+for common standard library types. Frequently with bindgen when we are dealing
+with an idiomatic C++ API we would end up manually wrapping that API in C-style
+raw pointer functions, applying bindgen to get unsafe raw pointer Rust
+functions, and replicating the API again to expose those idiomatically in Rust.
+That's a much worse form of repetition because it is unsafe all the way through.
+
+By using a CXX bridge as the shared understanding between the languages, rather
+than `extern "C"` C-style signatures as the shared understanding, common FFI use
+cases become expressible using 100% safe code.
+
+It would also be reasonable to mix and match, using CXX bridge for the 95% of
+your FFI that is straightforward and doing the remaining few oddball signatures
+the old fashioned way with bindgen and cbindgen, if for some reason CXX's static
+restrictions get in the way. Please file an issue if you end up taking this
+approach so that we know what ways it would be worthwhile to make the tool more
+expressive.
+
+<br>
+
+## Cargo-based setup
+
+For builds that are orchestrated by Cargo, you will use a build script that runs
+CXX's C++ code generator and compiles the resulting C++ code along with any
+other C++ code for your crate.
+
+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
+
+```toml
+# Cargo.toml
+
+[build-dependencies]
+cxx-build = "1.0"
+```
+
+```rust
+// build.rs
+
+fn main() {
+    cxx_build::bridge("src/main.rs")  // returns a cc::Build
+        .file("src/demo.cc")
+        .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");
+}
+```
+
+<br>
+
+## Non-Cargo setup
+
+For use in non-Cargo builds like Bazel or Buck, CXX provides an alternate way of
+invoking the C++ code generator as a standalone command line tool. The tool is
+packaged as the `cxxbridge-cmd` crate on crates.io or can be built from the
+*gen/cmd* directory of this repo.
+
+```bash
+$ cargo install cxxbridge-cmd
+
+$ cxxbridge src/main.rs --header > path/to/mybridge.h
+$ cxxbridge src/main.rs > path/to/mybridge.cc
+```
+
+<br>
+
+## Safety
+
+Be aware that the design of this library is intentionally restrictive and
+opinionated! It isn't a goal to be powerful enough to handle arbitrary
+signatures in either language. Instead this project is about carving out a
+reasonably expressive set of functionality about which we can make useful safety
+guarantees today and maybe 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
+are used to.
+
+Some of the considerations that go into ensuring safety are:
+
+- By design, our paired code generators work together to control both sides of
+  the FFI boundary. Ordinarily in Rust writing your own `extern "C"` blocks is
+  unsafe because the Rust compiler has no way to know whether the signatures
+  you've written actually match the signatures implemented in the other
+  language. With CXX we achieve that visibility and know what's on the other
+  side.
+
+- Our static analysis detects and prevents passing types by value that shouldn't
+  be passed by value from C++ to Rust, for example because they may contain
+  internal pointers that would be screwed up by Rust's move behavior.
+
+- To many people's surprise, it is possible to have a struct in Rust and a
+  struct in C++ with exactly the same layout / fields / alignment / everything,
+  and still not the same ABI when passed by value. This is a longstanding
+  bindgen bug that leads to segfaults in absolutely correct-looking code
+  ([rust-lang/rust-bindgen#778]). CXX knows about this and can insert the
+  necessary zero-cost workaround transparently where needed, so go ahead and
+  pass your structs by value without worries. This is made possible by owning
+  both sides of the boundary rather than just one.
+
+- Template instantiations: for example in order to expose a UniquePtr\<T\> type
+  in Rust backed by a real C++ unique\_ptr, we have a way of using a Rust trait
+  to connect the behavior back to the template instantiations performed by the
+  other language.
+
+[rust-lang/rust-bindgen#778]: https://github.com/rust-lang/rust-bindgen/issues/778
+
+<br>
+
+## Builtin types
+
+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 functions.
+
+<table>
+<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;[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/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/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>
+
+The C++ API of the `rust` namespace is defined by the *include/cxx.h* file in
+this repo. You will need to include this header in your C++ code when working
+with those types.
+
+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.
+
+<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>
+
+<br>
+
+## Remaining work
+
+This is still early days for CXX; I am releasing it as a minimum viable product
+to collect feedback on the direction and invite collaborators. Please check the
+open issues.
+
+Especially please report issues if you run into trouble building or linking any
+of this stuff. I'm sure there are ways to make the build aspects friendlier or
+more robust.
+
+Finally, I know more about Rust library design than C++ library design so I
+would appreciate help making the C++ APIs in this project more idiomatic where
+anyone has suggestions.
+
+<br>
+
+#### License
+
+<sup>
+Licensed under either of <a href="LICENSE-APACHE">Apache License, Version
+2.0</a> or <a href="LICENSE-MIT">MIT license</a> at your option.
+</sup>
+
+<br>
+
+<sub>
+Unless you explicitly state otherwise, any contribution intentionally submitted
+for inclusion in this project by you, as defined in the Apache-2.0 license,
+shall be dual licensed as above, without any additional terms or conditions.
+</sub>
diff --git a/book/.gitignore b/book/.gitignore
new file mode 100644 (file)
index 0000000..3c7d187
--- /dev/null
@@ -0,0 +1,3 @@
+/build/
+/mdbook
+/node_modules/
diff --git a/book/README.md b/book/README.md
new file mode 100644 (file)
index 0000000..e4916e0
--- /dev/null
@@ -0,0 +1,9 @@
+Published automatically to https://cxx.rs from master branch.
+
+To build and view locally:
+
+- Install [mdBook]: `cargo install mdbook`.
+- Run `mdbook build` in this directory.
+- Open the generated *build/index.html*.
+
+[mdBook]: https://github.com/rust-lang/mdBook
diff --git a/book/book.toml b/book/book.toml
new file mode 100644 (file)
index 0000000..a8148fe
--- /dev/null
@@ -0,0 +1,22 @@
+[book]
+#title = "Rust ♡ C++"
+authors = ["David Tolnay"]
+description = "CXX — safe interop between Rust and C++ by David Tolnay. This library provides a safe mechanism for calling C++ code from Rust and Rust code from C++."
+
+[rust]
+edition = "2021"
+
+[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 (executable)
index 0000000..85da7bf
--- /dev/null
@@ -0,0 +1,137 @@
+#!/usr/bin/env node
+
+const fs = require('fs');
+const cheerio = require('cheerio');
+const entities = require('html-entities');
+const hljs = require('./build/highlight.js');
+
+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 themejs = `\
+var theme;
+try { theme = localStorage.getItem('mdbook-theme'); } catch(e) {}
+if (theme === null || theme === undefined) { theme = default_theme; }
+const html = document.documentElement;
+html.classList.remove('light')
+html.classList.add(theme);
+html.classList.add("js");`;
+
+const themejsReplacement = `\
+const html = document.documentElement;
+html.classList.add('js');`;
+
+const dirs = ['build'];
+while (dirs.length) {
+  const dir = dirs.pop();
+  fs.readdirSync(dir).forEach((entry) => {
+    const 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,
+      xml: { xmlMode: false },
+    });
+
+    $('head').append(opengraph);
+    $('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 originalLines = node.html().split('\n');
+      const boring = originalLines.map((line) =>
+        line.includes('<span class="boring">'),
+      );
+      const ellipsis = originalLines.map((line) => line.includes('// ...'));
+      const target = entities.decode(node.text());
+      const highlightedLines = hljs.highlight(lang, target).value.split('\n');
+      const result = highlightedLines
+        .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;
+          }
+          if (i + 1 === highlightedLines.length && (boring[i] || ellipsis[i])) {
+            line = line + '</span>';
+          }
+          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');
+    });
+
+    var foundScript = false;
+    $('body script').each(function () {
+      const node = $(this);
+      if (node.text().replace(/\s/g, '') === themejs.replace(/\s/g, '')) {
+        node.text(themejsReplacement);
+        foundScript = true;
+      }
+    });
+    const pathsWithoutScript = [
+      'build/toc.html',
+      'build/build/index.html',
+      'build/binding/index.html',
+    ];
+    if (!foundScript && !pathsWithoutScript.includes(path)) {
+      throw new Error('theme script not found');
+    }
+
+    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);', '')
+  .replace(
+    'document.querySelectorAll("code.hljs")',
+    'document.querySelectorAll("code.hidelines")',
+  );
+fs.writeFileSync('build/book.js', bookjs);
diff --git a/book/build.sh b/book/build.sh
new file mode 100755 (executable)
index 0000000..783d304
--- /dev/null
@@ -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 (file)
index 0000000..68d32db
--- /dev/null
@@ -0,0 +1,49 @@
+: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;
+}
+
+pre > .buttons {
+    visibility: visible;
+    opacity: 0.3;
+}
diff --git a/book/diagram/.gitignore b/book/diagram/.gitignore
new file mode 100644 (file)
index 0000000..0017281
--- /dev/null
@@ -0,0 +1,7 @@
+/*.aux
+/*.fdb_latexmk
+/*.fls
+/*.log
+/*.pdf
+/*.png
+/*.svg
diff --git a/book/diagram/Makefile b/book/diagram/Makefile
new file mode 100644 (file)
index 0000000..1723a9b
--- /dev/null
@@ -0,0 +1,8 @@
+overview.svg: overview.pdf
+       pdf2svg $< $@
+
+overview.pdf: overview.tex
+       latexmk $<
+
+overview.png: overview.svg
+       svgexport $< $@ 3x
diff --git a/book/diagram/overview.tex b/book/diagram/overview.tex
new file mode 100644 (file)
index 0000000..a613bb7
--- /dev/null
@@ -0,0 +1,45 @@
+\documentclass{standalone}
+\usepackage{makecell}
+\usepackage{pgfplots}
+\usepackage{sansmath}
+\usetikzlibrary{arrows.meta}
+\pgfplotsset{compat=1.16}
+\begin{document}
+\pagecolor{white}
+\begin{tikzpicture}[
+  x=1cm,
+  y=-.6cm,
+  every node/.append style={
+    line width=1.5pt,
+    font=\Large\sansmath\sffamily,
+  },
+  every path/.append style={
+    >={Latex[length=10pt,width=8pt]},
+    line width=1.5pt,
+  },
+  execute at end node={\vphantom{bg}},
+]
+\node[draw, rounded corners=5, inner xsep=30pt, inner ysep=2pt]
+  (bridge) at (0, .25) {\makecell{\texttt{\#\hspace{-1pt}[}cxx::bridge\texttt{]} mod\\[-4pt]description of boundary}};
+\node[draw, rounded corners, inner xsep=10pt, inner ysep=6pt, text depth=1pt]
+  (rust-bindings) at (-3.5, 6.5) {Rust bindings};
+\node[draw, rounded corners, inner xsep=10pt, inner ysep=6pt, text depth=1pt]
+  (cpp-bindings) at (3.5, 6.5) {C\texttt{++} bindings};
+\node[inner xsep=4pt, inner ysep=-0pt]
+  (rust-code) at (-9, 6.5) {\makecell[r]{\\[-8pt]Rust\\[-4pt]code}};
+\node[inner xsep=4pt, inner ysep=-0pt]
+  (cpp-code) at (9, 6.5) {\makecell[l]{\\[-8pt]C\texttt{++}\\[-4pt]code}};
+\draw (bridge) -- (0, 4);
+\draw[<->] (rust-bindings) |- (0, 4) -| (cpp-bindings);
+\draw[<->] (rust-code) -- (rust-bindings);
+\draw[<->, dash pattern=on 8pt off 6pt] (rust-bindings) -- (cpp-bindings);
+\draw[<->] (cpp-bindings) -- (cpp-code);
+\draw (-.75, 4) node[anchor=south east] {Macro expansion};
+\draw (.75, 4) node[anchor=south west] {Code generation};
+\draw (0, 6.5) node[anchor=south, inner ysep=4pt] {Hidden C ABI};
+\draw (-6.75, 6.5) node[anchor=south, inner ysep=1pt] {\makecell{Safe\\[-4pt]straightforward\\[-4pt]Rust APIs}};
+\draw (6.75, 6.5) node[anchor=south, inner ysep=1pt] {\makecell{Straightforward\\[-4pt]C\texttt{++} APIs}};
+\pgfresetboundingbox\path
+  (-9.5, 0) -- (rust-bindings.south)+(0, .3) -- (9.5, 0) -- (bridge.north);
+\end{tikzpicture}
+\end{document}
diff --git a/book/eslint.config.mjs b/book/eslint.config.mjs
new file mode 100644 (file)
index 0000000..6ad9c6c
--- /dev/null
@@ -0,0 +1,8 @@
+import pluginJs from '@eslint/js';
+
+/** @type {import('eslint').Linter.Config[]} */
+export default [
+  { ignores: ['build/*'] },
+  { files: ['**/*.js'], languageOptions: { sourceType: 'commonjs' } },
+  pluginJs.configs.recommended,
+];
diff --git a/book/package-lock.json b/book/package-lock.json
new file mode 100644 (file)
index 0000000..a6af7c2
--- /dev/null
@@ -0,0 +1,1365 @@
+{
+  "name": "cxx-book-build",
+  "version": "0.0.0",
+  "lockfileVersion": 3,
+  "requires": true,
+  "packages": {
+    "": {
+      "name": "cxx-book-build",
+      "version": "0.0.0",
+      "dependencies": {
+        "cheerio": "^1.0.0",
+        "html-entities": "^2.5.2"
+      },
+      "devDependencies": {
+        "@eslint/js": "^9.19.0",
+        "eslint": "^9.19.0"
+      }
+    },
+    "node_modules/@eslint-community/eslint-utils": {
+      "version": "4.4.1",
+      "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.1.tgz",
+      "integrity": "sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "eslint-visitor-keys": "^3.4.3"
+      },
+      "engines": {
+        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/eslint"
+      },
+      "peerDependencies": {
+        "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0"
+      }
+    },
+    "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": {
+      "version": "3.4.3",
+      "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
+      "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "engines": {
+        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/eslint"
+      }
+    },
+    "node_modules/@eslint-community/regexpp": {
+      "version": "4.12.1",
+      "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz",
+      "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": "^12.0.0 || ^14.0.0 || >=16.0.0"
+      }
+    },
+    "node_modules/@eslint/config-array": {
+      "version": "0.19.1",
+      "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.19.1.tgz",
+      "integrity": "sha512-fo6Mtm5mWyKjA/Chy1BYTdn5mGJoDNjC7C64ug20ADsRDGrA85bN3uK3MaKbeRkRuuIEAR5N33Jr1pbm411/PA==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@eslint/object-schema": "^2.1.5",
+        "debug": "^4.3.1",
+        "minimatch": "^3.1.2"
+      },
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      }
+    },
+    "node_modules/@eslint/core": {
+      "version": "0.10.0",
+      "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.10.0.tgz",
+      "integrity": "sha512-gFHJ+xBOo4G3WRlR1e/3G8A6/KZAH6zcE/hkLRCZTi/B9avAG365QhFA8uOGzTMqgTghpn7/fSnscW++dpMSAw==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@types/json-schema": "^7.0.15"
+      },
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      }
+    },
+    "node_modules/@eslint/eslintrc": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.2.0.tgz",
+      "integrity": "sha512-grOjVNN8P3hjJn/eIETF1wwd12DdnwFDoyceUJLYYdkpbwq3nLi+4fqrTAONx7XDALqlL220wC/RHSC/QTI/0w==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "ajv": "^6.12.4",
+        "debug": "^4.3.2",
+        "espree": "^10.0.1",
+        "globals": "^14.0.0",
+        "ignore": "^5.2.0",
+        "import-fresh": "^3.2.1",
+        "js-yaml": "^4.1.0",
+        "minimatch": "^3.1.2",
+        "strip-json-comments": "^3.1.1"
+      },
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/eslint"
+      }
+    },
+    "node_modules/@eslint/eslintrc/node_modules/globals": {
+      "version": "14.0.0",
+      "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz",
+      "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/@eslint/js": {
+      "version": "9.19.0",
+      "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.19.0.tgz",
+      "integrity": "sha512-rbq9/g38qjfqFLOVPvwjIvFFdNziEC5S65jmjPw5r6A//QH+W91akh9irMwjDN8zKUTak6W9EsAv4m/7Wnw0UQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      }
+    },
+    "node_modules/@eslint/object-schema": {
+      "version": "2.1.5",
+      "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.5.tgz",
+      "integrity": "sha512-o0bhxnL89h5Bae5T318nFoFzGy+YE5i/gGkoPAgkmTVdRKTiv3p8JHevPiPaMwoloKfEiiaHlawCqaZMqRm+XQ==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      }
+    },
+    "node_modules/@eslint/plugin-kit": {
+      "version": "0.2.5",
+      "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.5.tgz",
+      "integrity": "sha512-lB05FkqEdUg2AA0xEbUz0SnkXT1LcCTa438W4IWTUh4hdOnVbQyOJ81OrDXsJk/LSiJHubgGEFoR5EHq1NsH1A==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@eslint/core": "^0.10.0",
+        "levn": "^0.4.1"
+      },
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      }
+    },
+    "node_modules/@humanfs/core": {
+      "version": "0.19.1",
+      "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz",
+      "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "engines": {
+        "node": ">=18.18.0"
+      }
+    },
+    "node_modules/@humanfs/node": {
+      "version": "0.16.6",
+      "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz",
+      "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@humanfs/core": "^0.19.1",
+        "@humanwhocodes/retry": "^0.3.0"
+      },
+      "engines": {
+        "node": ">=18.18.0"
+      }
+    },
+    "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": {
+      "version": "0.3.1",
+      "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz",
+      "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "engines": {
+        "node": ">=18.18"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/nzakas"
+      }
+    },
+    "node_modules/@humanwhocodes/module-importer": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
+      "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "engines": {
+        "node": ">=12.22"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/nzakas"
+      }
+    },
+    "node_modules/@humanwhocodes/retry": {
+      "version": "0.4.1",
+      "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.1.tgz",
+      "integrity": "sha512-c7hNEllBlenFTHBky65mhq8WD2kbN9Q6gk0bTk8lSBvc554jpXSkST1iePudpt7+A/AQvuHs9EMqjHDXMY1lrA==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "engines": {
+        "node": ">=18.18"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/nzakas"
+      }
+    },
+    "node_modules/@types/estree": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz",
+      "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/@types/json-schema": {
+      "version": "7.0.15",
+      "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
+      "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/acorn": {
+      "version": "8.14.0",
+      "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz",
+      "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==",
+      "dev": true,
+      "license": "MIT",
+      "bin": {
+        "acorn": "bin/acorn"
+      },
+      "engines": {
+        "node": ">=0.4.0"
+      }
+    },
+    "node_modules/acorn-jsx": {
+      "version": "5.3.2",
+      "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
+      "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
+      "dev": true,
+      "license": "MIT",
+      "peerDependencies": {
+        "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
+      }
+    },
+    "node_modules/ajv": {
+      "version": "6.12.6",
+      "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
+      "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "fast-deep-equal": "^3.1.1",
+        "fast-json-stable-stringify": "^2.0.0",
+        "json-schema-traverse": "^0.4.1",
+        "uri-js": "^4.2.2"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/epoberezkin"
+      }
+    },
+    "node_modules/ansi-styles": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+      "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "color-convert": "^2.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+      }
+    },
+    "node_modules/argparse": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+      "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
+      "dev": true,
+      "license": "Python-2.0"
+    },
+    "node_modules/balanced-match": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+      "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/boolbase": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
+      "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==",
+      "license": "ISC"
+    },
+    "node_modules/brace-expansion": {
+      "version": "1.1.11",
+      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+      "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "balanced-match": "^1.0.0",
+        "concat-map": "0.0.1"
+      }
+    },
+    "node_modules/callsites": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
+      "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/chalk": {
+      "version": "4.1.2",
+      "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+      "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "ansi-styles": "^4.1.0",
+        "supports-color": "^7.1.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/chalk?sponsor=1"
+      }
+    },
+    "node_modules/cheerio": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0.tgz",
+      "integrity": "sha512-quS9HgjQpdaXOvsZz82Oz7uxtXiy6UIsIQcpBj7HRw2M63Skasm9qlDocAM7jNuaxdhpPU7c4kJN+gA5MCu4ww==",
+      "license": "MIT",
+      "dependencies": {
+        "cheerio-select": "^2.1.0",
+        "dom-serializer": "^2.0.0",
+        "domhandler": "^5.0.3",
+        "domutils": "^3.1.0",
+        "encoding-sniffer": "^0.2.0",
+        "htmlparser2": "^9.1.0",
+        "parse5": "^7.1.2",
+        "parse5-htmlparser2-tree-adapter": "^7.0.0",
+        "parse5-parser-stream": "^7.1.2",
+        "undici": "^6.19.5",
+        "whatwg-mimetype": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=18.17"
+      },
+      "funding": {
+        "url": "https://github.com/cheeriojs/cheerio?sponsor=1"
+      }
+    },
+    "node_modules/cheerio-select": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-2.1.0.tgz",
+      "integrity": "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==",
+      "license": "BSD-2-Clause",
+      "dependencies": {
+        "boolbase": "^1.0.0",
+        "css-select": "^5.1.0",
+        "css-what": "^6.1.0",
+        "domelementtype": "^2.3.0",
+        "domhandler": "^5.0.3",
+        "domutils": "^3.0.1"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/fb55"
+      }
+    },
+    "node_modules/color-convert": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+      "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "color-name": "~1.1.4"
+      },
+      "engines": {
+        "node": ">=7.0.0"
+      }
+    },
+    "node_modules/color-name": {
+      "version": "1.1.4",
+      "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+      "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/concat-map": {
+      "version": "0.0.1",
+      "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+      "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/cross-spawn": {
+      "version": "7.0.6",
+      "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
+      "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "path-key": "^3.1.0",
+        "shebang-command": "^2.0.0",
+        "which": "^2.0.1"
+      },
+      "engines": {
+        "node": ">= 8"
+      }
+    },
+    "node_modules/css-select": {
+      "version": "5.1.0",
+      "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz",
+      "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==",
+      "license": "BSD-2-Clause",
+      "dependencies": {
+        "boolbase": "^1.0.0",
+        "css-what": "^6.1.0",
+        "domhandler": "^5.0.2",
+        "domutils": "^3.0.1",
+        "nth-check": "^2.0.1"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/fb55"
+      }
+    },
+    "node_modules/css-what": {
+      "version": "6.1.0",
+      "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz",
+      "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==",
+      "license": "BSD-2-Clause",
+      "engines": {
+        "node": ">= 6"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/fb55"
+      }
+    },
+    "node_modules/debug": {
+      "version": "4.4.0",
+      "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz",
+      "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "ms": "^2.1.3"
+      },
+      "engines": {
+        "node": ">=6.0"
+      },
+      "peerDependenciesMeta": {
+        "supports-color": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/deep-is": {
+      "version": "0.1.4",
+      "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
+      "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/dom-serializer": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz",
+      "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==",
+      "license": "MIT",
+      "dependencies": {
+        "domelementtype": "^2.3.0",
+        "domhandler": "^5.0.2",
+        "entities": "^4.2.0"
+      },
+      "funding": {
+        "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1"
+      }
+    },
+    "node_modules/domelementtype": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz",
+      "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==",
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/fb55"
+        }
+      ],
+      "license": "BSD-2-Clause"
+    },
+    "node_modules/domhandler": {
+      "version": "5.0.3",
+      "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz",
+      "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==",
+      "license": "BSD-2-Clause",
+      "dependencies": {
+        "domelementtype": "^2.3.0"
+      },
+      "engines": {
+        "node": ">= 4"
+      },
+      "funding": {
+        "url": "https://github.com/fb55/domhandler?sponsor=1"
+      }
+    },
+    "node_modules/domutils": {
+      "version": "3.2.2",
+      "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz",
+      "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==",
+      "license": "BSD-2-Clause",
+      "dependencies": {
+        "dom-serializer": "^2.0.0",
+        "domelementtype": "^2.3.0",
+        "domhandler": "^5.0.3"
+      },
+      "funding": {
+        "url": "https://github.com/fb55/domutils?sponsor=1"
+      }
+    },
+    "node_modules/encoding-sniffer": {
+      "version": "0.2.0",
+      "resolved": "https://registry.npmjs.org/encoding-sniffer/-/encoding-sniffer-0.2.0.tgz",
+      "integrity": "sha512-ju7Wq1kg04I3HtiYIOrUrdfdDvkyO9s5XM8QAj/bN61Yo/Vb4vgJxy5vi4Yxk01gWHbrofpPtpxM8bKger9jhg==",
+      "license": "MIT",
+      "dependencies": {
+        "iconv-lite": "^0.6.3",
+        "whatwg-encoding": "^3.1.1"
+      },
+      "funding": {
+        "url": "https://github.com/fb55/encoding-sniffer?sponsor=1"
+      }
+    },
+    "node_modules/entities": {
+      "version": "4.5.0",
+      "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
+      "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
+      "license": "BSD-2-Clause",
+      "engines": {
+        "node": ">=0.12"
+      },
+      "funding": {
+        "url": "https://github.com/fb55/entities?sponsor=1"
+      }
+    },
+    "node_modules/escape-string-regexp": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+      "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/eslint": {
+      "version": "9.19.0",
+      "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.19.0.tgz",
+      "integrity": "sha512-ug92j0LepKlbbEv6hD911THhoRHmbdXt2gX+VDABAW/Ir7D3nqKdv5Pf5vtlyY6HQMTEP2skXY43ueqTCWssEA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@eslint-community/eslint-utils": "^4.2.0",
+        "@eslint-community/regexpp": "^4.12.1",
+        "@eslint/config-array": "^0.19.0",
+        "@eslint/core": "^0.10.0",
+        "@eslint/eslintrc": "^3.2.0",
+        "@eslint/js": "9.19.0",
+        "@eslint/plugin-kit": "^0.2.5",
+        "@humanfs/node": "^0.16.6",
+        "@humanwhocodes/module-importer": "^1.0.1",
+        "@humanwhocodes/retry": "^0.4.1",
+        "@types/estree": "^1.0.6",
+        "@types/json-schema": "^7.0.15",
+        "ajv": "^6.12.4",
+        "chalk": "^4.0.0",
+        "cross-spawn": "^7.0.6",
+        "debug": "^4.3.2",
+        "escape-string-regexp": "^4.0.0",
+        "eslint-scope": "^8.2.0",
+        "eslint-visitor-keys": "^4.2.0",
+        "espree": "^10.3.0",
+        "esquery": "^1.5.0",
+        "esutils": "^2.0.2",
+        "fast-deep-equal": "^3.1.3",
+        "file-entry-cache": "^8.0.0",
+        "find-up": "^5.0.0",
+        "glob-parent": "^6.0.2",
+        "ignore": "^5.2.0",
+        "imurmurhash": "^0.1.4",
+        "is-glob": "^4.0.0",
+        "json-stable-stringify-without-jsonify": "^1.0.1",
+        "lodash.merge": "^4.6.2",
+        "minimatch": "^3.1.2",
+        "natural-compare": "^1.4.0",
+        "optionator": "^0.9.3"
+      },
+      "bin": {
+        "eslint": "bin/eslint.js"
+      },
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      },
+      "funding": {
+        "url": "https://eslint.org/donate"
+      },
+      "peerDependencies": {
+        "jiti": "*"
+      },
+      "peerDependenciesMeta": {
+        "jiti": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/eslint-scope": {
+      "version": "8.2.0",
+      "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.2.0.tgz",
+      "integrity": "sha512-PHlWUfG6lvPc3yvP5A4PNyBL1W8fkDUccmI21JUu/+GKZBoH/W5u6usENXUrWFRsyoW5ACUjFGgAFQp5gUlb/A==",
+      "dev": true,
+      "license": "BSD-2-Clause",
+      "dependencies": {
+        "esrecurse": "^4.3.0",
+        "estraverse": "^5.2.0"
+      },
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/eslint"
+      }
+    },
+    "node_modules/eslint-visitor-keys": {
+      "version": "4.2.0",
+      "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz",
+      "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/eslint"
+      }
+    },
+    "node_modules/espree": {
+      "version": "10.3.0",
+      "resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz",
+      "integrity": "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==",
+      "dev": true,
+      "license": "BSD-2-Clause",
+      "dependencies": {
+        "acorn": "^8.14.0",
+        "acorn-jsx": "^5.3.2",
+        "eslint-visitor-keys": "^4.2.0"
+      },
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/eslint"
+      }
+    },
+    "node_modules/esquery": {
+      "version": "1.6.0",
+      "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz",
+      "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==",
+      "dev": true,
+      "license": "BSD-3-Clause",
+      "dependencies": {
+        "estraverse": "^5.1.0"
+      },
+      "engines": {
+        "node": ">=0.10"
+      }
+    },
+    "node_modules/esrecurse": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
+      "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
+      "dev": true,
+      "license": "BSD-2-Clause",
+      "dependencies": {
+        "estraverse": "^5.2.0"
+      },
+      "engines": {
+        "node": ">=4.0"
+      }
+    },
+    "node_modules/estraverse": {
+      "version": "5.3.0",
+      "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+      "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+      "dev": true,
+      "license": "BSD-2-Clause",
+      "engines": {
+        "node": ">=4.0"
+      }
+    },
+    "node_modules/esutils": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
+      "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
+      "dev": true,
+      "license": "BSD-2-Clause",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/fast-deep-equal": {
+      "version": "3.1.3",
+      "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+      "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/fast-json-stable-stringify": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
+      "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/fast-levenshtein": {
+      "version": "2.0.6",
+      "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
+      "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/file-entry-cache": {
+      "version": "8.0.0",
+      "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz",
+      "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "flat-cache": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=16.0.0"
+      }
+    },
+    "node_modules/find-up": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
+      "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "locate-path": "^6.0.0",
+        "path-exists": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/flat-cache": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz",
+      "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "flatted": "^3.2.9",
+        "keyv": "^4.5.4"
+      },
+      "engines": {
+        "node": ">=16"
+      }
+    },
+    "node_modules/flatted": {
+      "version": "3.3.2",
+      "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.2.tgz",
+      "integrity": "sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA==",
+      "dev": true,
+      "license": "ISC"
+    },
+    "node_modules/glob-parent": {
+      "version": "6.0.2",
+      "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
+      "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "is-glob": "^4.0.3"
+      },
+      "engines": {
+        "node": ">=10.13.0"
+      }
+    },
+    "node_modules/has-flag": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+      "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/html-entities": {
+      "version": "2.5.2",
+      "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.5.2.tgz",
+      "integrity": "sha512-K//PSRMQk4FZ78Kyau+mZurHn3FH0Vwr+H36eE0rPbeYkRRi9YxceYPhuN60UwWorxyKHhqoAJl2OFKa4BVtaA==",
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/mdevils"
+        },
+        {
+          "type": "patreon",
+          "url": "https://patreon.com/mdevils"
+        }
+      ],
+      "license": "MIT"
+    },
+    "node_modules/htmlparser2": {
+      "version": "9.1.0",
+      "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-9.1.0.tgz",
+      "integrity": "sha512-5zfg6mHUoaer/97TxnGpxmbR7zJtPwIYFMZ/H5ucTlPZhKvtum05yiPK3Mgai3a0DyVxv7qYqoweaEd2nrYQzQ==",
+      "funding": [
+        "https://github.com/fb55/htmlparser2?sponsor=1",
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/fb55"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "domelementtype": "^2.3.0",
+        "domhandler": "^5.0.3",
+        "domutils": "^3.1.0",
+        "entities": "^4.5.0"
+      }
+    },
+    "node_modules/iconv-lite": {
+      "version": "0.6.3",
+      "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
+      "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
+      "license": "MIT",
+      "dependencies": {
+        "safer-buffer": ">= 2.1.2 < 3.0.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/ignore": {
+      "version": "5.3.2",
+      "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
+      "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 4"
+      }
+    },
+    "node_modules/import-fresh": {
+      "version": "3.3.0",
+      "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
+      "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "parent-module": "^1.0.0",
+        "resolve-from": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=6"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/imurmurhash": {
+      "version": "0.1.4",
+      "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
+      "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.8.19"
+      }
+    },
+    "node_modules/is-extglob": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+      "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/is-glob": {
+      "version": "4.0.3",
+      "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
+      "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "is-extglob": "^2.1.1"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/isexe": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+      "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
+      "dev": true,
+      "license": "ISC"
+    },
+    "node_modules/js-yaml": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
+      "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "argparse": "^2.0.1"
+      },
+      "bin": {
+        "js-yaml": "bin/js-yaml.js"
+      }
+    },
+    "node_modules/json-buffer": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
+      "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/json-schema-traverse": {
+      "version": "0.4.1",
+      "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+      "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/json-stable-stringify-without-jsonify": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
+      "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/keyv": {
+      "version": "4.5.4",
+      "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
+      "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "json-buffer": "3.0.1"
+      }
+    },
+    "node_modules/levn": {
+      "version": "0.4.1",
+      "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
+      "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "prelude-ls": "^1.2.1",
+        "type-check": "~0.4.0"
+      },
+      "engines": {
+        "node": ">= 0.8.0"
+      }
+    },
+    "node_modules/locate-path": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
+      "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "p-locate": "^5.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/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==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/minimatch": {
+      "version": "3.1.2",
+      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+      "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "brace-expansion": "^1.1.7"
+      },
+      "engines": {
+        "node": "*"
+      }
+    },
+    "node_modules/ms": {
+      "version": "2.1.3",
+      "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+      "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/natural-compare": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
+      "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/nth-check": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz",
+      "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==",
+      "license": "BSD-2-Clause",
+      "dependencies": {
+        "boolbase": "^1.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/fb55/nth-check?sponsor=1"
+      }
+    },
+    "node_modules/optionator": {
+      "version": "0.9.4",
+      "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
+      "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "deep-is": "^0.1.3",
+        "fast-levenshtein": "^2.0.6",
+        "levn": "^0.4.1",
+        "prelude-ls": "^1.2.1",
+        "type-check": "^0.4.0",
+        "word-wrap": "^1.2.5"
+      },
+      "engines": {
+        "node": ">= 0.8.0"
+      }
+    },
+    "node_modules/p-limit": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
+      "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "yocto-queue": "^0.1.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/p-locate": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
+      "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "p-limit": "^3.0.2"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/parent-module": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
+      "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "callsites": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/parse5": {
+      "version": "7.2.1",
+      "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.2.1.tgz",
+      "integrity": "sha512-BuBYQYlv1ckiPdQi/ohiivi9Sagc9JG+Ozs0r7b/0iK3sKmrb0b9FdWdBbOdx6hBCM/F9Ir82ofnBhtZOjCRPQ==",
+      "license": "MIT",
+      "dependencies": {
+        "entities": "^4.5.0"
+      },
+      "funding": {
+        "url": "https://github.com/inikulin/parse5?sponsor=1"
+      }
+    },
+    "node_modules/parse5-htmlparser2-tree-adapter": {
+      "version": "7.1.0",
+      "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.1.0.tgz",
+      "integrity": "sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g==",
+      "license": "MIT",
+      "dependencies": {
+        "domhandler": "^5.0.3",
+        "parse5": "^7.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/inikulin/parse5?sponsor=1"
+      }
+    },
+    "node_modules/parse5-parser-stream": {
+      "version": "7.1.2",
+      "resolved": "https://registry.npmjs.org/parse5-parser-stream/-/parse5-parser-stream-7.1.2.tgz",
+      "integrity": "sha512-JyeQc9iwFLn5TbvvqACIF/VXG6abODeB3Fwmv/TGdLk2LfbWkaySGY72at4+Ty7EkPZj854u4CrICqNk2qIbow==",
+      "license": "MIT",
+      "dependencies": {
+        "parse5": "^7.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/inikulin/parse5?sponsor=1"
+      }
+    },
+    "node_modules/path-exists": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
+      "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/path-key": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
+      "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/prelude-ls": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
+      "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.8.0"
+      }
+    },
+    "node_modules/punycode": {
+      "version": "2.3.1",
+      "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
+      "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/resolve-from": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
+      "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/safer-buffer": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+      "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
+      "license": "MIT"
+    },
+    "node_modules/shebang-command": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
+      "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "shebang-regex": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/shebang-regex": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
+      "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/strip-json-comments": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
+      "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/supports-color": {
+      "version": "7.2.0",
+      "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+      "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "has-flag": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/type-check": {
+      "version": "0.4.0",
+      "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
+      "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "prelude-ls": "^1.2.1"
+      },
+      "engines": {
+        "node": ">= 0.8.0"
+      }
+    },
+    "node_modules/undici": {
+      "version": "6.21.1",
+      "resolved": "https://registry.npmjs.org/undici/-/undici-6.21.1.tgz",
+      "integrity": "sha512-q/1rj5D0/zayJB2FraXdaWxbhWiNKDvu8naDT2dl1yTlvJp4BLtOcp2a5BvgGNQpYYJzau7tf1WgKv3b+7mqpQ==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=18.17"
+      }
+    },
+    "node_modules/uri-js": {
+      "version": "4.4.1",
+      "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
+      "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
+      "dev": true,
+      "license": "BSD-2-Clause",
+      "dependencies": {
+        "punycode": "^2.1.0"
+      }
+    },
+    "node_modules/whatwg-encoding": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz",
+      "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==",
+      "license": "MIT",
+      "dependencies": {
+        "iconv-lite": "0.6.3"
+      },
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/whatwg-mimetype": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz",
+      "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/which": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+      "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "isexe": "^2.0.0"
+      },
+      "bin": {
+        "node-which": "bin/node-which"
+      },
+      "engines": {
+        "node": ">= 8"
+      }
+    },
+    "node_modules/word-wrap": {
+      "version": "1.2.5",
+      "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz",
+      "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/yocto-queue": {
+      "version": "0.1.0",
+      "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
+      "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    }
+  }
+}
diff --git a/book/package.json b/book/package.json
new file mode 100644 (file)
index 0000000..bbed765
--- /dev/null
@@ -0,0 +1,16 @@
+{
+  "name": "cxx-book-build",
+  "version": "0.0.0",
+  "main": "build.js",
+  "dependencies": {
+    "cheerio": "^1.0.0",
+    "html-entities": "^2.5.2"
+  },
+  "devDependencies": {
+    "@eslint/js": "^9.19.0",
+    "eslint": "^9.19.0"
+  },
+  "prettier": {
+    "singleQuote": true
+  }
+}
diff --git a/book/src/404.md b/book/src/404.md
new file mode 100644 (file)
index 0000000..c5b71f2
--- /dev/null
@@ -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
new file mode 100644 (file)
index 0000000..2d2502e
--- /dev/null
@@ -0,0 +1,37 @@
+# Summary
+
+- [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 or Buck2](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/async.md b/book/src/async.md
new file mode 100644 (file)
index 0000000..0f3fed1
--- /dev/null
@@ -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
+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 (file)
index 0000000..9c33b77
--- /dev/null
@@ -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 (file)
index 0000000..abd40d6
--- /dev/null
@@ -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 (file)
index 0000000..dc2619c
--- /dev/null
@@ -0,0 +1,140 @@
+{{#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 <string>
+#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", 2021.},
+          {"repository", json::null}});
+}
+```
diff --git a/book/src/binding/cxxvector.md b/book/src/binding/cxxvector.md
new file mode 100644 (file)
index 0000000..fd95a2d
--- /dev/null
@@ -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 (file)
index 0000000..a32ad52
--- /dev/null
@@ -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 (file)
index 0000000..1794211
--- /dev/null
@@ -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 (file)
index 0000000..2a47531
--- /dev/null
@@ -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
+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 (file)
index 0000000..a3b7070
--- /dev/null
@@ -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 (file)
index 0000000..4054bce
--- /dev/null
@@ -0,0 +1,178 @@
+{{#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;
+
+  template <typename C>
+  explicit Slice(C &c) : Slice(c.data(), c.size());
+
+  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:
+...#if __cplusplus >= 202002L
+...  using iterator_category = std::contiguous_iterator_tag;
+...#else
+...  using iterator_category = std::random_access_iterator_tag;
+...#endif
+...  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 (file)
index 0000000..214d12d
--- /dev/null
@@ -0,0 +1,121 @@
+{{#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;
+#if __cplusplus >= 201703L
+  explicit operator std::string_view() const;
+#endif
+
+  // Note: no null terminator.
+  const char *data() const noexcept;
+  size_t size() const noexcept;
+  size_t length() const noexcept;
+  bool empty() const noexcept;
+
+  using iterator = const char *;
+  using const_iterator = const char *;
+  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 (file)
index 0000000..7875685
--- /dev/null
@@ -0,0 +1,134 @@
+{{#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(const char8_t *);
+  String(const char8_t *, size_t);
+
+  // Replaces invalid UTF-8 data with the replacement character (U+FFFD).
+  static String lossy(const std::string &) noexcept;
+  static String lossy(const char *) noexcept;
+  static String lossy(const char *, size_t) noexcept;
+
+  // Throws std::invalid_argument if not UTF-16.
+  String(const char16_t *);
+  String(const char16_t *, size_t);
+
+  // Replaces invalid UTF-16 data with the replacement character (U+FFFD).
+  static String lossy(const char16_t *) noexcept;
+  static String lossy(const char16_t *, size_t) 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;
+  bool empty() const noexcept;
+
+  const char *c_str() noexcept;
+
+  size_t capacity() const noexcept;
+  void reserve(size_t new_cap) noexcept;
+
+  using iterator = char *;
+  iterator begin() noexcept;
+  iterator end() noexcept;
+
+  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 (file)
index 0000000..eefbc34
--- /dev/null
@@ -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 (file)
index 0000000..3e883a2
--- /dev/null
@@ -0,0 +1,200 @@
+{{#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);
+  void truncate(size_t len);
+  void clear();
+
+  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:
+...#if __cplusplus >= 202002L
+...  using iterator_category = std::contiguous_iterator_tag;
+...#else
+...  using iterator_category = std::random_access_iterator_tag;
+...#endif
+...  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:
+...#if __cplusplus >= 202002L
+...  using iterator_category = std::contiguous_iterator_tag;
+...#else
+...  using iterator_category = std::random_access_iterator_tag;
+...#endif
+...  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 (file)
index 0000000..bcb51d8
--- /dev/null
@@ -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 (file)
index 0000000..698bded
--- /dev/null
@@ -0,0 +1,109 @@
+{{#title Bazel, Buck2 — Rust ♡ C++}}
+## Bazel, Buck2, 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.bazel` and [Buck2] `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.
+
+[Bazel]: https://bazel.build
+[Buck2]: https://buck2.build
+
+```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.bazel
+
+load("@rules_cc//cc:defs.bzl", "cc_library")
+load("@rules_rust//rust:defs.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 (file)
index 0000000..6e9af80
--- /dev/null
@@ -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 = "2021"
+
+[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")
+        .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 (file)
index 0000000..478552e
--- /dev/null
@@ -0,0 +1,47 @@
+{{#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
+
+---
+
+- **<https://github.com/trondhe/rusty_cmake>**
+
+  - Alias target that can be linked into a C++ project
+  - Tested on Windows 10 with GNU target, and on Linux
+
+---
+
+- **<https://github.com/geekbrother/cxx-corrosion-cmake>**
+
+  - Improved rusty_cmake CMake file to use modern C++
+  - Rich examples of using different primitive types and Rust's Result return to C++
+  - MacOS and Linux only
+
+---
+
+- **<https://github.com/paandahl/cpp-with-rust>**
+
+  - Same blobstore example as the official demo, but inverted languages
+  - Minimal CMake configuration
+  - Tested on Linux, macOS, and Windows
+
+---
diff --git a/book/src/build/other.md b/book/src/build/other.md
new file mode 100644 (file)
index 0000000..c0c6e91
--- /dev/null
@@ -0,0 +1,87 @@
+{{#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.
+
+*Not all build systems are created equal. If you're hoping to use a build system
+from the '90s, especially if you're hoping to overlaying the limitations of 2 or
+more build systems (like automake+cargo) and expect to solve them
+simultaneously, then be mindful that your expectations are set accordingly and
+seek sympathy from those who have imposed the same approach on themselves.*
+
+### 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.
+
+The generated C++ code and the Rust code generated by the procedural macro both
+depend on each other. Simple examples may only require one or the other, but in
+general your linking will need to handle both directions. For some linkers, such
+as llvm-ld, this is not a problem at all. For others, such as GNU ld, flags like
+`--start-lib`/`--end-lib` may help.
+
+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 (file)
index 0000000..c75939e
--- /dev/null
@@ -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 (file)
index 0000000..75daedd
--- /dev/null
@@ -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 (file)
index 0000000..516ee91
--- /dev/null
@@ -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 (file)
index 0000000..07118aa
Binary files /dev/null and b/book/src/cxx.png differ
diff --git a/book/src/extern-c++.md b/book/src/extern-c++.md
new file mode 100644 (file)
index 0000000..11ed7b5
--- /dev/null
@@ -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 (file)
index 0000000..40f2237
--- /dev/null
@@ -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" {
+        type 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 (file)
index 0000000..7add044
--- /dev/null
@@ -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&logo=docs.rs" 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:190px;width=718px;padding:44px 0 44px">
+<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 (file)
index 0000000..df4fcf4
--- /dev/null
@@ -0,0 +1,444 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="541.903pt" height="142.287pt" viewBox="0 0 541.903 142.287" version="1.1">
+<defs>
+<g>
+<symbol overflow="visible" id="glyph0-0">
+<path style="stroke:none;" d=""/>
+</symbol>
+<symbol overflow="visible" id="glyph0-1">
+<path style="stroke:none;" d="M 5.140625 -2.359375 L 6.234375 -2.359375 C 6.421875 -2.359375 6.890625 -2.359375 6.890625 -2.828125 C 6.890625 -3.296875 6.4375 -3.296875 6.234375 -3.296875 L 5.265625 -3.296875 L 5.515625 -5.46875 L 6.234375 -5.46875 C 6.421875 -5.46875 6.890625 -5.46875 6.890625 -5.921875 C 6.890625 -6.390625 6.4375 -6.390625 6.234375 -6.390625 L 5.65625 -6.390625 L 5.875 -8.125 C 5.9375 -8.59375 5.6875 -8.765625 5.421875 -8.765625 C 5.015625 -8.765625 4.984375 -8.390625 4.953125 -8.203125 L 4.71875 -6.390625 L 3.171875 -6.390625 L 3.40625 -8.125 C 3.453125 -8.59375 3.203125 -8.765625 2.9375 -8.765625 C 2.53125 -8.765625 2.5 -8.390625 2.46875 -8.203125 L 2.234375 -6.390625 L 1.140625 -6.390625 C 0.953125 -6.390625 0.484375 -6.390625 0.484375 -5.9375 C 0.484375 -5.46875 0.9375 -5.46875 1.140625 -5.46875 L 2.109375 -5.46875 L 1.84375 -3.296875 L 1.140625 -3.296875 C 0.953125 -3.296875 0.484375 -3.296875 0.484375 -2.84375 C 0.484375 -2.359375 0.9375 -2.359375 1.140625 -2.359375 L 1.71875 -2.359375 L 1.484375 -0.640625 C 1.40625 0 1.90625 0 1.953125 0 C 2.359375 0 2.390625 -0.375 2.421875 -0.5625 L 2.65625 -2.359375 L 4.203125 -2.359375 L 3.96875 -0.640625 C 3.890625 0 4.390625 0 4.4375 0 C 4.828125 0 4.875 -0.375 4.90625 -0.5625 Z M 3.046875 -5.46875 L 4.59375 -5.46875 L 4.328125 -3.296875 L 2.78125 -3.296875 Z M 3.046875 -5.46875 "/>
+</symbol>
+<symbol overflow="visible" id="glyph0-2">
+<path style="stroke:none;" d="M 6.21875 -9.125 C 6.421875 -9.125 6.796875 -9.125 6.796875 -9.53125 C 6.796875 -9.953125 6.421875 -9.953125 6.21875 -9.953125 L 3.734375 -9.953125 C 3.25 -9.953125 3.15625 -9.828125 3.15625 -9.359375 L 3.15625 0.609375 C 3.15625 1.046875 3.234375 1.1875 3.734375 1.1875 L 6.21875 1.1875 C 6.421875 1.1875 6.796875 1.1875 6.796875 0.78125 C 6.796875 0.359375 6.421875 0.359375 6.21875 0.359375 L 4.09375 0.359375 L 4.09375 -9.125 Z M 6.21875 -9.125 "/>
+</symbol>
+<symbol overflow="visible" id="glyph0-3">
+<path style="stroke:none;" d="M 4.21875 -9.359375 C 4.21875 -9.8125 4.125 -9.953125 3.640625 -9.953125 L 1.140625 -9.953125 C 0.953125 -9.953125 0.578125 -9.953125 0.578125 -9.53125 C 0.578125 -9.125 0.953125 -9.125 1.140625 -9.125 L 3.28125 -9.125 L 3.28125 0.359375 L 1.140625 0.359375 C 0.953125 0.359375 0.578125 0.359375 0.578125 0.78125 C 0.578125 1.1875 0.953125 1.1875 1.140625 1.1875 L 3.640625 1.1875 C 4.109375 1.1875 4.21875 1.0625 4.21875 0.609375 Z M 4.21875 -9.359375 "/>
+</symbol>
+<symbol overflow="visible" id="glyph0-4">
+<path style="stroke:none;" d="M 4.15625 -3.921875 L 6.203125 -3.921875 C 6.390625 -3.921875 6.84375 -3.921875 6.84375 -4.375 C 6.84375 -4.84375 6.390625 -4.84375 6.203125 -4.84375 L 4.15625 -4.84375 L 4.15625 -6.90625 C 4.15625 -7.078125 4.15625 -7.546875 3.703125 -7.546875 C 3.234375 -7.546875 3.234375 -7.09375 3.234375 -6.90625 L 3.234375 -4.84375 L 1.171875 -4.84375 C 0.984375 -4.84375 0.53125 -4.84375 0.53125 -4.390625 C 0.53125 -3.921875 0.96875 -3.921875 1.171875 -3.921875 L 3.234375 -3.921875 L 3.234375 -1.859375 C 3.234375 -1.671875 3.234375 -1.21875 3.6875 -1.21875 C 4.15625 -1.21875 4.15625 -1.65625 4.15625 -1.859375 Z M 4.15625 -3.921875 "/>
+</symbol>
+<symbol overflow="visible" id="glyph1-0">
+<path style="stroke:none;" d=""/>
+</symbol>
+<symbol overflow="visible" id="glyph1-1">
+<path style="stroke:none;" d="M 5.75 -1.546875 C 5.09375 -1.015625 4.359375 -0.75 3.5625 -0.75 C 2.359375 -0.75 1.59375 -1.8125 1.59375 -3.203125 C 1.59375 -4.390625 2.171875 -5.6875 3.59375 -5.6875 C 4.5625 -5.6875 4.96875 -5.46875 5.578125 -5.046875 L 5.75 -5.984375 C 4.921875 -6.46875 4.453125 -6.578125 3.59375 -6.578125 C 1.625 -6.578125 0.5 -4.828125 0.5 -3.203125 C 0.5 -1.359375 1.828125 0.140625 3.546875 0.140625 C 4.25 0.140625 5.046875 -0.03125 5.84375 -0.609375 C 5.84375 -0.640625 5.796875 -1.09375 5.78125 -1.140625 Z M 5.75 -1.546875 "/>
+</symbol>
+<symbol overflow="visible" id="glyph1-2">
+<path style="stroke:none;" d="M 3.640625 -3.28125 L 6.1875 -6.375 L 4.984375 -6.375 L 3.171875 -4.046875 L 1.296875 -6.375 L 0.078125 -6.375 L 2.6875 -3.28125 L 0 0 L 1.1875 0 L 3.171875 -2.671875 L 5.203125 0 L 6.40625 0 Z M 3.640625 -3.28125 "/>
+</symbol>
+<symbol overflow="visible" id="glyph1-3">
+<path style="stroke:none;" d="M 2.53125 -6.375 L 1.359375 -6.375 L 1.359375 -5.203125 L 2.53125 -5.203125 Z M 1.359375 -1.15625 L 1.359375 0 L 2.53125 0 L 2.53125 -1.15625 Z M 1.359375 -1.15625 "/>
+</symbol>
+<symbol overflow="visible" id="glyph1-4">
+<path style="stroke:none;" d="M 2.1875 -9.953125 L 1.125 -9.953125 L 1.125 0 L 2.203125 0 L 2.203125 -0.640625 C 2.875 -0.015625 3.578125 0.140625 4.078125 0.140625 C 5.4375 0.140625 6.6875 -1.25 6.6875 -3.203125 C 6.6875 -4.96875 5.734375 -6.515625 4.40625 -6.515625 C 3.984375 -6.515625 3.0625 -6.421875 2.1875 -5.703125 Z M 2.203125 -4.828125 C 2.46875 -5.234375 2.984375 -5.65625 3.6875 -5.65625 C 4.5625 -5.65625 5.59375 -4.984375 5.59375 -3.203125 C 5.59375 -1.328125 4.421875 -0.71875 3.546875 -0.71875 C 3.140625 -0.71875 2.875 -0.859375 2.609375 -1.0625 C 2.265625 -1.359375 2.203125 -1.625 2.203125 -1.859375 Z M 2.203125 -4.828125 "/>
+</symbol>
+<symbol overflow="visible" id="glyph1-5">
+<path style="stroke:none;" d="M 2.1875 -3.140625 C 2.1875 -4.59375 3.265625 -5.53125 4.5625 -5.546875 L 4.5625 -6.515625 C 3.25 -6.5 2.5 -5.734375 2.109375 -5.171875 L 2.109375 -6.4375 L 1.125 -6.4375 L 1.125 0 L 2.1875 0 Z M 2.1875 -3.140625 "/>
+</symbol>
+<symbol overflow="visible" id="glyph1-6">
+<path style="stroke:none;" d="M 2.265625 -9.734375 L 1.03125 -9.734375 L 1.03125 -8.5 L 2.265625 -8.5 Z M 2.1875 -6.375 L 1.125 -6.375 L 1.125 0 L 2.1875 0 Z M 2.1875 -6.375 "/>
+</symbol>
+<symbol overflow="visible" id="glyph1-7">
+<path style="stroke:none;" d="M 6.0625 -9.953125 L 5 -9.953125 L 5 -5.765625 C 4.34375 -6.3125 3.59375 -6.515625 2.984375 -6.515625 C 1.625 -6.515625 0.5 -5.046875 0.5 -3.1875 C 0.5 -1.328125 1.5625 0.140625 2.921875 0.140625 C 3.453125 0.140625 4.265625 -0.03125 4.984375 -0.734375 L 4.984375 0 L 6.0625 0 Z M 4.984375 -1.6875 C 4.640625 -1.078125 4.125 -0.71875 3.5 -0.71875 C 2.625 -0.71875 1.59375 -1.390625 1.59375 -3.171875 C 1.59375 -5.078125 2.8125 -5.65625 3.640625 -5.65625 C 4.203125 -5.65625 4.65625 -5.359375 4.984375 -4.890625 Z M 4.984375 -1.6875 "/>
+</symbol>
+<symbol overflow="visible" id="glyph1-8">
+<path style="stroke:none;" d="M 4.890625 -5.65625 C 4.890625 -5.65625 4.921875 -5.625 4.9375 -5.59375 C 5.234375 -5.625 5.546875 -5.671875 6.078125 -5.671875 C 6.125 -5.671875 6.453125 -5.671875 6.8125 -5.640625 L 6.65625 -6.515625 C 6.390625 -6.515625 5.40625 -6.5 4.453125 -6.03125 C 3.984375 -6.4375 3.40625 -6.515625 3.125 -6.515625 C 1.875 -6.515625 0.875 -5.46875 0.875 -4.203125 C 0.875 -3.6875 1.03125 -3.21875 1.34375 -2.78125 C 1.015625 -2.328125 0.9375 -1.890625 0.9375 -1.546875 C 0.9375 -1.015625 1.140625 -0.65625 1.296875 -0.46875 C 0.5625 0 0.390625 0.609375 0.390625 1.015625 C 0.390625 2.0625 1.75 2.9375 3.5 2.9375 C 5.25 2.9375 6.625 2.09375 6.625 1 C 6.625 -1 4.296875 -1 3.71875 -1 L 2.5 -1 C 2.296875 -1 1.671875 -1 1.671875 -1.765625 C 1.671875 -2.078125 1.734375 -2.171875 1.828125 -2.296875 C 2.0625 -2.125 2.53125 -1.875 3.109375 -1.875 C 4.34375 -1.875 5.359375 -2.890625 5.359375 -4.203125 C 5.359375 -4.71875 5.171875 -5.25 4.875 -5.640625 Z M 3.125 -2.671875 C 2.5 -2.671875 1.859375 -3.09375 1.859375 -4.1875 C 1.859375 -5.46875 2.671875 -5.71875 3.109375 -5.71875 C 3.75 -5.71875 4.375 -5.296875 4.375 -4.203125 C 4.375 -2.921875 3.578125 -2.671875 3.125 -2.671875 Z M 3.734375 0.03125 C 4.046875 0.03125 5.625 0.03125 5.625 1.015625 C 5.625 1.671875 4.640625 2.15625 3.515625 2.15625 C 2.390625 2.15625 1.390625 1.703125 1.390625 1 C 1.390625 0.96875 1.390625 0.03125 2.484375 0.03125 Z M 3.734375 0.03125 "/>
+</symbol>
+<symbol overflow="visible" id="glyph1-9">
+<path style="stroke:none;" d="M 5.84375 -3.234375 C 5.84375 -3.84375 5.78125 -4.71875 5.328125 -5.484375 C 4.734375 -6.46875 3.734375 -6.578125 3.3125 -6.578125 C 1.765625 -6.578125 0.46875 -5.09375 0.46875 -3.234375 C 0.46875 -1.328125 1.84375 0.140625 3.515625 0.140625 C 4.171875 0.140625 4.96875 -0.046875 5.75 -0.609375 C 5.75 -0.671875 5.703125 -1.125 5.703125 -1.140625 C 5.6875 -1.15625 5.671875 -1.484375 5.671875 -1.53125 C 4.8125 -0.8125 3.96875 -0.71875 3.546875 -0.71875 C 2.4375 -0.71875 1.484375 -1.703125 1.46875 -3.234375 Z M 1.546875 -4 C 1.796875 -4.96875 2.46875 -5.71875 3.3125 -5.71875 C 3.765625 -5.71875 4.75 -5.515625 4.984375 -4 Z M 1.546875 -4 "/>
+</symbol>
+<symbol overflow="visible" id="glyph1-10">
+<path style="stroke:none;" d="M 10 -4.3125 C 10 -5.3125 9.734375 -6.515625 8.0625 -6.515625 C 6.84375 -6.515625 6.171875 -5.671875 5.953125 -5.34375 C 5.6875 -6.25 4.90625 -6.515625 4.15625 -6.515625 C 2.984375 -6.515625 2.359375 -5.75 2.125 -5.453125 L 2.125 -6.4375 L 1.109375 -6.4375 L 1.109375 0 L 2.1875 0 L 2.1875 -3.578125 C 2.1875 -4.515625 2.5625 -5.65625 3.609375 -5.65625 C 4.9375 -5.65625 5 -4.75 5 -4.21875 L 5 0 L 6.09375 0 L 6.09375 -3.578125 C 6.09375 -4.515625 6.46875 -5.65625 7.515625 -5.65625 C 8.828125 -5.65625 8.90625 -4.75 8.90625 -4.21875 L 8.90625 0 L 10 0 Z M 10 -4.3125 "/>
+</symbol>
+<symbol overflow="visible" id="glyph1-11">
+<path style="stroke:none;" d="M 6.578125 -3.15625 C 6.578125 -5.09375 5.15625 -6.578125 3.515625 -6.578125 C 1.828125 -6.578125 0.4375 -5.0625 0.4375 -3.15625 C 0.4375 -1.28125 1.859375 0.140625 3.5 0.140625 C 5.1875 0.140625 6.578125 -1.3125 6.578125 -3.15625 Z M 3.515625 -0.75 C 2.484375 -0.75 1.515625 -1.609375 1.515625 -3.296875 C 1.515625 -5.03125 2.59375 -5.71875 3.5 -5.71875 C 4.484375 -5.71875 5.5 -4.984375 5.5 -3.296875 C 5.5 -1.53125 4.46875 -0.75 3.515625 -0.75 Z M 3.515625 -0.75 "/>
+</symbol>
+<symbol overflow="visible" id="glyph1-12">
+<path style="stroke:none;" d="M 4.8125 -6.140625 C 3.90625 -6.5625 3.125 -6.578125 2.796875 -6.578125 C 2.46875 -6.578125 0.46875 -6.578125 0.46875 -4.71875 C 0.46875 -4.09375 0.8125 -3.65625 1.03125 -3.453125 C 1.546875 -3.015625 1.90625 -2.9375 2.75 -2.765625 C 3.234375 -2.671875 4.0625 -2.5 4.0625 -1.734375 C 4.0625 -0.75 2.9375 -0.75 2.734375 -0.75 C 2.171875 -0.75 1.328125 -0.90625 0.5625 -1.46875 L 0.390625 -0.484375 C 0.453125 -0.4375 1.4375 0.140625 2.75 0.140625 C 4.609375 0.140625 5.046875 -0.96875 5.046875 -1.828125 C 5.046875 -2.546875 4.609375 -3.015625 4.546875 -3.078125 C 4.015625 -3.625 3.578125 -3.71875 2.71875 -3.890625 C 2.1875 -3.984375 1.46875 -4.125 1.46875 -4.84375 C 1.46875 -5.734375 2.46875 -5.734375 2.65625 -5.734375 C 3.40625 -5.734375 4.015625 -5.5625 4.640625 -5.203125 Z M 4.8125 -6.140625 "/>
+</symbol>
+<symbol overflow="visible" id="glyph1-13">
+<path style="stroke:none;" d="M 2.203125 -0.640625 C 2.703125 -0.203125 3.296875 0.140625 4.078125 0.140625 C 5.453125 0.140625 6.6875 -1.25 6.6875 -3.203125 C 6.6875 -4.9375 5.765625 -6.515625 4.4375 -6.515625 C 3.84375 -6.515625 2.921875 -6.328125 2.1875 -5.6875 L 2.1875 -6.375 L 1.125 -6.375 L 1.125 2.78125 L 2.203125 2.78125 Z M 2.203125 -4.8125 C 2.53125 -5.3125 3.078125 -5.625 3.671875 -5.625 C 4.734375 -5.625 5.59375 -4.546875 5.59375 -3.1875 C 5.59375 -1.75 4.609375 -0.71875 3.546875 -0.71875 C 3.140625 -0.71875 2.875 -0.859375 2.609375 -1.0625 C 2.25 -1.375 2.203125 -1.640625 2.203125 -1.859375 Z M 2.203125 -4.8125 "/>
+</symbol>
+<symbol overflow="visible" id="glyph1-14">
+<path style="stroke:none;" d="M 2.453125 -5.53125 L 4.4375 -5.53125 L 4.4375 -6.375 L 2.453125 -6.375 L 2.453125 -8.1875 L 1.46875 -8.1875 L 1.46875 -6.375 L 0.265625 -6.375 L 0.265625 -5.53125 L 1.4375 -5.53125 L 1.4375 -1.671875 C 1.4375 -0.84375 1.625 0.140625 2.609375 0.140625 C 3.578125 0.140625 4.234375 -0.1875 4.671875 -0.421875 L 4.453125 -1.25 C 3.953125 -0.796875 3.421875 -0.75 3.171875 -0.75 C 2.53125 -0.75 2.453125 -1.421875 2.453125 -1.921875 Z M 2.453125 -5.53125 "/>
+</symbol>
+<symbol overflow="visible" id="glyph1-15">
+<path style="stroke:none;" d="M 6.078125 -4.3125 C 6.078125 -5.1875 5.890625 -6.515625 4.15625 -6.515625 C 3.359375 -6.515625 2.671875 -6.15625 2.125 -5.46875 L 2.125 -6.4375 L 1.109375 -6.4375 L 1.109375 0 L 2.1875 0 L 2.1875 -3.578125 C 2.1875 -4.453125 2.53125 -5.65625 3.609375 -5.65625 C 4.953125 -5.65625 4.984375 -4.71875 4.984375 -4.21875 L 4.984375 0 L 6.078125 0 Z M 6.078125 -4.3125 "/>
+</symbol>
+<symbol overflow="visible" id="glyph1-16">
+<path style="stroke:none;" d="M 2.484375 -5.53125 L 4.015625 -5.53125 L 4.015625 -6.375 L 2.453125 -6.375 L 2.453125 -8.09375 C 2.453125 -9.0625 3.265625 -9.234375 3.75 -9.234375 C 4.25 -9.234375 4.6875 -9.078125 4.859375 -9.015625 L 4.859375 -9.953125 C 4.765625 -9.984375 4.28125 -10.09375 3.765625 -10.09375 C 2.390625 -10.09375 1.421875 -9.046875 1.421875 -7.65625 L 1.421875 -6.375 L 0.375 -6.375 L 0.375 -5.53125 L 1.421875 -5.53125 L 1.421875 0 L 2.484375 0 Z M 2.484375 -5.53125 "/>
+</symbol>
+<symbol overflow="visible" id="glyph1-17">
+<path style="stroke:none;" d="M 6.078125 -6.375 L 4.984375 -6.375 L 4.984375 -2.265625 C 4.984375 -1.171875 4.234375 -0.640625 3.296875 -0.640625 C 2.296875 -0.640625 2.1875 -1.015625 2.1875 -1.65625 L 2.1875 -6.375 L 1.109375 -6.375 L 1.109375 -1.59375 C 1.109375 -0.5625 1.4375 0.140625 2.609375 0.140625 C 3.015625 0.140625 4.140625 0.078125 5.015625 -0.71875 L 5.015625 0 L 6.078125 0 Z M 6.078125 -6.375 "/>
+</symbol>
+<symbol overflow="visible" id="glyph1-18">
+<path style="stroke:none;" d="M 5.609375 -4.203125 C 5.609375 -5.578125 4.625 -6.578125 3.28125 -6.578125 C 2.421875 -6.578125 1.78125 -6.390625 1.078125 -5.984375 L 1.15625 -5.03125 C 1.796875 -5.5 2.453125 -5.75 3.28125 -5.75 C 3.9375 -5.75 4.515625 -5.21875 4.515625 -4.171875 L 4.515625 -3.578125 C 3.875 -3.5625 3.015625 -3.515625 2.15625 -3.234375 C 1.1875 -2.890625 0.625 -2.359375 0.625 -1.65625 C 0.625 -1.015625 1 0.140625 2.21875 0.140625 C 3.015625 0.140625 4.015625 -0.09375 4.546875 -0.53125 L 4.546875 0 L 5.609375 0 Z M 4.515625 -1.921875 C 4.515625 -1.640625 4.515625 -1.296875 4.046875 -1 C 3.65625 -0.765625 3.171875 -0.71875 2.953125 -0.71875 C 2.1875 -0.71875 1.65625 -1.140625 1.65625 -1.65625 C 1.65625 -2.640625 3.640625 -2.859375 4.515625 -2.859375 Z M 4.515625 -1.921875 "/>
+</symbol>
+<symbol overflow="visible" id="glyph1-19">
+<path style="stroke:none;" d="M 6.203125 -6.375 L 5.140625 -6.375 C 4.46875 -4.671875 3.375 -1.859375 3.3125 -0.828125 L 3.296875 -0.828125 C 3.234375 -1.671875 2.46875 -3.578125 2.359375 -3.859375 L 1.328125 -6.375 L 0.203125 -6.375 L 2.875 0 L 2.359375 1.3125 C 2.046875 2.015625 1.84375 2.09375 1.578125 2.09375 C 1.375 2.09375 0.921875 2.046875 0.46875 1.875 L 0.5625 2.8125 C 0.640625 2.828125 1.125 2.921875 1.578125 2.921875 C 1.921875 2.921875 2.59375 2.921875 3.21875 1.34375 Z M 6.203125 -6.375 "/>
+</symbol>
+<symbol overflow="visible" id="glyph1-20">
+<path style="stroke:none;" d="M 5.84375 -4.671875 C 7.40625 -5.125 8.25 -6.203125 8.25 -7.265625 C 8.25 -8.703125 6.71875 -9.953125 4.6875 -9.953125 L 1.3125 -9.953125 L 1.3125 0 L 2.515625 0 L 2.515625 -4.546875 L 4.75 -4.546875 L 7.40625 0 L 8.640625 0 Z M 2.515625 -5.40625 L 2.515625 -9.15625 L 4.53125 -9.15625 C 6.265625 -9.15625 7.109375 -8.234375 7.109375 -7.265625 C 7.109375 -6.390625 6.34375 -5.40625 4.53125 -5.40625 Z M 2.515625 -5.40625 "/>
+</symbol>
+<symbol overflow="visible" id="glyph1-21">
+<path style="stroke:none;" d="M 8.171875 -1.65625 C 7.296875 -1 6.703125 -0.765625 5.453125 -0.765625 C 3.578125 -0.765625 2.171875 -2.703125 2.171875 -5 C 2.171875 -6.9375 3.328125 -9.21875 5.515625 -9.21875 C 6.453125 -9.21875 7.171875 -8.9375 7.890625 -8.453125 L 8.109375 -9.625 C 7.171875 -9.984375 6.46875 -10.109375 5.484375 -10.109375 C 3.046875 -10.109375 0.953125 -7.953125 0.953125 -4.984375 C 0.953125 -1.84375 3.25 0.125 5.34375 0.125 C 6.78125 0.125 7.09375 0.046875 8.265625 -0.65625 Z M 8.171875 -1.65625 "/>
+</symbol>
+<symbol overflow="visible" id="glyph1-22">
+<path style="stroke:none;" d="M 3 -9.953125 L 1.359375 -9.953125 L 1.359375 0 L 2.46875 0 L 2.46875 -8.84375 L 2.484375 -8.84375 C 2.6875 -7.875 5 -1.734375 5.546875 -0.296875 L 6.59375 -0.296875 C 7.078125 -1.53125 9.4375 -7.78125 9.703125 -8.859375 L 9.703125 0 L 10.8125 0 L 10.8125 -9.953125 L 9.1875 -9.953125 L 7.546875 -5.53125 C 6.796875 -3.53125 6.234375 -2.015625 6.09375 -1.359375 L 6.078125 -1.359375 C 6.03125 -1.609375 5.859375 -2.140625 5.671875 -2.703125 C 5.40625 -3.46875 5.40625 -3.5 5.171875 -4.09375 Z M 3 -9.953125 "/>
+</symbol>
+<symbol overflow="visible" id="glyph1-23">
+<path style="stroke:none;" d="M 8.53125 -9.953125 L 7.296875 -9.953125 L 7.296875 -5.609375 L 2.53125 -5.609375 L 2.53125 -9.953125 L 1.296875 -9.953125 L 1.296875 0 L 2.53125 0 L 2.53125 -4.75 L 7.296875 -4.75 L 7.296875 0 L 8.53125 0 Z M 8.53125 -9.953125 "/>
+</symbol>
+<symbol overflow="visible" id="glyph1-24">
+<path style="stroke:none;" d="M 5.265625 -9.953125 L 4 -9.953125 L 0.390625 0 L 1.453125 0 L 2.5 -2.90625 L 6.546875 -2.90625 L 7.609375 0 L 8.875 0 Z M 4.515625 -8.921875 L 6.234375 -3.71875 L 2.8125 -3.71875 Z M 4.515625 -8.921875 "/>
+</symbol>
+<symbol overflow="visible" id="glyph1-25">
+<path style="stroke:none;" d="M 1.328125 -9.953125 L 1.328125 0 L 5.03125 0 C 7.0625 0 8.53125 -1.296875 8.53125 -2.703125 C 8.53125 -3.953125 7.28125 -4.984375 5.8125 -5.203125 C 7.484375 -5.671875 8.140625 -6.625 8.140625 -7.453125 C 8.140625 -8.75 6.671875 -9.953125 4.640625 -9.953125 Z M 2.5 -5.609375 L 2.5 -9.15625 L 4.265625 -9.15625 C 5.796875 -9.15625 7.03125 -8.390625 7.03125 -7.4375 C 7.03125 -6.4375 5.796875 -5.609375 4.28125 -5.609375 Z M 2.5 -0.796875 L 2.5 -4.75 L 4.40625 -4.75 C 6.25 -4.75 7.40625 -3.71875 7.40625 -2.71875 C 7.40625 -1.703125 6.234375 -0.796875 4.640625 -0.796875 Z M 2.5 -0.796875 "/>
+</symbol>
+<symbol overflow="visible" id="glyph1-26">
+<path style="stroke:none;" d="M 2.53125 -9.953125 L 1.296875 -9.953125 L 1.296875 0 L 2.53125 0 Z M 2.53125 -9.953125 "/>
+</symbol>
+<symbol overflow="visible" id="glyph1-27">
+<path style="stroke:none;" d="M 6.53125 -9.546875 C 5.9375 -9.875 5.21875 -10.25 3.9375 -10.25 C 2.015625 -10.25 0.78125 -8.875 0.78125 -7.4375 C 0.78125 -6.421875 1.390625 -5.765625 1.46875 -5.6875 C 2.171875 -4.984375 2.65625 -4.84375 3.578125 -4.625 C 4.796875 -4.3125 4.984375 -4.265625 5.453125 -3.78125 C 5.6875 -3.53125 5.90625 -3.0625 5.90625 -2.5625 C 5.90625 -1.578125 5.09375 -0.65625 3.859375 -0.65625 C 2.8125 -0.65625 1.6875 -1.078125 0.84375 -1.84375 L 0.625 -0.6875 C 1.9375 0.171875 3.203125 0.296875 3.859375 0.296875 C 5.703125 0.296875 7.015625 -1.125 7.015625 -2.71875 C 7.015625 -3.625 6.5625 -4.3125 6.25 -4.671875 C 5.59375 -5.375 5.0625 -5.515625 4.109375 -5.765625 C 3.125 -6.015625 2.765625 -6.09375 2.453125 -6.390625 C 2.265625 -6.59375 1.875 -6.953125 1.875 -7.609375 C 1.875 -8.484375 2.671875 -9.328125 3.9375 -9.328125 C 5.15625 -9.328125 5.796875 -8.84375 6.3125 -8.390625 Z M 6.53125 -9.546875 "/>
+</symbol>
+<symbol overflow="visible" id="glyph1-28">
+<path style="stroke:none;" d="M 6.078125 -4.3125 C 6.078125 -5.1875 5.890625 -6.515625 4.15625 -6.515625 C 3.171875 -6.515625 2.515625 -5.953125 2.171875 -5.515625 L 2.171875 -9.953125 L 1.109375 -9.953125 L 1.109375 0 L 2.1875 0 L 2.1875 -3.578125 C 2.1875 -4.453125 2.53125 -5.65625 3.609375 -5.65625 C 4.953125 -5.65625 4.984375 -4.71875 4.984375 -4.21875 L 4.984375 0 L 6.078125 0 Z M 6.078125 -4.3125 "/>
+</symbol>
+<symbol overflow="visible" id="glyph1-29">
+<path style="stroke:none;" d="M 9.328125 -6.375 L 8.28125 -6.375 L 7.421875 -3.453125 C 7.21875 -2.765625 6.8125 -1.421875 6.75 -0.796875 L 6.734375 -0.796875 C 6.703125 -1.140625 6.46875 -2.109375 6.171875 -3.125 L 5.21875 -6.375 L 4.234375 -6.375 L 3.40625 -3.5625 C 3.15625 -2.671875 2.8125 -1.4375 2.765625 -0.828125 L 2.75 -0.828125 C 2.734375 -1.109375 2.578125 -1.859375 2.3125 -2.828125 L 1.296875 -6.375 L 0.203125 -6.375 L 2.125 0 L 3.234375 0 C 3.765625 -1.8125 4.609375 -4.53125 4.6875 -5.53125 L 4.703125 -5.53125 C 4.734375 -4.953125 5.015625 -3.921875 5.125 -3.578125 L 6.140625 0 L 7.40625 0 Z M 9.328125 -6.375 "/>
+</symbol>
+<symbol overflow="visible" id="glyph1-30">
+<path style="stroke:none;" d="M 4.8125 -4.140625 C 6.78125 -4.140625 8.140625 -5.5625 8.140625 -7.0625 C 8.140625 -8.578125 6.75 -9.953125 4.8125 -9.953125 L 1.3125 -9.953125 L 1.3125 0 L 2.53125 0 L 2.53125 -4.140625 Z M 4.5 -9.15625 C 6.1875 -9.15625 7.015625 -8.140625 7.015625 -7.078125 C 7.015625 -5.9375 6.140625 -4.984375 4.5 -4.984375 L 2.515625 -4.984375 L 2.515625 -9.15625 Z M 4.5 -9.15625 "/>
+</symbol>
+</g>
+<clipPath id="clip1">
+  <path d="M 0 0 L 541.902344 0 L 541.902344 142.285156 L 0 142.285156 Z M 0 0 "/>
+</clipPath>
+<clipPath id="clip2">
+  <path d="M 102 92 L 245 92 L 245 142.285156 L 102 142.285156 Z M 102 92 "/>
+</clipPath>
+<clipPath id="clip3">
+  <path d="M 302 92 L 442 92 L 442 142.285156 L 302 142.285156 Z M 302 92 "/>
+</clipPath>
+<clipPath id="clip4">
+  <path d="M 17 101 L 67 101 L 67 142.285156 L 17 142.285156 Z M 17 101 "/>
+</clipPath>
+<clipPath id="clip5">
+  <path d="M 92 101 L 142 101 L 142 142.285156 L 92 142.285156 Z M 92 101 "/>
+</clipPath>
+<clipPath id="clip6">
+  <path d="M 205 101 L 255 101 L 255 142.285156 L 205 142.285156 Z M 205 101 "/>
+</clipPath>
+<clipPath id="clip7">
+  <path d="M 292 101 L 342 101 L 342 142.285156 L 292 142.285156 Z M 292 101 "/>
+</clipPath>
+<clipPath id="clip8">
+  <path d="M 402 101 L 452 101 L 452 142.285156 L 402 142.285156 Z M 402 101 "/>
+</clipPath>
+<clipPath id="clip9">
+  <path d="M 479 101 L 528 101 L 528 142.285156 L 479 142.285156 Z M 479 101 "/>
+</clipPath>
+</defs>
+<g id="surface1">
+<g clip-path="url(#clip1)" clip-rule="nonzero">
+<path style=" stroke:none;fill-rule:nonzero;fill:rgb(100%,100%,100%);fill-opacity:1;" d="M 0 142.285156 L 543.933594 142.285156 L 543.933594 -0.535156 L 0 -0.535156 Z M 0 142.285156 "/>
+</g>
+<path style="fill:none;stroke-width:1.49442;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 93.618469 13.681906 L -93.619813 13.681906 C -96.369813 13.681906 -98.600281 11.451438 -98.600281 8.701438 L -98.600281 -17.204812 C -98.600281 -19.954812 -96.369813 -22.185281 -93.619813 -22.185281 L 93.618469 -22.185281 C 96.368469 -22.185281 98.598937 -19.954812 98.598937 -17.204812 L 98.598937 8.701438 C 98.598937 11.451438 96.368469 13.681906 93.618469 13.681906 Z M 93.618469 13.681906 " transform="matrix(1,0,0,-1,272.612,14.428)"/>
+<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
+  <use xlink:href="#glyph0-1" x="215.699" y="15.293"/>
+</g>
+<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
+  <use xlink:href="#glyph0-2" x="222.091667" y="15.293"/>
+</g>
+<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
+  <use xlink:href="#glyph1-1" x="229.467" y="15.293"/>
+  <use xlink:href="#glyph1-2" x="235.710466" y="15.293"/>
+  <use xlink:href="#glyph1-2" x="242.136129" y="15.293"/>
+  <use xlink:href="#glyph1-3" x="248.561792" y="15.293"/>
+  <use xlink:href="#glyph1-3" x="252.463959" y="15.293"/>
+  <use xlink:href="#glyph1-4" x="256.366125" y="15.293"/>
+</g>
+<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
+  <use xlink:href="#glyph1-5" x="263.184874" y="15.293"/>
+  <use xlink:href="#glyph1-6" x="267.959289" y="15.293"/>
+  <use xlink:href="#glyph1-7" x="271.263219" y="15.293"/>
+  <use xlink:href="#glyph1-8" x="278.469315" y="15.293"/>
+  <use xlink:href="#glyph1-9" x="285.493215" y="15.293"/>
+</g>
+<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
+  <use xlink:href="#glyph0-3" x="291.733" y="15.293"/>
+</g>
+<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
+  <use xlink:href="#glyph1-10" x="303.797" y="15.293"/>
+  <use xlink:href="#glyph1-11" x="314.905263" y="15.293"/>
+</g>
+<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
+  <use xlink:href="#glyph1-7" x="322.31651" y="15.293"/>
+</g>
+<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
+  <use xlink:href="#glyph1-7" x="203.901" y="29.24"/>
+  <use xlink:href="#glyph1-9" x="211.107096" y="29.24"/>
+  <use xlink:href="#glyph1-12" x="217.350563" y="29.24"/>
+  <use xlink:href="#glyph1-1" x="222.734691" y="29.24"/>
+  <use xlink:href="#glyph1-5" x="228.978158" y="29.24"/>
+  <use xlink:href="#glyph1-6" x="233.752573" y="29.24"/>
+  <use xlink:href="#glyph1-13" x="237.056503" y="29.24"/>
+  <use xlink:href="#glyph1-14" x="244.262599" y="29.24"/>
+  <use xlink:href="#glyph1-6" x="249.335415" y="29.24"/>
+  <use xlink:href="#glyph1-11" x="252.639345" y="29.24"/>
+  <use xlink:href="#glyph1-15" x="259.663245" y="29.24"/>
+</g>
+<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
+  <use xlink:href="#glyph1-11" x="271.546202" y="29.24"/>
+  <use xlink:href="#glyph1-16" x="278.570102" y="29.24"/>
+</g>
+<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
+  <use xlink:href="#glyph1-4" x="287.553692" y="29.24"/>
+</g>
+<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
+  <use xlink:href="#glyph1-11" x="295.147136" y="29.24"/>
+  <use xlink:href="#glyph1-17" x="302.171035" y="29.24"/>
+  <use xlink:href="#glyph1-15" x="309.377132" y="29.24"/>
+  <use xlink:href="#glyph1-7" x="316.583228" y="29.24"/>
+  <use xlink:href="#glyph1-18" x="323.789324" y="29.24"/>
+</g>
+<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
+  <use xlink:href="#glyph1-5" x="330.126041" y="29.24"/>
+  <use xlink:href="#glyph1-19" x="334.900456" y="29.24"/>
+</g>
+<g clip-path="url(#clip2)" clip-rule="nonzero">
+<path style="fill:none;stroke-width:1.49442;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M -53.619813 -99.095437 L -144.807313 -99.095437 C -147.006531 -99.095437 -148.791688 -100.880594 -148.791688 -103.079812 L -148.791688 -118.025125 C -148.791688 -120.22825 -147.006531 -122.0095 -144.807313 -122.0095 L -53.619813 -122.0095 C -51.420594 -122.0095 -49.635438 -120.22825 -49.635438 -118.025125 L -49.635438 -103.079812 C -49.635438 -100.880594 -51.420594 -99.095437 -53.619813 -99.095437 Z M -53.619813 -99.095437 " transform="matrix(1,0,0,-1,272.612,14.428)"/>
+</g>
+<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
+  <use xlink:href="#glyph1-20" x="133.785" y="129.463"/>
+  <use xlink:href="#glyph1-17" x="142.825975" y="129.463"/>
+  <use xlink:href="#glyph1-12" x="150.032072" y="129.463"/>
+  <use xlink:href="#glyph1-14" x="155.4162" y="129.463"/>
+</g>
+<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
+  <use xlink:href="#glyph1-4" x="165.165878" y="129.463"/>
+  <use xlink:href="#glyph1-6" x="172.371974" y="129.463"/>
+  <use xlink:href="#glyph1-15" x="175.675904" y="129.463"/>
+  <use xlink:href="#glyph1-7" x="182.882" y="129.463"/>
+  <use xlink:href="#glyph1-6" x="190.088097" y="129.463"/>
+  <use xlink:href="#glyph1-15" x="193.392026" y="129.463"/>
+</g>
+<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
+  <use xlink:href="#glyph1-8" x="200.612469" y="129.463"/>
+</g>
+<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
+  <use xlink:href="#glyph1-12" x="207.622022" y="129.463"/>
+</g>
+<g clip-path="url(#clip3)" clip-rule="nonzero">
+<path style="fill:none;stroke-width:1.49442;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 143.3255 -99.095437 L 55.102844 -99.095437 C 52.903625 -99.095437 51.118469 -100.880594 51.118469 -103.079812 L 51.118469 -118.025125 C 51.118469 -120.22825 52.903625 -122.0095 55.102844 -122.0095 L 143.3255 -122.0095 C 145.524719 -122.0095 147.309875 -120.22825 147.309875 -118.025125 L 147.309875 -103.079812 C 147.309875 -100.880594 145.524719 -99.095437 143.3255 -99.095437 Z M 143.3255 -99.095437 " transform="matrix(1,0,0,-1,272.612,14.428)"/>
+</g>
+<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
+  <use xlink:href="#glyph1-21" x="333.693" y="129.463"/>
+</g>
+<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
+  <use xlink:href="#glyph0-4" x="342.668" y="129.463"/>
+  <use xlink:href="#glyph0-4" x="350.050555" y="129.463"/>
+</g>
+<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
+  <use xlink:href="#glyph1-4" x="362.115" y="129.463"/>
+  <use xlink:href="#glyph1-6" x="369.321096" y="129.463"/>
+  <use xlink:href="#glyph1-15" x="372.625026" y="129.463"/>
+  <use xlink:href="#glyph1-7" x="379.831122" y="129.463"/>
+  <use xlink:href="#glyph1-6" x="387.037219" y="129.463"/>
+  <use xlink:href="#glyph1-15" x="390.341148" y="129.463"/>
+  <use xlink:href="#glyph1-8" x="397.547245" y="129.463"/>
+  <use xlink:href="#glyph1-12" x="404.571144" y="129.463"/>
+</g>
+<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
+  <use xlink:href="#glyph1-20" x="4.343" y="126.574"/>
+  <use xlink:href="#glyph1-17" x="13.383975" y="126.574"/>
+  <use xlink:href="#glyph1-12" x="20.590072" y="126.574"/>
+  <use xlink:href="#glyph1-14" x="25.9742" y="126.574"/>
+</g>
+<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
+  <use xlink:href="#glyph1-1" x="3.941" y="140.522"/>
+  <use xlink:href="#glyph1-11" x="10.184466" y="140.522"/>
+</g>
+<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
+  <use xlink:href="#glyph1-7" x="17.595713" y="140.522"/>
+  <use xlink:href="#glyph1-9" x="24.801809" y="140.522"/>
+</g>
+<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
+  <use xlink:href="#glyph1-21" x="514.177" y="126.574"/>
+</g>
+<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
+  <use xlink:href="#glyph0-4" x="523.151" y="126.574"/>
+  <use xlink:href="#glyph0-4" x="530.533555" y="126.574"/>
+</g>
+<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
+  <use xlink:href="#glyph1-1" x="514.177" y="140.522"/>
+  <use xlink:href="#glyph1-11" x="520.420466" y="140.522"/>
+</g>
+<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
+  <use xlink:href="#glyph1-7" x="527.831713" y="140.522"/>
+  <use xlink:href="#glyph1-9" x="535.037809" y="140.522"/>
+</g>
+<path style="fill:none;stroke-width:1.49442;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 0.00128125 -22.931375 L 0.00128125 -68.032937 " transform="matrix(1,0,0,-1,272.612,14.428)"/>
+<path style="fill:none;stroke-width:1.49442;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M -99.213563 -89.1345 L -99.213563 -68.032937 L 99.212219 -68.032937 L 99.212219 -89.1345 " transform="matrix(1,0,0,-1,272.612,14.428)"/>
+<path style="fill-rule:nonzero;fill:rgb(0%,0%,0%);fill-opacity:1;stroke-width:1.49442;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 6.317146 0.0002575 C 5.539802 0.207289 2.129646 1.371351 0.00074 2.640882 L 0.00074 -2.640368 C 2.129646 -1.370836 5.539802 -0.206774 6.317146 0.0002575 Z M 6.317146 0.0002575 " transform="matrix(0,1,1,0,173.39818,103.56176)"/>
+<path style="fill-rule:nonzero;fill:rgb(0%,0%,0%);fill-opacity:1;stroke-width:1.49442;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 6.317146 -0.00160125 C 5.539802 0.20543 2.129646 1.373399 0.00074 2.639024 L 0.00074 -2.63832 C 2.129646 -1.372695 5.539802 -0.204726 6.317146 -0.00160125 Z M 6.317146 -0.00160125 " transform="matrix(0,1,1,0,371.82582,103.56176)"/>
+<path style="fill:none;stroke-width:1.49442;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M -227.619813 -110.552469 L -158.752625 -110.552469 " transform="matrix(1,0,0,-1,272.612,14.428)"/>
+<path style=" stroke:none;fill-rule:nonzero;fill:rgb(0%,0%,0%);fill-opacity:1;" d="M 38.675781 124.980469 C 39.453125 125.1875 42.859375 126.351562 44.992188 127.621094 L 44.992188 122.339844 C 42.859375 123.609375 39.453125 124.777344 38.675781 124.980469 Z M 38.675781 124.980469 "/>
+<g clip-path="url(#clip4)" clip-rule="nonzero">
+<path style="fill:none;stroke-width:1.49442;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 6.316149 -0.00123125 C 5.538805 0.2058 2.132555 1.369862 -0.0002575 2.639394 L -0.0002575 -2.641856 C 2.132555 -1.372325 5.538805 -0.204356 6.316149 -0.00123125 Z M 6.316149 -0.00123125 " transform="matrix(-1,0,0,1,44.99193,124.9817)"/>
+</g>
+<path style=" stroke:none;fill-rule:nonzero;fill:rgb(0%,0%,0%);fill-opacity:1;" d="M 120.171875 124.980469 C 119.398438 124.777344 115.988281 123.609375 113.859375 122.339844 L 113.859375 127.621094 C 115.988281 126.351562 119.398438 125.1875 120.171875 124.980469 Z M 120.171875 124.980469 "/>
+<g clip-path="url(#clip5)" clip-rule="nonzero">
+<path style="fill:none;stroke-width:1.49442;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 6.313975 0.00123125 C 5.540537 0.204356 2.130381 1.372325 0.001475 2.641856 L 0.001475 -2.639394 C 2.130381 -1.369862 5.540537 -0.2058 6.313975 0.00123125 Z M 6.313975 0.00123125 " transform="matrix(1,0,0,-1,113.8579,124.9817)"/>
+</g>
+<path style="fill:none;stroke-width:1.49442;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-dasharray:7.97021,5.97766;stroke-miterlimit:10;" d="M -39.6745 -110.552469 L 41.157531 -110.552469 " transform="matrix(1,0,0,-1,272.612,14.428)"/>
+<path style=" stroke:none;fill-rule:nonzero;fill:rgb(0%,0%,0%);fill-opacity:1;" d="M 226.621094 124.980469 C 227.398438 125.1875 230.808594 126.351562 232.9375 127.621094 L 232.9375 122.339844 C 230.808594 123.609375 227.398438 124.777344 226.621094 124.980469 Z M 226.621094 124.980469 "/>
+<g clip-path="url(#clip6)" clip-rule="nonzero">
+<path style="fill:none;stroke-width:1.49442;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 6.317366 -0.00123125 C 5.540023 0.2058 2.129866 1.369862 0.00096 2.639394 L 0.00096 -2.641856 C 2.129866 -1.372325 5.540023 -0.204356 6.317366 -0.00123125 Z M 6.317366 -0.00123125 " transform="matrix(-1,0,0,1,232.93846,124.9817)"/>
+</g>
+<path style=" stroke:none;fill-rule:nonzero;fill:rgb(0%,0%,0%);fill-opacity:1;" d="M 320.082031 124.980469 C 319.308594 124.777344 315.898438 123.609375 313.769531 122.339844 L 313.769531 127.621094 C 315.898438 126.351562 319.308594 125.1875 320.082031 124.980469 Z M 320.082031 124.980469 "/>
+<g clip-path="url(#clip7)" clip-rule="nonzero">
+<path style="fill:none;stroke-width:1.49442;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 6.313811 0.00123125 C 5.540374 0.204356 2.130217 1.372325 0.00131125 2.641856 L 0.00131125 -2.639394 C 2.130217 -1.369862 5.540374 -0.2058 6.313811 0.00123125 Z M 6.313811 0.00123125 " transform="matrix(1,0,0,-1,313.76822,124.9817)"/>
+</g>
+<path style="fill:none;stroke-width:1.49442;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 157.270812 -110.552469 L 227.618469 -110.552469 " transform="matrix(1,0,0,-1,272.612,14.428)"/>
+<path style=" stroke:none;fill-rule:nonzero;fill:rgb(0%,0%,0%);fill-opacity:1;" d="M 423.566406 124.980469 C 424.34375 125.1875 427.753906 126.351562 429.882812 127.621094 L 429.882812 122.339844 C 427.753906 123.609375 424.34375 124.777344 423.566406 124.980469 Z M 423.566406 124.980469 "/>
+<g clip-path="url(#clip8)" clip-rule="nonzero">
+<path style="fill:none;stroke-width:1.49442;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 6.317014 -0.00123125 C 5.53967 0.2058 2.129514 1.369862 0.0006075 2.639394 L 0.0006075 -2.641856 C 2.129514 -1.372325 5.53967 -0.204356 6.317014 -0.00123125 Z M 6.317014 -0.00123125 " transform="matrix(-1,0,0,1,429.88342,124.9817)"/>
+</g>
+<path style=" stroke:none;fill-rule:nonzero;fill:rgb(0%,0%,0%);fill-opacity:1;" d="M 506.546875 124.980469 C 505.773438 124.777344 502.363281 123.609375 500.230469 122.339844 L 500.230469 127.621094 C 502.363281 126.351562 505.773438 125.1875 506.546875 124.980469 Z M 506.546875 124.980469 "/>
+<g clip-path="url(#clip9)" clip-rule="nonzero">
+<path style="fill:none;stroke-width:1.49442;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 6.314805 0.00123125 C 5.541367 0.204356 2.131211 1.372325 -0.00160125 2.641856 L -0.00160125 -2.639394 C 2.131211 -1.369862 5.541367 -0.2058 6.314805 0.00123125 Z M 6.314805 0.00123125 " transform="matrix(1,0,0,-1,500.23207,124.9817)"/>
+</g>
+<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
+  <use xlink:href="#glyph1-22" x="148.925" y="75.603"/>
+  <use xlink:href="#glyph1-18" x="161.112097" y="75.603"/>
+  <use xlink:href="#glyph1-1" x="167.836161" y="75.603"/>
+  <use xlink:href="#glyph1-5" x="174.079627" y="75.603"/>
+  <use xlink:href="#glyph1-11" x="178.854042" y="75.603"/>
+</g>
+<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
+  <use xlink:href="#glyph1-9" x="190.554803" y="75.603"/>
+  <use xlink:href="#glyph1-2" x="196.798269" y="75.603"/>
+  <use xlink:href="#glyph1-13" x="203.223932" y="75.603"/>
+  <use xlink:href="#glyph1-18" x="210.430029" y="75.603"/>
+  <use xlink:href="#glyph1-15" x="217.154093" y="75.603"/>
+  <use xlink:href="#glyph1-12" x="224.360189" y="75.603"/>
+  <use xlink:href="#glyph1-6" x="229.744318" y="75.603"/>
+  <use xlink:href="#glyph1-11" x="233.048248" y="75.603"/>
+  <use xlink:href="#glyph1-15" x="240.072147" y="75.603"/>
+</g>
+<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
+  <use xlink:href="#glyph1-21" x="297.94" y="75.603"/>
+  <use xlink:href="#glyph1-11" x="306.914983" y="75.603"/>
+</g>
+<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
+  <use xlink:href="#glyph1-7" x="314.32623" y="75.603"/>
+  <use xlink:href="#glyph1-9" x="321.532326" y="75.603"/>
+</g>
+<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
+  <use xlink:href="#glyph1-8" x="332.452653" y="75.603"/>
+  <use xlink:href="#glyph1-9" x="339.476553" y="75.603"/>
+  <use xlink:href="#glyph1-15" x="345.720019" y="75.603"/>
+  <use xlink:href="#glyph1-9" x="352.926115" y="75.603"/>
+  <use xlink:href="#glyph1-5" x="359.169582" y="75.603"/>
+  <use xlink:href="#glyph1-18" x="363.943997" y="75.603"/>
+  <use xlink:href="#glyph1-14" x="370.668061" y="75.603"/>
+  <use xlink:href="#glyph1-6" x="375.740877" y="75.603"/>
+  <use xlink:href="#glyph1-11" x="379.044807" y="75.603"/>
+  <use xlink:href="#glyph1-15" x="386.068707" y="75.603"/>
+</g>
+<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
+  <use xlink:href="#glyph1-23" x="231.724" y="117.459"/>
+  <use xlink:href="#glyph1-6" x="241.569797" y="117.459"/>
+  <use xlink:href="#glyph1-7" x="244.873727" y="117.459"/>
+  <use xlink:href="#glyph1-7" x="252.079823" y="117.459"/>
+  <use xlink:href="#glyph1-9" x="259.285919" y="117.459"/>
+  <use xlink:href="#glyph1-15" x="265.529386" y="117.459"/>
+</g>
+<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
+  <use xlink:href="#glyph1-21" x="277.412343" y="117.459"/>
+</g>
+<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
+  <use xlink:href="#glyph1-24" x="291.078533" y="117.459"/>
+  <use xlink:href="#glyph1-25" x="300.360525" y="117.459"/>
+  <use xlink:href="#glyph1-26" x="309.68412" y="117.459"/>
+</g>
+<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
+  <use xlink:href="#glyph1-27" x="68.742" y="89.962"/>
+  <use xlink:href="#glyph1-18" x="76.546333" y="89.962"/>
+  <use xlink:href="#glyph1-16" x="83.270397" y="89.962"/>
+  <use xlink:href="#glyph1-9" x="87.56278" y="89.962"/>
+</g>
+<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
+  <use xlink:href="#glyph1-12" x="37.407" y="103.909"/>
+  <use xlink:href="#glyph1-14" x="42.791129" y="103.909"/>
+  <use xlink:href="#glyph1-5" x="47.863945" y="103.909"/>
+  <use xlink:href="#glyph1-18" x="52.638361" y="103.909"/>
+  <use xlink:href="#glyph1-6" x="59.362424" y="103.909"/>
+  <use xlink:href="#glyph1-8" x="62.666354" y="103.909"/>
+  <use xlink:href="#glyph1-28" x="69.690254" y="103.909"/>
+  <use xlink:href="#glyph1-14" x="76.89635" y="103.909"/>
+  <use xlink:href="#glyph1-16" x="81.969166" y="103.909"/>
+  <use xlink:href="#glyph1-11" x="86.261549" y="103.909"/>
+</g>
+<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
+  <use xlink:href="#glyph1-5" x="92.898102" y="103.909"/>
+  <use xlink:href="#glyph1-29" x="97.672517" y="103.909"/>
+</g>
+<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
+  <use xlink:href="#glyph1-18" x="106.832566" y="103.909"/>
+</g>
+<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
+  <use xlink:href="#glyph1-5" x="113.154936" y="103.909"/>
+  <use xlink:href="#glyph1-7" x="117.929351" y="103.909"/>
+</g>
+<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
+  <use xlink:href="#glyph1-20" x="51.871" y="117.857"/>
+  <use xlink:href="#glyph1-17" x="60.911975" y="117.857"/>
+  <use xlink:href="#glyph1-12" x="68.118072" y="117.857"/>
+  <use xlink:href="#glyph1-14" x="73.5022" y="117.857"/>
+</g>
+<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
+  <use xlink:href="#glyph1-24" x="83.251878" y="117.857"/>
+  <use xlink:href="#glyph1-30" x="92.533869" y="117.857"/>
+  <use xlink:href="#glyph1-26" x="101.467248" y="117.857"/>
+  <use xlink:href="#glyph1-12" x="105.286206" y="117.857"/>
+</g>
+<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
+  <use xlink:href="#glyph1-27" x="418.874" y="103.909"/>
+  <use xlink:href="#glyph1-14" x="426.678333" y="103.909"/>
+  <use xlink:href="#glyph1-5" x="431.751149" y="103.909"/>
+  <use xlink:href="#glyph1-18" x="436.525564" y="103.909"/>
+  <use xlink:href="#glyph1-6" x="443.249628" y="103.909"/>
+  <use xlink:href="#glyph1-8" x="446.553558" y="103.909"/>
+  <use xlink:href="#glyph1-28" x="453.577458" y="103.909"/>
+  <use xlink:href="#glyph1-14" x="460.783554" y="103.909"/>
+  <use xlink:href="#glyph1-16" x="465.85637" y="103.909"/>
+  <use xlink:href="#glyph1-11" x="470.148753" y="103.909"/>
+</g>
+<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
+  <use xlink:href="#glyph1-5" x="476.785306" y="103.909"/>
+  <use xlink:href="#glyph1-29" x="481.559721" y="103.909"/>
+</g>
+<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
+  <use xlink:href="#glyph1-18" x="490.705423" y="103.909"/>
+</g>
+<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
+  <use xlink:href="#glyph1-5" x="497.04214" y="103.909"/>
+  <use xlink:href="#glyph1-7" x="501.816555" y="103.909"/>
+</g>
+<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
+  <use xlink:href="#glyph1-21" x="436.031" y="117.857"/>
+</g>
+<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
+  <use xlink:href="#glyph0-4" x="445.005" y="117.857"/>
+  <use xlink:href="#glyph0-4" x="452.387555" y="117.857"/>
+</g>
+<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
+  <use xlink:href="#glyph1-24" x="464.452" y="117.857"/>
+  <use xlink:href="#glyph1-30" x="473.733991" y="117.857"/>
+  <use xlink:href="#glyph1-26" x="482.66737" y="117.857"/>
+  <use xlink:href="#glyph1-12" x="486.486329" y="117.857"/>
+</g>
+</g>
+</svg>
diff --git a/book/src/reference.md b/book/src/reference.md
new file mode 100644 (file)
index 0000000..62ca35c
--- /dev/null
@@ -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 (file)
index 0000000..4043db1
--- /dev/null
@@ -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 (file)
index 0000000..1182dc2
--- /dev/null
@@ -0,0 +1,692 @@
+{{#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 = "2021"
+
+[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 = "2021"
+
+[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");
+
+    println!("cargo:rerun-if-changed=src/main.rs");
+    println!("cargo:rerun-if-changed=src/blobstore.cc");
+    println!("cargo:rerun-if-changed=include/blobstore.h");
+}
+```
+
+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")
+        .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>
+#include <string>
+...
+...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/book/theme/head.hbs b/book/theme/head.hbs
new file mode 100644 (file)
index 0000000..d6b32cb
--- /dev/null
@@ -0,0 +1,7 @@
+<script async src="https://www.googletagmanager.com/gtag/js?id=G-DG41MK6DDN"></script>
+<script>
+  window.dataLayer = window.dataLayer || [];
+  function gtag(){dataLayer.push(arguments);}
+  gtag('js', new Date());
+  gtag('config', 'G-DG41MK6DDN', {anonymize_ip: true, cookie_domain: 'cxx.rs', cookie_flags: 'samesite=strict;secure'});
+</script>
diff --git a/build.rs b/build.rs
new file mode 100644 (file)
index 0000000..2fbb018
--- /dev/null
+++ b/build.rs
@@ -0,0 +1,75 @@
+#![allow(unknown_lints)]
+#![allow(unexpected_cfgs)]
+
+use std::env;
+use std::path::{Path, PathBuf};
+use std::process::Command;
+
+fn main() {
+    let manifest_dir_opt = env::var_os("CARGO_MANIFEST_DIR").map(PathBuf::from);
+    let manifest_dir = manifest_dir_opt.as_deref().unwrap_or(Path::new(""));
+
+    cc::Build::new()
+        .file(manifest_dir.join("src/cxx.cc"))
+        .cpp(true)
+        .cpp_link_stdlib(None) // linked via link-cplusplus crate
+        .std(cxxbridge_flags::STD)
+        .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) = &manifest_dir_opt {
+        let cxx_h = manifest_dir.join("include").join("cxx.h");
+        println!("cargo:HEADER={}", cxx_h.to_string_lossy());
+    }
+
+    if let Some(rustc) = rustc_version() {
+        if rustc.minor >= 80 {
+            println!("cargo:rustc-check-cfg=cfg(built_with_cargo)");
+            println!("cargo:rustc-check-cfg=cfg(compile_error_if_alloc)");
+            println!("cargo:rustc-check-cfg=cfg(compile_error_if_std)");
+            println!("cargo:rustc-check-cfg=cfg(cxx_experimental_no_alloc)");
+            println!("cargo:rustc-check-cfg=cfg(error_in_core)");
+            println!("cargo:rustc-check-cfg=cfg(seek_relative)");
+            println!("cargo:rustc-check-cfg=cfg(skip_ui_tests)");
+        }
+
+        if rustc.minor < 73 {
+            println!("cargo:warning=The cxx crate requires a rustc version 1.73.0 or newer.");
+            println!(
+                "cargo:warning=You appear to be building with: {}",
+                rustc.version,
+            );
+        }
+
+        if rustc.minor >= 80 {
+            // std::io::Seek::seek_relative
+            println!("cargo:rustc-cfg=seek_relative");
+        }
+
+        if rustc.minor >= 81 {
+            // core::error::Error
+            println!("cargo:rustc-cfg=error_in_core");
+        }
+    }
+}
+
+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 (file)
index 0000000..c24e3b5
--- /dev/null
@@ -0,0 +1 @@
+-std=c++11
diff --git a/include/cxx.h b/include/cxx.h
new file mode 100644 (file)
index 0000000..4e261a3
--- /dev/null
@@ -0,0 +1,1149 @@
+#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>
+#include <type_traits>
+#include <utility>
+#include <vector>
+#if defined(_WIN32)
+#include <basetsd.h>
+#else
+#include <sys/types.h>
+#endif
+
+#if __cplusplus >= 201703L
+#include <string_view>
+#endif
+
+#if __cplusplus >= 202002L
+#include <ranges>
+#endif
+
+namespace rust {
+inline namespace cxxbridge1 {
+
+struct unsafe_bitcopy_t;
+
+namespace {
+template <typename T>
+class impl;
+}
+
+#ifndef CXXBRIDGE1_RUST_STRING
+#define CXXBRIDGE1_RUST_STRING
+// https://cxx.rs/binding/string.html
+class String final {
+public:
+  String() noexcept;
+  String(const String &) noexcept;
+  String(String &&) noexcept;
+  ~String() noexcept;
+
+  String(const std::string &);
+  String(const char *);
+  String(const char *, std::size_t);
+  String(const char16_t *);
+  String(const char16_t *, std::size_t);
+#ifdef __cpp_char8_t
+  String(const char8_t *s);
+  String(const char8_t *s, std::size_t len);
+#endif
+
+  // Replace invalid Unicode data with the replacement character (U+FFFD).
+  static String lossy(const std::string &) noexcept;
+  static String lossy(const char *) noexcept;
+  static String lossy(const char *, std::size_t) noexcept;
+  static String lossy(const char16_t *) noexcept;
+  static String lossy(const char16_t *, std::size_t) noexcept;
+
+  String &operator=(const String &) & noexcept;
+  String &operator=(String &&) & noexcept;
+
+  explicit operator std::string() const;
+
+  // Note: no null terminator.
+  const char *data() const noexcept;
+  std::size_t size() const noexcept;
+  std::size_t length() const noexcept;
+  bool empty() const noexcept;
+
+  const char *c_str() noexcept;
+
+  std::size_t capacity() const noexcept;
+  void reserve(size_t new_cap) noexcept;
+
+  using iterator = char *;
+  iterator begin() noexcept;
+  iterator end() noexcept;
+
+  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:
+  struct lossy_t;
+  String(lossy_t, const char *, std::size_t) noexcept;
+  String(lossy_t, const char16_t *, std::size_t) noexcept;
+  friend void swap(String &lhs, String &rhs) noexcept { lhs.swap(rhs); }
+
+  // Size and alignment statically verified by rust_string.rs.
+  std::array<std::uintptr_t, 3> repr;
+};
+#endif // CXXBRIDGE1_RUST_STRING
+
+#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 *, std::size_t);
+
+  Str &operator=(const Str &) & noexcept = default;
+
+  explicit operator std::string() const;
+#if __cplusplus >= 201703L
+  explicit operator std::string_view() const;
+#endif
+
+  // Note: no null terminator.
+  const char *data() const noexcept;
+  std::size_t size() const noexcept;
+  std::size_t length() const noexcept;
+  bool empty() const noexcept;
+
+  // Important in order for System V ABI to pass in registers.
+  Str(const Str &) noexcept = default;
+  ~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>;
+
+  std::array<std::uintptr_t, 2> repr;
+};
+#endif // CXXBRIDGE1_RUST_STR
+
+#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
+    : private detail::copy_assignable_if<std::is_const<T>::value> {
+public:
+  using value_type = T;
+
+  Slice() noexcept;
+  Slice(T *, std::size_t count) noexcept;
+
+  template <typename C>
+  explicit Slice(C &c) : Slice(c.data(), c.size()) {}
+
+  Slice &operator=(const Slice<T> &) & noexcept = default;
+  Slice &operator=(Slice<T> &&) & noexcept = default;
+
+  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>;
+  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;
+};
+
+#ifdef __cpp_deduction_guides
+template <typename C>
+explicit Slice(C &c)
+    -> Slice<std::remove_reference_t<decltype(*std::declval<C>().data())>>;
+#endif // __cpp_deduction_guides
+
+template <typename T>
+class Slice<T>::iterator final {
+public:
+#if __cplusplus >= 202002L
+  using iterator_category = std::contiguous_iterator_tag;
+#else
+  using iterator_category = std::random_access_iterator_tag;
+#endif
+  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;
+  friend inline iterator operator+(difference_type lhs, iterator rhs) noexcept {
+    return rhs + lhs;
+  }
+  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;
+};
+
+#if __cplusplus >= 202002L
+static_assert(std::ranges::contiguous_range<rust::Slice<const uint8_t>>);
+static_assert(std::contiguous_iterator<rust::Slice<const uint8_t>::iterator>);
+#endif
+
+#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() = delete;
+  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;
+
+  /* Deprecated */ using value_type = element_type;
+
+private:
+  class uninit;
+  class allocation;
+  Box(uninit) noexcept;
+  void drop() noexcept;
+
+  friend void swap(Box &lhs, Box &rhs) noexcept { lhs.swap(rhs); }
+
+  T *ptr;
+};
+#endif // CXXBRIDGE1_RUST_BOX
+
+#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=(const Vec &) &;
+
+  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[](std::size_t n) const noexcept;
+  const T &at(std::size_t n) const;
+  const T &front() const noexcept;
+  const T &back() const noexcept;
+
+  T &operator[](std::size_t n) noexcept;
+  T &at(std::size_t n);
+  T &front() noexcept;
+  T &back() noexcept;
+
+  void reserve(std::size_t new_cap);
+  void push_back(const T &value);
+  void push_back(T &&value);
+  template <typename... Args>
+  void emplace_back(Args &&...args);
+  void truncate(std::size_t len);
+  void clear();
+
+  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:
+  void reserve_total(std::size_t new_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<std::uintptr_t, 3> repr;
+};
+#endif // CXXBRIDGE1_RUST_VEC
+
+#ifndef CXXBRIDGE1_RUST_FN
+// https://cxx.rs/binding/fn.html
+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;
+
+private:
+  Ret (*trampoline)(Args..., void *fn) noexcept;
+  void *fn;
+};
+#endif // CXXBRIDGE1_RUST_FN
+
+#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 override;
+
+  Error &operator=(const Error &) &;
+  Error &operator=(Error &&) & noexcept;
+
+  const char *what() const noexcept override;
+
+private:
+  Error() noexcept = default;
+  friend impl<Error>;
+  const char *msg;
+  std::size_t len;
+};
+#endif // CXXBRIDGE1_RUST_ERROR
+
+#ifndef CXXBRIDGE1_RUST_ISIZE
+#define CXXBRIDGE1_RUST_ISIZE
+#if defined(_WIN32)
+using isize = SSIZE_T;
+#else
+using isize = ssize_t;
+#endif
+#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.
+//
+// There may be legitimate reasons to opt out of the check for support of types
+// that the programmer knows are soundly Rust-movable despite not being
+// recognized as such by the C++ type system due to a move constructor or
+// destructor. To opt out of the relocatability check, do either of the
+// following things in any header used by `include!` in the bridge.
+//
+//      --- if you define the type:
+//      struct MyType {
+//        ...
+//    +   using IsRelocatable = std::true_type;
+//      };
+//
+//      --- otherwise:
+//    + template <>
+//    + struct rust::IsRelocatable<MyType> : std::true_type {};
+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 <typename T>
+using slice = Slice<T>;
+template <typename T>
+using box = Box<T>;
+template <typename T>
+using vec = Vec<T>;
+using error = Error;
+template <typename Signature>
+using fn = Fn<Signature>;
+template <typename T>
+using is_relocatable = IsRelocatable<T>;
+
+
+
+////////////////////////////////////////////////////////////////////////////////
+/// end public API, begin implementation details
+
+#ifndef CXXBRIDGE1_PANIC
+#define CXXBRIDGE1_PANIC
+template <typename Exception>
+void panic [[noreturn]] (const char *msg);
+#endif // CXXBRIDGE1_PANIC
+
+#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::forward<Args>(args)..., this->fn);
+}
+
+template <typename Ret, typename... Args>
+Fn<Ret(Args...)> Fn<Ret(Args...)>::operator*() const noexcept {
+  return *this;
+}
+#endif // CXXBRIDGE1_RUST_FN
+
+#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 // CXXBRIDGE1_RUST_BITCOPY
+
+#ifndef CXXBRIDGE1_RUST_SLICE
+#define CXXBRIDGE1_RUST_SLICE
+template <typename T>
+Slice<T>::Slice() noexcept {
+  sliceInit(this, reinterpret_cast<void *>(align_of<T>()), 0);
+}
+
+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);
+}
+
+template <typename T>
+T *Slice<T>::data() const noexcept {
+  return reinterpret_cast<T *>(slicePtr(this));
+}
+
+template <typename T>
+std::size_t Slice<T>::size() const noexcept {
+  return sliceLen(this);
+}
+
+template <typename T>
+std::size_t Slice<T>::length() const noexcept {
+  return this->size();
+}
+
+template <typename T>
+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 ptr = static_cast<char *>(slicePtr(this)) + size_of<T>() * n;
+  return *reinterpret_cast<T *>(ptr);
+}
+
+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>
+typename Slice<T>::iterator::reference Slice<T>::iterator::operator[](
+    typename Slice<T>::iterator::difference_type n) const noexcept {
+  auto ptr = static_cast<char *>(this->pos) + this->stride * n;
+  return *reinterpret_cast<T *>(ptr);
+}
+
+template <typename T>
+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--() 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 / static_cast<typename Slice<T>::iterator::difference_type>(
+                    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) {
+  other.ptr = nullptr;
+}
+
+template <typename T>
+Box<T>::Box(const T &val) {
+  allocation alloc;
+  ::new (alloc.ptr) T(val);
+  this->ptr = alloc.ptr;
+  alloc.ptr = nullptr;
+}
+
+template <typename T>
+Box<T>::Box(T &&val) {
+  allocation alloc;
+  ::new (alloc.ptr) T(std::move(val));
+  this->ptr = alloc.ptr;
+  alloc.ptr = nullptr;
+}
+
+template <typename T>
+Box<T>::~Box() noexcept {
+  if (this->ptr) {
+    this->drop();
+  }
+}
+
+template <typename T>
+Box<T> &Box<T>::operator=(Box &&other) & noexcept {
+  if (this->ptr) {
+    this->drop();
+  }
+  this->ptr = other.ptr;
+  other.ptr = nullptr;
+  return *this;
+}
+
+template <typename T>
+const T *Box<T>::operator->() const noexcept {
+  return this->ptr;
+}
+
+template <typename T>
+const T &Box<T>::operator*() const noexcept {
+  return *this->ptr;
+}
+
+template <typename T>
+T *Box<T>::operator->() noexcept {
+  return this->ptr;
+}
+
+template <typename T>
+T &Box<T>::operator*() noexcept {
+  return *this->ptr;
+}
+
+template <typename T>
+template <typename... Fields>
+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 = uninit{};
+  box.ptr = raw;
+  return box;
+}
+
+template <typename T>
+T *Box<T>::into_raw() noexcept {
+  T *raw = this->ptr;
+  this->ptr = nullptr;
+  return raw;
+}
+
+template <typename T>
+Box<T>::Box(uninit) noexcept {}
+#endif // CXXBRIDGE1_RUST_BOX
+
+#ifndef CXXBRIDGE1_RUST_VEC
+#define CXXBRIDGE1_RUST_VEC
+template <typename T>
+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();
+}
+
+template <typename T>
+Vec<T>::~Vec() noexcept {
+  this->drop();
+}
+
+template <typename T>
+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();
+    new (this) Vec(other);
+  }
+  return *this;
+}
+
+template <typename T>
+bool Vec<T>::empty() const noexcept {
+  return this->size() == 0;
+}
+
+template <typename T>
+T *Vec<T>::data() noexcept {
+  return const_cast<T *>(const_cast<const Vec<T> *>(this)->data());
+}
+
+template <typename T>
+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 * 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>
+T &Vec<T>::at(std::size_t n) {
+  if (n >= this->size()) {
+    panic<std::out_of_range>("rust::Vec index out of range");
+  }
+  return (*this)[n];
+}
+
+template <typename T>
+T &Vec<T>::front() noexcept {
+  assert(!this->empty());
+  return (*this)[0];
+}
+
+template <typename T>
+T &Vec<T>::back() noexcept {
+  assert(!this->empty());
+  return (*this)[this->size() - 1];
+}
+
+template <typename T>
+void Vec<T>::reserve(std::size_t new_cap) {
+  this->reserve_total(new_cap);
+}
+
+template <typename T>
+void Vec<T>::push_back(const T &value) {
+  this->emplace_back(value);
+}
+
+template <typename T>
+void Vec<T>::push_back(T &&value) {
+  this->emplace_back(std::move(value));
+}
+
+template <typename T>
+template <typename... 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 * size_of<T>()))
+      T(std::forward<Args>(args)...);
+  this->set_len(size + 1);
+}
+
+template <typename T>
+void Vec<T>::clear() {
+  this->truncate(0);
+}
+
+template <typename T>
+typename Vec<T>::iterator Vec<T>::begin() noexcept {
+  return Slice<T>(this->data(), this->size()).begin();
+}
+
+template <typename T>
+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>::begin() const noexcept {
+  return this->cbegin();
+}
+
+template <typename T>
+typename Vec<T>::const_iterator Vec<T>::end() const noexcept {
+  return this->cend();
+}
+
+template <typename T>
+typename Vec<T>::const_iterator Vec<T>::cbegin() const noexcept {
+  return Slice<const T>(this->data(), this->size()).begin();
+}
+
+template <typename T>
+typename Vec<T>::const_iterator Vec<T>::cend() const noexcept {
+  return Slice<const T>(this->data(), this->size()).end();
+}
+
+template <typename T>
+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>
+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>();
+}
+
+template <typename T>
+std::size_t align_of() {
+  return layout::align_of<T>();
+}
+#endif // CXXBRIDGE1_LAYOUT
+
+#ifndef CXXBRIDGE1_RELOCATABLE
+#define CXXBRIDGE1_RELOCATABLE
+namespace detail {
+template <typename... Ts>
+struct make_void {
+  using type = void;
+};
+
+template <typename... Ts>
+using void_t = typename make_void<Ts...>::type;
+
+template <typename Void, template <typename...> class, typename...>
+struct detect : std::false_type {};
+template <template <typename...> class T, typename... A>
+struct detect<void_t<T<A...>>, T, A...> : std::true_type {};
+
+template <template <typename...> class T, typename... A>
+using is_detected = detect<void, T, A...>;
+
+template <typename T>
+using detect_IsRelocatable = typename T::IsRelocatable;
+
+template <typename T>
+struct get_IsRelocatable
+    : std::is_same<typename T::IsRelocatable, std::true_type> {};
+} // namespace detail
+
+template <typename T>
+struct IsRelocatable
+    : std::conditional<
+          detail::is_detected<detail::detect_IsRelocatable, T>::value,
+          detail::get_IsRelocatable<T>,
+          std::integral_constant<
+              bool, std::is_trivially_move_constructible<T>::value &&
+                        std::is_trivially_destructible<T>::value>>::type {};
+#endif // CXXBRIDGE1_RELOCATABLE
+
+} // namespace cxxbridge1
+} // namespace rust
diff --git a/reindeer.toml b/reindeer.toml
new file mode 100644 (file)
index 0000000..4fc8abb
--- /dev/null
@@ -0,0 +1 @@
+error = "This is the wrong directory. Run `reindeer buckify` in the third-party directory."
diff --git a/rust-toolchain.toml b/rust-toolchain.toml
new file mode 100644 (file)
index 0000000..20fe888
--- /dev/null
@@ -0,0 +1,2 @@
+[toolchain]
+components = ["rust-src"]
diff --git a/src/cxx.cc b/src/cxx.cc
new file mode 100644 (file)
index 0000000..45d9aac
--- /dev/null
@@ -0,0 +1,815 @@
+#include "../include/cxx.h"
+#include <cstdio>
+#include <cstring>
+#include <iostream>
+#include <memory>
+
+extern "C" {
+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();
+}
+
+std::size_t cxxbridge1$cxx_string$length(const std::string &s) noexcept {
+  return s.length();
+}
+
+void cxxbridge1$cxx_string$clear(std::string &s) noexcept { s.clear(); }
+
+void cxxbridge1$cxx_string$reserve_total(std::string &s,
+                                         size_t new_cap) noexcept {
+  s.reserve(new_cap);
+}
+
+void cxxbridge1$cxx_string$push(std::string &s, const std::uint8_t *ptr,
+                                std::size_t len) noexcept {
+  s.append(reinterpret_cast<const char *>(ptr), len);
+}
+
+// rust::String
+void cxxbridge1$string$new(rust::String *self) noexcept;
+void cxxbridge1$string$clone(rust::String *self,
+                             const rust::String &other) noexcept;
+bool cxxbridge1$string$from_utf8(rust::String *self, const char *ptr,
+                                 std::size_t len) noexcept;
+void cxxbridge1$string$from_utf8_lossy(rust::String *self, const char *ptr,
+                                       std::size_t len) noexcept;
+bool cxxbridge1$string$from_utf16(rust::String *self, const char16_t *ptr,
+                                  std::size_t len) noexcept;
+void cxxbridge1$string$from_utf16_lossy(rust::String *self, const char16_t *ptr,
+                                        std::size_t len) noexcept;
+void cxxbridge1$string$drop(rust::String *self) noexcept;
+const char *cxxbridge1$string$ptr(const rust::String *self) noexcept;
+std::size_t cxxbridge1$string$len(const rust::String *self) noexcept;
+std::size_t cxxbridge1$string$capacity(const rust::String *self) noexcept;
+void cxxbridge1$string$reserve_additional(rust::String *self,
+                                          size_t additional) noexcept;
+void cxxbridge1$string$reserve_total(rust::String *self,
+                                     size_t new_cap) noexcept;
+
+// rust::Str
+void cxxbridge1$str$new(rust::Str *self) noexcept;
+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 cxxbridge1 {
+
+template <typename Exception>
+void panic [[noreturn]] (const char *msg) {
+#if defined(RUST_CXX_NO_EXCEPTIONS)
+  std::fprintf(stderr, "Error: %s. Aborting.\n", msg);
+  std::abort();
+#else
+  throw Exception(msg);
+#endif
+}
+
+template void panic<std::out_of_range> [[noreturn]] (const char *msg);
+
+template <typename T>
+static bool is_aligned(const void *ptr) noexcept {
+  auto iptr = reinterpret_cast<std::uintptr_t>(ptr);
+  return !(iptr % alignof(T));
+}
+
+String::String() noexcept { cxxbridge1$string$new(this); }
+
+String::String(const String &other) noexcept {
+  cxxbridge1$string$clone(this, other);
+}
+
+String::String(String &&other) noexcept : repr(other.repr) {
+  cxxbridge1$string$new(&other);
+}
+
+String::~String() noexcept { cxxbridge1$string$drop(this); }
+
+static void initString(String *self, const char *s, std::size_t len) {
+  if (!cxxbridge1$string$from_utf8(self, s, len)) {
+    panic<std::invalid_argument>("data for rust::String is not utf-8");
+  }
+}
+
+static void initString(String *self, const char16_t *s, std::size_t len) {
+  if (!cxxbridge1$string$from_utf16(self, s, len)) {
+    panic<std::invalid_argument>("data for rust::String is not utf-16");
+  }
+}
+
+String::String(const std::string &s) { initString(this, s.data(), s.length()); }
+
+String::String(const char *s) {
+  assert(s != nullptr);
+  initString(this, s, std::strlen(s));
+}
+
+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);
+}
+
+#ifdef __cpp_char8_t
+String::String(const char8_t *s) : String(reinterpret_cast<const char *>(s)) {}
+
+String::String(const char8_t *s, std::size_t len)
+    : String(reinterpret_cast<const char *>(s), len) {}
+#endif
+
+String::String(const char16_t *s) {
+  assert(s != nullptr);
+  assert(is_aligned<char16_t>(s));
+  initString(this, s, std::char_traits<char16_t>::length(s));
+}
+
+String::String(const char16_t *s, std::size_t len) {
+  assert(s != nullptr || len == 0);
+  assert(is_aligned<char16_t>(s));
+  initString(this,
+             s == nullptr && len == 0 ? reinterpret_cast<const char16_t *>(2)
+                                      : s,
+             len);
+}
+
+struct String::lossy_t {};
+
+String::String(lossy_t, const char *s, std::size_t len) noexcept {
+  cxxbridge1$string$from_utf8_lossy(
+      this, s == nullptr && len == 0 ? reinterpret_cast<const char *>(1) : s,
+      len);
+}
+
+String::String(lossy_t, const char16_t *s, std::size_t len) noexcept {
+  cxxbridge1$string$from_utf16_lossy(
+      this,
+      s == nullptr && len == 0 ? reinterpret_cast<const char16_t *>(2) : s,
+      len);
+}
+
+String String::lossy(const std::string &s) noexcept {
+  return String::lossy(s.data(), s.length());
+}
+
+String String::lossy(const char *s) noexcept {
+  assert(s != nullptr);
+  return String::lossy(s, std::strlen(s));
+}
+
+String String::lossy(const char *s, std::size_t len) noexcept {
+  assert(s != nullptr || len == 0);
+  return String(lossy_t{}, s, len);
+}
+
+String String::lossy(const char16_t *s) noexcept {
+  assert(s != nullptr);
+  assert(is_aligned<char16_t>(s));
+  return String(lossy_t{}, s, std::char_traits<char16_t>::length(s));
+}
+
+String String::lossy(const char16_t *s, std::size_t len) noexcept {
+  assert(s != nullptr || len == 0);
+  assert(is_aligned<char16_t>(s));
+  return String(lossy_t{}, s, len);
+}
+
+String &String::operator=(const String &other) & noexcept {
+  if (this != &other) {
+    cxxbridge1$string$drop(this);
+    cxxbridge1$string$clone(this, other);
+  }
+  return *this;
+}
+
+String &String::operator=(String &&other) & noexcept {
+  cxxbridge1$string$drop(this);
+  this->repr = other.repr;
+  cxxbridge1$string$new(&other);
+  return *this;
+}
+
+String::operator std::string() const {
+  return std::string(this->data(), this->size());
+}
+
+const char *String::data() const noexcept {
+  return cxxbridge1$string$ptr(this);
+}
+
+std::size_t String::size() const noexcept {
+  return cxxbridge1$string$len(this);
+}
+
+std::size_t String::length() const noexcept {
+  return cxxbridge1$string$len(this);
+}
+
+bool String::empty() const noexcept { return this->size() == 0; }
+
+const char *String::c_str() noexcept {
+  auto len = this->length();
+  cxxbridge1$string$reserve_additional(this, 1);
+  auto ptr = this->data();
+  const_cast<char *>(ptr)[len] = '\0';
+  return ptr;
+}
+
+std::size_t String::capacity() const noexcept {
+  return cxxbridge1$string$capacity(this);
+}
+
+void String::reserve(std::size_t new_cap) noexcept {
+  cxxbridge1$string$reserve_total(this, new_cap);
+}
+
+String::iterator String::begin() noexcept {
+  return const_cast<char *>(this->data());
+}
+
+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(); }
+
+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) {}
+
+std::ostream &operator<<(std::ostream &os, const String &s) {
+  os.write(s.data(), static_cast<std::streamsize>(s.size()));
+  return os;
+}
+
+Str::Str() noexcept { cxxbridge1$str$new(this); }
+
+Str::Str(const String &s) noexcept { cxxbridge1$str$ref(this, &s); }
+
+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) { initStr(this, s.data(), s.length()); }
+
+Str::Str(const char *s) {
+  assert(s != nullptr);
+  initStr(this, s, std::strlen(s));
+}
+
+Str::Str(const char *s, std::size_t len) {
+  assert(s != nullptr || len == 0);
+  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());
+}
+
+#if __cplusplus >= 201703L
+Str::operator std::string_view() const {
+  return std::string_view(this->data(), this->size());
+}
+#endif
+
+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(); }
+
+bool Str::empty() const noexcept { return this->size() == 0; }
+
+Str::const_iterator Str::begin() const noexcept { return this->cbegin(); }
+
+Str::const_iterator Str::end() const noexcept { return this->cend(); }
+
+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(), static_cast<std::streamsize>(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()");
+
+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::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(other.msg ? errorCopy(other.msg, other.len) : nullptr),
+      len(other.len) {}
+
+Error::Error(Error &&other) noexcept
+    : std::exception(std::move(other)), msg(other.msg), len(other.len) {
+  other.msg = nullptr;
+  other.len = 0;
+}
+
+Error::~Error() noexcept { delete[] this->msg; }
+
+Error &Error::operator=(const Error &other) & {
+  if (this != &other) {
+    std::exception::operator=(other);
+    delete[] this->msg;
+    this->msg = nullptr;
+    if (other.msg) {
+      this->msg = errorCopy(other.msg, other.len);
+      this->len = other.len;
+    }
+  }
+  return *this;
+}
+
+Error &Error::operator=(Error &&other) & noexcept {
+  std::exception::operator=(std::move(other));
+  delete[] this->msg;
+  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 {
+template <typename T>
+union MaybeUninit {
+  T value;
+  MaybeUninit() {}
+  ~MaybeUninit() {}
+};
+} // namespace
+
+namespace repr {
+struct PtrLen final {
+  void *ptr;
+  std::size_t len;
+};
+} // namespace repr
+
+extern "C" {
+repr::PtrLen cxxbridge1$exception(const char *, std::size_t len) noexcept;
+}
+
+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;
+// Similarly, on some platforms char may just be an alias for [u]int8_t.
+using char_if_unique =
+    typename std::conditional<std::is_same<char, uint8_t>::value ||
+                                  std::is_same<char, int8_t>::value,
+                              struct char_ignore, char>::type;
+
+class Fail final {
+  repr::PtrLen &throw$;
+
+public:
+  Fail(repr::PtrLen &throw$) noexcept : throw$(throw$) {}
+  void operator()(const char *) noexcept;
+  void operator()(const std::string &) noexcept;
+};
+
+void Fail::operator()(const char *catch$) noexcept {
+  throw$ = cxxbridge1$exception(catch$, std::strlen(catch$));
+}
+
+void Fail::operator()(const std::string &catch$) noexcept {
+  throw$ = cxxbridge1$exception(catch$.data(), catch$.length());
+}
+} // namespace detail
+
+} // namespace cxxbridge1
+} // namespace rust
+
+namespace {
+template <typename T>
+void destroy(T *ptr) {
+  ptr->~T();
+}
+} // namespace
+
+extern "C" {
+void cxxbridge1$unique_ptr$std$string$null(
+    std::unique_ptr<std::string> *ptr) noexcept {
+  new (ptr) std::unique_ptr<std::string>();
+}
+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 *cxxbridge1$unique_ptr$std$string$get(
+    const std::unique_ptr<std::string> &ptr) noexcept {
+  return ptr.get();
+}
+std::string *cxxbridge1$unique_ptr$std$string$release(
+    std::unique_ptr<std::string> &ptr) noexcept {
+  return ptr.release();
+}
+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)                                    \
+  std::vector<CXX_TYPE> *cxxbridge1$std$vector$##RUST_TYPE##$new() noexcept {  \
+    return new std::vector<CXX_TYPE>();                                        \
+  }                                                                            \
+  std::size_t cxxbridge1$std$vector$##RUST_TYPE##$size(                        \
+      const std::vector<CXX_TYPE> &s) noexcept {                               \
+    return s.size();                                                           \
+  }                                                                            \
+  CXX_TYPE *cxxbridge1$std$vector$##RUST_TYPE##$get_unchecked(                 \
+      std::vector<CXX_TYPE> *s, std::size_t pos) noexcept {                    \
+    return &(*s)[pos];                                                         \
+  }                                                                            \
+  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 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>                                                  \
+      *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>                                                        \
+      *cxxbridge1$unique_ptr$std$vector$##RUST_TYPE##$release(                 \
+          std::unique_ptr<std::vector<CXX_TYPE>> &ptr) noexcept {              \
+    return ptr.release();                                                      \
+  }                                                                            \
+  void cxxbridge1$unique_ptr$std$vector$##RUST_TYPE##$drop(                    \
+      std::unique_ptr<std::vector<CXX_TYPE>> *ptr) noexcept {                  \
+    ptr->~unique_ptr();                                                        \
+  }
+
+#define STD_VECTOR_TRIVIAL_OPS(RUST_TYPE, CXX_TYPE)                            \
+  void cxxbridge1$std$vector$##RUST_TYPE##$push_back(                          \
+      std::vector<CXX_TYPE> *v, CXX_TYPE *value) noexcept {                    \
+    v->push_back(std::move(*value));                                           \
+    destroy(value);                                                            \
+  }                                                                            \
+  void cxxbridge1$std$vector$##RUST_TYPE##$pop_back(std::vector<CXX_TYPE> *v,  \
+                                                    CXX_TYPE *out) noexcept {  \
+    new (out) CXX_TYPE(std::move(v->back()));                                  \
+    v->pop_back();                                                             \
+  }
+
+#define RUST_VEC_EXTERNS(RUST_TYPE, CXX_TYPE)                                  \
+  void cxxbridge1$rust_vec$##RUST_TYPE##$new(                                  \
+      rust::Vec<CXX_TYPE> *ptr) noexcept;                                      \
+  void cxxbridge1$rust_vec$##RUST_TYPE##$drop(                                 \
+      rust::Vec<CXX_TYPE> *ptr) noexcept;                                      \
+  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 *cxxbridge1$rust_vec$##RUST_TYPE##$data(                      \
+      const rust::Vec<CXX_TYPE> *ptr) noexcept;                                \
+  void cxxbridge1$rust_vec$##RUST_TYPE##$reserve_total(                        \
+      rust::Vec<CXX_TYPE> *ptr, std::size_t new_cap) noexcept;                 \
+  void cxxbridge1$rust_vec$##RUST_TYPE##$set_len(rust::Vec<CXX_TYPE> *ptr,     \
+                                                 std::size_t len) noexcept;    \
+  void cxxbridge1$rust_vec$##RUST_TYPE##$truncate(rust::Vec<CXX_TYPE> *ptr,    \
+                                                  std::size_t len) noexcept;
+
+#define RUST_VEC_OPS(RUST_TYPE, CXX_TYPE)                                      \
+  template <>                                                                  \
+  Vec<CXX_TYPE>::Vec() noexcept {                                              \
+    cxxbridge1$rust_vec$##RUST_TYPE##$new(this);                               \
+  }                                                                            \
+  template <>                                                                  \
+  void Vec<CXX_TYPE>::drop() noexcept {                                        \
+    return cxxbridge1$rust_vec$##RUST_TYPE##$drop(this);                       \
+  }                                                                            \
+  template <>                                                                  \
+  std::size_t Vec<CXX_TYPE>::size() const noexcept {                           \
+    return cxxbridge1$rust_vec$##RUST_TYPE##$len(this);                        \
+  }                                                                            \
+  template <>                                                                  \
+  std::size_t Vec<CXX_TYPE>::capacity() const noexcept {                       \
+    return cxxbridge1$rust_vec$##RUST_TYPE##$capacity(this);                   \
+  }                                                                            \
+  template <>                                                                  \
+  const CXX_TYPE *Vec<CXX_TYPE>::data() const noexcept {                       \
+    return cxxbridge1$rust_vec$##RUST_TYPE##$data(this);                       \
+  }                                                                            \
+  template <>                                                                  \
+  void Vec<CXX_TYPE>::reserve_total(std::size_t new_cap) noexcept {            \
+    cxxbridge1$rust_vec$##RUST_TYPE##$reserve_total(this, new_cap);            \
+  }                                                                            \
+  template <>                                                                  \
+  void Vec<CXX_TYPE>::set_len(std::size_t len) noexcept {                      \
+    cxxbridge1$rust_vec$##RUST_TYPE##$set_len(this, len);                      \
+  }                                                                            \
+  template <>                                                                  \
+  void Vec<CXX_TYPE>::truncate(std::size_t len) {                              \
+    cxxbridge1$rust_vec$##RUST_TYPE##$truncate(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, 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_TRIVIAL_STD_VECTOR(MACRO)                                     \
+  FOR_EACH_NUMERIC(MACRO)                                                      \
+  MACRO(usize, std::size_t)                                                    \
+  MACRO(isize, rust::isize)
+
+#define FOR_EACH_STD_VECTOR(MACRO)                                             \
+  FOR_EACH_TRIVIAL_STD_VECTOR(MACRO)                                           \
+  MACRO(string, std::string)
+
+#define FOR_EACH_RUST_VEC(MACRO)                                               \
+  FOR_EACH_NUMERIC(MACRO)                                                      \
+  MACRO(bool, bool)                                                            \
+  MACRO(char, rust::detail::char_if_unique)                                    \
+  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_TRIVIAL_STD_VECTOR(STD_VECTOR_TRIVIAL_OPS)
+FOR_EACH_RUST_VEC(RUST_VEC_EXTERNS)
+FOR_EACH_SHARED_PTR(SHARED_PTR_OPS)
+} // extern "C"
+
+namespace rust {
+inline namespace cxxbridge1 {
+FOR_EACH_RUST_VEC(RUST_VEC_OPS)
+} // namespace cxxbridge1
+} // namespace rust
diff --git a/src/cxx_string.rs b/src/cxx_string.rs
new file mode 100644 (file)
index 0000000..c58d8d8
--- /dev/null
@@ -0,0 +1,329 @@
+use crate::actually_private::Private;
+use crate::lossy;
+#[cfg(feature = "alloc")]
+use alloc::borrow::Cow;
+#[cfg(feature = "alloc")]
+use alloc::string::String;
+use core::cmp::Ordering;
+use core::ffi::{c_char, CStr};
+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 = "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$clear"]
+    fn string_clear(this: Pin<&mut CxxString>);
+    #[link_name = "cxxbridge1$cxx_string$reserve_total"]
+    fn string_reserve_total(this: Pin<&mut CxxString>, new_cap: usize);
+    #[link_name = "cxxbridge1$cxx_string$push"]
+    fn string_push(this: Pin<&mut CxxString>, ptr: *const u8, len: usize);
+}
+
+/// Binding to C++ `std::string`.
+///
+/// # Invariants
+///
+/// As an invariant of this API and the static analysis of the cxx::bridge
+/// macro, in Rust code we 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 `UniquePtr<CxxString>`.
+#[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].
+    ///
+    /// [size]: https://en.cppreference.com/w/cpp/string/basic_string/size
+    pub fn len(&self) -> usize {
+        unsafe { string_length(self) }
+    }
+
+    /// Returns true if `self` has a length of zero bytes.
+    ///
+    /// Matches the behavior of C++ [std::string::empty][empty].
+    ///
+    /// [empty]: https://en.cppreference.com/w/cpp/string/basic_string/empty
+    pub fn is_empty(&self) -> bool {
+        self.len() == 0
+    }
+
+    /// Returns a byte slice of this string's contents.
+    pub fn as_bytes(&self) -> &[u8] {
+        let data = self.as_ptr();
+        let len = self.len();
+        unsafe { slice::from_raw_parts(data, len) }
+    }
+
+    /// Produces a pointer to the first character of the string.
+    ///
+    /// Matches the behavior of C++ [std::string::data][data].
+    ///
+    /// Note that the return type may look like `const char *` but is not a
+    /// `const char *` in the typical C sense, as C++ strings may contain
+    /// internal null bytes. As such, the returned pointer only makes sense as a
+    /// string in combination with the length returned by [`len()`][len].
+    ///
+    /// Modifying the string data through this pointer has undefined behavior.
+    ///
+    /// [data]: https://en.cppreference.com/w/cpp/string/basic_string/data
+    /// [len]: #method.len
+    pub fn as_ptr(&self) -> *const u8 {
+        unsafe { string_data(self) }
+    }
+
+    /// Produces a nul-terminated string view of this string's contents.
+    ///
+    /// Matches the behavior of C++ [std::string::c_str][c_str].
+    ///
+    /// If this string contains no internal '\0' bytes, then
+    /// `self.as_c_str().count_bytes() == self.len()`. But if it does, the CStr
+    /// only refers to the part of the string up to the first nul byte.
+    ///
+    /// [c_str]: https://en.cppreference.com/w/cpp/string/basic_string/c_str
+    pub fn as_c_str(&self) -> &CStr {
+        // Since C++11, string[string.size()] is guaranteed to be \0.
+        unsafe { CStr::from_ptr(self.as_ptr().cast::<c_char>()) }
+    }
+
+    /// Validates that the C++ string contains UTF-8 data and produces a view of
+    /// it as a Rust &amp;str, otherwise an error.
+    pub fn to_str(&self) -> Result<&str, Utf8Error> {
+        str::from_utf8(self.as_bytes())
+    }
+
+    /// If the contents of the C++ string are valid UTF-8, this function returns
+    /// a view as a Cow::Borrowed &amp;str. Otherwise replaces any invalid UTF-8
+    /// sequences with the U+FFFD [replacement character] and returns a
+    /// Cow::Owned String.
+    ///
+    /// [replacement character]: char::REPLACEMENT_CHARACTER
+    #[cfg(feature = "alloc")]
+    #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
+    pub fn to_string_lossy(&self) -> Cow<str> {
+        String::from_utf8_lossy(self.as_bytes())
+    }
+
+    /// Removes all characters from the string.
+    ///
+    /// Matches the behavior of C++ [std::string::clear][clear].
+    ///
+    /// Note: **unlike** the guarantee of Rust's `std::string::String::clear`,
+    /// the C++ standard does not require that capacity is unchanged by this
+    /// operation. In practice existing implementations do not change the
+    /// capacity but all pointers, references, and iterators into the string
+    /// contents are nevertheless invalidated.
+    ///
+    /// [clear]: https://en.cppreference.com/w/cpp/string/basic_string/clear
+    pub fn clear(self: Pin<&mut Self>) {
+        unsafe { string_clear(self) }
+    }
+
+    /// Ensures that this string's capacity is at least `additional` bytes
+    /// larger than its length.
+    ///
+    /// The capacity may be increased by more than `additional` bytes if it
+    /// chooses, to amortize the cost of frequent reallocations.
+    ///
+    /// **The meaning of the argument is not the same as
+    /// [std::string::reserve][reserve] in C++.** The C++ standard library and
+    /// Rust standard library both have a `reserve` method on strings, but in
+    /// C++ code the argument always refers to total capacity, whereas in Rust
+    /// code it always refers to additional capacity. This API on `CxxString`
+    /// follows the Rust convention, the same way that for the length accessor
+    /// we use the Rust conventional `len()` naming and not C++ `size()` or
+    /// `length()`.
+    ///
+    /// # Panics
+    ///
+    /// Panics if the new capacity overflows usize.
+    ///
+    /// [reserve]: https://en.cppreference.com/w/cpp/string/basic_string/reserve
+    pub fn reserve(self: Pin<&mut Self>, additional: usize) {
+        let new_cap = self
+            .len()
+            .checked_add(additional)
+            .expect("CxxString capacity overflow");
+        unsafe { string_reserve_total(self, new_cap) }
+    }
+
+    /// Appends a given string slice onto the end of this C++ string.
+    pub fn push_str(self: Pin<&mut Self>, s: &str) {
+        self.push_bytes(s.as_bytes());
+    }
+
+    /// 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 {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        lossy::display(self.as_bytes(), f)
+    }
+}
+
+impl Debug for CxxString {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        lossy::debug(self.as_bytes(), f)
+    }
+}
+
+impl PartialEq for CxxString {
+    fn eq(&self, other: &Self) -> bool {
+        self.as_bytes() == other.as_bytes()
+    }
+}
+
+impl PartialEq<CxxString> for str {
+    fn eq(&self, other: &CxxString) -> bool {
+        self.as_bytes() == other.as_bytes()
+    }
+}
+
+impl PartialEq<str> for CxxString {
+    fn eq(&self, other: &str) -> bool {
+        self.as_bytes() == other.as_bytes()
+    }
+}
+
+impl Eq for CxxString {}
+
+impl PartialOrd for CxxString {
+    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
+        Some(self.cmp(other))
+    }
+}
+
+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);
+    }
+}
+
+impl fmt::Write for Pin<&mut CxxString> {
+    fn write_str(&mut self, s: &str) -> fmt::Result {
+        self.as_mut().push_str(s);
+        Ok(())
+    }
+}
+
+#[cfg(feature = "std")]
+impl std::io::Write for Pin<&mut CxxString> {
+    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
+        self.as_mut().push_bytes(buf);
+        Ok(buf.len())
+    }
+
+    fn flush(&mut self) -> std::io::Result<()> {
+        Ok(())
+    }
+}
+
+#[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]>,
+}
+
+#[allow(missing_docs)]
+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();
+        unsafe {
+            let this = &mut *self.space.as_mut_ptr().cast::<MaybeUninit<CxxString>>();
+            string_init(this, value.as_ptr(), value.len());
+            Pin::new_unchecked(&mut *this.as_mut_ptr())
+        }
+    }
+}
+
+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
new file mode 100644 (file)
index 0000000..53ae071
--- /dev/null
@@ -0,0 +1,503 @@
+//! 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 crate::unique_ptr::UniquePtr;
+use core::ffi::c_void;
+use core::fmt::{self, Debug};
+use core::iter::FusedIterator;
+use core::marker::{PhantomData, PhantomPinned};
+use core::mem::{self, ManuallyDrop, MaybeUninit};
+use core::pin::Pin;
+use core::slice;
+
+/// Binding to C++ `std::vector<T, std::allocator<T>>`.
+///
+/// # Invariants
+///
+/// As an invariant of this API and the static analysis of the cxx::bridge
+/// macro, in Rust code we 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>>`.
+#[repr(C, packed)]
+pub struct CxxVector<T> {
+    // A thing, because repr(C) structs are not allowed to consist exclusively
+    // of PhantomData fields.
+    _void: [c_void; 0],
+    // The conceptual vector elements to ensure that autotraits are propagated
+    // correctly, e.g. CxxVector is UnwindSafe iff T is.
+    _elements: PhantomData<[T]>,
+    // Prevent unpin operation from Pin<&mut CxxVector<T>> to &mut CxxVector<T>.
+    _pinned: PhantomData<PhantomPinned>,
+}
+
+impl<T> CxxVector<T>
+where
+    T: VectorElement,
+{
+    /// Constructs a new heap allocated vector, wrapped by UniquePtr.
+    ///
+    /// The C++ vector is default constructed.
+    pub fn new() -> UniquePtr<Self> {
+        unsafe { UniquePtr::from_raw(T::__vector_new()) }
+    }
+
+    /// Returns the number of elements in the vector.
+    ///
+    /// Matches the behavior of C++ [std::vector\<T\>::size][size].
+    ///
+    /// [size]: https://en.cppreference.com/w/cpp/container/vector/size
+    pub fn len(&self) -> usize {
+        T::__vector_size(self)
+    }
+
+    /// Returns true if the vector contains no elements.
+    ///
+    /// Matches the behavior of C++ [std::vector\<T\>::empty][empty].
+    ///
+    /// [empty]: https://en.cppreference.com/w/cpp/container/vector/empty
+    pub fn is_empty(&self) -> bool {
+        self.len() == 0
+    }
+
+    /// Returns a reference to an element at the given position, or `None` if
+    /// out of bounds.
+    pub fn get(&self, pos: usize) -> Option<&T> {
+        if pos < self.len() {
+            Some(unsafe { self.get_unchecked(pos) })
+        } else {
+            None
+        }
+    }
+
+    /// Returns a pinned mutable reference to an element at the given position,
+    /// or `None` if out of bounds.
+    ///
+    /// This method cannot be named "get\_mut" due to a conflict with
+    /// `Pin::get_mut`.
+    #[doc(alias = "get_mut")]
+    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
+    /// 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\[\] const][operator_at].
+    ///
+    /// [operator_at]: https://en.cppreference.com/w/cpp/container/vector/operator_at
+    pub unsafe fn get_unchecked(&self, pos: usize) -> &T {
+        let this = self as *const CxxVector<T> as *mut CxxVector<T>;
+        unsafe {
+            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
+    ///
+    /// This method cannot be named "get\_unchecked\_mut" due to a conflict with
+    /// `Pin::get_unchecked_mut`.
+    #[doc(alias = "get_unchecked_mut")]
+    pub unsafe fn index_unchecked_mut(self: Pin<&mut Self>, pos: usize) -> Pin<&mut T> {
+        unsafe {
+            let ptr = T::__get_unchecked(self.get_unchecked_mut(), pos);
+            Pin::new_unchecked(&mut *ptr)
+        }
+    }
+
+    /// Returns a slice to the underlying contiguous array of elements.
+    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
+            // and properly aligned data ptr. C++ standard does not guarantee
+            // that data() on a vector with size 0 would return a nonnull
+            // pointer or sufficiently aligned pointer, so using it would be
+            // undefined behavior. Create our own empty slice in Rust instead
+            // which upholds the invariants.
+            &[]
+        } else {
+            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 }
+    }
+
+    /// Appends an element to the back of the vector.
+    ///
+    /// Matches the behavior of C++ [std::vector\<T\>::push_back][push_back].
+    ///
+    /// [push_back]: https://en.cppreference.com/w/cpp/container/vector/push_back
+    pub fn push(self: Pin<&mut Self>, value: T)
+    where
+        T: ExternType<Kind = Trivial>,
+    {
+        let mut value = ManuallyDrop::new(value);
+        unsafe {
+            // C++ calls move constructor followed by destructor on `value`.
+            T::__push_back(self, &mut value);
+        }
+    }
+
+    /// Removes the last element from a vector and returns it, or `None` if the
+    /// vector is empty.
+    pub fn pop(self: Pin<&mut Self>) -> Option<T>
+    where
+        T: ExternType<Kind = Trivial>,
+    {
+        if self.is_empty() {
+            None
+        } else {
+            let mut value = MaybeUninit::uninit();
+            Some(unsafe {
+                T::__pop_back(self, &mut value);
+                value.assume_init()
+            })
+        }
+    }
+}
+
+/// Iterator over elements of a `CxxVector` by shared reference.
+///
+/// The iterator element type is `&'a T`.
+pub struct Iter<'a, T> {
+    v: &'a CxxVector<T>,
+    index: usize,
+}
+
+impl<'a, T> IntoIterator for &'a CxxVector<T>
+where
+    T: VectorElement,
+{
+    type Item = &'a T;
+    type IntoIter = Iter<'a, T>;
+
+    fn into_iter(self) -> Self::IntoIter {
+        self.iter()
+    }
+}
+
+impl<'a, T> Iterator for Iter<'a, T>
+where
+    T: VectorElement,
+{
+    type Item = &'a T;
+
+    fn next(&mut self) -> Option<Self::Item> {
+        let next = self.v.get(self.index)?;
+        self.index += 1;
+        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
+    }
+}
+
+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<'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<'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 {
+        formatter.debug_list().entries(self).finish()
+    }
+}
+
+/// 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 requires using [explicit shim trait impls],
+/// adding the line `impl CxxVector<MyType> {}` in the same `cxx::bridge` that
+/// defines `MyType`.
+///
+/// # 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.
+///
+/// [explicit shim trait impls]: https://cxx.rs/extern-c++.html#explicit-shim-trait-impls
+pub unsafe trait VectorElement: Sized {
+    #[doc(hidden)]
+    fn __typename(f: &mut fmt::Formatter) -> fmt::Result;
+    #[doc(hidden)]
+    fn __vector_new() -> *mut CxxVector<Self>;
+    #[doc(hidden)]
+    fn __vector_size(v: &CxxVector<Self>) -> usize;
+    #[doc(hidden)]
+    unsafe fn __get_unchecked(v: *mut CxxVector<Self>, pos: usize) -> *mut Self;
+    #[doc(hidden)]
+    unsafe fn __push_back(v: Pin<&mut CxxVector<Self>>, value: &mut ManuallyDrop<Self>) {
+        // Opaque C type vector elements do not get this method because they can
+        // never exist by value on the Rust side of the bridge.
+        let _ = v;
+        let _ = value;
+        unreachable!()
+    }
+    #[doc(hidden)]
+    unsafe fn __pop_back(v: Pin<&mut CxxVector<Self>>, out: &mut MaybeUninit<Self>) {
+        // Opaque C type vector elements do not get this method because they can
+        // never exist by value on the Rust side of the bridge.
+        let _ = v;
+        let _ = out;
+        unreachable!()
+    }
+    #[doc(hidden)]
+    fn __unique_ptr_null() -> MaybeUninit<*mut c_void>;
+    #[doc(hidden)]
+    unsafe fn __unique_ptr_raw(raw: *mut CxxVector<Self>) -> MaybeUninit<*mut c_void>;
+    #[doc(hidden)]
+    unsafe fn __unique_ptr_get(repr: MaybeUninit<*mut c_void>) -> *const CxxVector<Self>;
+    #[doc(hidden)]
+    unsafe fn __unique_ptr_release(repr: MaybeUninit<*mut c_void>) -> *mut CxxVector<Self>;
+    #[doc(hidden)]
+    unsafe fn __unique_ptr_drop(repr: MaybeUninit<*mut c_void>);
+}
+
+macro_rules! vector_element_by_value_methods {
+    (opaque, $segment:expr, $ty:ty) => {};
+    (trivial, $segment:expr, $ty:ty) => {
+        unsafe fn __push_back(v: Pin<&mut CxxVector<$ty>>, value: &mut ManuallyDrop<$ty>) {
+            extern "C" {
+                #[link_name = concat!("cxxbridge1$std$vector$", $segment, "$push_back")]
+                fn __push_back(_: Pin<&mut CxxVector<$ty>>, _: &mut ManuallyDrop<$ty>);
+            }
+            unsafe { __push_back(v, value) }
+        }
+        unsafe fn __pop_back(v: Pin<&mut CxxVector<$ty>>, out: &mut MaybeUninit<$ty>) {
+            extern "C" {
+                #[link_name = concat!("cxxbridge1$std$vector$", $segment, "$pop_back")]
+                fn __pop_back(_: Pin<&mut CxxVector<$ty>>, _: &mut MaybeUninit<$ty>);
+            }
+            unsafe { __pop_back(v, out) }
+        }
+    };
+}
+
+macro_rules! impl_vector_element {
+    ($kind:ident, $segment:expr, $name:expr, $ty:ty) => {
+        const_assert_eq!(0, mem::size_of::<CxxVector<$ty>>());
+        const_assert_eq!(1, mem::align_of::<CxxVector<$ty>>());
+
+        unsafe impl VectorElement for $ty {
+            fn __typename(f: &mut fmt::Formatter) -> fmt::Result {
+                f.write_str($name)
+            }
+            fn __vector_new() -> *mut CxxVector<Self> {
+                extern "C" {
+                    #[link_name = concat!("cxxbridge1$std$vector$", $segment, "$new")]
+                    fn __vector_new() -> *mut CxxVector<$ty>;
+                }
+                unsafe { __vector_new() }
+            }
+            fn __vector_size(v: &CxxVector<$ty>) -> usize {
+                extern "C" {
+                    #[link_name = concat!("cxxbridge1$std$vector$", $segment, "$size")]
+                    fn __vector_size(_: &CxxVector<$ty>) -> usize;
+                }
+                unsafe { __vector_size(v) }
+            }
+            unsafe fn __get_unchecked(v: *mut CxxVector<$ty>, pos: usize) -> *mut $ty {
+                extern "C" {
+                    #[link_name = concat!("cxxbridge1$std$vector$", $segment, "$get_unchecked")]
+                    fn __get_unchecked(_: *mut CxxVector<$ty>, _: usize) -> *mut $ty;
+                }
+                unsafe { __get_unchecked(v, pos) }
+            }
+            vector_element_by_value_methods!($kind, $segment, $ty);
+            fn __unique_ptr_null() -> MaybeUninit<*mut c_void> {
+                extern "C" {
+                    #[link_name = concat!("cxxbridge1$unique_ptr$std$vector$", $segment, "$null")]
+                    fn __unique_ptr_null(this: *mut MaybeUninit<*mut c_void>);
+                }
+                let mut repr = MaybeUninit::uninit();
+                unsafe { __unique_ptr_null(&mut repr) }
+                repr
+            }
+            unsafe fn __unique_ptr_raw(raw: *mut CxxVector<Self>) -> MaybeUninit<*mut c_void> {
+                extern "C" {
+                    #[link_name = concat!("cxxbridge1$unique_ptr$std$vector$", $segment, "$raw")]
+                    fn __unique_ptr_raw(this: *mut MaybeUninit<*mut c_void>, raw: *mut CxxVector<$ty>);
+                }
+                let mut repr = MaybeUninit::uninit();
+                unsafe { __unique_ptr_raw(&mut repr, raw) }
+                repr
+            }
+            unsafe fn __unique_ptr_get(repr: MaybeUninit<*mut c_void>) -> *const CxxVector<Self> {
+                extern "C" {
+                    #[link_name = concat!("cxxbridge1$unique_ptr$std$vector$", $segment, "$get")]
+                    fn __unique_ptr_get(this: *const MaybeUninit<*mut c_void>) -> *const CxxVector<$ty>;
+                }
+                unsafe { __unique_ptr_get(&repr) }
+            }
+            unsafe fn __unique_ptr_release(mut repr: MaybeUninit<*mut c_void>) -> *mut CxxVector<Self> {
+                extern "C" {
+                    #[link_name = concat!("cxxbridge1$unique_ptr$std$vector$", $segment, "$release")]
+                    fn __unique_ptr_release(this: *mut MaybeUninit<*mut c_void>) -> *mut CxxVector<$ty>;
+                }
+                unsafe { __unique_ptr_release(&mut repr) }
+            }
+            unsafe fn __unique_ptr_drop(mut repr: MaybeUninit<*mut c_void>) {
+                extern "C" {
+                    #[link_name = concat!("cxxbridge1$unique_ptr$std$vector$", $segment, "$drop")]
+                    fn __unique_ptr_drop(this: *mut MaybeUninit<*mut c_void>);
+                }
+                unsafe { __unique_ptr_drop(&mut repr) }
+            }
+        }
+    };
+}
+
+macro_rules! impl_vector_element_for_primitive {
+    ($ty:ident) => {
+        impl_vector_element!(trivial, stringify!($ty), stringify!($ty), $ty);
+    };
+}
+
+impl_vector_element_for_primitive!(u8);
+impl_vector_element_for_primitive!(u16);
+impl_vector_element_for_primitive!(u32);
+impl_vector_element_for_primitive!(u64);
+impl_vector_element_for_primitive!(usize);
+impl_vector_element_for_primitive!(i8);
+impl_vector_element_for_primitive!(i16);
+impl_vector_element_for_primitive!(i32);
+impl_vector_element_for_primitive!(i64);
+impl_vector_element_for_primitive!(isize);
+impl_vector_element_for_primitive!(f32);
+impl_vector_element_for_primitive!(f64);
+
+impl_vector_element!(opaque, "string", "CxxString", CxxString);
diff --git a/src/exception.rs b/src/exception.rs
new file mode 100644 (file)
index 0000000..9831f99
--- /dev/null
@@ -0,0 +1,32 @@
+#![cfg(feature = "alloc")]
+
+use alloc::boxed::Box;
+use core::fmt::{self, Display};
+
+#[cfg(error_in_core)]
+use core::error::Error as StdError;
+#[cfg(all(feature = "std", not(error_in_core)))]
+use std::error::Error as StdError;
+
+/// Exception thrown from an `extern "C++"` function.
+#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
+#[derive(Debug)]
+pub struct Exception {
+    pub(crate) what: Box<str>,
+}
+
+impl Display for Exception {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        f.write_str(&self.what)
+    }
+}
+
+#[cfg(any(error_in_core, feature = "std"))]
+impl StdError for Exception {}
+
+impl Exception {
+    #[allow(missing_docs)]
+    pub fn what(&self) -> &str {
+        &self.what
+    }
+}
diff --git a/src/extern_type.rs b/src/extern_type.rs
new file mode 100644 (file)
index 0000000..5ab856e
--- /dev/null
@@ -0,0 +1,225 @@
+use self::kind::{Kind, Opaque, Trivial};
+use crate::CxxString;
+#[cfg(feature = "alloc")]
+use alloc::string::String;
+
+/// A type for which the layout is determined by its C++ definition.
+///
+/// This trait serves the following two related purposes.
+///
+/// <br>
+///
+/// ## Safely unifying occurrences of the same extern type
+///
+/// `ExternType` makes it possible for CXX to safely share a consistent Rust
+/// type across multiple #\[cxx::bridge\] invocations that refer to a common
+/// extern C++ type.
+///
+/// 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. The CXX code generator will use an automatically
+/// generated `ExternType` impl emitted in file1 to statically verify that in
+/// file2 `crate::file1::ffi::Demo` really does refer to the C++ type
+/// `example::Demo` as expected in file2.
+///
+/// ```no_run
+/// // file1.rs
+/// # mod file1 {
+/// #[cxx::bridge(namespace = "example")]
+/// pub mod ffi {
+///     unsafe extern "C++" {
+///         type Demo;
+///
+///         fn create_demo() -> UniquePtr<Demo>;
+///     }
+/// }
+/// # }
+///
+/// // file2.rs
+/// #[cxx::bridge(namespace = "example")]
+/// pub mod ffi {
+///     unsafe extern "C++" {
+///         type Demo = crate::file1::ffi::Demo;
+///
+///         fn take_ref_demo(demo: &Demo);
+///     }
+/// }
+/// #
+/// # fn main() {}
+/// ```
+///
+/// <br><br>
+///
+/// ## Integrating with bindgen-generated types
+///
+/// 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.
+///
+/// ```no_run
+/// # const _: &str = stringify! {
+/// mod folly_sys;  // the bindgen-generated bindings
+/// # };
+/// # mod folly_sys {
+/// #     #[repr(transparent)]
+/// #     pub struct StringPiece([usize; 2]);
+/// # }
+///
+/// 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.
+/// #
+/// # fn main() {}
+/// ```
+pub unsafe trait ExternType {
+    /// A type-level representation of the type's C++ namespace and type name.
+    ///
+    /// This will always be defined using `type_id!` in the following form:
+    ///
+    /// ```
+    /// # struct TypeName;
+    /// # unsafe impl cxx::ExternType for TypeName {
+    /// type Id = cxx::type_id!("name::space::of::TypeName");
+    /// #     type Kind = cxx::kind::Opaque;
+    /// # }
+    /// ```
+    type Id;
+
+    /// Either [`cxx::kind::Opaque`] or [`cxx::kind::Trivial`].
+    ///
+    /// [`cxx::kind::Opaque`]: kind::Opaque
+    /// [`cxx::kind::Trivial`]: kind::Trivial
+    ///
+    /// 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.
+    ///
+    /// [move constructor is trivial]: https://en.cppreference.com/w/cpp/types/is_move_constructible
+    ///
+    /// If you believe your C++ type reflected by this ExternType impl is indeed
+    /// fine to hold by value and move in Rust, you can specify:
+    ///
+    /// ```
+    /// # struct TypeName;
+    /// # 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.
+    type Kind: Kind;
+}
+
+/// Marker types identifying Rust's knowledge about an extern C++ type.
+///
+/// These markers are used in the [`Kind`][ExternType::Kind] associated type in
+/// impls of the `ExternType` trait. Refer to the documentation of `Kind` for an
+/// overview of their purpose.
+pub mod kind {
+    use super::private;
+
+    /// An opaque type which cannot be passed or held by value within Rust.
+    ///
+    /// Rust's move semantics are such that every move is equivalent to a
+    /// memcpy. This is incompatible in general with C++'s constructor-based
+    /// move semantics, so a C++ type which has a destructor or nontrivial move
+    /// constructor must never exist by value in Rust. In CXX, such types are
+    /// called opaque C++ types.
+    ///
+    /// When passed across an FFI boundary, an opaque C++ type must be behind an
+    /// indirection such as a reference or UniquePtr.
+    pub enum Opaque {}
+
+    /// A type with trivial move constructor and no destructor, which can
+    /// therefore be owned and moved around in Rust code without requiring
+    /// indirection.
+    pub enum Trivial {}
+
+    #[allow(missing_docs)]
+    pub trait Kind: private::Sealed {}
+    impl Kind for Opaque {}
+    impl Kind for Trivial {}
+}
+
+mod private {
+    pub trait Sealed {}
+    impl Sealed for super::Opaque {}
+    impl Sealed for super::Trivial {}
+}
+
+#[doc(hidden)]
+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] $($(#[$($attr:tt)*])* $ty:path = $cxxpath:literal)*)*) => {
+        $($(
+            $(#[$($attr)*])*
+            unsafe impl ExternType for $ty {
+                #[allow(unused_attributes)] // incorrect lint; this doc(hidden) attr *is* respected by rustdoc
+                #[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"
+
+    #[cfg(feature = "alloc")]
+    #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
+    String = "rust::String"
+
+    [Opaque]
+    CxxString = "std::string"
+}
diff --git a/src/fmt.rs b/src/fmt.rs
new file mode 100644 (file)
index 0000000..69614f8
--- /dev/null
@@ -0,0 +1,16 @@
+use core::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/function.rs b/src/function.rs
new file mode 100644 (file)
index 0000000..f0a2010
--- /dev/null
@@ -0,0 +1,9 @@
+#![allow(missing_docs)]
+
+use core::ffi::c_void;
+
+#[repr(C)]
+pub struct FatFunction {
+    pub trampoline: *const c_void,
+    pub ptr: *const c_void,
+}
diff --git a/src/hash.rs b/src/hash.rs
new file mode 100644 (file)
index 0000000..ee349b4
--- /dev/null
@@ -0,0 +1,6 @@
+use core::hash::{BuildHasher as _, Hash};
+
+#[doc(hidden)]
+pub fn hash<V: Hash>(value: &V) -> usize {
+    foldhash::quality::FixedState::default().hash_one(value) as usize
+}
diff --git a/src/lib.rs b/src/lib.rs
new file mode 100644 (file)
index 0000000..ca1386d
--- /dev/null
@@ -0,0 +1,538 @@
+//! [![github]](https://github.com/dtolnay/cxx)&ensp;[![crates-io]](https://crates.io/crates/cxx)&ensp;[![docs-rs]](https://docs.rs/cxx)
+//!
+//! [github]: https://img.shields.io/badge/github-8da0cb?style=for-the-badge&labelColor=555555&logo=github
+//! [crates-io]: https://img.shields.io/badge/crates.io-fc8d62?style=for-the-badge&labelColor=555555&logo=rust
+//! [docs-rs]: https://img.shields.io/badge/docs.rs-66c2a5?style=for-the-badge&labelColor=555555&logo=docs.rs
+//!
+//! <br>
+//!
+//! This library provides a **safe** mechanism for calling C++ code from Rust
+//! and Rust code from C++, not subject to the many ways that things can go
+//! wrong when using bindgen or cbindgen to generate unsafe C-style bindings.
+//!
+//! This doesn't change the fact that 100% of C++ code is unsafe. When auditing
+//! a project, you would be on the hook for auditing all the unsafe Rust code
+//! and *all* the C++ code. The core safety claim under this new model is that
+//! auditing just the C++ side would be sufficient to catch all problems, i.e.
+//! the Rust side can be 100% safe.
+//!
+//! <br>
+//!
+//! *Compiler support: requires rustc 1.73+ 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
+//! embedded together in one Rust module (the next section shows an example).
+//! From this, CXX receives a complete picture of the boundary to perform static
+//! analyses against the types and function signatures to uphold both Rust's and
+//! C++'s invariants and requirements.
+//!
+//! If everything checks out statically, then CXX uses a pair of code generators
+//! to emit the relevant `extern "C"` signatures on both sides together with any
+//! necessary static assertions for later in the build process to verify
+//! correctness. On the Rust side this code generator is simply an attribute
+//! procedural macro. On the C++ side it can be a small Cargo build script if
+//! your build is managed by Cargo, or for other build systems like Bazel or
+//! Buck we provide a command line tool which generates the header and source
+//! file and should be easy to integrate.
+//!
+//! 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 types from whichever side they
+//! please, such as Rust's `String` or C++'s `std::string`, Rust's `Box` or
+//! C++'s `std::unique_ptr`, Rust's `Vec` or C++'s `std::vector`, etc in any
+//! combination. CXX guarantees an ABI-compatible signature that both sides
+//! understand, based on builtin bindings for key standard library types to
+//! expose an idiomatic API on those types to the other language. For example
+//! when manipulating a C++ string from Rust, its `len()` method becomes a call
+//! of the `size()` member function defined by C++; when manipulation a Rust
+//! string from C++, its `size()` member function calls Rust's `len()`.
+//!
+//! <br>
+//!
+//! # Example
+//!
+//! In this example we are writing a Rust application that wishes to take
+//! advantage of an existing C++ client for 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 chunks, or fragments of a file spread across memory for
+//! some other reason.
+//!
+//! A runnable version of this example is provided under the *demo* directory of
+//! <https://github.com/dtolnay/cxx>. To try it out, run `cargo run` from that
+//! directory.
+//!
+//! ```no_run
+//! #[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. 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;
+//!     }
+//! }
+//! #
+//! # pub struct MultiBuf;
+//! #
+//! # fn next_chunk(_buf: &mut MultiBuf) -> &[u8] {
+//! #     unimplemented!()
+//! # }
+//! #
+//! # fn main() {}
+//! ```
+//!
+//! 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.
+//!
+//! Here are links to the complete set of source files involved in the demo:
+//!
+//! - [demo/src/main.rs](https://github.com/dtolnay/cxx/blob/master/demo/src/main.rs)
+//! - [demo/build.rs](https://github.com/dtolnay/cxx/blob/master/demo/build.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)
+//!
+//! To look at the code generated in both languages for the example by the CXX
+//! code generators:
+//!
+//! ```console
+//!    # run Rust code generator and print to stdout
+//!    # (requires https://github.com/dtolnay/cargo-expand)
+//! $ cargo expand --manifest-path demo/Cargo.toml
+//!
+//!    # run C++ code generator and print to stdout
+//! $ cargo run --manifest-path gen/cmd/Cargo.toml -- demo/src/main.rs
+//! ```
+//!
+//! <br>
+//!
+//! # Details
+//!
+//! As seen in the example, the language of the FFI boundary involves 3 kinds of
+//! items:
+//!
+//! - **Shared structs** &mdash; their fields are made visible to both
+//!   languages. The definition written within cxx::bridge is the single 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 `UniquePtr`. 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.
+//!
+//! 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
+//! right shims where necessary to make it all work.
+//!
+//! <br>
+//!
+//! # Comparison vs bindgen and cbindgen
+//!
+//! Notice that with CXX there is repetition of all the function signatures:
+//! they are typed out once where the implementation is defined (in C++ or Rust)
+//! and again inside the cxx::bridge module, though compile-time assertions
+//! guarantee these are kept in sync. This is different from [bindgen] and
+//! [cbindgen] where function signatures are typed by a human once and the tool
+//! consumes them in one language and emits them in the other language.
+//!
+//! [bindgen]: https://github.com/rust-lang/rust-bindgen
+//! [cbindgen]: https://github.com/eqrion/cbindgen/
+//!
+//! This is because CXX fills a somewhat different role. It is a lower level
+//! tool than bindgen or cbindgen in a sense; you can think of it as being a
+//! replacement for the concept of `extern "C"` signatures as we know them,
+//! rather than a replacement for a bindgen. It would be reasonable to build a
+//! higher level bindgen-like tool on top of CXX which consumes a C++ header
+//! and/or Rust module (and/or IDL like Thrift) as source of truth and generates
+//! the cxx::bridge, eliminating the repetition while leveraging the static
+//! analysis safety guarantees of CXX.
+//!
+//! But note in other ways CXX is higher level than the bindgens, with rich
+//! support for common standard library types. Frequently with bindgen when we
+//! are dealing with an idiomatic C++ API we would end up manually wrapping that
+//! API in C-style raw pointer functions, applying bindgen to get unsafe raw
+//! pointer Rust functions, and replicating the API again to expose those
+//! idiomatically in Rust. That's a much worse form of repetition because it is
+//! unsafe all the way through.
+//!
+//! By using a CXX bridge as the shared understanding between the languages,
+//! rather than `extern "C"` C-style signatures as the shared understanding,
+//! common FFI use cases become expressible using 100% safe code.
+//!
+//! It would also be reasonable to mix and match, using CXX bridge for the 95%
+//! of your FFI that is straightforward and doing the remaining few oddball
+//! signatures the old fashioned way with bindgen and cbindgen, if for some
+//! reason CXX's static restrictions get in the way. Please file an issue if you
+//! end up taking this approach so that we know what ways it would be worthwhile
+//! to make the tool more expressive.
+//!
+//! <br>
+//!
+//! # Cargo-based setup
+//!
+//! For builds that are orchestrated by Cargo, you will use a build script that
+//! runs CXX's C++ code generator and compiles the resulting C++ code along with
+//! any other C++ code for your crate.
+//!
+//! 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
+//!
+//! ```toml
+//! # Cargo.toml
+//!
+//! [build-dependencies]
+//! cxx-build = "1.0"
+//! ```
+//!
+//! ```no_run
+//! // build.rs
+//!
+//! fn main() {
+//!     cxx_build::bridge("src/main.rs")  // returns a cc::Build
+//!         .file("src/demo.cc")
+//!         .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");
+//! }
+//! ```
+//!
+//! <br><br>
+//!
+//! # Non-Cargo setup
+//!
+//! For use in non-Cargo builds like Bazel or Buck, CXX provides an alternate
+//! way of invoking the C++ code generator as a standalone command line tool.
+//! The tool is packaged as the `cxxbridge-cmd` crate on crates.io or can be
+//! built from the *gen/cmd* directory of <https://github.com/dtolnay/cxx>.
+//!
+//! ```bash
+//! $ cargo install cxxbridge-cmd
+//!
+//! $ cxxbridge src/main.rs --header > path/to/mybridge.h
+//! $ cxxbridge src/main.rs > path/to/mybridge.cc
+//! ```
+//!
+//! <br>
+//!
+//! # Safety
+//!
+//! Be aware that the design of this library is intentionally restrictive and
+//! opinionated! It isn't a goal to be powerful enough to handle arbitrary
+//! signatures in either language. Instead this project is about carving out a
+//! reasonably expressive set of functionality about which we can make useful
+//! safety guarantees today and maybe 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 are used to.
+//!
+//! Some of the considerations that go into ensuring safety are:
+//!
+//! - By design, our paired code generators work together to control both sides
+//!   of the FFI boundary. Ordinarily in Rust writing your own `extern "C"`
+//!   blocks is unsafe because the Rust compiler has no way to know whether the
+//!   signatures you've written actually match the signatures implemented in the
+//!   other language. With CXX we achieve that visibility and know what's on the
+//!   other side.
+//!
+//! - Our static analysis detects and prevents passing types by value that
+//!   shouldn't be passed by value from C++ to Rust, for example because they
+//!   may contain internal pointers that would be screwed up by Rust's move
+//!   behavior.
+//!
+//! - To many people's surprise, it is possible to have a struct in Rust and a
+//!   struct in C++ with exactly the same layout / fields / alignment /
+//!   everything, and still not the same ABI when passed by value. This is a
+//!   longstanding bindgen bug that leads to segfaults in absolutely
+//!   correct-looking code ([rust-lang/rust-bindgen#778]). CXX knows about this
+//!   and can insert the necessary zero-cost workaround transparently where
+//!   needed, so go ahead and pass your structs by value without worries. This
+//!   is made possible by owning both sides of the boundary rather than just
+//!   one.
+//!
+//! - Template instantiations: for example in order to expose a UniquePtr\<T\>
+//!   type in Rust backed by a real C++ unique\_ptr, we have a way of using a
+//!   Rust trait to connect the behavior back to the template instantiations
+//!   performed by the other language.
+//!
+//! [rust-lang/rust-bindgen#778]: https://github.com/rust-lang/rust-bindgen/issues/778
+//!
+//! <br>
+//!
+//! # Builtin types
+//!
+//! 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 functions.
+//!
+//! <table>
+//! <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;[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>
+//!
+//! The C++ API of the `rust` namespace is defined by the *include/cxx.h* file
+//! in <https://github.com/dtolnay/cxx>. You will need to include this header in
+//! your C++ code when working with those types.
+//!
+//! 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.
+//!
+//! <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>
+
+#![no_std]
+#![doc(html_root_url = "https://docs.rs/cxx/1.0.157")]
+#![cfg_attr(docsrs, feature(doc_cfg))]
+#![deny(
+    improper_ctypes,
+    improper_ctypes_definitions,
+    missing_docs,
+    unsafe_op_in_unsafe_fn
+)]
+#![warn(
+    clippy::alloc_instead_of_core,
+    clippy::std_instead_of_alloc,
+    clippy::std_instead_of_core
+)]
+#![allow(non_camel_case_types)]
+#![allow(
+    clippy::cast_possible_truncation,
+    clippy::doc_markdown,
+    clippy::elidable_lifetime_names,
+    clippy::items_after_statements,
+    clippy::len_without_is_empty,
+    clippy::missing_errors_doc,
+    clippy::missing_safety_doc,
+    clippy::must_use_candidate,
+    clippy::needless_doctest_main,
+    clippy::needless_lifetimes,
+    clippy::new_without_default,
+    clippy::ptr_as_ptr,
+    clippy::ptr_cast_constness,
+    clippy::ref_as_ptr,
+    clippy::uninlined_format_args
+)]
+
+#[cfg(built_with_cargo)]
+extern crate link_cplusplus;
+
+extern crate self as cxx;
+
+#[doc(hidden)]
+pub extern crate core;
+
+#[cfg(feature = "alloc")]
+#[doc(hidden)]
+pub extern crate alloc;
+
+#[cfg(not(feature = "alloc"))]
+extern crate core as alloc;
+
+#[cfg(feature = "std")]
+#[doc(hidden)]
+pub extern crate std;
+
+// Block inadvertent use of items from libstd, which does not otherwise produce
+// a compile-time error on edition 2018+.
+#[cfg(not(feature = "std"))]
+extern crate core as std;
+
+#[cfg(not(any(feature = "alloc", cxx_experimental_no_alloc)))]
+compile_error! {
+    r#"cxx support for no_alloc is incomplete and semver exempt; you must build with at least one of feature="std", feature="alloc", or RUSTFLAGS='--cfg cxx_experimental_no_alloc'"#
+}
+
+#[cfg(all(compile_error_if_alloc, feature = "alloc"))]
+compile_error! {
+    r#"feature="alloc" is unexpectedly enabled"#
+}
+
+#[cfg(all(compile_error_if_std, feature = "std"))]
+compile_error! {
+    r#"feature="std" is unexpectedly enabled"#
+}
+
+#[macro_use]
+mod macros;
+
+mod cxx_vector;
+mod exception;
+mod extern_type;
+mod fmt;
+mod function;
+mod hash;
+mod lossy;
+pub mod memory;
+mod opaque;
+mod result;
+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_vector::CxxVector;
+#[cfg(feature = "alloc")]
+#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
+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;
+
+/// Synonym for `CxxString`.
+///
+/// To avoid confusion with Rust's standard library string you probably
+/// shouldn't import this type with `use`. Instead, write `cxx::String`, or
+/// import and use `CxxString`.
+pub type String = CxxString;
+
+/// Synonym for `CxxVector`.
+///
+/// To avoid confusion with Rust's standard library vector you probably
+/// shouldn't import this type with `use`. Instead, write `cxx::Vector<T>`, or
+/// import and use `CxxVector`.
+pub type Vector<T> = CxxVector<T>;
+
+// Not public API.
+#[doc(hidden)]
+pub mod private {
+    pub use crate::cxx_vector::VectorElement;
+    pub use crate::extern_type::{verify_extern_kind, verify_extern_type};
+    pub use crate::function::FatFunction;
+    pub use crate::hash::hash;
+    pub use crate::opaque::Opaque;
+    #[cfg(feature = "alloc")]
+    pub use crate::result::{r#try, Result};
+    pub use crate::rust_slice::RustSlice;
+    pub use crate::rust_str::RustStr;
+    #[cfg(feature = "alloc")]
+    pub use crate::rust_string::RustString;
+    pub use crate::rust_type::{ImplBox, ImplVec, RustType};
+    #[cfg(feature = "alloc")]
+    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::prevent_unwind;
+    pub use crate::weak_ptr::WeakPtrTarget;
+    pub use core::{concat, module_path};
+    pub use cxxbridge_macro::type_id;
+}
+
+mod actually_private {
+    pub trait Private {}
+}
+
+macro_rules! chars {
+    ($($ch:ident)*) => {
+        $(
+            #[doc(hidden)]
+            pub enum $ch {}
+        )*
+    };
+}
+
+chars! {
+    _0 _1 _2 _3 _4 _5 _6 _7 _8 _9
+    A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
+    a b c d e f g h i j k l m n o p q r s t u v w x y z
+    __ // underscore
+}
+
+#[repr(transparent)]
+struct void(#[allow(dead_code)] core::ffi::c_void);
diff --git a/src/lossy.rs b/src/lossy.rs
new file mode 100644 (file)
index 0000000..0140392
--- /dev/null
@@ -0,0 +1,67 @@
+use core::char;
+use core::fmt::{self, Write as _};
+use core::str;
+
+pub(crate) fn display(mut bytes: &[u8], f: &mut fmt::Formatter) -> fmt::Result {
+    loop {
+        match str::from_utf8(bytes) {
+            Ok(valid) => return f.write_str(valid),
+            Err(utf8_error) => {
+                let valid_up_to = utf8_error.valid_up_to();
+                let valid = unsafe { str::from_utf8_unchecked(&bytes[..valid_up_to]) };
+                f.write_str(valid)?;
+                f.write_char(char::REPLACEMENT_CHARACTER)?;
+                if let Some(error_len) = utf8_error.error_len() {
+                    bytes = &bytes[valid_up_to + error_len..];
+                } else {
+                    return Ok(());
+                }
+            }
+        }
+    }
+}
+
+pub(crate) fn debug(mut bytes: &[u8], f: &mut fmt::Formatter) -> fmt::Result {
+    f.write_char('"')?;
+
+    while !bytes.is_empty() {
+        let from_utf8_result = str::from_utf8(bytes);
+        let valid = match from_utf8_result {
+            Ok(valid) => valid,
+            Err(utf8_error) => {
+                let valid_up_to = utf8_error.valid_up_to();
+                unsafe { str::from_utf8_unchecked(&bytes[..valid_up_to]) }
+            }
+        };
+
+        let mut written = 0;
+        for (i, ch) in valid.char_indices() {
+            let esc = ch.escape_debug();
+            if esc.len() != 1 && ch != '\'' {
+                f.write_str(&valid[written..i])?;
+                for ch in esc {
+                    f.write_char(ch)?;
+                }
+                written = i + ch.len_utf8();
+            }
+        }
+        f.write_str(&valid[written..])?;
+
+        match from_utf8_result {
+            Ok(_valid) => break,
+            Err(utf8_error) => {
+                let end_of_broken = if let Some(error_len) = utf8_error.error_len() {
+                    valid.len() + error_len
+                } else {
+                    bytes.len()
+                };
+                for b in &bytes[valid.len()..end_of_broken] {
+                    write!(f, "\\x{:02x}", b)?;
+                }
+                bytes = &bytes[end_of_broken..];
+            }
+        }
+    }
+
+    f.write_char('"')
+}
diff --git a/src/macros/assert.rs b/src/macros/assert.rs
new file mode 100644 (file)
index 0000000..5d5ea9e
--- /dev/null
@@ -0,0 +1,7 @@
+#[macro_export]
+#[doc(hidden)]
+macro_rules! const_assert_eq {
+    ($left:expr, $right:expr $(,)?) => {
+        const _: [(); $left] = [(); $right];
+    };
+}
diff --git a/src/macros/mod.rs b/src/macros/mod.rs
new file mode 100644 (file)
index 0000000..b070c05
--- /dev/null
@@ -0,0 +1,2 @@
+#[macro_use]
+mod assert;
diff --git a/src/memory.rs b/src/memory.rs
new file mode 100644 (file)
index 0000000..fd8df12
--- /dev/null
@@ -0,0 +1,9 @@
+//! 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;
+pub use crate::weak_ptr::WeakPtrTarget;
+#[doc(no_inline)]
+pub use cxx::{SharedPtr, UniquePtr};
diff --git a/src/opaque.rs b/src/opaque.rs
new file mode 100644 (file)
index 0000000..11c011c
--- /dev/null
@@ -0,0 +1,33 @@
+#![allow(missing_docs)]
+
+use crate::void;
+use core::cell::UnsafeCell;
+use core::marker::{PhantomData, PhantomPinned};
+use core::mem;
+use core::panic::RefUnwindSafe;
+
+// . size = 0
+// . align = 1
+// . ffi-safe
+// . !Send
+// . !Sync
+// . !Unpin
+// . not readonly
+// . unwind-safe
+#[repr(C, packed)]
+pub struct Opaque {
+    _private: [*const void; 0],
+    _pinned: PhantomData<PhantomPinned>,
+    _mutable: SyncUnsafeCell<PhantomData<()>>,
+}
+
+impl RefUnwindSafe for Opaque {}
+
+// TODO: https://github.com/rust-lang/rust/issues/95439
+#[repr(transparent)]
+struct SyncUnsafeCell<T>(UnsafeCell<T>);
+
+unsafe impl<T> Sync for SyncUnsafeCell<T> {}
+
+const_assert_eq!(0, mem::size_of::<Opaque>());
+const_assert_eq!(1, mem::align_of::<Opaque>());
diff --git a/src/result.rs b/src/result.rs
new file mode 100644 (file)
index 0000000..e93c8e6
--- /dev/null
@@ -0,0 +1,70 @@
+#![cfg(feature = "alloc")]
+#![allow(missing_docs)]
+
+use crate::exception::Exception;
+use alloc::boxed::Box;
+use alloc::string::{String, ToString};
+use core::fmt::Display;
+use core::ptr::{self, NonNull};
+use core::result::Result as StdResult;
+use core::slice;
+use core::str;
+
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub(crate) struct PtrLen {
+    pub ptr: NonNull<u8>,
+    pub len: usize,
+}
+
+#[repr(C)]
+pub union Result {
+    err: PtrLen,
+    ok: *const u8, // null
+}
+
+pub unsafe fn r#try<T, E>(ret: *mut T, result: StdResult<T, E>) -> Result
+where
+    E: Display,
+{
+    match result {
+        Ok(ok) => {
+            unsafe { ptr::write(ret, ok) }
+            Result { ok: ptr::null() }
+        }
+        Err(err) => unsafe { to_c_error(err.to_string()) },
+    }
+}
+
+unsafe fn to_c_error(msg: String) -> Result {
+    let mut msg = msg;
+    unsafe { msg.as_mut_vec() }.push(b'\0');
+    let ptr = msg.as_ptr();
+    let len = msg.len();
+
+    extern "C" {
+        #[link_name = "cxxbridge1$error"]
+        fn error(ptr: *const u8, len: usize) -> NonNull<u8>;
+    }
+
+    let copy = unsafe { error(ptr, len) };
+    let err = PtrLen { ptr: copy, len };
+    Result { err }
+}
+
+impl Result {
+    pub unsafe fn exception(self) -> StdResult<(), Exception> {
+        unsafe {
+            if self.ok.is_null() {
+                Ok(())
+            } else {
+                let err = self.err;
+                let slice = slice::from_raw_parts_mut(err.ptr.as_ptr(), err.len);
+                let s = str::from_utf8_unchecked_mut(slice);
+                Err(Exception {
+                    what: Box::from_raw(s),
+                })
+            }
+        }
+    }
+}
diff --git a/src/rust_slice.rs b/src/rust_slice.rs
new file mode 100644 (file)
index 0000000..0696311
--- /dev/null
@@ -0,0 +1,66 @@
+#![allow(missing_docs)]
+
+use core::mem::{self, MaybeUninit};
+use core::ptr::{self, NonNull};
+use core::slice;
+
+// ABI compatible with C++ rust::Slice<T> (not necessarily &[T]).
+#[repr(C)]
+pub struct RustSlice {
+    repr: [MaybeUninit<usize>; mem::size_of::<NonNull<[()]>>() / mem::size_of::<usize>()],
+}
+
+impl RustSlice {
+    pub fn from_ref<T>(slice: &[T]) -> Self {
+        let ptr = NonNull::from(slice).cast::<T>();
+        let len = slice.len();
+        Self::from_raw_parts(ptr, len)
+    }
+
+    pub fn from_mut<T>(slice: &mut [T]) -> Self {
+        let ptr = NonNull::from(&mut *slice).cast::<T>();
+        let len = slice.len();
+        Self::from_raw_parts(ptr, len)
+    }
+
+    pub unsafe fn as_slice<'a, T>(self) -> &'a [T] {
+        let ptr = self.as_non_null_ptr().as_ptr();
+        let len = self.len();
+        unsafe { slice::from_raw_parts(ptr, len) }
+    }
+
+    pub unsafe fn as_mut_slice<'a, T>(self) -> &'a mut [T] {
+        let ptr = self.as_non_null_ptr().as_ptr();
+        let len = self.len();
+        unsafe { slice::from_raw_parts_mut(ptr, len) }
+    }
+
+    pub(crate) fn from_raw_parts<T>(ptr: NonNull<T>, len: usize) -> Self {
+        // TODO: use NonNull::from_raw_parts(ptr.cast(), len) when stable.
+        // https://doc.rust-lang.org/nightly/std/ptr/struct.NonNull.html#method.from_raw_parts
+        // https://github.com/rust-lang/rust/issues/81513
+        let ptr = ptr::slice_from_raw_parts_mut(ptr.as_ptr().cast(), len);
+        unsafe { mem::transmute::<NonNull<[()]>, RustSlice>(NonNull::new_unchecked(ptr)) }
+    }
+
+    pub(crate) fn as_non_null_ptr<T>(&self) -> NonNull<T> {
+        let rust_slice = RustSlice { repr: self.repr };
+        let repr = unsafe { mem::transmute::<RustSlice, NonNull<[()]>>(rust_slice) };
+        repr.cast()
+    }
+
+    pub(crate) fn len(&self) -> usize {
+        let rust_slice = RustSlice { repr: self.repr };
+        let repr = unsafe { mem::transmute::<RustSlice, NonNull<[()]>>(rust_slice) };
+        // TODO: use repr.len() when stable.
+        // https://doc.rust-lang.org/nightly/std/ptr/struct.NonNull.html#method.len
+        // https://github.com/rust-lang/rust/issues/71146
+        unsafe { repr.as_ref() }.len()
+    }
+}
+
+const_assert_eq!(mem::size_of::<NonNull<[()]>>(), mem::size_of::<RustSlice>());
+const_assert_eq!(
+    mem::align_of::<NonNull<[()]>>(),
+    mem::align_of::<RustSlice>(),
+);
diff --git a/src/rust_str.rs b/src/rust_str.rs
new file mode 100644 (file)
index 0000000..b1b46e3
--- /dev/null
@@ -0,0 +1,28 @@
+#![allow(missing_docs)]
+
+use core::mem::{self, MaybeUninit};
+use core::ptr::NonNull;
+use core::str;
+
+// ABI compatible with C++ rust::Str (not necessarily &str).
+#[repr(C)]
+pub struct RustStr {
+    repr: [MaybeUninit<usize>; mem::size_of::<NonNull<str>>() / mem::size_of::<usize>()],
+}
+
+impl RustStr {
+    pub fn from(repr: &str) -> Self {
+        let repr = NonNull::from(repr);
+        unsafe { mem::transmute::<NonNull<str>, RustStr>(repr) }
+    }
+
+    pub unsafe fn as_str<'a>(self) -> &'a str {
+        unsafe {
+            let repr = mem::transmute::<RustStr, NonNull<str>>(self);
+            &*repr.as_ptr()
+        }
+    }
+}
+
+const_assert_eq!(mem::size_of::<NonNull<str>>(), mem::size_of::<RustStr>());
+const_assert_eq!(mem::align_of::<NonNull<str>>(), mem::align_of::<RustStr>());
diff --git a/src/rust_string.rs b/src/rust_string.rs
new file mode 100644 (file)
index 0000000..0e0c5a8
--- /dev/null
@@ -0,0 +1,48 @@
+#![cfg(feature = "alloc")]
+#![allow(missing_docs)]
+
+use alloc::string::String;
+use core::mem::{self, MaybeUninit};
+use core::ptr;
+
+// ABI compatible with C++ rust::String (not necessarily alloc::string::String).
+#[repr(C)]
+pub struct RustString {
+    repr: [MaybeUninit<usize>; mem::size_of::<String>() / mem::size_of::<usize>()],
+}
+
+impl RustString {
+    pub fn from(s: String) -> Self {
+        unsafe { mem::transmute::<String, RustString>(s) }
+    }
+
+    pub fn from_ref(s: &String) -> &Self {
+        unsafe { &*(s as *const String as *const RustString) }
+    }
+
+    pub fn from_mut(s: &mut String) -> &mut Self {
+        unsafe { &mut *(s as *mut String as *mut RustString) }
+    }
+
+    pub fn into_string(self) -> String {
+        unsafe { mem::transmute::<RustString, String>(self) }
+    }
+
+    pub fn as_string(&self) -> &String {
+        unsafe { &*(self as *const RustString as *const String) }
+    }
+
+    pub fn as_mut_string(&mut self) -> &mut String {
+        unsafe { &mut *(self as *mut RustString as *mut String) }
+    }
+}
+
+impl Drop for RustString {
+    fn drop(&mut self) {
+        unsafe { ptr::drop_in_place(self.as_mut_string()) }
+    }
+}
+
+const_assert_eq!(mem::size_of::<[usize; 3]>(), mem::size_of::<RustString>());
+const_assert_eq!(mem::size_of::<String>(), mem::size_of::<RustString>());
+const_assert_eq!(mem::align_of::<String>(), mem::align_of::<RustString>());
diff --git a/src/rust_type.rs b/src/rust_type.rs
new file mode 100644 (file)
index 0000000..eacb530
--- /dev/null
@@ -0,0 +1,5 @@
+#![allow(missing_docs)]
+
+pub unsafe trait RustType {}
+pub unsafe trait ImplBox {}
+pub unsafe trait ImplVec {}
diff --git a/src/rust_vec.rs b/src/rust_vec.rs
new file mode 100644 (file)
index 0000000..acb7e89
--- /dev/null
@@ -0,0 +1,115 @@
+#![cfg(feature = "alloc")]
+#![allow(missing_docs)]
+
+use crate::rust_string::RustString;
+use alloc::string::String;
+use alloc::vec::Vec;
+use core::ffi::c_void;
+use core::marker::PhantomData;
+use core::mem::{self, ManuallyDrop, MaybeUninit};
+use core::ptr;
+
+// ABI compatible with C++ rust::Vec<T> (not necessarily alloc::vec::Vec<T>).
+#[repr(C)]
+pub struct RustVec<T> {
+    repr: [MaybeUninit<usize>; mem::size_of::<Vec<c_void>>() / mem::size_of::<usize>()],
+    marker: PhantomData<Vec<T>>,
+}
+
+impl<T> RustVec<T> {
+    pub fn new() -> Self {
+        Self::from(Vec::new())
+    }
+
+    pub fn from(v: Vec<T>) -> Self {
+        unsafe { mem::transmute::<Vec<T>, RustVec<T>>(v) }
+    }
+
+    pub fn from_ref(v: &Vec<T>) -> &Self {
+        unsafe { &*(v as *const Vec<T> as *const RustVec<T>) }
+    }
+
+    pub fn from_mut(v: &mut Vec<T>) -> &mut Self {
+        unsafe { &mut *(v as *mut Vec<T> as *mut RustVec<T>) }
+    }
+
+    pub fn into_vec(self) -> Vec<T> {
+        unsafe { mem::transmute::<RustVec<T>, Vec<T>>(self) }
+    }
+
+    pub fn as_vec(&self) -> &Vec<T> {
+        unsafe { &*(self as *const RustVec<T> as *const Vec<T>) }
+    }
+
+    pub fn as_mut_vec(&mut self) -> &mut Vec<T> {
+        unsafe { &mut *(self as *mut RustVec<T> as *mut Vec<T>) }
+    }
+
+    pub fn len(&self) -> usize {
+        self.as_vec().len()
+    }
+
+    pub fn capacity(&self) -> usize {
+        self.as_vec().capacity()
+    }
+
+    pub fn as_ptr(&self) -> *const T {
+        self.as_vec().as_ptr()
+    }
+
+    pub fn reserve_total(&mut self, new_cap: usize) {
+        let vec = self.as_mut_vec();
+        if new_cap > vec.capacity() {
+            let additional = new_cap - vec.len();
+            vec.reserve(additional);
+        }
+    }
+
+    pub unsafe fn set_len(&mut self, len: usize) {
+        unsafe { self.as_mut_vec().set_len(len) }
+    }
+
+    pub fn truncate(&mut self, len: usize) {
+        self.as_mut_vec().truncate(len);
+    }
+}
+
+impl RustVec<RustString> {
+    pub fn from_vec_string(v: Vec<String>) -> Self {
+        let mut v = ManuallyDrop::new(v);
+        let ptr = v.as_mut_ptr().cast::<RustString>();
+        let len = v.len();
+        let cap = v.capacity();
+        Self::from(unsafe { Vec::from_raw_parts(ptr, len, cap) })
+    }
+
+    pub fn from_ref_vec_string(v: &Vec<String>) -> &Self {
+        Self::from_ref(unsafe { &*(v as *const Vec<String> as *const Vec<RustString>) })
+    }
+
+    pub fn from_mut_vec_string(v: &mut Vec<String>) -> &mut Self {
+        Self::from_mut(unsafe { &mut *(v as *mut Vec<String> as *mut Vec<RustString>) })
+    }
+
+    pub fn into_vec_string(self) -> Vec<String> {
+        let mut v = ManuallyDrop::new(self.into_vec());
+        let ptr = v.as_mut_ptr().cast::<String>();
+        let len = v.len();
+        let cap = v.capacity();
+        unsafe { Vec::from_raw_parts(ptr, len, cap) }
+    }
+
+    pub fn as_vec_string(&self) -> &Vec<String> {
+        unsafe { &*(self as *const RustVec<RustString> as *const Vec<String>) }
+    }
+
+    pub fn as_mut_vec_string(&mut self) -> &mut Vec<String> {
+        unsafe { &mut *(self as *mut RustVec<RustString> as *mut Vec<String>) }
+    }
+}
+
+impl<T> Drop for RustVec<T> {
+    fn drop(&mut self) {
+        unsafe { ptr::drop_in_place(self.as_mut_vec()) }
+    }
+}
diff --git a/src/shared_ptr.rs b/src/shared_ptr.rs
new file mode 100644 (file)
index 0000000..3d24042
--- /dev/null
@@ -0,0 +1,316 @@
+use crate::fmt::display;
+use crate::kind::Trivial;
+use crate::string::CxxString;
+use crate::weak_ptr::{WeakPtr, WeakPtrTarget};
+use crate::ExternType;
+use core::cmp::Ordering;
+use core::ffi::c_void;
+use core::fmt::{self, Debug, Display};
+use core::hash::{Hash, Hasher};
+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: [MaybeUninit<*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()
+        }
+    }
+}
+
+// SharedPtr is not a self-referential type and is safe to move out of a Pin,
+// regardless whether the pointer's target is Unpin.
+impl<T> Unpin for SharedPtr<T> where T: SharedPtrTarget {}
+
+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),
+        }
+    }
+}
+
+impl<T> PartialEq for SharedPtr<T>
+where
+    T: PartialEq + SharedPtrTarget,
+{
+    fn eq(&self, other: &Self) -> bool {
+        self.as_ref() == other.as_ref()
+    }
+}
+
+impl<T> Eq for SharedPtr<T> where T: Eq + SharedPtrTarget {}
+
+impl<T> PartialOrd for SharedPtr<T>
+where
+    T: PartialOrd + SharedPtrTarget,
+{
+    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
+        PartialOrd::partial_cmp(&self.as_ref(), &other.as_ref())
+    }
+}
+
+impl<T> Ord for SharedPtr<T>
+where
+    T: Ord + SharedPtrTarget,
+{
+    fn cmp(&self, other: &Self) -> Ordering {
+        Ord::cmp(&self.as_ref(), &other.as_ref())
+    }
+}
+
+impl<T> Hash for SharedPtr<T>
+where
+    T: Hash + SharedPtrTarget,
+{
+    fn hash<H>(&self, hasher: &mut H)
+    where
+        H: Hasher,
+    {
+        self.as_ref().hash(hasher);
+    }
+}
+
+/// 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,
+    {
+        // Opaque 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 {
+            fn __typename(f: &mut fmt::Formatter) -> fmt::Result {
+                f.write_str($name)
+            }
+            unsafe fn __null(new: *mut c_void) {
+                extern "C" {
+                    #[link_name = concat!("cxxbridge1$std$shared_ptr$", $segment, "$null")]
+                    fn __null(new: *mut c_void);
+                }
+                unsafe { __null(new) }
+            }
+            unsafe fn __new(value: Self, new: *mut c_void) {
+                extern "C" {
+                    #[link_name = concat!("cxxbridge1$std$shared_ptr$", $segment, "$uninit")]
+                    fn __uninit(new: *mut c_void) -> *mut c_void;
+                }
+                unsafe { __uninit(new).cast::<$ty>().write(value) }
+            }
+            unsafe fn __clone(this: *const c_void, new: *mut c_void) {
+                extern "C" {
+                    #[link_name = concat!("cxxbridge1$std$shared_ptr$", $segment, "$clone")]
+                    fn __clone(this: *const c_void, new: *mut c_void);
+                }
+                unsafe { __clone(this, new) }
+            }
+            unsafe fn __get(this: *const c_void) -> *const Self {
+                extern "C" {
+                    #[link_name = concat!("cxxbridge1$std$shared_ptr$", $segment, "$get")]
+                    fn __get(this: *const c_void) -> *const c_void;
+                }
+                unsafe { __get(this) }.cast()
+            }
+            unsafe fn __drop(this: *mut c_void) {
+                extern "C" {
+                    #[link_name = concat!("cxxbridge1$std$shared_ptr$", $segment, "$drop")]
+                    fn __drop(this: *mut c_void);
+                }
+                unsafe { __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
new file mode 100644 (file)
index 0000000..b8fe1b5
--- /dev/null
@@ -0,0 +1,18 @@
+#![cfg(feature = "alloc")]
+
+use crate::result::PtrLen;
+use alloc::boxed::Box;
+use alloc::string::String;
+use core::ptr::NonNull;
+use core::slice;
+
+#[export_name = "cxxbridge1$exception"]
+unsafe extern "C" fn exception(ptr: *const u8, len: usize) -> PtrLen {
+    let slice = unsafe { slice::from_raw_parts(ptr, len) };
+    let string = String::from_utf8_lossy(slice);
+    let len = string.len();
+    let raw_str = Box::into_raw(string.into_owned().into_boxed_str());
+    let raw_u8 = raw_str.cast::<u8>();
+    let nonnull = unsafe { NonNull::new_unchecked(raw_u8) };
+    PtrLen { ptr: nonnull, len }
+}
diff --git a/src/symbols/mod.rs b/src/symbols/mod.rs
new file mode 100644 (file)
index 0000000..e00bb55
--- /dev/null
@@ -0,0 +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 (file)
index 0000000..df215ac
--- /dev/null
@@ -0,0 +1,20 @@
+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: NonNull<()>, len: usize) {
+    let this = this.as_mut_ptr();
+    let rust_slice = RustSlice::from_raw_parts(ptr, len);
+    unsafe { ptr::write(this, rust_slice) }
+}
+
+#[export_name = "cxxbridge1$slice$ptr"]
+unsafe extern "C" fn slice_ptr(this: &RustSlice) -> NonNull<()> {
+    this.as_non_null_ptr()
+}
+
+#[export_name = "cxxbridge1$slice$len"]
+unsafe extern "C" fn slice_len(this: &RustSlice) -> usize {
+    this.len()
+}
diff --git a/src/symbols/rust_str.rs b/src/symbols/rust_str.rs
new file mode 100644 (file)
index 0000000..3b33bc4
--- /dev/null
@@ -0,0 +1,43 @@
+#[cfg(feature = "alloc")]
+use alloc::string::String;
+use core::mem::MaybeUninit;
+use core::ptr;
+use core::slice;
+use core::str;
+
+#[export_name = "cxxbridge1$str$new"]
+unsafe extern "C" fn str_new(this: &mut MaybeUninit<&str>) {
+    let this = this.as_mut_ptr();
+    unsafe { ptr::write(this, "") }
+}
+
+#[cfg(feature = "alloc")]
+#[export_name = "cxxbridge1$str$ref"]
+unsafe extern "C" fn str_ref<'a>(this: &mut MaybeUninit<&'a str>, string: &'a String) {
+    let this = this.as_mut_ptr();
+    let s = string.as_str();
+    unsafe { ptr::write(this, s) }
+}
+
+#[export_name = "cxxbridge1$str$from"]
+unsafe extern "C" fn str_from(this: &mut MaybeUninit<&str>, ptr: *const u8, len: usize) -> bool {
+    let slice = unsafe { slice::from_raw_parts(ptr, len) };
+    match str::from_utf8(slice) {
+        Ok(s) => {
+            let this = this.as_mut_ptr();
+            unsafe { ptr::write(this, 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
new file mode 100644 (file)
index 0000000..8b7c8c4
--- /dev/null
@@ -0,0 +1,114 @@
+#![cfg(feature = "alloc")]
+
+use alloc::borrow::ToOwned;
+use alloc::string::String;
+use core::mem::{ManuallyDrop, MaybeUninit};
+use core::ptr;
+use core::slice;
+use core::str;
+
+#[export_name = "cxxbridge1$string$new"]
+unsafe extern "C" fn string_new(this: &mut MaybeUninit<String>) {
+    let this = this.as_mut_ptr();
+    let new = String::new();
+    unsafe { ptr::write(this, new) }
+}
+
+#[export_name = "cxxbridge1$string$clone"]
+unsafe extern "C" fn string_clone(this: &mut MaybeUninit<String>, other: &String) {
+    let this = this.as_mut_ptr();
+    let clone = other.clone();
+    unsafe { ptr::write(this, clone) }
+}
+
+#[export_name = "cxxbridge1$string$from_utf8"]
+unsafe extern "C" fn string_from_utf8(
+    this: &mut MaybeUninit<String>,
+    ptr: *const u8,
+    len: usize,
+) -> bool {
+    let slice = unsafe { slice::from_raw_parts(ptr, len) };
+    match str::from_utf8(slice) {
+        Ok(s) => {
+            let this = this.as_mut_ptr();
+            let owned = s.to_owned();
+            unsafe { ptr::write(this, owned) }
+            true
+        }
+        Err(_) => false,
+    }
+}
+
+#[export_name = "cxxbridge1$string$from_utf8_lossy"]
+unsafe extern "C" fn string_from_utf8_lossy(
+    this: &mut MaybeUninit<String>,
+    ptr: *const u8,
+    len: usize,
+) {
+    let slice = unsafe { slice::from_raw_parts(ptr, len) };
+    let owned = String::from_utf8_lossy(slice).into_owned();
+    let this = this.as_mut_ptr();
+    unsafe { ptr::write(this, owned) }
+}
+
+#[export_name = "cxxbridge1$string$from_utf16"]
+unsafe extern "C" fn string_from_utf16(
+    this: &mut MaybeUninit<String>,
+    ptr: *const u16,
+    len: usize,
+) -> bool {
+    let slice = unsafe { slice::from_raw_parts(ptr, len) };
+    match String::from_utf16(slice) {
+        Ok(s) => {
+            let this = this.as_mut_ptr();
+            unsafe { ptr::write(this, s) }
+            true
+        }
+        Err(_) => false,
+    }
+}
+
+#[export_name = "cxxbridge1$string$from_utf16_lossy"]
+unsafe extern "C" fn string_from_utf16_lossy(
+    this: &mut MaybeUninit<String>,
+    ptr: *const u16,
+    len: usize,
+) {
+    let slice = unsafe { slice::from_raw_parts(ptr, len) };
+    let owned = String::from_utf16_lossy(slice);
+    let this = this.as_mut_ptr();
+    unsafe { ptr::write(this, owned) }
+}
+
+#[export_name = "cxxbridge1$string$drop"]
+unsafe extern "C" fn string_drop(this: &mut ManuallyDrop<String>) {
+    unsafe { ManuallyDrop::drop(this) }
+}
+
+#[export_name = "cxxbridge1$string$ptr"]
+unsafe extern "C" fn string_ptr(this: &String) -> *const u8 {
+    this.as_ptr()
+}
+
+#[export_name = "cxxbridge1$string$len"]
+unsafe extern "C" fn string_len(this: &String) -> usize {
+    this.len()
+}
+
+#[export_name = "cxxbridge1$string$capacity"]
+unsafe extern "C" fn string_capacity(this: &String) -> usize {
+    this.capacity()
+}
+
+#[export_name = "cxxbridge1$string$reserve_additional"]
+unsafe extern "C" fn string_reserve_additional(this: &mut String, additional: usize) {
+    this.reserve(additional);
+}
+
+#[export_name = "cxxbridge1$string$reserve_total"]
+unsafe extern "C" fn string_reserve_total(this: &mut String, new_cap: usize) {
+    if new_cap > this.capacity() {
+        let additional = new_cap - this.len();
+        this.reserve(additional);
+    }
+}
diff --git a/src/symbols/rust_vec.rs b/src/symbols/rust_vec.rs
new file mode 100644 (file)
index 0000000..eaf025e
--- /dev/null
@@ -0,0 +1,75 @@
+#![cfg(feature = "alloc")]
+
+use crate::rust_string::RustString;
+use crate::rust_vec::RustVec;
+use alloc::vec::Vec;
+use core::ffi::c_char;
+use core::mem;
+use core::ptr;
+
+macro_rules! rust_vec_shims {
+    ($segment:expr, $ty:ty) => {
+        const_assert_eq!(mem::size_of::<[usize; 3]>(), mem::size_of::<RustVec<$ty>>());
+        const_assert_eq!(mem::size_of::<Vec<$ty>>(), mem::size_of::<RustVec<$ty>>());
+        const_assert_eq!(mem::align_of::<Vec<$ty>>(), mem::align_of::<RustVec<$ty>>());
+
+        const _: () = {
+            #[export_name = concat!("cxxbridge1$rust_vec$", $segment, "$new")]
+            unsafe extern "C" fn __new(this: *mut RustVec<$ty>) {
+                unsafe { ptr::write(this, RustVec::new()) }
+            }
+            #[export_name = concat!("cxxbridge1$rust_vec$", $segment, "$drop")]
+            unsafe extern "C" fn __drop(this: *mut RustVec<$ty>) {
+                unsafe { ptr::drop_in_place(this) }
+            }
+            #[export_name = concat!("cxxbridge1$rust_vec$", $segment, "$len")]
+            unsafe extern "C" fn __len(this: *const RustVec<$ty>) -> usize {
+                unsafe { &*this }.len()
+            }
+            #[export_name = concat!("cxxbridge1$rust_vec$", $segment, "$capacity")]
+            unsafe extern "C" fn __capacity(this: *const RustVec<$ty>) -> usize {
+                unsafe { &*this }.capacity()
+            }
+            #[export_name = concat!("cxxbridge1$rust_vec$", $segment, "$data")]
+            unsafe extern "C" fn __data(this: *const RustVec<$ty>) -> *const $ty {
+                unsafe { &*this }.as_ptr()
+            }
+            #[export_name = concat!("cxxbridge1$rust_vec$", $segment, "$reserve_total")]
+            unsafe extern "C" fn __reserve_total(this: *mut RustVec<$ty>, new_cap: usize) {
+                unsafe { &mut *this }.reserve_total(new_cap);
+            }
+            #[export_name = concat!("cxxbridge1$rust_vec$", $segment, "$set_len")]
+            unsafe extern "C" fn __set_len(this: *mut RustVec<$ty>, len: usize) {
+                unsafe { (*this).set_len(len) }
+            }
+            #[export_name = concat!("cxxbridge1$rust_vec$", $segment, "$truncate")]
+            unsafe extern "C" fn __truncate(this: *mut RustVec<$ty>, len: usize) {
+                unsafe { (*this).truncate(len) }
+            }
+        };
+    };
+}
+
+macro_rules! rust_vec_shims_for_primitive {
+    ($ty:ident) => {
+        rust_vec_shims!(stringify!($ty), $ty);
+    };
+}
+
+rust_vec_shims_for_primitive!(bool);
+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 (file)
index 0000000..c10c112
--- /dev/null
@@ -0,0 +1,9 @@
+/// For use in impls of the `ExternType` trait. See [`ExternType`].
+///
+/// [`ExternType`]: crate::ExternType
+#[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
new file mode 100644 (file)
index 0000000..7230535
--- /dev/null
@@ -0,0 +1,469 @@
+use crate::cxx_vector::{CxxVector, VectorElement};
+use crate::fmt::display;
+use crate::kind::Trivial;
+use crate::string::CxxString;
+use crate::ExternType;
+#[cfg(feature = "std")]
+use alloc::string::String;
+#[cfg(feature = "std")]
+use alloc::vec::Vec;
+use core::cmp::Ordering;
+use core::ffi::c_void;
+use core::fmt::{self, Debug, Display};
+use core::hash::{Hash, Hasher};
+use core::marker::PhantomData;
+use core::mem::{self, MaybeUninit};
+use core::ops::{Deref, DerefMut};
+use core::pin::Pin;
+#[cfg(feature = "std")]
+use std::io::{self, IoSlice, Read, Seek, SeekFrom, Write};
+
+/// Binding to C++ `std::unique_ptr<T, std::default_delete<T>>`.
+#[repr(C)]
+pub struct UniquePtr<T>
+where
+    T: UniquePtrTarget,
+{
+    repr: MaybeUninit<*mut c_void>,
+    ty: PhantomData<T>,
+}
+
+impl<T> UniquePtr<T>
+where
+    T: UniquePtrTarget,
+{
+    /// Makes a new UniquePtr wrapping a null pointer.
+    ///
+    /// Matches the behavior of default-constructing a std::unique\_ptr.
+    pub fn null() -> Self {
+        UniquePtr {
+            repr: T::__null(),
+            ty: PhantomData,
+        }
+    }
+
+    /// Allocates memory on the heap and makes a UniquePtr pointing to it.
+    pub fn new(value: T) -> Self
+    where
+        T: ExternType<Kind = Trivial>,
+    {
+        UniquePtr {
+            repr: T::__new(value),
+            ty: PhantomData,
+        }
+    }
+
+    /// Checks whether the UniquePtr does not own an object.
+    ///
+    /// This is the opposite of [std::unique_ptr\<T\>::operator bool](https://en.cppreference.com/w/cpp/memory/unique_ptr/operator_bool).
+    pub fn is_null(&self) -> bool {
+        self.as_ptr().is_null()
+    }
+
+    /// Returns a reference to the object owned by this UniquePtr if any,
+    /// otherwise None.
+    pub fn as_ref(&self) -> Option<&T> {
+        let ptr = self.as_ptr();
+        unsafe { ptr.as_ref() }
+    }
+
+    /// 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>> {
+        let ptr = self.as_mut_ptr();
+        unsafe {
+            let mut_reference = ptr.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),
+            ),
+        }
+    }
+
+    /// Returns a raw const pointer to the object owned by this UniquePtr if
+    /// any, otherwise the null pointer.
+    pub fn as_ptr(&self) -> *const T {
+        unsafe { T::__get(self.repr) }
+    }
+
+    /// Returns a raw mutable pointer to the object owned by this UniquePtr if
+    /// any, otherwise the null pointer.
+    ///
+    /// As with [std::unique_ptr\<T\>::get](https://en.cppreference.com/w/cpp/memory/unique_ptr/get),
+    /// this doesn't require that you hold an exclusive reference to the
+    /// UniquePtr. This differs from Rust norms, so extra care should be taken
+    /// in the way the pointer is used.
+    pub fn as_mut_ptr(&self) -> *mut T {
+        self.as_ptr() as *mut T
+    }
+
+    /// Consumes the UniquePtr, releasing its ownership of the heap-allocated T.
+    ///
+    /// Matches the behavior of [std::unique_ptr\<T\>::release](https://en.cppreference.com/w/cpp/memory/unique_ptr/release).
+    pub fn into_raw(self) -> *mut T {
+        let ptr = unsafe { T::__release(self.repr) };
+        mem::forget(self);
+        ptr
+    }
+
+    /// Constructs a UniquePtr retaking ownership of a pointer previously
+    /// obtained from `into_raw`.
+    ///
+    /// # Safety
+    ///
+    /// This function is unsafe because improper use may lead to memory
+    /// problems. For example a double-free may occur if the function is called
+    /// twice on the same raw pointer.
+    pub unsafe fn from_raw(raw: *mut T) -> Self {
+        UniquePtr {
+            repr: unsafe { T::__raw(raw) },
+            ty: PhantomData,
+        }
+    }
+}
+
+unsafe impl<T> Send for UniquePtr<T> where T: Send + UniquePtrTarget {}
+unsafe impl<T> Sync for UniquePtr<T> where T: Sync + UniquePtrTarget {}
+
+// UniquePtr is not a self-referential type and is safe to move out of a Pin,
+// regardless whether the pointer's target is Unpin.
+impl<T> Unpin for UniquePtr<T> where T: UniquePtrTarget {}
+
+impl<T> Drop for UniquePtr<T>
+where
+    T: UniquePtrTarget,
+{
+    fn drop(&mut self) {
+        unsafe { T::__drop(self.repr) }
+    }
+}
+
+impl<T> Deref for UniquePtr<T>
+where
+    T: UniquePtrTarget,
+{
+    type Target = T;
+
+    fn deref(&self) -> &Self::Target {
+        match self.as_ref() {
+            Some(target) => target,
+            None => panic!(
+                "called deref on a null UniquePtr<{}>",
+                display(T::__typename),
+            ),
+        }
+    }
+}
+
+impl<T> DerefMut for UniquePtr<T>
+where
+    T: UniquePtrTarget + Unpin,
+{
+    fn deref_mut(&mut self) -> &mut Self::Target {
+        match self.as_mut() {
+            Some(target) => Pin::into_inner(target),
+            None => panic!(
+                "called deref_mut on a null UniquePtr<{}>",
+                display(T::__typename),
+            ),
+        }
+    }
+}
+
+impl<T> Debug for UniquePtr<T>
+where
+    T: Debug + UniquePtrTarget,
+{
+    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 UniquePtr<T>
+where
+    T: Display + UniquePtrTarget,
+{
+    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+        match self.as_ref() {
+            None => formatter.write_str("nullptr"),
+            Some(value) => Display::fmt(value, formatter),
+        }
+    }
+}
+
+impl<T> PartialEq for UniquePtr<T>
+where
+    T: PartialEq + UniquePtrTarget,
+{
+    fn eq(&self, other: &Self) -> bool {
+        self.as_ref() == other.as_ref()
+    }
+}
+
+impl<T> Eq for UniquePtr<T> where T: Eq + UniquePtrTarget {}
+
+impl<T> PartialOrd for UniquePtr<T>
+where
+    T: PartialOrd + UniquePtrTarget,
+{
+    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
+        PartialOrd::partial_cmp(&self.as_ref(), &other.as_ref())
+    }
+}
+
+impl<T> Ord for UniquePtr<T>
+where
+    T: Ord + UniquePtrTarget,
+{
+    fn cmp(&self, other: &Self) -> Ordering {
+        Ord::cmp(&self.as_ref(), &other.as_ref())
+    }
+}
+
+impl<T> Hash for UniquePtr<T>
+where
+    T: Hash + UniquePtrTarget,
+{
+    fn hash<H>(&self, hasher: &mut H)
+    where
+        H: Hasher,
+    {
+        self.as_ref().hash(hasher);
+    }
+}
+
+/// Forwarding `Read` trait implementation in a manner similar to `Box<T>`.
+///
+/// Note that the implementation will panic for null `UniquePtr<T>`.
+#[cfg(feature = "std")]
+impl<T> Read for UniquePtr<T>
+where
+    for<'a> Pin<&'a mut T>: Read,
+    T: UniquePtrTarget,
+{
+    #[inline]
+    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
+        self.pin_mut().read(buf)
+    }
+
+    #[inline]
+    fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
+        self.pin_mut().read_to_end(buf)
+    }
+
+    #[inline]
+    fn read_to_string(&mut self, buf: &mut String) -> io::Result<usize> {
+        self.pin_mut().read_to_string(buf)
+    }
+
+    #[inline]
+    fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> {
+        self.pin_mut().read_exact(buf)
+    }
+
+    // TODO: Foward other `Read` trait methods when they get stabilized (e.g.
+    // `read_buf` and/or `is_read_vectored`).
+}
+
+/// Forwarding `Seek` trait implementation in a manner similar to `Box<T>`.
+///
+/// Note that the implementation will panic for null `UniquePtr<T>`.
+#[cfg(feature = "std")]
+impl<T> Seek for UniquePtr<T>
+where
+    for<'a> Pin<&'a mut T>: Seek,
+    T: UniquePtrTarget,
+{
+    #[inline]
+    fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
+        self.pin_mut().seek(pos)
+    }
+
+    #[inline]
+    fn rewind(&mut self) -> io::Result<()> {
+        self.pin_mut().rewind()
+    }
+
+    #[inline]
+    fn stream_position(&mut self) -> io::Result<u64> {
+        self.pin_mut().stream_position()
+    }
+
+    #[cfg(seek_relative)]
+    #[allow(clippy::incompatible_msrv)]
+    #[inline]
+    fn seek_relative(&mut self, offset: i64) -> io::Result<()> {
+        self.pin_mut().seek_relative(offset)
+    }
+
+    // TODO: Foward other `Seek` trait methods if/when possible:
+    // * `stream_len`: If/when stabilized
+}
+
+/// Forwarding `Write` trait implementation in a manner similar to `Box<T>`.
+///
+/// Note that the implementation will panic for null `UniquePtr<T>`.
+#[cfg(feature = "std")]
+impl<T> Write for UniquePtr<T>
+where
+    for<'a> Pin<&'a mut T>: Write,
+    T: UniquePtrTarget,
+{
+    #[inline]
+    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+        self.pin_mut().write(buf)
+    }
+
+    #[inline]
+    fn write_vectored(&mut self, bufs: &[IoSlice]) -> io::Result<usize> {
+        self.pin_mut().write_vectored(bufs)
+    }
+
+    #[inline]
+    fn flush(&mut self) -> io::Result<()> {
+        self.pin_mut().flush()
+    }
+
+    #[inline]
+    fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
+        self.pin_mut().write_all(buf)
+    }
+
+    #[inline]
+    fn write_fmt(&mut self, fmt: fmt::Arguments) -> io::Result<()> {
+        self.pin_mut().write_fmt(fmt)
+    }
+
+    // TODO: Foward other `Write` trait methods when they get stabilized (e.g.
+    // `write_all_vectored` and/or `is_write_vectored`).
+}
+
+/// 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)]
+    fn __typename(f: &mut fmt::Formatter) -> fmt::Result;
+    #[doc(hidden)]
+    fn __null() -> MaybeUninit<*mut c_void>;
+    #[doc(hidden)]
+    fn __new(value: Self) -> MaybeUninit<*mut c_void>
+    where
+        Self: Sized,
+    {
+        // Opaque C types do not get this method because they can never exist by
+        // value on the Rust side of the bridge.
+        let _ = value;
+        unreachable!()
+    }
+    #[doc(hidden)]
+    unsafe fn __raw(raw: *mut Self) -> MaybeUninit<*mut c_void>;
+    #[doc(hidden)]
+    unsafe fn __get(repr: MaybeUninit<*mut c_void>) -> *const Self;
+    #[doc(hidden)]
+    unsafe fn __release(repr: MaybeUninit<*mut c_void>) -> *mut Self;
+    #[doc(hidden)]
+    unsafe fn __drop(repr: MaybeUninit<*mut c_void>);
+}
+
+extern "C" {
+    #[link_name = "cxxbridge1$unique_ptr$std$string$null"]
+    fn unique_ptr_std_string_null(this: *mut MaybeUninit<*mut c_void>);
+    #[link_name = "cxxbridge1$unique_ptr$std$string$raw"]
+    fn unique_ptr_std_string_raw(this: *mut MaybeUninit<*mut c_void>, raw: *mut CxxString);
+    #[link_name = "cxxbridge1$unique_ptr$std$string$get"]
+    fn unique_ptr_std_string_get(this: *const MaybeUninit<*mut c_void>) -> *const CxxString;
+    #[link_name = "cxxbridge1$unique_ptr$std$string$release"]
+    fn unique_ptr_std_string_release(this: *mut MaybeUninit<*mut c_void>) -> *mut CxxString;
+    #[link_name = "cxxbridge1$unique_ptr$std$string$drop"]
+    fn unique_ptr_std_string_drop(this: *mut MaybeUninit<*mut c_void>);
+}
+
+unsafe impl UniquePtrTarget for CxxString {
+    fn __typename(f: &mut fmt::Formatter) -> fmt::Result {
+        f.write_str("CxxString")
+    }
+    fn __null() -> MaybeUninit<*mut c_void> {
+        let mut repr = MaybeUninit::uninit();
+        unsafe {
+            unique_ptr_std_string_null(&mut repr);
+        }
+        repr
+    }
+    unsafe fn __raw(raw: *mut Self) -> MaybeUninit<*mut c_void> {
+        let mut repr = MaybeUninit::uninit();
+        unsafe { unique_ptr_std_string_raw(&mut repr, raw) }
+        repr
+    }
+    unsafe fn __get(repr: MaybeUninit<*mut c_void>) -> *const Self {
+        unsafe { unique_ptr_std_string_get(&repr) }
+    }
+    unsafe fn __release(mut repr: MaybeUninit<*mut c_void>) -> *mut Self {
+        unsafe { unique_ptr_std_string_release(&mut repr) }
+    }
+    unsafe fn __drop(mut repr: MaybeUninit<*mut c_void>) {
+        unsafe { unique_ptr_std_string_drop(&mut repr) }
+    }
+}
+
+unsafe impl<T> UniquePtrTarget for CxxVector<T>
+where
+    T: VectorElement,
+{
+    fn __typename(f: &mut fmt::Formatter) -> fmt::Result {
+        write!(f, "CxxVector<{}>", display(T::__typename))
+    }
+    fn __null() -> MaybeUninit<*mut c_void> {
+        T::__unique_ptr_null()
+    }
+    unsafe fn __raw(raw: *mut Self) -> MaybeUninit<*mut c_void> {
+        unsafe { T::__unique_ptr_raw(raw) }
+    }
+    unsafe fn __get(repr: MaybeUninit<*mut c_void>) -> *const Self {
+        unsafe { T::__unique_ptr_get(repr) }
+    }
+    unsafe fn __release(repr: MaybeUninit<*mut c_void>) -> *mut Self {
+        unsafe { T::__unique_ptr_release(repr) }
+    }
+    unsafe fn __drop(repr: MaybeUninit<*mut c_void>) {
+        unsafe { T::__unique_ptr_drop(repr) }
+    }
+}
diff --git a/src/unwind.rs b/src/unwind.rs
new file mode 100644 (file)
index 0000000..18852da
--- /dev/null
@@ -0,0 +1,39 @@
+#![allow(missing_docs)]
+
+use core::mem;
+
+pub fn prevent_unwind<F, R>(label: &'static str, foreign_call: F) -> R
+where
+    F: FnOnce() -> R,
+{
+    // Goal is to make it impossible to propagate a panic across the C interface
+    // of an extern "Rust" function, which would be Undefined Behavior. We
+    // transform such panicks into a deterministic abort instead. When cxx is
+    // built in an application using panic=abort, this guard object is compiled
+    // out because its destructor is statically unreachable. When built with
+    // panic=unwind, an unwind from the foreign call will attempt to drop the
+    // guard object leading to a double panic, which is defined by Rust to
+    // abort. In no_std programs, on most platforms the current mechanism for
+    // this is for core::intrinsics::abort to invoke an invalid instruction. On
+    // Unix, the process will probably terminate with a signal like SIGABRT,
+    // SIGILL, SIGTRAP, SIGSEGV or SIGBUS. The precise behaviour is not
+    // guaranteed and not stable, but is safe.
+    let guard = Guard { label };
+
+    let ret = foreign_call();
+
+    // If we made it here, no uncaught panic occurred during the foreign call.
+    mem::forget(guard);
+    ret
+}
+
+struct Guard {
+    label: &'static str,
+}
+
+impl Drop for Guard {
+    #[cold]
+    fn drop(&mut self) {
+        panic!("panic in ffi function {}, aborting.", self.label);
+    }
+}
diff --git a/src/vector.rs b/src/vector.rs
new file mode 100644 (file)
index 0000000..4afd487
--- /dev/null
@@ -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 (file)
index 0000000..c34e969
--- /dev/null
@@ -0,0 +1,179 @@
+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: [MaybeUninit<*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)
+    }
+}
+
+/// Trait bound for types which may be used as the `T` inside of a `WeakPtr<T>`
+/// in generic code.
+///
+/// This trait has no publicly callable or implementable methods. Implementing
+/// it outside of the CXX codebase is not supported.
+pub unsafe trait WeakPtrTarget {
+    #[doc(hidden)]
+    fn __typename(f: &mut fmt::Formatter) -> fmt::Result;
+    #[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 {
+            fn __typename(f: &mut fmt::Formatter) -> fmt::Result {
+                f.write_str($name)
+            }
+            unsafe fn __null(new: *mut c_void) {
+                extern "C" {
+                    #[link_name = concat!("cxxbridge1$std$weak_ptr$", $segment, "$null")]
+                    fn __null(new: *mut c_void);
+                }
+                unsafe { __null(new) }
+            }
+            unsafe fn __clone(this: *const c_void, new: *mut c_void) {
+                extern "C" {
+                    #[link_name = concat!("cxxbridge1$std$weak_ptr$", $segment, "$clone")]
+                    fn __clone(this: *const c_void, new: *mut c_void);
+                }
+                unsafe { __clone(this, new) }
+            }
+            unsafe fn __downgrade(shared: *const c_void, weak: *mut c_void) {
+                extern "C" {
+                    #[link_name = concat!("cxxbridge1$std$weak_ptr$", $segment, "$downgrade")]
+                    fn __downgrade(shared: *const c_void, weak: *mut c_void);
+                }
+                unsafe { __downgrade(shared, weak) }
+            }
+            unsafe fn __upgrade(weak: *const c_void, shared: *mut c_void) {
+                extern "C" {
+                    #[link_name = concat!("cxxbridge1$std$weak_ptr$", $segment, "$upgrade")]
+                    fn __upgrade(weak: *const c_void, shared: *mut c_void);
+                }
+                unsafe { __upgrade(weak, shared) }
+            }
+            unsafe fn __drop(this: *mut c_void) {
+                extern "C" {
+                    #[link_name = concat!("cxxbridge1$std$weak_ptr$", $segment, "$drop")]
+                    fn __drop(this: *mut c_void);
+                }
+                unsafe { __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/tests/BUCK b/tests/BUCK
new file mode 100644 (file)
index 0000000..3985860
--- /dev/null
@@ -0,0 +1,58 @@
+load("//tools/buck:rust_cxx_bridge.bzl", "rust_cxx_bridge")
+
+rust_test(
+    name = "test",
+    srcs = ["test.rs"],
+    edition = "2021",
+    deps = [
+        ":ffi",
+        "//:cxx",
+    ],
+)
+
+rust_library(
+    name = "ffi",
+    srcs = [
+        "ffi/cast.rs",
+        "ffi/lib.rs",
+        "ffi/module.rs",
+    ],
+    crate = "cxx_test_suite",
+    edition = "2021",
+    deps = [
+        ":impl",
+        "//:cxx",
+    ],
+)
+
+cxx_library(
+    name = "impl",
+    srcs = [
+        "ffi/tests.cc",
+        ":bridge/source",
+        ":module/source",
+    ],
+    exported_deps = ["//:core"],
+    exported_headers = [
+        ":bridge/header",
+        ":module/header",
+        "ffi/tests.h",
+    ],
+    preferred_linkage = "static",
+)
+
+rust_cxx_bridge(
+    name = "bridge",
+    src = "ffi/lib.rs",
+    deps = [
+        ":impl",
+    ],
+)
+
+rust_cxx_bridge(
+    name = "module",
+    src = "ffi/module.rs",
+    deps = [
+        ":impl",
+    ],
+)
diff --git a/tests/BUILD.bazel b/tests/BUILD.bazel
new file mode 100644 (file)
index 0000000..634c8ac
--- /dev/null
@@ -0,0 +1,60 @@
+load("@rules_cc//cc:defs.bzl", "cc_library")
+load("@rules_rust//rust:defs.bzl", "rust_library", "rust_test")
+load("//tools/bazel:rust_cxx_bridge.bzl", "rust_cxx_bridge")
+
+rust_test(
+    name = "test",
+    size = "small",
+    srcs = ["test.rs"],
+    edition = "2021",
+    deps = [
+        ":cxx_test_suite",
+        "//:cxx",
+    ],
+)
+
+rust_library(
+    name = "cxx_test_suite",
+    testonly = True,
+    srcs = [
+        "ffi/cast.rs",
+        "ffi/lib.rs",
+        "ffi/module.rs",
+    ],
+    edition = "2021",
+    deps = [
+        ":impl",
+        "//:cxx",
+    ],
+)
+
+cc_library(
+    name = "impl",
+    testonly = True,
+    srcs = [
+        "ffi/tests.cc",
+        ":bridge/source",
+        ":module/source",
+    ],
+    hdrs = ["ffi/tests.h"],
+    linkstatic = True,
+    deps = [
+        ":bridge/include",
+        ":module/include",
+        "//:core",
+    ],
+)
+
+rust_cxx_bridge(
+    name = "bridge",
+    testonly = True,
+    src = "ffi/lib.rs",
+    deps = [":impl"],
+)
+
+rust_cxx_bridge(
+    name = "module",
+    testonly = True,
+    src = "ffi/module.rs",
+    deps = [":impl"],
+)
diff --git a/tests/compiletest.rs b/tests/compiletest.rs
new file mode 100644 (file)
index 0000000..97ab136
--- /dev/null
@@ -0,0 +1,9 @@
+#[allow(unused_attributes)]
+#[rustversion::attr(not(nightly), ignore = "requires nightly")]
+#[cfg_attr(skip_ui_tests, ignore = "disabled by `--cfg=skip_ui_tests`")]
+#[cfg_attr(miri, ignore = "incompatible with miri")]
+#[test]
+fn ui() {
+    let t = trybuild::TestCases::new();
+    t.compile_fail("tests/ui/*.rs");
+}
diff --git a/tests/cxx_gen.rs b/tests/cxx_gen.rs
new file mode 100644 (file)
index 0000000..f17f887
--- /dev/null
@@ -0,0 +1,32 @@
+use cxx_gen::{generate_header_and_cc, Opt};
+use std::str;
+
+const BRIDGE0: &str = r#"
+    #[cxx::bridge]
+    mod ffi {
+        unsafe extern "C++" {
+            pub fn do_cpp_thing(foo: &str);
+        }
+    }
+"#;
+
+#[test]
+fn test_extern_c_function() {
+    let opt = Opt::default();
+    let source = BRIDGE0.parse().unwrap();
+    let generated = generate_header_and_cc(source, &opt).unwrap();
+    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 cxxbridge1$do_cpp_thing(::rust::Str foo)"));
+}
+
+#[test]
+fn test_impl_annotation() {
+    let mut opt = Opt::default();
+    opt.cxx_impl_annotations = Some("ANNOTATION".to_owned());
+    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 cxxbridge1$do_cpp_thing(::rust::Str foo)"));
+}
diff --git a/tests/cxx_string.rs b/tests/cxx_string.rs
new file mode 100644 (file)
index 0000000..878be94
--- /dev/null
@@ -0,0 +1,54 @@
+#![allow(
+    clippy::items_after_statements,
+    clippy::uninlined_format_args,
+    clippy::unused_async
+)]
+
+use cxx::{let_cxx_string, CxxString};
+use std::fmt::Write as _;
+
+#[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());
+}
+
+#[test]
+fn test_display() {
+    let_cxx_string!(s = b"w\"x\'y\xF1\x80\xF1\x80z");
+
+    assert_eq!(format!("{}", s), "w\"x'y\u{fffd}\u{fffd}z");
+}
+
+#[test]
+fn test_debug() {
+    let_cxx_string!(s = b"w\"x\'y\xF1\x80z");
+
+    assert_eq!(format!("{:?}", s), r#""w\"x'y\xf1\x80z""#);
+}
+
+#[test]
+fn test_fmt_write() {
+    let_cxx_string!(s = "");
+
+    let name = "world";
+    write!(s, "Hello, {name}!").unwrap();
+    assert_eq!(s.to_str(), Ok("Hello, world!"));
+}
+
+#[test]
+fn test_io_write() {
+    let_cxx_string!(s = "");
+    let mut reader: &[u8] = b"Hello, world!";
+
+    std::io::copy(&mut reader, &mut s).unwrap();
+    assert_eq!(s.to_str(), Ok("Hello, world!"));
+}
diff --git a/tests/cxx_vector.rs b/tests/cxx_vector.rs
new file mode 100644 (file)
index 0000000..de9e8ef
--- /dev/null
@@ -0,0 +1,7 @@
+use cxx::CxxVector;
+
+#[test]
+fn test_cxx_vector_new() {
+    let vector = CxxVector::<i32>::new();
+    assert!(vector.is_empty());
+}
diff --git a/tests/test.rs b/tests/test.rs
new file mode 100644 (file)
index 0000000..516092d
--- /dev/null
@@ -0,0 +1,397 @@
+#![allow(
+    clippy::assertions_on_constants,
+    clippy::cast_possible_truncation,
+    clippy::cast_possible_wrap,
+    clippy::float_cmp,
+    clippy::needless_pass_by_value,
+    clippy::ptr_cast_constness,
+    clippy::unit_cmp
+)]
+
+use cxx::{SharedPtr, UniquePtr};
+use cxx_test_suite::module::ffi2;
+use cxx_test_suite::{cast, ffi, R};
+use std::cell::Cell;
+use std::ffi::CStr;
+use std::panic::{self, RefUnwindSafe, UnwindSafe};
+
+thread_local! {
+    static CORRECT: Cell<bool> = const { Cell::new(false) };
+}
+
+#[no_mangle]
+extern "C" fn cxx_test_suite_set_correct() {
+    CORRECT.with(|correct| correct.set(true));
+}
+
+macro_rules! check {
+    ($run:expr) => {{
+        CORRECT.with(|correct| correct.set(false));
+        $run;
+        assert!(CORRECT.with(Cell::get), "{}", stringify!($run));
+    }};
+}
+
+#[test]
+fn test_c_return() {
+    let shared = ffi::Shared { z: 2020 };
+    let ns_shared = ffi::AShared { z: 2020 };
+    let nested_ns_shared = ffi::ABShared { z: 2020 };
+
+    assert_eq!(2020, ffi::c_return_primitive());
+    assert_eq!(2020, ffi::c_return_shared().z);
+    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",
+        cast::c_char_to_unsigned(ffi::c_return_slice_char(&shared)),
+    );
+    assert_eq!("2020", ffi::c_return_rust_string());
+    assert_eq!("Hello \u{fffd}World", ffi::c_return_rust_string_lossy());
+    assert_eq!("2020", ffi::c_return_unique_ptr_string().to_str().unwrap());
+    assert_eq!(c"2020", ffi::c_return_unique_ptr_string().as_c_str());
+    assert_eq!(4, ffi::c_return_unique_ptr_vector_u8().len());
+    assert_eq!(
+        200_u8,
+        ffi::c_return_unique_ptr_vector_u8().into_iter().sum(),
+    );
+    assert_eq!(
+        200.5_f64,
+        ffi::c_return_unique_ptr_vector_f64().into_iter().sum(),
+    );
+    assert_eq!(2, ffi::c_return_unique_ptr_vector_shared().len());
+    assert_eq!(
+        2021_usize,
+        ffi::c_return_unique_ptr_vector_shared()
+            .into_iter()
+            .map(|o| o.z)
+            .sum(),
+    );
+    assert_eq!(b"\x02\0\x02\0"[..], ffi::c_return_rust_vec_u8());
+    assert_eq!([true, true, false][..], ffi::c_return_rust_vec_bool());
+    assert_eq!(2020, ffi::c_return_identity(2020));
+    assert_eq!(2021, ffi::c_return_sum(2020, 1));
+    match ffi::c_return_enum(0) {
+        enm @ ffi::Enum::AVal => assert_eq!(0, enm.repr),
+        _ => assert!(false),
+    }
+    match ffi::c_return_enum(1) {
+        enm @ ffi::Enum::BVal => assert_eq!(2020, enm.repr),
+        _ => assert!(false),
+    }
+    match ffi::c_return_enum(2021) {
+        enm @ ffi::Enum::LastVal => assert_eq!(2021, enm.repr),
+        _ => assert!(false),
+    }
+    match ffi::c_return_ns_enum(0) {
+        enm @ ffi::AEnum::AAVal => assert_eq!(0, enm.repr),
+        _ => assert!(false),
+    }
+    match ffi::c_return_nested_ns_enum(0) {
+        enm @ ffi::ABEnum::ABAVal => assert_eq!(0, enm.repr),
+        _ => assert!(false),
+    }
+}
+
+#[test]
+fn test_c_try_return() {
+    assert_eq!((), ffi::c_try_return_void().unwrap());
+    assert_eq!(2020, ffi::c_try_return_primitive().unwrap());
+    assert_eq!(
+        "logic error",
+        ffi::c_fail_return_primitive().unwrap_err().what(),
+    );
+    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());
+    assert_eq!("2020", ffi::c_try_return_rust_string().unwrap());
+    assert_eq!("2020", &*ffi::c_try_return_unique_ptr_string().unwrap());
+}
+
+#[test]
+fn test_c_take() {
+    let unique_ptr = ffi::c_return_unique_ptr();
+    let unique_ptr_ns = ffi2::c_return_ns_unique_ptr();
+
+    check!(ffi::c_take_primitive(2020));
+    check!(ffi::c_take_shared(ffi::Shared { z: 2020 }));
+    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(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_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()
+    ));
+    let mut vector = ffi::c_return_unique_ptr_vector_u8();
+    assert_eq!(vector.pin_mut().pop(), Some(9));
+    check!(ffi::c_take_unique_ptr_vector_u8(vector));
+    let mut vector = ffi::c_return_unique_ptr_vector_f64();
+    vector.pin_mut().push(9.0);
+    check!(ffi::c_take_unique_ptr_vector_f64(vector));
+    let mut vector = ffi::c_return_unique_ptr_vector_shared();
+    vector.pin_mut().push(ffi::Shared { z: 9 });
+    check!(ffi::c_take_unique_ptr_vector_shared(vector));
+    check!(ffi::c_take_ref_vector(&ffi::c_return_unique_ptr_vector_u8()));
+    let test_vec = [86_u8, 75_u8, 30_u8, 9_u8].to_vec();
+    check!(ffi::c_take_rust_vec(test_vec.clone()));
+    check!(ffi::c_take_rust_vec_index(test_vec.clone()));
+    let shared_test_vec = vec![ffi::Shared { z: 1010 }, ffi::Shared { z: 1011 }];
+    check!(ffi::c_take_rust_vec_shared(shared_test_vec.clone()));
+    check!(ffi::c_take_rust_vec_shared_index(shared_test_vec.clone()));
+    check!(ffi::c_take_rust_vec_shared_push(shared_test_vec.clone()));
+    check!(ffi::c_take_rust_vec_shared_truncate(
+        shared_test_vec.clone()
+    ));
+    check!(ffi::c_take_rust_vec_shared_clear(shared_test_vec.clone()));
+    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));
+    check!(ffi::c_take_ref_shared_string(&ffi::SharedString {
+        msg: "2020".to_owned()
+    }));
+    let ns_shared_test_vec = vec![ffi::AShared { z: 1010 }, ffi::AShared { z: 1011 }];
+    check!(ffi::c_take_rust_vec_ns_shared(ns_shared_test_vec));
+    let nested_ns_shared_test_vec = vec![ffi::ABShared { z: 1010 }, ffi::ABShared { z: 1011 }];
+    check!(ffi::c_take_rust_vec_nested_ns_shared(
+        nested_ns_shared_test_vec
+    ));
+
+    check!(ffi::c_take_enum(ffi::Enum::AVal));
+    check!(ffi::c_take_ns_enum(ffi::AEnum::AAVal));
+    check!(ffi::c_take_nested_ns_enum(ffi::ABEnum::ABAVal));
+}
+
+#[test]
+fn test_c_callback() {
+    fn callback(s: String) -> usize {
+        if s == "2020" {
+            cxx_test_suite_set_correct();
+        }
+        0
+    }
+
+    #[allow(clippy::ptr_arg)]
+    fn callback_ref(s: &String) {
+        if s == "2020" {
+            cxx_test_suite_set_correct();
+        }
+    }
+
+    fn callback_mut(s: &mut String) {
+        if s == "2020" {
+            cxx_test_suite_set_correct();
+        }
+    }
+
+    check!(ffi::c_take_callback(callback));
+    check!(ffi::c_take_callback_ref(callback_ref));
+    check!(ffi::c_take_callback_ref_lifetime(callback_ref));
+    check!(ffi::c_take_callback_mut(callback_mut));
+}
+
+#[test]
+fn test_c_call_r() {
+    fn cxx_run_test() {
+        extern "C" {
+            fn cxx_run_test() -> *const i8;
+        }
+        let failure = unsafe { cxx_run_test() };
+        if !failure.is_null() {
+            let msg = unsafe { CStr::from_ptr(failure as *mut std::os::raw::c_char) };
+            eprintln!("{}", msg.to_string_lossy());
+        }
+    }
+    check!(cxx_run_test());
+}
+
+#[test]
+fn test_c_method_calls() {
+    let mut unique_ptr = ffi::c_return_unique_ptr();
+
+    let old_value = unique_ptr.get();
+    assert_eq!(2020, old_value);
+    assert_eq!(2021, unique_ptr.pin_mut().set(2021));
+    assert_eq!(2021, unique_ptr.get());
+    assert_eq!(2021, unique_ptr.get2());
+    assert_eq!(2021, *unique_ptr.getRef());
+    assert_eq!(2021, unsafe { &mut *unique_ptr.as_mut_ptr() }.get());
+    assert_eq!(2021, unsafe { &*unique_ptr.as_ptr() }.get());
+    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],
+        b: ffi::Buffer::default(),
+    };
+    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]
+fn test_c_ns_method_calls() {
+    let unique_ptr = ffi2::ns_c_return_unique_ptr_ns();
+
+    let old_value = unique_ptr.get();
+    assert_eq!(1000, old_value);
+}
+
+#[test]
+fn test_enum_representations() {
+    assert_eq!(0, ffi::Enum::AVal.repr);
+    assert_eq!(2020, ffi::Enum::BVal.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 R {
+    Box::into_raw(Box::new(R(2020usize)))
+}
+
+#[no_mangle]
+unsafe extern "C" fn cxx_test_suite_r_is_correct(r: *const R) -> bool {
+    (*r).0 == 2020
+}
+
+#[test]
+fn test_rust_name_attribute() {
+    assert_eq!("2020", ffi::i32_overloaded_function(2020));
+    assert_eq!("2020", ffi::str_overloaded_function("2020"));
+    let unique_ptr = ffi::c_return_unique_ptr();
+    assert_eq!("2020", unique_ptr.i32_overloaded_method(2020));
+    assert_eq!("2020", unique_ptr.str_overloaded_method("2020"));
+}
+
+#[test]
+fn test_extern_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 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();
+    check!(ffi2::ns_c_take_trivial(d));
+
+    let g = ffi2::c_return_trivial_ns();
+    check!(ffi2::c_take_trivial_ns_ref(&g));
+    check!(ffi2::c_take_trivial_ns(g));
+    let g = ffi2::c_return_trivial_ns_ptr();
+    check!(ffi2::c_take_trivial_ns_ptr(g));
+    cxx::UniquePtr::new(ffi2::G { g: 42 });
+}
+
+#[test]
+fn test_extern_opaque() {
+    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
+}
+
+#[test]
+#[allow(clippy::items_after_statements, clippy::no_effect_underscore_binding)]
+fn test_unwind_safe() {
+    fn inspect(_c: &ffi::C) {}
+    let _unwind_safe = |c: UniquePtr<ffi::C>| panic::catch_unwind(|| drop(c));
+    let _ref_unwind_safe = |c: &ffi::C| panic::catch_unwind(|| inspect(c));
+
+    fn require_unwind_safe<T: UnwindSafe>() {}
+    require_unwind_safe::<ffi::C>();
+
+    fn require_ref_unwind_safe<T: RefUnwindSafe>() {}
+    require_ref_unwind_safe::<ffi::C>();
+}
diff --git a/tests/ui/array_len_expr.rs b/tests/ui/array_len_expr.rs
new file mode 100644 (file)
index 0000000..73da57c
--- /dev/null
@@ -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 (file)
index 0000000..c58cfbc
--- /dev/null
@@ -0,0 +1,17 @@
+error: array length must be an integer literal
+ --> tests/ui/array_len_expr.rs:4:28
+  |
+4 |         arraystr: [String; "13"],
+  |                            ^^^^
+
+error: unsupported expression, array length must be an integer literal
+ --> tests/ui/array_len_expr.rs:5:28
+  |
+5 |         arraysub: [String; 15 - 1],
+  |                            ^^^^^^
+
+error: array with zero size is not supported
+ --> tests/ui/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 (file)
index 0000000..1e9d514
--- /dev/null
@@ -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 (file)
index 0000000..7dafc22
--- /dev/null
@@ -0,0 +1,11 @@
+error[E0308]: mismatched types
+ --> tests/ui/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; 12u16];
+4 +         fn array() -> [String; 12usize];
+  |
diff --git a/tests/ui/async_fn.rs b/tests/ui/async_fn.rs
new file mode 100644 (file)
index 0000000..4e663a0
--- /dev/null
@@ -0,0 +1,14 @@
+#[cxx::bridge]
+mod ffi {
+    extern "Rust" {
+        async fn f();
+    }
+
+    extern "C++" {
+        async fn g();
+    }
+}
+
+async fn f() {}
+
+fn main() {}
diff --git a/tests/ui/async_fn.stderr b/tests/ui/async_fn.stderr
new file mode 100644 (file)
index 0000000..7810865
--- /dev/null
@@ -0,0 +1,11 @@
+error: async function is not directly supported yet, but see https://cxx.rs/async.html for a working approach, and https://github.com/pcwalton/cxx-async for some helpers; eventually what you wrote will work but it isn't integrated into the cxx::bridge macro yet
+ --> tests/ui/async_fn.rs:4:9
+  |
+4 |         async fn f();
+  |         ^^^^^^^^^^^^^
+
+error: async function is not directly supported yet, but see https://cxx.rs/async.html for a working approach, and https://github.com/pcwalton/cxx-async for some helpers; eventually what you wrote will work but it isn't integrated into the cxx::bridge macro yet
+ --> tests/ui/async_fn.rs:8:9
+  |
+8 |         async fn g();
+  |         ^^^^^^^^^^^^^
diff --git a/tests/ui/bad_explicit_impl.rs b/tests/ui/bad_explicit_impl.rs
new file mode 100644 (file)
index 0000000..2106446
--- /dev/null
@@ -0,0 +1,10 @@
+#[cxx::bridge]
+mod ffi {
+    struct S {
+        x: u8,
+    }
+
+    impl fn() -> &S {}
+}
+
+fn main() {}
diff --git a/tests/ui/bad_explicit_impl.stderr b/tests/ui/bad_explicit_impl.stderr
new file mode 100644 (file)
index 0000000..c4748f4
--- /dev/null
@@ -0,0 +1,5 @@
+error: unsupported Self type of explicit impl
+ --> tests/ui/bad_explicit_impl.rs:7:5
+  |
+7 |     impl fn() -> &S {}
+  |     ^^^^^^^^^^^^^^^^^^
diff --git a/tests/ui/by_value_not_supported.rs b/tests/ui/by_value_not_supported.rs
new file mode 100644 (file)
index 0000000..62bf8d4
--- /dev/null
@@ -0,0 +1,22 @@
+#[cxx::bridge]
+mod ffi {
+    struct S {
+        c: C,
+        r: R,
+        s: CxxString,
+    }
+
+    extern "C++" {
+        type C;
+    }
+
+    extern "Rust" {
+        type R;
+
+        fn f(c: C) -> C;
+        fn g(r: R) -> R;
+        fn h(s: CxxString) -> CxxString;
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/by_value_not_supported.stderr b/tests/ui/by_value_not_supported.stderr
new file mode 100644 (file)
index 0000000..254c7bb
--- /dev/null
@@ -0,0 +1,59 @@
+error: using opaque C++ type by value is not supported
+ --> tests/ui/by_value_not_supported.rs:4:9
+  |
+4 |         c: C,
+  |         ^^^^
+
+error: using opaque Rust type by value is not supported
+ --> tests/ui/by_value_not_supported.rs:5:9
+  |
+5 |         r: R,
+  |         ^^^^
+
+error: using C++ string by value is not supported
+ --> tests/ui/by_value_not_supported.rs:6:9
+  |
+6 |         s: CxxString,
+  |         ^^^^^^^^^^^^
+
+error: needs a cxx::ExternType impl in order to be used as a field of `S`, argument of `f` or return value of `f`
+  --> tests/ui/by_value_not_supported.rs:10:9
+   |
+10 |         type C;
+   |         ^^^^^^
+
+error: passing opaque C++ type by value is not supported
+  --> tests/ui/by_value_not_supported.rs:16:14
+   |
+16 |         fn f(c: C) -> C;
+   |              ^^^^
+
+error: returning opaque C++ type by value is not supported
+  --> tests/ui/by_value_not_supported.rs:16:23
+   |
+16 |         fn f(c: C) -> C;
+   |                       ^
+
+error: passing opaque Rust type by value is not supported
+  --> tests/ui/by_value_not_supported.rs:17:14
+   |
+17 |         fn g(r: R) -> R;
+   |              ^^^^
+
+error: returning opaque Rust type by value is not supported
+  --> tests/ui/by_value_not_supported.rs:17:23
+   |
+17 |         fn g(r: R) -> R;
+   |                       ^
+
+error: passing C++ string by value is not supported
+  --> tests/ui/by_value_not_supported.rs:18:14
+   |
+18 |         fn h(s: CxxString) -> CxxString;
+   |              ^^^^^^^^^^^^
+
+error: returning C++ string by value is not supported
+  --> tests/ui/by_value_not_supported.rs:18:31
+   |
+18 |         fn h(s: CxxString) -> CxxString;
+   |                               ^^^^^^^^^
diff --git a/tests/ui/const_fn.rs b/tests/ui/const_fn.rs
new file mode 100644 (file)
index 0000000..1ad4894
--- /dev/null
@@ -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 (file)
index 0000000..2dd6608
--- /dev/null
@@ -0,0 +1,5 @@
+error: const extern function is not supported
+ --> tests/ui/const_fn.rs:4:9
+  |
+4 |         const fn f();
+  |         ^^^^^^^^^^^^^
diff --git a/tests/ui/cxx_crate_name_qualified_cxx_string.rs b/tests/ui/cxx_crate_name_qualified_cxx_string.rs
new file mode 100644 (file)
index 0000000..14bac14
--- /dev/null
@@ -0,0 +1,17 @@
+#[cxx::bridge]
+mod ffi {
+    extern "Rust" {
+        fn foo(x: CxxString);
+        fn bar(x: &cxx::CxxString);
+    }
+}
+
+fn foo(_: &cxx::CxxString) {
+    todo!()
+}
+
+fn bar(_: &cxx::CxxString) {
+    todo!()
+}
+
+fn main() {}
diff --git a/tests/ui/cxx_crate_name_qualified_cxx_string.stderr b/tests/ui/cxx_crate_name_qualified_cxx_string.stderr
new file mode 100644 (file)
index 0000000..7859cef
--- /dev/null
@@ -0,0 +1,5 @@
+error: unexpected `cxx::` qualifier found in a `#[cxx::bridge]`
+ --> tests/ui/cxx_crate_name_qualified_cxx_string.rs:5:20
+  |
+5 |         fn bar(x: &cxx::CxxString);
+  |                    ^^^^^^^^^^^^^^
diff --git a/tests/ui/data_enums.rs b/tests/ui/data_enums.rs
new file mode 100644 (file)
index 0000000..aa23200
--- /dev/null
@@ -0,0 +1,8 @@
+#[cxx::bridge]
+mod ffi {
+    enum A {
+        Field(u64),
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/data_enums.stderr b/tests/ui/data_enums.stderr
new file mode 100644 (file)
index 0000000..d8aa09e
--- /dev/null
@@ -0,0 +1,5 @@
+error: enums with data are not supported yet
+ --> tests/ui/data_enums.rs:4:9
+  |
+4 |         Field(u64),
+  |         ^^^^^^^^^^
diff --git a/tests/ui/deny_elided_lifetimes.rs b/tests/ui/deny_elided_lifetimes.rs
new file mode 100644 (file)
index 0000000..0ab3f75
--- /dev/null
@@ -0,0 +1,27 @@
+#![deny(elided_lifetimes_in_paths)]
+
+#[cxx::bridge]
+mod ffi {
+    #[derive(PartialEq, PartialOrd, Hash)]
+    struct Struct<'a> {
+        reference: &'a i32,
+    }
+
+    extern "Rust" {
+        type Rust<'a>;
+    }
+
+    unsafe extern "C++" {
+        type Cpp<'a>;
+
+        fn lifetime_named<'a>(s: &'a i32) -> UniquePtr<Cpp<'a>>;
+
+        fn lifetime_underscore(s: &i32) -> UniquePtr<Cpp<'_>>;
+
+        fn lifetime_elided(s: &i32) -> UniquePtr<Cpp>;
+    }
+}
+
+pub struct Rust<'a>(&'a i32);
+
+fn main() {}
diff --git a/tests/ui/deny_elided_lifetimes.stderr b/tests/ui/deny_elided_lifetimes.stderr
new file mode 100644 (file)
index 0000000..857bb5b
--- /dev/null
@@ -0,0 +1,15 @@
+error: hidden lifetime parameters in types are deprecated
+  --> tests/ui/deny_elided_lifetimes.rs:21:50
+   |
+21 |         fn lifetime_elided(s: &i32) -> UniquePtr<Cpp>;
+   |                                                  ^^^ expected lifetime parameter
+   |
+note: the lint level is defined here
+  --> tests/ui/deny_elided_lifetimes.rs:1:9
+   |
+1  | #![deny(elided_lifetimes_in_paths)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^
+help: indicate the anonymous lifetime
+   |
+21 |         fn lifetime_elided(s: &i32) -> UniquePtr<Cpp<'_>>;
+   |                                                     ++++
diff --git a/tests/ui/deny_missing_docs.rs b/tests/ui/deny_missing_docs.rs
new file mode 100644 (file)
index 0000000..037894b
--- /dev/null
@@ -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 (file)
index 0000000..54ab987
--- /dev/null
@@ -0,0 +1,47 @@
+error: missing documentation for a struct
+  --> tests/ui/deny_missing_docs.rs:11:5
+   |
+11 |     pub struct UndocumentedStruct {
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+note: the lint level is defined here
+  --> tests/ui/deny_missing_docs.rs:6:9
+   |
+6  | #![deny(missing_docs)]
+   |         ^^^^^^^^^^^^
+
+error: missing documentation for a struct field
+  --> tests/ui/deny_missing_docs.rs:12:9
+   |
+12 |         pub undocumented_field: u8,
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: missing documentation for a struct
+  --> tests/ui/deny_missing_docs.rs:21:5
+   |
+21 |     pub enum UndocumentedEnum {
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: missing documentation for an associated constant
+  --> tests/ui/deny_missing_docs.rs:22:9
+   |
+22 |         UndocumentedVariant = 0,
+   |         ^^^^^^^^^^^^^^^^^^^
+
+error: missing documentation for a struct
+  --> tests/ui/deny_missing_docs.rs:44:9
+   |
+44 |         pub type UndocumentedForeignType;
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: missing documentation for a type alias
+  --> tests/ui/deny_missing_docs.rs:49:9
+   |
+49 |         pub type UndocumentedTypeAlias = crate::bindgen::UndocumentedTypeAlias;
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: missing documentation for a function
+  --> tests/ui/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 (file)
index 0000000..3061f76
--- /dev/null
@@ -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 (file)
index 0000000..7592086
--- /dev/null
@@ -0,0 +1,7 @@
+error[E0119]: conflicting implementations of trait `Clone` for type `Struct`
+ --> tests/ui/derive_duplicate.rs:3:21
+  |
+3 |     #[derive(Clone, Clone)]
+  |              -----  ^^^^^ conflicting implementation for `Struct`
+  |              |
+  |              first implementation here
diff --git a/tests/ui/derive_noncopy.rs b/tests/ui/derive_noncopy.rs
new file mode 100644 (file)
index 0000000..ae705fd
--- /dev/null
@@ -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 (file)
index 0000000..359581a
--- /dev/null
@@ -0,0 +1,7 @@
+error[E0204]: the trait `std::marker::Copy` cannot be implemented for this type
+ --> tests/ui/derive_noncopy.rs:4:12
+  |
+4 |     struct TryCopy {
+  |            ^^^^^^^
+5 |         other: Other,
+  |         ------------ this field does not implement `std::marker::Copy`
diff --git a/tests/ui/drop_shared.rs b/tests/ui/drop_shared.rs
new file mode 100644 (file)
index 0000000..5e3de4a
--- /dev/null
@@ -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 (file)
index 0000000..41a5287
--- /dev/null
@@ -0,0 +1,8 @@
+error[E0119]: conflicting implementations of trait `forbid::Drop` for type `Shared`
+ --> tests/ui/drop_shared.rs:3:5
+  |
+1 | #[cxx::bridge]
+  | -------------- first implementation here
+2 | mod ffi {
+3 |     struct Shared {
+  |     ^^^^^^^^^^^^^ conflicting implementation for `Shared`
diff --git a/tests/ui/empty_enum.rs b/tests/ui/empty_enum.rs
new file mode 100644 (file)
index 0000000..987004b
--- /dev/null
@@ -0,0 +1,6 @@
+#[cxx::bridge]
+mod ffi {
+    enum A {}
+}
+
+fn main() {}
diff --git a/tests/ui/empty_enum.stderr b/tests/ui/empty_enum.stderr
new file mode 100644 (file)
index 0000000..60d3b5d
--- /dev/null
@@ -0,0 +1,5 @@
+error: explicit #[repr(...)] is required for enum without any variants
+ --> tests/ui/empty_enum.rs:3:5
+  |
+3 |     enum A {}
+  |     ^^^^^^^^^
diff --git a/tests/ui/empty_struct.rs b/tests/ui/empty_struct.rs
new file mode 100644 (file)
index 0000000..060cfe0
--- /dev/null
@@ -0,0 +1,6 @@
+#[cxx::bridge]
+mod ffi {
+    struct Empty {}
+}
+
+fn main() {}
diff --git a/tests/ui/empty_struct.stderr b/tests/ui/empty_struct.stderr
new file mode 100644 (file)
index 0000000..f6fbfc1
--- /dev/null
@@ -0,0 +1,5 @@
+error: structs without any fields are not supported
+ --> tests/ui/empty_struct.rs:3:5
+  |
+3 |     struct Empty {}
+  |     ^^^^^^^^^^^^^^^
diff --git a/tests/ui/enum_inconsistent.rs b/tests/ui/enum_inconsistent.rs
new file mode 100644 (file)
index 0000000..cd5ffa5
--- /dev/null
@@ -0,0 +1,9 @@
+#[cxx::bridge]
+mod ffi {
+    enum Bad {
+        A = 1u16,
+        B = 2i64,
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/enum_inconsistent.stderr b/tests/ui/enum_inconsistent.stderr
new file mode 100644 (file)
index 0000000..d6d7837
--- /dev/null
@@ -0,0 +1,5 @@
+error: expected u16, found i64
+ --> tests/ui/enum_inconsistent.rs:5:9
+  |
+5 |         B = 2i64,
+  |         ^^^^^^^^
diff --git a/tests/ui/enum_match_without_wildcard.rs b/tests/ui/enum_match_without_wildcard.rs
new file mode 100644 (file)
index 0000000..1a11942
--- /dev/null
@@ -0,0 +1,16 @@
+#[cxx::bridge]
+mod ffi {
+    enum A {
+        FieldA,
+        FieldB,
+    }
+}
+
+fn main() {}
+
+fn matcher(a: ffi::A) -> u32 {
+    match a {
+        ffi::A::FieldA => 2020,
+        ffi::A::FieldB => 2021,
+    }
+}
diff --git a/tests/ui/enum_match_without_wildcard.stderr b/tests/ui/enum_match_without_wildcard.stderr
new file mode 100644 (file)
index 0000000..5808d6f
--- /dev/null
@@ -0,0 +1,17 @@
+error[E0004]: non-exhaustive patterns: `ffi::A { repr: 2_u8..=u8::MAX }` not covered
+  --> tests/ui/enum_match_without_wildcard.rs:12:11
+   |
+12 |     match a {
+   |           ^ pattern `ffi::A { repr: 2_u8..=u8::MAX }` not covered
+   |
+note: `ffi::A` defined here
+  --> tests/ui/enum_match_without_wildcard.rs:3:10
+   |
+3  |     enum A {
+   |          ^
+   = note: the matched value is of type `ffi::A`
+help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
+   |
+14 ~         ffi::A::FieldB => 2021,
+15 ~         ffi::A { repr: 2_u8..=u8::MAX } => todo!(),
+   |
diff --git a/tests/ui/enum_out_of_bounds.rs b/tests/ui/enum_out_of_bounds.rs
new file mode 100644 (file)
index 0000000..9e0090b
--- /dev/null
@@ -0,0 +1,13 @@
+#[cxx::bridge]
+mod ffi {
+    #[repr(u32)]
+    enum Bad1 {
+        A = 0xFFFF_FFFF_FFFF_FFFF,
+    }
+    enum Bad2 {
+        A = 2000,
+        B = 1u8,
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/enum_out_of_bounds.stderr b/tests/ui/enum_out_of_bounds.stderr
new file mode 100644 (file)
index 0000000..3244b6a
--- /dev/null
@@ -0,0 +1,11 @@
+error: discriminant value `18446744073709551615` is outside the limits of u32
+ --> tests/ui/enum_out_of_bounds.rs:5:9
+  |
+5 |         A = 0xFFFF_FFFF_FFFF_FFFF,
+  |         ^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: discriminant value `2000` is outside the limits of u8
+ --> tests/ui/enum_out_of_bounds.rs:9:9
+  |
+9 |         B = 1u8,
+  |         ^^^^^^^
diff --git a/tests/ui/enum_overflows.rs b/tests/ui/enum_overflows.rs
new file mode 100644 (file)
index 0000000..29de1a0
--- /dev/null
@@ -0,0 +1,17 @@
+#[cxx::bridge]
+mod ffi {
+    enum Good1 {
+        A = 0xFFFF_FFFF_FFFF_FFFF,
+    }
+    enum Good2 {
+        B = 0xFFFF_FFFF_FFFF_FFFF,
+        C = 2020,
+    }
+    enum Bad {
+        D = 0xFFFF_FFFF_FFFF_FFFE,
+        E,
+        F,
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/enum_overflows.stderr b/tests/ui/enum_overflows.stderr
new file mode 100644 (file)
index 0000000..76c37bb
--- /dev/null
@@ -0,0 +1,5 @@
+error: discriminant overflow on value after 18446744073709551615
+  --> tests/ui/enum_overflows.rs:13:9
+   |
+13 |         F,
+   |         ^
diff --git a/tests/ui/enum_receiver.rs b/tests/ui/enum_receiver.rs
new file mode 100644 (file)
index 0000000..a27f274
--- /dev/null
@@ -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 (file)
index 0000000..ace7677
--- /dev/null
@@ -0,0 +1,5 @@
+error: unsupported receiver type; C++ does not allow member functions on enums
+ --> tests/ui/enum_receiver.rs:7:20
+  |
+7 |         fn f(self: &Enum);
+  |                    ^^^^^
diff --git a/tests/ui/enum_unsatisfiable.rs b/tests/ui/enum_unsatisfiable.rs
new file mode 100644 (file)
index 0000000..6191287
--- /dev/null
@@ -0,0 +1,9 @@
+#[cxx::bridge]
+mod ffi {
+    enum Bad {
+        A = -0xFFFF_FFFF_FFFF_FFFF,
+        B = 0xFFFF_FFFF_FFFF_FFFF,
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/enum_unsatisfiable.stderr b/tests/ui/enum_unsatisfiable.stderr
new file mode 100644 (file)
index 0000000..e2b37bd
--- /dev/null
@@ -0,0 +1,8 @@
+error: these discriminant values do not fit in any supported enum repr type
+ --> tests/ui/enum_unsatisfiable.rs:3:5
+  |
+3 | /     enum Bad {
+4 | |         A = -0xFFFF_FFFF_FFFF_FFFF,
+5 | |         B = 0xFFFF_FFFF_FFFF_FFFF,
+6 | |     }
+  | |_____^
diff --git a/tests/ui/expected_named.rs b/tests/ui/expected_named.rs
new file mode 100644 (file)
index 0000000..31626d1
--- /dev/null
@@ -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 (file)
index 0000000..c0fa04d
--- /dev/null
@@ -0,0 +1,11 @@
+error[E0106]: missing lifetime specifier
+ --> tests/ui/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, but this is uncommon unless you're returning a borrowed value from a `const` or a `static`, or if you will only have owned values
+  |
+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 (file)
index 0000000..1f93338
--- /dev/null
@@ -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 (file)
index 0000000..32ef9c3
--- /dev/null
@@ -0,0 +1,5 @@
+error: explicit ABI on extern function is not supported
+ --> tests/ui/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 (file)
index 0000000..958accd
--- /dev/null
@@ -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 (file)
index 0000000..1d6796b
--- /dev/null
@@ -0,0 +1,11 @@
+error: extern type bounds are not implemented yet
+ --> tests/ui/extern_type_bound.rs:4:22
+  |
+4 |         type Opaque: PartialEq + PartialOrd;
+  |                      ^^^^^^^^^^^^^^^^^^^^^^
+
+error: unsupported trait
+  --> tests/ui/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 (file)
index 0000000..4de2c98
--- /dev/null
@@ -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 (file)
index 0000000..2b312f0
--- /dev/null
@@ -0,0 +1,5 @@
+error: extern type with generic type parameter is not supported yet
+ --> tests/ui/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 (file)
index 0000000..ad58181
--- /dev/null
@@ -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 (file)
index 0000000..6c3fc7f
--- /dev/null
@@ -0,0 +1,5 @@
+error: lifetime parameter with bounds is not supported yet
+ --> tests/ui/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 (file)
index 0000000..c45813d
--- /dev/null
@@ -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 (file)
index 0000000..4635ec8
--- /dev/null
@@ -0,0 +1,5 @@
+error: function pointer returning Result is not supported yet
+ --> tests/ui/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 (file)
index 0000000..52be79a
--- /dev/null
@@ -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 (file)
index 0000000..f2078df
--- /dev/null
@@ -0,0 +1,5 @@
+error: expected `;`
+ --> tests/ui/function_with_body.rs:4:16
+  |
+4 |         fn f() {}
+  |                ^
diff --git a/tests/ui/generic_enum.rs b/tests/ui/generic_enum.rs
new file mode 100644 (file)
index 0000000..feeb94b
--- /dev/null
@@ -0,0 +1,16 @@
+#[cxx::bridge]
+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
new file mode 100644 (file)
index 0000000..2529af7
--- /dev/null
@@ -0,0 +1,17 @@
+error: enum with generic parameters is not supported
+ --> tests/ui/generic_enum.rs:3:5
+  |
+3 |     enum A<T> {
+  |     ^^^^^^^^^
+
+error: enum with generic parameters is not supported
+ --> tests/ui/generic_enum.rs:7:5
+  |
+7 |     enum B<T> where T: Copy {
+  |     ^^^^^^^^^
+
+error: enum with where-clause is not supported
+  --> tests/ui/generic_enum.rs:11:12
+   |
+11 |     enum C where void: Copy {
+   |            ^^^^^^^^^^^^^^^^
diff --git a/tests/ui/impl_trait_for_type.rs b/tests/ui/impl_trait_for_type.rs
new file mode 100644 (file)
index 0000000..9284f73
--- /dev/null
@@ -0,0 +1,10 @@
+#[cxx::bridge]
+mod ffi {
+    struct S {
+        x: u8,
+    }
+
+    impl UniquePtrTarget for S {}
+}
+
+fn main() {}
diff --git a/tests/ui/impl_trait_for_type.stderr b/tests/ui/impl_trait_for_type.stderr
new file mode 100644 (file)
index 0000000..fa99de5
--- /dev/null
@@ -0,0 +1,5 @@
+error: unexpected impl, expected something like `impl UniquePtr<T> {}`
+ --> tests/ui/impl_trait_for_type.rs:7:10
+  |
+7 |     impl UniquePtrTarget for S {}
+  |          ^^^^^^^^^^^^^^^^^^^^^
diff --git a/tests/ui/include.rs b/tests/ui/include.rs
new file mode 100644 (file)
index 0000000..3e848b5
--- /dev/null
@@ -0,0 +1,12 @@
+#[cxx::bridge]
+mod ffi {
+    extern "C++" {
+        include!("path/to" what);
+        include!(<path/to> what);
+        include!(<path/to);
+        include!(<path[to]>);
+        include!(...);
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/include.stderr b/tests/ui/include.stderr
new file mode 100644 (file)
index 0000000..b801530
--- /dev/null
@@ -0,0 +1,29 @@
+error: unexpected token
+ --> tests/ui/include.rs:4:28
+  |
+4 |         include!("path/to" what);
+  |                            ^^^^
+
+error: unexpected token
+ --> tests/ui/include.rs:5:28
+  |
+5 |         include!(<path/to> what);
+  |                            ^^^^
+
+error: expected `>`
+ --> tests/ui/include.rs:6:26
+  |
+6 |         include!(<path/to);
+  |                          ^
+
+error: unexpected token in include path
+ --> tests/ui/include.rs:7:23
+  |
+7 |         include!(<path[to]>);
+  |                       ^^^^
+
+error: expected "quoted/path/to" or <bracketed/path/to>
+ --> tests/ui/include.rs:8:18
+  |
+8 |         include!(...);
+  |                  ^
diff --git a/tests/ui/lifetime_extern_cxx.rs b/tests/ui/lifetime_extern_cxx.rs
new file mode 100644 (file)
index 0000000..e85b339
--- /dev/null
@@ -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 (file)
index 0000000..a5cc3bd
--- /dev/null
@@ -0,0 +1,5 @@
+error: extern C++ function with lifetimes must be declared in `unsafe extern "C++"` block
+ --> tests/ui/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 (file)
index 0000000..72c37cb
--- /dev/null
@@ -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 (file)
index 0000000..b2ca495
--- /dev/null
@@ -0,0 +1,5 @@
+error: must be `unsafe fn f` in order to expose explicit lifetimes to C++
+ --> tests/ui/lifetime_extern_rust.rs:5:9
+  |
+5 |         fn f<'a>(&'a self, arg: &str) -> &'a str;
+  |         ^^^^^^^^
diff --git a/tests/ui/missing_unsafe.rs b/tests/ui/missing_unsafe.rs
new file mode 100644 (file)
index 0000000..d8c0a23
--- /dev/null
@@ -0,0 +1,10 @@
+#[cxx::bridge]
+mod ffi {
+    extern "Rust" {
+        fn f(x: i32);
+    }
+}
+
+unsafe fn f(_x: i32) {}
+
+fn main() {}
diff --git a/tests/ui/missing_unsafe.stderr b/tests/ui/missing_unsafe.stderr
new file mode 100644 (file)
index 0000000..31ef9e2
--- /dev/null
@@ -0,0 +1,9 @@
+error[E0133]: call to unsafe function `f` is unsafe and requires unsafe function or block
+ --> tests/ui/missing_unsafe.rs:4:12
+  |
+4 |         fn f(x: i32);
+  |            ^        - items do not inherit unsafety from separate enclosing items
+  |            |
+  |            call to unsafe function
+  |
+  = note: consult the function's documentation for information on how to avoid undefined behavior
diff --git a/tests/ui/multiple_parse_error.rs b/tests/ui/multiple_parse_error.rs
new file mode 100644 (file)
index 0000000..138d6d6
--- /dev/null
@@ -0,0 +1,8 @@
+#[cxx::bridge]
+mod ffi {
+    struct Monad<T>;
+
+    extern "Haskell" {}
+}
+
+fn main() {}
diff --git a/tests/ui/multiple_parse_error.stderr b/tests/ui/multiple_parse_error.stderr
new file mode 100644 (file)
index 0000000..32b8e56
--- /dev/null
@@ -0,0 +1,11 @@
+error: unit structs are not supported
+ --> tests/ui/multiple_parse_error.rs:3:5
+  |
+3 |     struct Monad<T>;
+  |     ^^^^^^^^^^^^^^^^
+
+error: unrecognized ABI, requires either "C++" or "Rust"
+ --> tests/ui/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 (file)
index 0000000..f30988b
--- /dev/null
@@ -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 (file)
index 0000000..37e947a
--- /dev/null
@@ -0,0 +1,11 @@
+error: &mut return type is not allowed unless there is a &mut argument
+  --> tests/ui/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
+  --> tests/ui/mut_return.rs:14:9
+   |
+14 |         fn j(t: &Thing) -> &mut [u8];
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/tests/ui/non_integer_discriminant_enum.rs b/tests/ui/non_integer_discriminant_enum.rs
new file mode 100644 (file)
index 0000000..388b463
--- /dev/null
@@ -0,0 +1,8 @@
+#[cxx::bridge]
+mod ffi {
+    enum A {
+        Field = 2020 + 1,
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/non_integer_discriminant_enum.stderr b/tests/ui/non_integer_discriminant_enum.stderr
new file mode 100644 (file)
index 0000000..aa4388f
--- /dev/null
@@ -0,0 +1,5 @@
+error: enums with non-integer literal discriminants are not supported yet
+ --> tests/ui/non_integer_discriminant_enum.rs:4:9
+  |
+4 |         Field = 2020 + 1,
+  |         ^^^^^^^^^^^^^^^^
diff --git a/tests/ui/nonempty_impl_block.rs b/tests/ui/nonempty_impl_block.rs
new file mode 100644 (file)
index 0000000..239d1ec
--- /dev/null
@@ -0,0 +1,12 @@
+#[cxx::bridge]
+mod ffi {
+    struct S {
+        x: u8,
+    }
+
+    impl UniquePtr<S> {
+        fn new() -> Self;
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/nonempty_impl_block.stderr b/tests/ui/nonempty_impl_block.stderr
new file mode 100644 (file)
index 0000000..6f69830
--- /dev/null
@@ -0,0 +1,8 @@
+error: expected an empty impl block
+ --> tests/ui/nonempty_impl_block.rs:7:23
+  |
+7 |       impl UniquePtr<S> {
+  |  _______________________^
+8 | |         fn new() -> Self;
+9 | |     }
+  | |_____^
diff --git a/tests/ui/nonlocal_rust_type.rs b/tests/ui/nonlocal_rust_type.rs
new file mode 100644 (file)
index 0000000..d8a238a
--- /dev/null
@@ -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 (file)
index 0000000..1df7a2c
--- /dev/null
@@ -0,0 +1,23 @@
+error[E0117]: only traits defined in the current crate can be implemented for types defined outside of the crate
+  --> tests/ui/nonlocal_rust_type.rs:10:9
+   |
+10 |         type OptBuilder<'a>;
+   |         ^^^^^--------------
+   |              |
+   |              `Option` is not defined in the current crate
+   |
+   = note: impl doesn't have any local type before any uncovered type parameters
+   = note: for more information see https://doc.rust-lang.org/reference/items/implementations.html#orphan-rules
+   = note: define and implement a trait or new type instead
+
+error[E0117]: only traits defined in the current crate can be implemented for types defined outside of the crate
+  --> tests/ui/nonlocal_rust_type.rs:14:13
+   |
+14 |         rs: Box<OptBuilder<'a>>,
+   |             ^^^^--------------
+   |                 |
+   |                 `Option` is not defined in the current crate
+   |
+   = note: impl doesn't have any local type before any uncovered type parameters
+   = note: for more information see https://doc.rust-lang.org/reference/items/implementations.html#orphan-rules
+   = 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 (file)
index 0000000..99406d7
--- /dev/null
@@ -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 (file)
index 0000000..64a64ee
--- /dev/null
@@ -0,0 +1,76 @@
+error[E0277]: `*const cxx::void` cannot be sent between threads safely
+  --> tests/ui/opaque_autotraits.rs:13:19
+   |
+13 |     assert_send::<ffi::Opaque>();
+   |                   ^^^^^^^^^^^ `*const cxx::void` cannot be sent between threads safely
+   |
+   = help: within `ffi::Opaque`, the trait `Send` is not implemented for `*const cxx::void`
+   = note: required because it appears within the type `[*const cxx::void; 0]`
+note: required because it appears within the type `cxx::private::Opaque`
+  --> src/opaque.rs
+   |
+   | pub struct Opaque {
+   |            ^^^^^^
+note: required because it appears within the type `ffi::Opaque`
+  --> tests/ui/opaque_autotraits.rs:4:14
+   |
+4  |         type Opaque;
+   |              ^^^^^^
+note: required by a bound in `assert_send`
+  --> tests/ui/opaque_autotraits.rs:8:19
+   |
+8  | fn assert_send<T: Send>() {}
+   |                   ^^^^ required by this bound in `assert_send`
+
+error[E0277]: `*const cxx::void` cannot be shared between threads safely
+  --> tests/ui/opaque_autotraits.rs:14:19
+   |
+14 |     assert_sync::<ffi::Opaque>();
+   |                   ^^^^^^^^^^^ `*const cxx::void` cannot be shared between threads safely
+   |
+   = help: within `ffi::Opaque`, the trait `Sync` is not implemented for `*const cxx::void`
+   = note: required because it appears within the type `[*const cxx::void; 0]`
+note: required because it appears within the type `cxx::private::Opaque`
+  --> src/opaque.rs
+   |
+   | pub struct Opaque {
+   |            ^^^^^^
+note: required because it appears within the type `ffi::Opaque`
+  --> tests/ui/opaque_autotraits.rs:4:14
+   |
+4  |         type Opaque;
+   |              ^^^^^^
+note: required by a bound in `assert_sync`
+  --> tests/ui/opaque_autotraits.rs:9:19
+   |
+9  | fn assert_sync<T: Sync>() {}
+   |                   ^^^^ required by this bound in `assert_sync`
+
+error[E0277]: `PhantomPinned` cannot be unpinned
+  --> tests/ui/opaque_autotraits.rs:15:20
+   |
+15 |     assert_unpin::<ffi::Opaque>();
+   |                    ^^^^^^^^^^^ within `ffi::Opaque`, the trait `Unpin` is not implemented for `PhantomPinned`
+   |
+   = note: consider using the `pin!` macro
+           consider using `Box::pin` if you need to access the pinned value outside of the current scope
+note: required because it appears within the type `PhantomData<PhantomPinned>`
+  --> $RUST/core/src/marker.rs
+   |
+   | pub struct PhantomData<T: ?Sized>;
+   |            ^^^^^^^^^^^
+note: required because it appears within the type `cxx::private::Opaque`
+  --> src/opaque.rs
+   |
+   | pub struct Opaque {
+   |            ^^^^^^
+note: required because it appears within the type `ffi::Opaque`
+  --> tests/ui/opaque_autotraits.rs:4:14
+   |
+4  |         type Opaque;
+   |              ^^^^^^
+note: required by a bound in `assert_unpin`
+  --> tests/ui/opaque_autotraits.rs:10:20
+   |
+10 | fn assert_unpin<T: Unpin>() {}
+   |                    ^^^^^ required by this bound in `assert_unpin`
diff --git a/tests/ui/opaque_not_sized.rs b/tests/ui/opaque_not_sized.rs
new file mode 100644 (file)
index 0000000..754e8d3
--- /dev/null
@@ -0,0 +1,10 @@
+#[cxx::bridge]
+mod ffi {
+    extern "Rust" {
+        type TypeR;
+    }
+}
+
+struct TypeR(str);
+
+fn main() {}
diff --git a/tests/ui/opaque_not_sized.stderr b/tests/ui/opaque_not_sized.stderr
new file mode 100644 (file)
index 0000000..85be4af
--- /dev/null
@@ -0,0 +1,17 @@
+error[E0277]: the size for values of type `str` cannot be known at compilation time
+ --> tests/ui/opaque_not_sized.rs:4:14
+  |
+4 |         type TypeR;
+  |              ^^^^^ doesn't have a size known at compile-time
+  |
+  = help: within `TypeR`, the trait `Sized` is not implemented for `str`
+note: required because it appears within the type `TypeR`
+ --> tests/ui/opaque_not_sized.rs:8:8
+  |
+8 | struct TypeR(str);
+  |        ^^^^^
+note: required by a bound in `__AssertSized`
+ --> tests/ui/opaque_not_sized.rs:4:9
+  |
+4 |         type TypeR;
+  |         ^^^^^^^^^^^ required by this bound in `__AssertSized`
diff --git a/tests/ui/pin_mut_opaque.rs b/tests/ui/pin_mut_opaque.rs
new file mode 100644 (file)
index 0000000..ac1ca43
--- /dev/null
@@ -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 (file)
index 0000000..8a5e019
--- /dev/null
@@ -0,0 +1,35 @@
+error: mutable reference to C++ type requires a pin -- use Pin<&mut Opaque>
+ --> tests/ui/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>
+ --> tests/ui/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<...>>
+ --> tests/ui/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`
+ --> tests/ui/pin_mut_opaque.rs:4:9
+  |
+4 |         type Opaque;
+  |         ^^^^^^^^^^^
+
+error: mutable reference to opaque C++ type requires a pin -- use `self: Pin<&mut Opaque>`
+ --> tests/ui/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>`
+ --> tests/ui/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 (file)
index 0000000..73c97c6
--- /dev/null
@@ -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 (file)
index 0000000..f429b1b
--- /dev/null
@@ -0,0 +1,5 @@
+error: pointer argument requires that the function pointer be marked unsafe
+ --> tests/ui/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 (file)
index 0000000..56f0007
--- /dev/null
@@ -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 (file)
index 0000000..d65481b
--- /dev/null
@@ -0,0 +1,5 @@
+error: pointer argument requires that the function be marked unsafe
+ --> tests/ui/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 (file)
index 0000000..eecec3f
--- /dev/null
@@ -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 (file)
index 0000000..a6d4478
--- /dev/null
@@ -0,0 +1,18 @@
+error: expected `mut` or `const` keyword in raw pointer type
+ --> tests/ui/ptr_no_const_mut.rs:6:43
+  |
+6 |         fn get_neither_const_nor_mut() -> *C;
+  |                                           ^
+  |
+help: add `mut` or `const` here
+  |
+6 |         fn get_neither_const_nor_mut() -> *mut C;
+  |                                            +++
+6 |         fn get_neither_const_nor_mut() -> *const C;
+  |                                            +++++
+
+error: expected `const` or `mut`
+ --> tests/ui/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 (file)
index 0000000..9d59c03
--- /dev/null
@@ -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 (file)
index 0000000..ea1dafd
--- /dev/null
@@ -0,0 +1,17 @@
+error: C++ does not allow pointer to reference as a type
+ --> tests/ui/ptr_unsupported.rs:6:38
+  |
+6 |         fn get_ptr_to_reference() -> *mut &C;
+  |                                      ^^^^^^^
+
+error: unsupported unique_ptr target type
+ --> tests/ui/ptr_unsupported.rs:7:38
+  |
+7 |         fn get_uniqueptr_to_ptr() -> UniquePtr<*mut C>;
+  |                                      ^^^^^^^^^^^^^^^^^
+
+error: unsupported vector element type
+ --> tests/ui/ptr_unsupported.rs:8:45
+  |
+8 |         fn get_vector_of_ptr() -> UniquePtr<CxxVector<*mut C>>;
+  |                                             ^^^^^^^^^^^^^^^^^
diff --git a/tests/ui/raw_ident_namespace.rs b/tests/ui/raw_ident_namespace.rs
new file mode 100644 (file)
index 0000000..2c8286c
--- /dev/null
@@ -0,0 +1,53 @@
+use cxx::{type_id, ExternType};
+
+#[repr(transparent)]
+pub struct QuotedRaw(usize);
+
+unsafe impl ExternType for QuotedRaw {
+    type Id = type_id!("org::r#box::implementation::QuotedRaw");
+    type Kind = cxx::kind::Trivial;
+}
+
+#[repr(transparent)]
+pub struct QuotedKeyword(usize);
+
+unsafe impl ExternType for QuotedKeyword {
+    type Id = type_id!("org::box::implementation::QuotedKeyword");
+    type Kind = cxx::kind::Trivial;
+}
+
+#[repr(transparent)]
+pub struct UnquotedRaw(usize);
+
+unsafe impl ExternType for UnquotedRaw {
+    type Id = type_id!(org::r#box::implementation::UnquotedRaw);
+    type Kind = cxx::kind::Trivial;
+}
+
+#[repr(transparent)]
+pub struct UnquotedKeyword(usize);
+
+unsafe impl ExternType for UnquotedKeyword {
+    type Id = type_id!(org::box::implementation::UnquotedKeyword);
+    type Kind = cxx::kind::Trivial;
+}
+
+#[cxx::bridge]
+pub mod ffi {
+    extern "C++" {
+        #[namespace = "org::r#box::implementation"]
+        type QuotedRaw = crate::QuotedRaw;
+
+        #[namespace = "org::box::implementation"]
+        type QuotedKeyword = crate::QuotedKeyword;
+
+        #[namespace = org::r#box::implementation]
+        type UnquotedRaw = crate::UnquotedRaw;
+
+        // Not allowed by rustc (independent of cxx):
+        // #[namespace = org::box::implementation]
+        // type UnquotedKeyword = crate::UnquotedKeyword;
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/raw_ident_namespace.stderr b/tests/ui/raw_ident_namespace.stderr
new file mode 100644 (file)
index 0000000..86c8b6f
--- /dev/null
@@ -0,0 +1,11 @@
+error: raw identifier `r#box` is not allowed in a quoted namespace; use `box`, or remove quotes
+ --> tests/ui/raw_ident_namespace.rs:7:24
+  |
+7 |     type Id = type_id!("org::r#box::implementation::QuotedRaw");
+  |                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: raw identifier `r#box` is not allowed in a quoted namespace; use `box`, or remove quotes
+  --> tests/ui/raw_ident_namespace.rs:38:23
+   |
+38 |         #[namespace = "org::r#box::implementation"]
+   |                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/tests/ui/reference_to_reference.rs b/tests/ui/reference_to_reference.rs
new file mode 100644 (file)
index 0000000..91fe160
--- /dev/null
@@ -0,0 +1,13 @@
+#[cxx::bridge]
+mod ffi {
+    unsafe extern "C++" {
+        type ThingC;
+        fn repro_c(t: &&ThingC);
+    }
+    extern "Rust" {
+        type ThingR;
+        fn repro_r(t: &&ThingR);
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/reference_to_reference.stderr b/tests/ui/reference_to_reference.stderr
new file mode 100644 (file)
index 0000000..765e442
--- /dev/null
@@ -0,0 +1,11 @@
+error: C++ does not allow references to references
+ --> tests/ui/reference_to_reference.rs:5:23
+  |
+5 |         fn repro_c(t: &&ThingC);
+  |                       ^^^^^^^^
+
+error: C++ does not allow references to references
+ --> tests/ui/reference_to_reference.rs:9:23
+  |
+9 |         fn repro_r(t: &&ThingR);
+  |                       ^^^^^^^^
diff --git a/tests/ui/reserved_lifetime.rs b/tests/ui/reserved_lifetime.rs
new file mode 100644 (file)
index 0000000..179a4db
--- /dev/null
@@ -0,0 +1,10 @@
+#[cxx::bridge]
+mod ffi {
+    unsafe extern "C++" {
+        type Logger;
+
+        fn logger<'static>() -> Pin<&'static Logger>;
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/reserved_lifetime.stderr b/tests/ui/reserved_lifetime.stderr
new file mode 100644 (file)
index 0000000..e958267
--- /dev/null
@@ -0,0 +1,5 @@
+error[E0262]: invalid lifetime parameter name: `'static`
+ --> tests/ui/reserved_lifetime.rs:6:19
+  |
+6 |         fn logger<'static>() -> Pin<&'static Logger>;
+  |                   ^^^^^^^ 'static is a reserved lifetime name
diff --git a/tests/ui/reserved_name.rs b/tests/ui/reserved_name.rs
new file mode 100644 (file)
index 0000000..409e67c
--- /dev/null
@@ -0,0 +1,16 @@
+#[cxx::bridge]
+mod ffi {
+    struct UniquePtr {
+        val: usize,
+    }
+
+    extern "C++" {
+        type Box;
+    }
+
+    extern "Rust" {
+        type String;
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/reserved_name.stderr b/tests/ui/reserved_name.stderr
new file mode 100644 (file)
index 0000000..7636f87
--- /dev/null
@@ -0,0 +1,17 @@
+error: reserved name
+ --> tests/ui/reserved_name.rs:3:12
+  |
+3 |     struct UniquePtr {
+  |            ^^^^^^^^^
+
+error: reserved name
+ --> tests/ui/reserved_name.rs:8:14
+  |
+8 |         type Box;
+  |              ^^^
+
+error: reserved name
+  --> tests/ui/reserved_name.rs:12:14
+   |
+12 |         type String;
+   |              ^^^^^^
diff --git a/tests/ui/result_no_display.rs b/tests/ui/result_no_display.rs
new file mode 100644 (file)
index 0000000..b535677
--- /dev/null
@@ -0,0 +1,14 @@
+#[cxx::bridge]
+mod ffi {
+    extern "Rust" {
+        fn f() -> Result<()>;
+    }
+}
+
+pub struct NonError;
+
+fn f() -> Result<(), NonError> {
+    Ok(())
+}
+
+fn main() {}
diff --git a/tests/ui/result_no_display.stderr b/tests/ui/result_no_display.stderr
new file mode 100644 (file)
index 0000000..44d4b31
--- /dev/null
@@ -0,0 +1,8 @@
+error[E0277]: `NonError` doesn't implement `std::fmt::Display`
+ --> tests/ui/result_no_display.rs:4:19
+  |
+4 |         fn f() -> Result<()>;
+  |                   ^^^^^^^^^^ `NonError` cannot be formatted with the default formatter
+  |
+  = help: the trait `std::fmt::Display` is not implemented for `NonError`
+  = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
diff --git a/tests/ui/root_namespace.rs b/tests/ui/root_namespace.rs
new file mode 100644 (file)
index 0000000..886fbd9
--- /dev/null
@@ -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 (file)
index 0000000..b834d91
--- /dev/null
@@ -0,0 +1,5 @@
+error: expected expression, found `]`
+ --> tests/ui/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 (file)
index 0000000..34ca7e3
--- /dev/null
@@ -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 (file)
index 0000000..ba1852b
--- /dev/null
@@ -0,0 +1,18 @@
+error[E0277]: `PhantomPinned` cannot be unpinned
+  --> tests/ui/rust_pinned.rs:6:14
+   |
+6  |         type Pinned;
+   |              ^^^^^^ within `Pinned`, the trait `Unpin` is not implemented for `PhantomPinned`
+   |
+   = note: consider using the `pin!` macro
+           consider using `Box::pin` if you need to access the pinned value outside of the current scope
+note: required because it appears within the type `Pinned`
+  --> tests/ui/rust_pinned.rs:10:12
+   |
+10 | pub struct Pinned {
+   |            ^^^^^^
+note: required by a bound in `__AssertUnpin`
+  --> tests/ui/rust_pinned.rs:6:9
+   |
+6  |         type Pinned;
+   |         ^^^^^^^^^^^^ required by this bound in `__AssertUnpin`
diff --git a/tests/ui/slice_of_type_alias.rs b/tests/ui/slice_of_type_alias.rs
new file mode 100644 (file)
index 0000000..a7bbc11
--- /dev/null
@@ -0,0 +1,30 @@
+use cxx::{type_id, ExternType};
+
+#[repr(C)]
+struct ElementTrivial(usize);
+
+#[repr(C)]
+struct ElementOpaque(usize);
+
+#[cxx::bridge]
+mod ffi {
+    unsafe extern "C++" {
+        type ElementTrivial = crate::ElementTrivial;
+        type ElementOpaque = crate::ElementOpaque;
+
+        fn f(slice: &mut [ElementTrivial]);
+        fn g(slice: &[ElementOpaque]);
+    }
+}
+
+unsafe impl ExternType for ElementTrivial {
+    type Id = type_id!("ElementTrivial");
+    type Kind = cxx::kind::Trivial;
+}
+
+unsafe impl ExternType for ElementOpaque {
+    type Id = type_id!("ElementOpaque");
+    type Kind = cxx::kind::Opaque;
+}
+
+fn main() {}
diff --git a/tests/ui/slice_of_type_alias.stderr b/tests/ui/slice_of_type_alias.stderr
new file mode 100644 (file)
index 0000000..9339da3
--- /dev/null
@@ -0,0 +1,16 @@
+error[E0271]: type mismatch resolving `<ElementOpaque as ExternType>::Kind == Trivial`
+  --> tests/ui/slice_of_type_alias.rs:13:14
+   |
+13 |         type ElementOpaque = crate::ElementOpaque;
+   |              ^^^^^^^^^^^^^ type mismatch resolving `<ElementOpaque as ExternType>::Kind == Trivial`
+   |
+note: expected this to be `Trivial`
+  --> tests/ui/slice_of_type_alias.rs:27:17
+   |
+27 |     type Kind = cxx::kind::Opaque;
+   |                 ^^^^^^^^^^^^^^^^^
+note: required by a bound in `verify_extern_kind`
+  --> 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/slice_unsupported.rs b/tests/ui/slice_unsupported.rs
new file mode 100644 (file)
index 0000000..7a148dd
--- /dev/null
@@ -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 (file)
index 0000000..b781bfd
--- /dev/null
@@ -0,0 +1,11 @@
+error: unsupported &mut [T] element type: opaque C++ type is not supported yet
+ --> tests/ui/slice_unsupported.rs:6:17
+  |
+6 |         fn f(_: &mut [Opaque]);
+  |                 ^^^^^^^^^^^^^
+
+error: needs a cxx::ExternType impl in order to be used as a slice element in &mut [Opaque]
+ --> tests/ui/slice_unsupported.rs:4:9
+  |
+4 |         type Opaque;
+  |         ^^^^^^^^^^^
diff --git a/tests/ui/struct_cycle.rs b/tests/ui/struct_cycle.rs
new file mode 100644 (file)
index 0000000..e74c2b1
--- /dev/null
@@ -0,0 +1,34 @@
+#[cxx::bridge]
+mod ffi {
+    struct Node0 {
+        i: i32,
+    }
+
+    struct Node1 {
+        node2: Node2,
+        vec: Vec<Node3>,
+    }
+
+    struct Node2 {
+        node4: Node4,
+    }
+
+    struct Node3 {
+        node1: Node1,
+    }
+
+    struct Node4 {
+        node0: Node0,
+        node5: Node5,
+    }
+
+    struct Node5 {
+        node2: Node2,
+    }
+
+    struct Node6 {
+        node2: Node2,
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/struct_cycle.stderr b/tests/ui/struct_cycle.stderr
new file mode 100644 (file)
index 0000000..9ee2d83
--- /dev/null
@@ -0,0 +1,23 @@
+error: unsupported cyclic data structure
+  --> tests/ui/struct_cycle.rs:26:9
+   |
+26 |         node2: Node2,
+   |         ^^^^^^^^^^^^
+
+error: unsupported cyclic data structure
+  --> tests/ui/struct_cycle.rs:22:9
+   |
+22 |         node5: Node5,
+   |         ^^^^^^^^^^^^
+
+error: unsupported cyclic data structure
+  --> tests/ui/struct_cycle.rs:13:9
+   |
+13 |         node4: Node4,
+   |         ^^^^^^^^^^^^
+
+error: unsupported cyclic data structure
+ --> tests/ui/struct_cycle.rs:8:9
+  |
+8 |         node2: Node2,
+  |         ^^^^^^^^^^^^
diff --git a/tests/ui/type_alias_rust.rs b/tests/ui/type_alias_rust.rs
new file mode 100644 (file)
index 0000000..67df489
--- /dev/null
@@ -0,0 +1,9 @@
+#[cxx::bridge]
+mod ffi {
+    extern "Rust" {
+        /// Incorrect.
+        type Alias = crate::Type;
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/type_alias_rust.stderr b/tests/ui/type_alias_rust.stderr
new file mode 100644 (file)
index 0000000..8cf9a56
--- /dev/null
@@ -0,0 +1,5 @@
+error: type alias in extern "Rust" block is not supported
+ --> tests/ui/type_alias_rust.rs:5:9
+  |
+5 |         type Alias = crate::Type;
+  |         ^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/tests/ui/unique_ptr_as_mut.rs b/tests/ui/unique_ptr_as_mut.rs
new file mode 100644 (file)
index 0000000..d2c758a
--- /dev/null
@@ -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 (file)
index 0000000..a8248db
--- /dev/null
@@ -0,0 +1,7 @@
+error[E0596]: cannot borrow data in dereference of `UniquePtr<ffi::Opaque>` as mutable
+  --> tests/ui/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
new file mode 100644 (file)
index 0000000..5226ca8
--- /dev/null
@@ -0,0 +1,23 @@
+mod outside {
+    #[repr(C)]
+    pub struct C {
+        pub a: u8,
+    }
+    unsafe impl cxx::ExternType for C {
+        type Id = cxx::type_id!("C");
+        type Kind = cxx::kind::Opaque;
+    }
+}
+
+#[cxx::bridge]
+mod ffi {
+    extern "C++" {
+        type C = crate::outside::C;
+    }
+
+    impl UniquePtr<C> {}
+}
+
+fn main() {
+    cxx::UniquePtr::new(outside::C { a: 4 });
+}
diff --git a/tests/ui/unique_ptr_to_opaque.stderr b/tests/ui/unique_ptr_to_opaque.stderr
new file mode 100644 (file)
index 0000000..7aa5d8a
--- /dev/null
@@ -0,0 +1,21 @@
+error[E0271]: type mismatch resolving `<C as ExternType>::Kind == Trivial`
+  --> tests/ui/unique_ptr_to_opaque.rs:22:25
+   |
+22 |     cxx::UniquePtr::new(outside::C { a: 4 });
+   |     ------------------- ^^^^^^^^^^^^^^^^^^^ type mismatch resolving `<C as ExternType>::Kind == Trivial`
+   |     |
+   |     required by a bound introduced by this call
+   |
+note: expected this to be `Trivial`
+  --> tests/ui/unique_ptr_to_opaque.rs:8:21
+   |
+8  |         type Kind = cxx::kind::Opaque;
+   |                     ^^^^^^^^^^^^^^^^^
+note: required by a bound in `UniquePtr::<T>::new`
+  --> src/unique_ptr.rs
+   |
+   |     pub fn new(value: T) -> Self
+   |            --- required by a bound in this associated function
+   |     where
+   |         T: ExternType<Kind = Trivial>,
+   |                       ^^^^^^^^^^^^^^ required by this bound in `UniquePtr::<T>::new`
diff --git a/tests/ui/unique_ptr_twice.rs b/tests/ui/unique_ptr_twice.rs
new file mode 100644 (file)
index 0000000..14c6f62
--- /dev/null
@@ -0,0 +1,19 @@
+#[cxx::bridge]
+mod here {
+    extern "C++" {
+        type C;
+    }
+
+    impl UniquePtr<C> {}
+}
+
+#[cxx::bridge]
+mod there {
+    extern "C++" {
+        type C = crate::here::C;
+    }
+
+    impl UniquePtr<C> {}
+}
+
+fn main() {}
diff --git a/tests/ui/unique_ptr_twice.stderr b/tests/ui/unique_ptr_twice.stderr
new file mode 100644 (file)
index 0000000..b21791f
--- /dev/null
@@ -0,0 +1,8 @@
+error[E0119]: conflicting implementations of trait `UniquePtrTarget` for type `here::C`
+  --> tests/ui/unique_ptr_twice.rs:16:5
+   |
+7  |     impl UniquePtr<C> {}
+   |     ---------------- first implementation here
+...
+16 |     impl UniquePtr<C> {}
+   |     ^^^^^^^^^^^^^^^^ conflicting implementation for `here::C`
diff --git a/tests/ui/unnamed_receiver.rs b/tests/ui/unnamed_receiver.rs
new file mode 100644 (file)
index 0000000..5f53a0a
--- /dev/null
@@ -0,0 +1,14 @@
+#[cxx::bridge]
+mod ffi {
+    unsafe extern "C++" {
+        type One;
+        type Two;
+        fn f(&mut self);
+    }
+
+    extern "Rust" {
+        fn f(self: &Self);
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/unnamed_receiver.stderr b/tests/ui/unnamed_receiver.stderr
new file mode 100644 (file)
index 0000000..d903b23
--- /dev/null
@@ -0,0 +1,11 @@
+error: unnamed receiver type is only allowed if the surrounding extern block contains exactly one extern type; use `self: &mut TheType`
+ --> tests/ui/unnamed_receiver.rs:6:14
+  |
+6 |         fn f(&mut self);
+  |              ^^^^^^^^^
+
+error: unnamed receiver type is only allowed if the surrounding extern block contains exactly one extern type; use `self: &TheType`
+  --> tests/ui/unnamed_receiver.rs:10:20
+   |
+10 |         fn f(self: &Self);
+   |                    ^^^^^
diff --git a/tests/ui/unpin_impl.rs b/tests/ui/unpin_impl.rs
new file mode 100644 (file)
index 0000000..129fcb1
--- /dev/null
@@ -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 (file)
index 0000000..888d64f
--- /dev/null
@@ -0,0 +1,12 @@
+error[E0283]: type annotations needed
+ --> tests/ui/unpin_impl.rs:4:14
+  |
+4 |         type Opaque;
+  |              ^^^^^^ cannot infer type
+  |
+note: multiple `impl`s satisfying `ffi::Opaque: __AmbiguousIfImpl<_>` found
+ --> tests/ui/unpin_impl.rs:1:1
+  |
+1 | #[cxx::bridge]
+  | ^^^^^^^^^^^^^^
+  = note: this error originates in the attribute macro `cxx::bridge` (in Nightly builds, run with -Z macro-backtrace for more info)
diff --git a/tests/ui/unrecognized_receiver.rs b/tests/ui/unrecognized_receiver.rs
new file mode 100644 (file)
index 0000000..eee8259
--- /dev/null
@@ -0,0 +1,8 @@
+#[cxx::bridge]
+mod ffi {
+    unsafe extern "C++" {
+        fn f(self: &Unrecognized);
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/unrecognized_receiver.stderr b/tests/ui/unrecognized_receiver.stderr
new file mode 100644 (file)
index 0000000..bc645fe
--- /dev/null
@@ -0,0 +1,5 @@
+error: unrecognized receiver type
+ --> tests/ui/unrecognized_receiver.rs:4:20
+  |
+4 |         fn f(self: &Unrecognized);
+  |                    ^^^^^^^^^^^^^
diff --git a/tests/ui/unsupported_elided.rs b/tests/ui/unsupported_elided.rs
new file mode 100644 (file)
index 0000000..4033319
--- /dev/null
@@ -0,0 +1,20 @@
+use std::marker::PhantomData;
+
+#[cxx::bridge]
+mod ffi {
+    extern "Rust" {
+        type T;
+
+        fn f(t: &T) -> &str;
+    }
+}
+
+pub struct T<'a> {
+    _lifetime: PhantomData<&'a ()>,
+}
+
+fn f<'a>(_t: &T<'a>) -> &'a str {
+    ""
+}
+
+fn main() {}
diff --git a/tests/ui/unsupported_elided.stderr b/tests/ui/unsupported_elided.stderr
new file mode 100644 (file)
index 0000000..4ccac6f
--- /dev/null
@@ -0,0 +1,22 @@
+error[E0726]: implicit elided lifetime not allowed here
+ --> tests/ui/unsupported_elided.rs:6:14
+  |
+6 |         type T;
+  |              ^ expected lifetime parameter
+  |
+help: indicate the anonymous lifetime
+  |
+6 |         type T<'_>;
+  |               ++++
+
+error[E0106]: missing lifetime specifier
+ --> tests/ui/unsupported_elided.rs:8:24
+  |
+8 |         fn f(t: &T) -> &str;
+  |                 --     ^ expected named lifetime parameter
+  |
+  = help: this function's return type contains a borrowed value, but the signature does not say which one of `t`'s 2 lifetimes it is borrowed from
+help: consider introducing a named lifetime parameter
+  |
+8 |         fn f<'a>(t: &'a T<'a>) -> &'a str;
+  |             ++++     ++  ++++      ++
diff --git a/tests/ui/vec_opaque.rs b/tests/ui/vec_opaque.rs
new file mode 100644 (file)
index 0000000..d0b279c
--- /dev/null
@@ -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 (file)
index 0000000..ae01adf
--- /dev/null
@@ -0,0 +1,23 @@
+error: Rust Vec containing C++ type is not supported yet
+  --> tests/ui/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>
+  --> tests/ui/vec_opaque.rs:11:9
+   |
+11 |         type Job;
+   |         ^^^^^^^^
+
+error[E0271]: type mismatch resolving `<Job as ExternType>::Kind == Trivial`
+  --> tests/ui/vec_opaque.rs:22:14
+   |
+22 |         type Job = crate::handle::Job;
+   |              ^^^ expected `Trivial`, found `Opaque`
+   |
+note: required by a bound in `verify_extern_kind`
+  --> 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/vector_autotraits.rs b/tests/ui/vector_autotraits.rs
new file mode 100644 (file)
index 0000000..cc918d5
--- /dev/null
@@ -0,0 +1,21 @@
+use cxx::CxxVector;
+
+#[cxx::bridge]
+mod ffi {
+    extern "C++" {
+        type ThreadSafe;
+        type NotThreadSafe;
+    }
+
+    impl CxxVector<ThreadSafe> {}
+    impl CxxVector<NotThreadSafe> {}
+}
+
+unsafe impl Send for ffi::ThreadSafe {}
+
+fn assert_send<T: Send>() {}
+
+fn main() {
+    assert_send::<CxxVector<ffi::ThreadSafe>>();
+    assert_send::<CxxVector<ffi::NotThreadSafe>>();
+}
diff --git a/tests/ui/vector_autotraits.stderr b/tests/ui/vector_autotraits.stderr
new file mode 100644 (file)
index 0000000..5bdb897
--- /dev/null
@@ -0,0 +1,34 @@
+error[E0277]: `*const cxx::void` cannot be sent between threads safely
+  --> tests/ui/vector_autotraits.rs:20:19
+   |
+20 |     assert_send::<CxxVector<ffi::NotThreadSafe>>();
+   |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `*const cxx::void` cannot be sent between threads safely
+   |
+   = help: within `CxxVector<NotThreadSafe>`, the trait `Send` is not implemented for `*const cxx::void`
+   = note: required because it appears within the type `[*const cxx::void; 0]`
+note: required because it appears within the type `cxx::private::Opaque`
+  --> src/opaque.rs
+   |
+   | pub struct Opaque {
+   |            ^^^^^^
+note: required because it appears within the type `NotThreadSafe`
+  --> tests/ui/vector_autotraits.rs:7:14
+   |
+7  |         type NotThreadSafe;
+   |              ^^^^^^^^^^^^^
+   = note: required because it appears within the type `[NotThreadSafe]`
+note: required because it appears within the type `PhantomData<[NotThreadSafe]>`
+  --> $RUST/core/src/marker.rs
+   |
+   | pub struct PhantomData<T: ?Sized>;
+   |            ^^^^^^^^^^^
+note: required because it appears within the type `CxxVector<NotThreadSafe>`
+  --> src/cxx_vector.rs
+   |
+   | pub struct CxxVector<T> {
+   |            ^^^^^^^^^
+note: required by a bound in `assert_send`
+  --> tests/ui/vector_autotraits.rs:16:19
+   |
+16 | fn assert_send<T: Send>() {}
+   |                   ^^^^ required by this bound in `assert_send`
diff --git a/tests/ui/wrong_type_id.rs b/tests/ui/wrong_type_id.rs
new file mode 100644 (file)
index 0000000..9137e51
--- /dev/null
@@ -0,0 +1,15 @@
+#[cxx::bridge(namespace = "folly")]
+mod here {
+    extern "C++" {
+        type StringPiece;
+    }
+}
+
+#[cxx::bridge(namespace = "folly")]
+mod there {
+    extern "C++" {
+        type ByteRange = crate::here::StringPiece;
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/wrong_type_id.stderr b/tests/ui/wrong_type_id.stderr
new file mode 100644 (file)
index 0000000..8cb7898
--- /dev/null
@@ -0,0 +1,13 @@
+error[E0271]: type mismatch resolving `<StringPiece as ExternType>::Id == (f, o, l, l, y, (), B, y, t, e, R, a, n, g, e)`
+  --> tests/ui/wrong_type_id.rs:11:14
+   |
+11 |         type ByteRange = crate::here::StringPiece;
+   |              ^^^^^^^^^ expected a tuple with 15 elements, found one with 17 elements
+   |
+   = note: expected tuple `(f, o, l, l, y, (), B, y, t, e, R, a, n, g, e)`
+              found tuple `(f, o, l, l, y, (), S, t, r, i, n, g, P, i, e, c, e)`
+note: required by a bound in `verify_extern_type`
+  --> src/extern_type.rs
+   |
+   | pub fn verify_extern_type<T: ExternType<Id = Id>, Id>() {}
+   |                                         ^^^^^^^ required by this bound in `verify_extern_type`
diff --git a/tests/unique_ptr.rs b/tests/unique_ptr.rs
new file mode 100644 (file)
index 0000000..e5eb66b
--- /dev/null
@@ -0,0 +1,8 @@
+use cxx::{CxxString, UniquePtr};
+
+#[test]
+#[should_panic = "called deref on a null UniquePtr<CxxString>"]
+fn test_deref_null() {
+    let unique_ptr = UniquePtr::<CxxString>::null();
+    let _: &CxxString = &unique_ptr;
+}
diff --git a/tools/bazel/BUILD.bazel b/tools/bazel/BUILD.bazel
new file mode 100644 (file)
index 0000000..63c8db9
--- /dev/null
@@ -0,0 +1,19 @@
+load("@bazel_skylib//:bzl_library.bzl", "bzl_library")
+
+bzl_library(
+    name = "bzl_srcs",
+    srcs = glob(["**/*.bzl"]),
+    visibility = ["//visibility:public"],
+)
+
+xcode_version(
+    name = "github_actions_xcode_14_2_0",
+    default_macos_sdk_version = "13.1",
+    version = "14.2",
+)
+
+xcode_config(
+    name = "github_actions_xcodes",
+    default = ":github_actions_xcode_14_2_0",
+    versions = [":github_actions_xcode_14_2_0"],
+)
diff --git a/tools/bazel/extension.bzl b/tools/bazel/extension.bzl
new file mode 100644 (file)
index 0000000..e74e081
--- /dev/null
@@ -0,0 +1,30 @@
+"""CXX bzlmod extensions"""
+
+load("@bazel_features//:features.bzl", "bazel_features")
+load("//third-party/bazel:crates.bzl", _crate_repositories = "crate_repositories")
+
+def _crates_vendor_remote_repository_impl(repository_ctx):
+    repository_ctx.symlink(repository_ctx.attr.build_file, "BUILD.bazel")
+
+_crates_vendor_remote_repository = repository_rule(
+    implementation = _crates_vendor_remote_repository_impl,
+    attrs = {
+        "build_file": attr.label(mandatory = True),
+    },
+)
+
+def _crate_repositories_impl(module_ctx):
+    _crate_repositories()
+    _crates_vendor_remote_repository(
+        name = "crates.io",
+        build_file = "//third-party/bazel:BUILD.bazel",
+    )
+
+    metadata_kwargs = {}
+    if bazel_features.external_deps.extension_metadata_has_reproducible:
+        metadata_kwargs["reproducible"] = True
+    return module_ctx.extension_metadata(**metadata_kwargs)
+
+crate_repositories = module_extension(
+    implementation = _crate_repositories_impl,
+)
diff --git a/tools/bazel/rust_cxx_bridge.bzl b/tools/bazel/rust_cxx_bridge.bzl
new file mode 100644 (file)
index 0000000..9aa2dd5
--- /dev/null
@@ -0,0 +1,57 @@
+"""CXX Bridge rules."""
+
+load("@bazel_skylib//rules:run_binary.bzl", "run_binary")
+load("@rules_cc//cc:defs.bzl", "cc_library")
+
+def rust_cxx_bridge(name, src, deps = [], linkstatic = True, **kwargs):
+    """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 [].
+        **kwargs: Common arguments to pass through to underlying rules.
+    """
+    native.alias(
+        name = "%s/header" % name,
+        actual = src + ".h",
+        **kwargs
+    )
+
+    native.alias(
+        name = "%s/source" % name,
+        actual = src + ".cc",
+        **kwargs
+    )
+
+    run_binary(
+        name = "%s/generated" % name,
+        srcs = [src],
+        outs = [
+            src + ".h",
+            src + ".cc",
+        ],
+        args = [
+            "$(execpath %s)" % src,
+            "-o",
+            "$(execpath %s.h)" % src,
+            "-o",
+            "$(execpath %s.cc)" % src,
+        ],
+        tool = "@cxx.rs//:codegen",
+        **kwargs
+    )
+
+    cc_library(
+        name = name,
+        srcs = [src + ".cc"],
+        deps = deps + [":%s/include" % name],
+        linkstatic = linkstatic,
+        **kwargs
+    )
+
+    cc_library(
+        name = "%s/include" % name,
+        hdrs = [src + ".h"],
+        **kwargs
+    )
diff --git a/tools/buck/rust_cxx_bridge.bzl b/tools/buck/rust_cxx_bridge.bzl
new file mode 100644 (file)
index 0000000..1dce395
--- /dev/null
@@ -0,0 +1,38 @@
+def rust_cxx_bridge(
+        name: str,
+        src: str,
+        deps: list[str] = []):
+    native.export_file(
+        name = "%s/header" % name,
+        src = ":%s/generated[generated.h]" % name,
+        out = src + ".h",
+    )
+
+    native.export_file(
+        name = "%s/source" % name,
+        src = ":%s/generated[generated.cc]" % name,
+        out = src + ".cc",
+    )
+
+    native.genrule(
+        name = "%s/generated" % name,
+        srcs = [src],
+        outs = {
+            "generated.cc": ["generated.cc"],
+            "generated.h": ["generated.h"],
+        },
+        cmd = "$(exe //:codegen) ${SRCS} -o ${OUT}/generated.h -o ${OUT}/generated.cc",
+        type = "cxxbridge",
+    )
+
+    native.cxx_library(
+        name = name,
+        srcs = [":%s/source" % name],
+        preferred_linkage = "static",
+        exported_deps = deps + [":%s/include" % name],
+    )
+
+    native.cxx_library(
+        name = "%s/include" % name,
+        exported_headers = [":%s/header" % name],
+    )
diff --git a/tools/buck/toolchains/BUCK b/tools/buck/toolchains/BUCK
new file mode 100644 (file)
index 0000000..411a82f
--- /dev/null
@@ -0,0 +1,48 @@
+load("@prelude//tests:test_toolchain.bzl", "noop_test_toolchain")
+load("@prelude//toolchains:cxx.bzl", "system_cxx_toolchain")
+load("@prelude//toolchains:genrule.bzl", "system_genrule_toolchain")
+load("@prelude//toolchains:python.bzl", "system_python_bootstrap_toolchain")
+load("@prelude//toolchains:remote_test_execution.bzl", "remote_test_execution_toolchain")
+load("@prelude//toolchains:rust.bzl", "system_rust_toolchain")
+
+system_cxx_toolchain(
+    name = "cxx",
+    cxx_flags = select({
+        "config//os:linux": ["-std=c++17"],
+        "config//os:macos": ["-std=c++17"],
+        "config//os:windows": [],
+    }),
+    link_flags = select({
+        "config//os:linux": ["-lstdc++"],
+        "config//os:macos": ["-lc++"],
+        "config//os:windows": [],
+    }),
+    visibility = ["PUBLIC"],
+)
+
+system_genrule_toolchain(
+    name = "genrule",
+    visibility = ["PUBLIC"],
+)
+
+system_python_bootstrap_toolchain(
+    name = "python_bootstrap",
+    visibility = ["PUBLIC"],
+)
+
+system_rust_toolchain(
+    name = "rust",
+    default_edition = None,
+    doctests = True,
+    visibility = ["PUBLIC"],
+)
+
+noop_test_toolchain(
+    name = "test",
+    visibility = ["PUBLIC"],
+)
+
+remote_test_execution_toolchain(
+    name = "remote_test_execution",
+    visibility = ["PUBLIC"],
+)
diff --git a/tools/cargo/build.rs b/tools/cargo/build.rs
new file mode 100644 (file)
index 0000000..8bbaf6a
--- /dev/null
@@ -0,0 +1,79 @@
+use std::io::{self, Write};
+#[cfg(windows)]
+use std::os::windows::fs as windows;
+use std::path::Path;
+use std::process;
+#[cfg(windows)]
+use std::{env, fs};
+
+const MISSING: &str = "
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+When building `cxx` from a git clone, git's symlink support needs
+to be enabled on platforms that have it off by default (Windows).
+Either use:
+
+   $ git config --global core.symlinks true
+
+prior to cloning, or else use:
+
+   $ git clone -c core.symlinks=true https://github.com/dtolnay/cxx
+
+for the clone.
+
+Symlinks are only required when compiling locally from a clone of
+the git repository---they are NOT required when building `cxx` as
+a Cargo-managed (possibly transitive) build dependency downloaded
+through crates.io.
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+";
+
+#[cfg(windows)]
+const DENIED: &str = "
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+When building `cxx` from a git clone on Windows we need Developer
+Mode enabled for symlink support.
+
+To enable Developer Mode: go under Settings to Update & Security,
+then 'For developers', and turn on the toggle for Developer Mode.
+
+For more explanation of symlinks in Windows, see these resources:
+> https://blogs.windows.com/windowsdeveloper/2016/12/02/symlinks-windows-10/
+> https://docs.microsoft.com/windows/uwp/get-started/enable-your-device-for-development
+
+Symlinks are only required when compiling locally from a clone of
+the git repository---they are NOT required when building `cxx` as
+a Cargo-managed (possibly transitive) build dependency downloaded
+through crates.io.
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+";
+
+fn main() {
+    println!("cargo:rerun-if-changed=build.rs");
+    println!("cargo:rustc-cfg=check_cfg");
+    println!("cargo:rustc-check-cfg=cfg(check_cfg)");
+    println!("cargo:rustc-check-cfg=cfg(feature, values(\"experimental-async-fn\"))");
+    println!("cargo:rustc-check-cfg=cfg(feature, values(\"experimental-enum-variants-from-header\"))");
+
+    if Path::new("src/syntax/mod.rs").exists() {
+        return;
+    }
+
+    #[allow(unused_mut)]
+    let mut message = MISSING;
+
+    #[cfg(windows)]
+    if let Some(out_dir) = env::var_os("OUT_DIR") {
+        let parent_dir = Path::new(&out_dir).join("symlink");
+        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;
+        }
+    }
+
+    let _ = io::stderr().write_all(message.as_bytes());
+    process::exit(1);
+}