Snap for 7272808 from f89b6a0193e78a7cf95fa1dc9c4df7bfd3b50dcd to sc-release

Change-Id: I86b45f909ebb18df00bc36c38418161ed5a27319
diff --git a/.clang-tidy b/.clang-tidy
new file mode 100644
index 0000000..b0a6da9
--- /dev/null
+++ b/.clang-tidy
@@ -0,0 +1,18 @@
+Checks:
+  clang-analyzer-*,
+  clang-diagnostic-*,
+  cppcoreguidelines-*,
+  modernize-*,
+  -cppcoreguidelines-macro-usage,
+  -cppcoreguidelines-owning-memory,
+  -cppcoreguidelines-pro-bounds-array-to-pointer-decay,
+  -cppcoreguidelines-pro-bounds-pointer-arithmetic,
+  -cppcoreguidelines-pro-type-const-cast,
+  -cppcoreguidelines-pro-type-member-init,
+  -cppcoreguidelines-pro-type-reinterpret-cast,
+  -cppcoreguidelines-pro-type-vararg,
+  -cppcoreguidelines-special-member-functions,
+  -modernize-use-default-member-init,
+  -modernize-use-equals-default,
+  -modernize-use-trailing-return-type,
+HeaderFilterRegex: cxx\.h
diff --git a/.clippy.toml b/.clippy.toml
new file mode 100644
index 0000000..11d46a7
--- /dev/null
+++ b/.clippy.toml
@@ -0,0 +1 @@
+msrv = "1.48.0"
diff --git a/.devcontainer/build.Dockerfile b/.devcontainer/build.Dockerfile
index 6085459..f276388 100644
--- a/.devcontainer/build.Dockerfile
+++ b/.devcontainer/build.Dockerfile
@@ -5,7 +5,7 @@
     && apt-get -y install --no-install-recommends openjdk-11-jdk lld \
     && rustup default nightly 2>&1 \
     && rustup component add rust-analyzer-preview rustfmt clippy 2>&1 \
-    && wget -q -O bin/install-bazel https://github.com/bazelbuild/bazel/releases/download/2.1.1/bazel-2.1.1-installer-linux-x86_64.sh \
+    && wget -q -O bin/install-bazel https://github.com/bazelbuild/bazel/releases/download/4.0.0/bazel-4.0.0-installer-linux-x86_64.sh \
     && wget -q -O bin/buck https://jitpack.io/com/github/facebook/buck/a5f0342ae3/buck-a5f0342ae3-java11.pex \
     && wget -q -O bin/buildifier https://github.com/bazelbuild/buildtools/releases/latest/download/buildifier \
     && wget -q -O tmp/watchman.zip https://github.com/facebook/watchman/releases/download/v2020.09.21.00/watchman-v2020.09.21.00-linux.zip \
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 26b2722..3fafd9b 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -16,7 +16,7 @@
           - rust: nightly
           - rust: beta
           - rust: stable
-          - rust: 1.43.0
+          - rust: 1.48.0
           - name: macOS
             rust: nightly
             os: macos
@@ -26,6 +26,10 @@
           - name: Windows (msvc)
             rust: nightly-x86_64-pc-windows-msvc
             os: windows
+            flags: /EHsc
+    env:
+      CXXFLAGS: ${{matrix.flags}}
+      RUSTFLAGS: --cfg deny_warnings
     steps:
       - name: Enable symlinks (windows)
         if: matrix.os == 'windows'
@@ -42,7 +46,8 @@
         # still ensure the test is kept passing on the basis of the scheduled
         # builds.
         if: matrix.os && github.event_name != 'schedule'
-        run: echo '::set-env name=RUSTFLAGS::--cfg skip_ui_tests'
+        run: echo "RUSTFLAGS=--cfg skip_ui_tests $RUSTFLAGS" >> $GITHUB_ENV
+        shell: bash
       - run: cargo run --manifest-path demo/Cargo.toml
       - run: cargo test --workspace --exclude cxx-test-suite
 
@@ -63,7 +68,7 @@
           chmod +x bin/buck
           echo bin >> $GITHUB_PATH
       - name: Install lld
-        run: sudo apt install lld
+        run: sudo apt-get install lld
       - name: Vendor dependencies
         run: |
           cp third-party/Cargo.lock .
@@ -79,7 +84,7 @@
       - uses: actions/checkout@v2
       - name: Install Bazel
         run: |
-          wget -q -O install.sh https://github.com/bazelbuild/bazel/releases/download/2.1.1/bazel-2.1.1-installer-linux-x86_64.sh
+          wget -q -O install.sh https://github.com/bazelbuild/bazel/releases/download/4.0.0/bazel-4.0.0-installer-linux-x86_64.sh
           chmod +x install.sh
           ./install.sh --user
           echo $HOME/bin >> $GITHUB_PATH
@@ -89,7 +94,19 @@
   clippy:
     name: Clippy
     runs-on: ubuntu-latest
+    if: github.event_name != 'pull_request'
     steps:
       - uses: actions/checkout@v2
       - uses: dtolnay/rust-toolchain@clippy
       - run: cargo clippy --workspace --tests -- -Dclippy::all
+
+  clang-tidy:
+    name: Clang Tidy
+    runs-on: ubuntu-latest
+    if: github.event_name != 'pull_request'
+    steps:
+      - uses: actions/checkout@v2
+      - name: Install clang-tidy
+        run: sudo apt-get install clang-tidy-11
+      - name: Run clang-tidy
+        run: clang-tidy-11 src/cxx.cc --warnings-as-errors=*
diff --git a/.github/workflows/site.yml b/.github/workflows/site.yml
index 73b8186..d6b0ca6 100644
--- a/.github/workflows/site.yml
+++ b/.github/workflows/site.yml
@@ -6,6 +6,7 @@
       - master
     paths:
       - book/**
+      - .github/workflows/site.yml
 
 jobs:
   deploy:
@@ -15,16 +16,15 @@
       - uses: actions/checkout@v2
 
       - name: Get mdBook
-        working-directory: book
         run: |
-          export MDBOOK_VERSION="v0.4.4"
+          export MDBOOK_VERSION="dtolnay"
           export MDBOOK_TARBALL="mdbook-${MDBOOK_VERSION}-x86_64-unknown-linux-gnu.tar.gz"
-          export MDBOOK_URL="https://github.com/rust-lang/mdBook/releases/download/${MDBOOK_VERSION}/${MDBOOK_TARBALL}"
-          curl -Lf "${MDBOOK_URL}" | tar -xz
+          export MDBOOK_URL="https://github.com/dtolnay/mdBook/releases/download/cxx/${MDBOOK_TARBALL}"
+          curl -Lf "${MDBOOK_URL}" | tar -xzC book
+          book/mdbook --version
 
       - name: Build
-        working-directory: book
-        run: ./mdbook build
+        run: book/build.sh
 
       - name: Push to gh-pages
         working-directory: book/build
diff --git a/.vscode/launch.json b/.vscode/launch.json
index 244f5c4..0218f47 100644
--- a/.vscode/launch.json
+++ b/.vscode/launch.json
@@ -12,6 +12,18 @@
                     "kind": "bin"
                 }
             }
+        },
+        {
+            "name": "Debug cargo tests",
+            "type": "lldb",
+            "request": "launch",
+            "cargo": {
+                "args": ["test", "--no-run"],
+                "filter": {
+                    "name": "test",
+                    "kind": "test"
+                }
+            }
         }
     ]
 }
diff --git a/Cargo.toml b/Cargo.toml
index f0692ae..e2b189e 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,13 +1,14 @@
 [package]
 name = "cxx"
-version = "0.5.9" # remember to update html_root_url
+version = "1.0.42" # remember to update html_root_url
 authors = ["David Tolnay <dtolnay@gmail.com>"]
 edition = "2018"
-links = "cxxbridge05"
+links = "cxxbridge1"
 license = "MIT OR Apache-2.0"
 description = "Safe interop between Rust and C++"
 repository = "https://github.com/dtolnay/cxx"
 documentation = "https://docs.rs/cxx"
+homepage = "https://cxx.rs"
 readme = "README.md"
 exclude = ["/demo", "/gen", "/syntax", "/third-party"]
 keywords = ["ffi"]
@@ -20,16 +21,16 @@
 "c++20" = ["cxxbridge-flags/c++20"]
 
 [dependencies]
-cxxbridge-macro = { version = "=0.5.9", path = "macro" }
+cxxbridge-macro = { version = "=1.0.42", path = "macro" }
 link-cplusplus = "1.0"
 
 [build-dependencies]
 cc = "1.0.49"
-cxxbridge-flags = { version = "=0.5.9", path = "flags", default-features = false }
+cxxbridge-flags = { version = "=1.0.42", path = "flags", default-features = false }
 
 [dev-dependencies]
-cxx-build = { version = "=0.5.9", path = "gen/build" }
-cxx-gen = { version = "0.6", path = "gen/lib" }
+cxx-build = { version = "=1.0.42", path = "gen/build" }
+cxx-gen = { version = "0.7", path = "gen/lib" }
 cxx-test-suite = { version = "0", path = "tests/ffi" }
 rustversion = "1.0"
 trybuild = { version = "1.0.33", features = ["diff"] }
@@ -39,3 +40,7 @@
 
 [package.metadata.docs.rs]
 targets = ["x86_64-unknown-linux-gnu"]
+
+[patch.crates-io]
+cxx = { path = "." }
+cxx-build = { path = "gen/build" }
diff --git a/README.md b/README.md
index 9c0289b..7dd4ded 100644
--- a/README.md
+++ b/README.md
@@ -18,17 +18,24 @@
 
 ```toml
 [dependencies]
-cxx = "0.5"
+cxx = "1.0"
 
 [build-dependencies]
-cxx-build = "0.5"
+cxx-build = "1.0"
 ```
 
-*Compiler support: requires rustc 1.43+ and c++11 or newer*<br>
+*Compiler support: requires rustc 1.48+ and c++11 or newer*<br>
 *[Release notes](https://github.com/dtolnay/cxx/releases)*
 
 <br>
 
+## Guide
+
+Please see **<https://cxx.rs>** for a tutorial, reference material, and example
+code.
+
+<br>
+
 ## Overview
 
 The idea is that we define the signatures of both sides of our FFI boundary
@@ -90,7 +97,7 @@
         fn next_chunk(buf: &mut MultiBuf) -> &[u8];
     }
 
-    extern "C++" {
+    unsafe extern "C++" {
         // One or more headers with the matching C++ declarations. Our code
         // generators don't read it but it gets #include'd and used in static
         // assertions to ensure our picture of the FFI boundary is accurate.
@@ -151,19 +158,19 @@
 - **Functions** &mdash; implemented in either language, callable from the other
   language.
 
-Within the `extern "C"` part of the CXX bridge we list the types and functions
-for which C++ is the source of truth, as well as the header(s) that declare
-those APIs. In the future it's possible that this section could be generated
-bindgen-style from the headers but for now we need the signatures written out;
-static assertions will verify that they are accurate.
+Within the `extern "Rust"` part of the CXX bridge we list the types and
+functions for which Rust is the source of truth. These all implicitly refer to
+the `super` module, the parent module of the CXX bridge. You can think of the
+two items listed in the example above as being like `use super::MultiBuf` and
+`use super::next_chunk` except re-exported to C++. The parent module will either
+contain the definitions directly for simple things, or contain the relevant
+`use` statements to bring them into scope from elsewhere.
 
-Within the `extern "Rust"` part, we list types and functions for which Rust is
-the source of truth. These all implicitly refer to the `super` module, the
-parent module of the CXX bridge. You can think of the two items listed in the
-example above as being like `use super::ThingR` and `use super::print_r` except
-re-exported to C++. The parent module will either contain the definitions
-directly for simple things, or contain the relevant `use` statements to bring
-them into scope from elsewhere.
+Within the `extern "C++"` part, we list types and functions for which C++ is the
+source of truth, as well as the header(s) that declare those APIs. In the future
+it's possible that this section could be generated bindgen-style from the
+headers but for now we need the signatures written out; static assertions will
+verify that they are accurate.
 
 Your function implementations themselves, whether in C++ or Rust, *do not* need
 to be defined as `extern "C"` ABI or no\_mangle. CXX will put in the right shims
@@ -228,7 +235,7 @@
 # Cargo.toml
 
 [build-dependencies]
-cxx-build = "0.5"
+cxx-build = "1.0"
 ```
 
 ```rust
@@ -315,12 +322,16 @@
 <tr><th>name in Rust</th><th>name in C++</th><th>restrictions</th></tr>
 <tr><td>String</td><td>rust::String</td><td></td></tr>
 <tr><td>&amp;str</td><td>rust::Str</td><td></td></tr>
-<tr><td>&amp;[u8]</td><td>rust::Slice&lt;uint8_t&gt;</td><td><sup><i>arbitrary &amp;[T] not implemented yet</i></sup></td></tr>
-<tr><td><a href="https://docs.rs/cxx/0.5/cxx/struct.CxxString.html">CxxString</a></td><td>std::string</td><td><sup><i>cannot be passed by value</i></sup></td></tr>
+<tr><td>&amp;[T]</td><td>rust::Slice&lt;const T&gt;</td><td><sup><i>cannot hold opaque C++ type</i></sup></td></tr>
+<tr><td>&amp;mut [T]</td><td>rust::Slice&lt;T&gt;</td><td><sup><i>cannot hold opaque C++ type</i></sup></td></tr>
+<tr><td><a href="https://docs.rs/cxx/1.0/cxx/struct.CxxString.html">CxxString</a></td><td>std::string</td><td><sup><i>cannot be passed by value</i></sup></td></tr>
 <tr><td>Box&lt;T&gt;</td><td>rust::Box&lt;T&gt;</td><td><sup><i>cannot hold opaque C++ type</i></sup></td></tr>
-<tr><td><a href="https://docs.rs/cxx/0.5/cxx/struct.UniquePtr.html">UniquePtr&lt;T&gt;</a></td><td>std::unique_ptr&lt;T&gt;</td><td><sup><i>cannot hold opaque Rust type</i></sup></td></tr>
+<tr><td><a href="https://docs.rs/cxx/1.0/cxx/struct.UniquePtr.html">UniquePtr&lt;T&gt;</a></td><td>std::unique_ptr&lt;T&gt;</td><td><sup><i>cannot hold opaque Rust type</i></sup></td></tr>
+<tr><td><a href="https://docs.rs/cxx/1.0/cxx/struct.SharedPtr.html">SharedPtr&lt;T&gt;</a></td><td>std::shared_ptr&lt;T&gt;</td><td><sup><i>cannot hold opaque Rust type</i></sup></td></tr>
+<tr><td>[T; N]</td><td>std::array&lt;T, N&gt;</td><td><sup><i>cannot hold opaque C++ type</i></sup></td></tr>
 <tr><td>Vec&lt;T&gt;</td><td>rust::Vec&lt;T&gt;</td><td><sup><i>cannot hold opaque C++ type</i></sup></td></tr>
-<tr><td><a href="https://docs.rs/cxx/0.5/cxx/struct.CxxVector.html">CxxVector&lt;T&gt;</a></td><td>std::vector&lt;T&gt;</td><td><sup><i>cannot be passed by value, cannot hold opaque Rust type</i></sup></td></tr>
+<tr><td><a href="https://docs.rs/cxx/1.0/cxx/struct.CxxVector.html">CxxVector&lt;T&gt;</a></td><td>std::vector&lt;T&gt;</td><td><sup><i>cannot be passed by value, cannot hold opaque Rust type</i></sup></td></tr>
+<tr><td>*mut T, *const T</td><td>T*, const T*</td><td><sup><i>fn with a raw pointer argument must be declared unsafe to call</i></sup></td></tr>
 <tr><td>fn(T, U) -&gt; V</td><td>rust::Fn&lt;V(T, U)&gt;</td><td><sup><i>only passing from Rust to C++ is implemented so far</i></sup></td></tr>
 <tr><td>Result&lt;T&gt;</td><td>throw/catch</td><td><sup><i>allowed as return type only</i></sup></td></tr>
 </table>
@@ -341,7 +352,6 @@
 <tr><td>Option&lt;T&gt;</td><td><sup><i>tbd</i></sup></td></tr>
 <tr><td><sup><i>tbd</i></sup></td><td>std::map&lt;K, V&gt;</td></tr>
 <tr><td><sup><i>tbd</i></sup></td><td>std::unordered_map&lt;K, V&gt;</td></tr>
-<tr><td><sup><i>tbd</i></sup></td><td>std::shared_ptr&lt;T&gt;</td></tr>
 </table>
 
 <br>
diff --git a/WORKSPACE b/WORKSPACE
index 6093eee..17a0af5 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -1,39 +1,23 @@
+workspace(name = "cxx.rs")
+
 load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
 load("//tools/bazel:vendor.bzl", "vendor")
 
 http_archive(
-    name = "io_bazel_rules_rust",
-    sha256 = "5ed804fcd10a506a5b8e9e59bc6b3b7f43bc30c87ce4670e6f78df43604894fd",
-    strip_prefix = "rules_rust-fdf9655ba95616e0314b4e0ebab40bb0c5fe005c",
-    # Master branch as of 2020-07-30
-    url = "https://github.com/bazelbuild/rules_rust/archive/fdf9655ba95616e0314b4e0ebab40bb0c5fe005c.tar.gz",
-)
-
-http_archive(
-    name = "bazel_skylib",
-    sha256 = "97e70364e9249702246c0e9444bccdc4b847bed1eb03c5a3ece4f83dfe6abc44",
+    name = "rules_rust",
+    sha256 = "e6d835ee673f388aa5b62dc23d82db8fc76497e93fa47d8a4afe97abaf09b10d",
+    strip_prefix = "rules_rust-f37b9d6a552e9412285e627f30cb124e709f4f7a",
     urls = [
-        "https://mirror.bazel.build/github.com/bazelbuild/bazel-skylib/releases/download/1.0.2/bazel-skylib-1.0.2.tar.gz",
-        "https://github.com/bazelbuild/bazel-skylib/releases/download/1.0.2/bazel-skylib-1.0.2.tar.gz",
+        # Master branch as of 2021-01-27
+        "https://github.com/bazelbuild/rules_rust/archive/f37b9d6a552e9412285e627f30cb124e709f4f7a.tar.gz",
     ],
 )
 
-load("@io_bazel_rules_rust//:workspace.bzl", "bazel_version")
+load("@rules_rust//rust:repositories.bzl", "rust_repositories")
 
-bazel_version(name = "bazel_version")
-
-load("@io_bazel_rules_rust//rust:repositories.bzl", "rust_repository_set")
-
-rust_repository_set(
-    name = "rust_1_47_linux",
-    exec_triple = "x86_64-unknown-linux-gnu",
-    version = "1.47.0",
-)
-
-rust_repository_set(
-    name = "rust_1_47_darwin",
-    exec_triple = "x86_64-apple-darwin",
-    version = "1.47.0",
+rust_repositories(
+    edition = "2018",
+    version = "1.50.0",
 )
 
 vendor(
diff --git a/book/.gitignore b/book/.gitignore
index 690b5b8..7277507 100644
--- a/book/.gitignore
+++ b/book/.gitignore
@@ -1,2 +1,3 @@
 /build
 /mdbook
+/node_modules
diff --git a/book/book.toml b/book/book.toml
index d3187e0..066f3a6 100644
--- a/book/book.toml
+++ b/book/book.toml
@@ -1,14 +1,22 @@
 [book]
-title = "CXX"
+#title = "Rust ♡ C++"
 authors = ["David Tolnay"]
-description = "Guide for the `cxx` crate, a safe approach to FFI between Rust and C++."
+description = "CXX — safe interop between Rust and C++"
 
 [rust]
 edition = "2018"
 
 [build]
 build-dir = "build"
+create-missing = false
 
 [output.html]
+additional-css = ["css/cxx.css"]
 cname = "cxx.rs"
 git-repository-url = "https://github.com/dtolnay/cxx"
+playground = { copyable = false }
+print = { enable = false }
+
+[output.html.redirect]
+"binding/index.html" = "../bindings.html"
+"build/index.html" = "../building.html"
diff --git a/book/build.js b/book/build.js
new file mode 100755
index 0000000..2cda586
--- /dev/null
+++ b/book/build.js
@@ -0,0 +1,104 @@
+#!/usr/bin/env node
+
+const fs = require('fs');
+const cheerio = require('cheerio');
+const hljs = require('./build/highlight.js');
+const Entities = require('html-entities').AllHtmlEntities;
+const entities = new Entities();
+
+const githublink = `\
+<li class="part-title">\
+<a href="https://github.com/dtolnay/cxx">\
+<i class="fa fa-github"></i>\
+https://github.com/dtolnay/cxx\
+</a>\
+</li>`;
+
+const opengraph = `\
+<meta property="og:image" content="https://cxx.rs/cxx.png" />\
+<meta property="og:site_name" content="CXX" />\
+<meta property="og:title" content="CXX — safe interop between Rust and C++" />\
+<meta name="twitter:image:src" content="https://cxx.rs/cxx.png" />\
+<meta name="twitter:site" content="@davidtolnay" />\
+<meta name="twitter:card" content="summary" />\
+<meta name="twitter:title" content="CXX — safe interop between Rust and C++" />`;
+
+const htmljs = `\
+var html = document.querySelector('html');
+html.classList.remove('no-js');
+html.classList.add('js');`;
+
+const dirs = ['build'];
+while (dirs.length) {
+  const dir = dirs.pop();
+  fs.readdirSync(dir).forEach((entry) => {
+    path = dir + '/' + entry;
+    const stat = fs.statSync(path);
+    if (stat.isDirectory()) {
+      dirs.push(path);
+      return;
+    }
+
+    if (!path.endsWith('.html')) {
+      return;
+    }
+
+    const index = fs.readFileSync(path, 'utf8');
+    const $ = cheerio.load(index, { decodeEntities: false });
+
+    $('head').append(opengraph);
+    $('script:nth-of-type(3)').text(htmljs);
+    $('nav#sidebar ol.chapter').append(githublink);
+    $('head link[href="tomorrow-night.css"]').attr('disabled', true);
+    $('head link[href="ayu-highlight.css"]').attr('disabled', true);
+    $('button#theme-toggle').attr('style', 'display:none');
+    $('pre code').each(function () {
+      const node = $(this);
+      const langClass = node.attr('class').split(' ', 2)[0];
+      if (!langClass.startsWith('language-')) {
+        return;
+      }
+      const lang = langClass.replace('language-', '');
+      const lines = node.html().split('\n');
+      const boring = lines.map((line) =>
+        line.includes('<span class="boring">')
+      );
+      const ellipsis = lines.map((line) => line.includes('// ...'));
+      const target = entities.decode(node.text());
+      const highlighted = hljs.highlight(lang, target).value;
+      const result = highlighted
+        .split('\n')
+        .map(function (line, i) {
+          if (boring[i]) {
+            line = '<span class="boring">' + line;
+          } else if (ellipsis[i]) {
+            line = '<span class="ellipsis">' + line;
+          }
+          if (i > 0 && (boring[i - 1] || ellipsis[i - 1])) {
+            line = '</span>' + line;
+          }
+          return line;
+        })
+        .join('\n');
+      node.text(result);
+      node.removeClass(langClass);
+      if (!node.hasClass('focuscomment')) {
+        node.addClass('hidelines');
+        node.addClass('hide-boring');
+      }
+    });
+    $('code').each(function () {
+      $(this).addClass('hljs');
+    });
+
+    const out = $.html();
+    fs.writeFileSync(path, out);
+  });
+}
+
+fs.copyFileSync('build/highlight.css', 'build/tomorrow-night.css');
+fs.copyFileSync('build/highlight.css', 'build/ayu-highlight.css');
+
+var bookjs = fs.readFileSync('build/book.js', 'utf8');
+bookjs = bookjs.replace('set_theme(theme, false);', '');
+fs.writeFileSync('build/book.js', bookjs);
diff --git a/book/build.sh b/book/build.sh
new file mode 100755
index 0000000..783d304
--- /dev/null
+++ b/book/build.sh
@@ -0,0 +1,17 @@
+#!/bin/bash
+
+set -e
+
+cd "$(dirname "$0")"
+
+if [ -f ./mdbook ]; then
+    ./mdbook build
+else
+    mdbook build
+fi
+
+if [ ! -d node_modules ]; then
+    npm install
+fi
+
+./build.js
diff --git a/book/css/cxx.css b/book/css/cxx.css
new file mode 100644
index 0000000..647f4f7
--- /dev/null
+++ b/book/css/cxx.css
@@ -0,0 +1,44 @@
+:root {
+    --sidebar-width: 310px;
+}
+
+.badges img {
+    margin: 0 7px 7px 0;
+}
+
+.badges {
+    margin: 16px 0 120px;
+}
+
+.boring {
+    opacity: 0.5;
+}
+
+.no-js code:not(.focuscomment) .boring {
+    display: none;
+}
+
+.js code:not(.hide-boring) .ellipsis {
+    display: none;
+}
+
+.focuscomment .hljs-comment {
+    font-weight: bold;
+    color: black;
+}
+
+.focuscomment .boring {
+    opacity: 0.5;
+}
+
+nav.sidebar li.part-title i.fa-github {
+    font-size: 20px;
+    padding-right: 5px;
+    padding-top: 12px;
+    position: relative;
+    top: 1px;
+}
+
+.sidebar .sidebar-scrollbox {
+    padding: 10px 0 10px 10px;
+}
diff --git a/book/diagram/.gitignore b/book/diagram/.gitignore
new file mode 100644
index 0000000..81631c6
--- /dev/null
+++ b/book/diagram/.gitignore
@@ -0,0 +1,2 @@
+/bin
+/build
diff --git a/book/diagram/Makefile b/book/diagram/Makefile
new file mode 100644
index 0000000..161da34
--- /dev/null
+++ b/book/diagram/Makefile
@@ -0,0 +1,12 @@
+all: overview.svg
+
+build/bin/svgbob:
+	cargo install --git https://github.com/ivanceras/svgbob --rev df01674c47350665158ececa476e63f51c58a9c7 --root build
+
+%.svg: %.ascii build/bin/svgbob
+	build/bin/svgbob $< > $@
+
+clean:
+	rm -f *.svg
+
+.PHONY: all clean
diff --git a/book/diagram/overview.ascii b/book/diagram/overview.ascii
new file mode 100644
index 0000000..dd1c4b9
--- /dev/null
+++ b/book/diagram/overview.ascii
@@ -0,0 +1,13 @@
+                            .-----------------------------.
+                           |     #[cxx::bridge]  mod       |
+                           |   description of boundary     |
+                            '--------------+--------------'
+                                           |
+                                           |
+                         "Macro expansion" |  "Code generation"
+                           +---------------+---------------+
+         Safe              |                               |
+    straightforward        v                               v      Straightforward
+       "Rust APIs".----------------. "Hidden C ABI".---------------.  "C++ APIs"
+Rust <----------->| "Rust bindings"|<~~~~~~~~~~~~~>| "C++ bindings"|<-----------> "C++"
+code              '----------------'               '---------------'              code
diff --git a/book/diagram/overview.svg b/book/diagram/overview.svg
new file mode 100644
index 0000000..e515466
--- /dev/null
+++ b/book/diagram/overview.svg
@@ -0,0 +1,132 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="696" height="224">
+    <style>line, path, circle,rect,polygon {
+                          stroke: black;
+                          stroke-width: 2;
+                          stroke-opacity: 1;
+                          fill-opacity: 1;
+                          stroke-linecap: round;
+                          stroke-linejoin: miter;
+                        }
+
+                    text {
+                        fill: black;
+                        }
+                        rect.backdrop{
+                            stroke: none;
+                            fill: white;
+                        }
+                        .broken{
+                            stroke-dasharray: 8;
+                        }
+                        .filled{
+                            fill: black;
+                        }
+                        .bg_filled{
+                            fill: white;
+                        }
+                        .nofill{
+                            fill: white;
+                        }
+
+                        text {
+                         font-family: monospace;
+                         font-size: 14px;
+                        }
+
+                        .end_marked_arrow{
+                            marker-end: url(#arrow);
+                         }
+                        .start_marked_arrow{
+                            marker-start: url(#arrow);
+                         }
+
+                        .end_marked_diamond{
+                            marker-end: url(#diamond);
+                         }
+                        .start_marked_diamond{
+                            marker-start: url(#diamond);
+                         }
+
+                        .end_marked_circle{
+                            marker-end: url(#circle);
+                         }
+                        .start_marked_circle{
+                            marker-start: url(#circle);
+                         }
+
+                        .end_marked_open_circle{
+                            marker-end: url(#open_circle);
+                         }
+                        .start_marked_open_circle{
+                            marker-start: url(#open_circle);
+                         }
+
+                        .end_marked_big_open_circle{
+                            marker-end: url(#big_open_circle);
+                         }
+                        .start_marked_big_open_circle{
+                            marker-start: url(#big_open_circle);
+                         }
+
+
+                        </style>
+    <defs>
+        <marker id="arrow" viewBox="-2 -2 8 8" refX="4" refY="2" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
+            <polygon points="0,0 0,4 4,2 0,0"></polygon>
+        </marker>
+        <marker id="diamond" viewBox="-2 -2 8 8" refX="4" refY="2" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
+            <polygon points="0,2 2,0 4,2 2,4 0,2"></polygon>
+        </marker>
+        <marker id="circle" viewBox="0 0 8 8" refX="4" refY="4" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
+            <circle cx="4" cy="4" r="2" class="filled"></circle>
+        </marker>
+        <marker id="open_circle" viewBox="0 0 8 8" refX="4" refY="4" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
+            <circle cx="4" cy="4" r="2" class="bg_filled"></circle>
+        </marker>
+        <marker id="big_open_circle" viewBox="0 0 8 8" refX="4" refY="4" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
+            <circle cx="4" cy="4" r="3" class="bg_filled"></circle>
+        </marker>
+    </defs>
+    <rect class="backdrop" x="0" y="0" width="696" height="224"></rect>
+    <rect x="148" y="168" width="136" height="32" class="solid nofill" rx="4"></rect>
+    <text x="162" y="188" >Rust bindings</text>
+    <rect x="412" y="168" width="128" height="32" class="solid nofill" rx="4"></rect>
+    <text x="426" y="188" >C++ bindings</text>
+    <text x="266" y="28" >#[cxx::bridge]</text>
+    <text x="394" y="28" >mod</text>
+    <text x="250" y="44" >description</text>
+    <text x="346" y="44" >of</text>
+    <text x="370" y="44" >boundary</text>
+    <line x1="220" y1="120" x2="220" y2="160" class="solid end_marked_arrow"></line>
+    <line x1="476" y1="120" x2="476" y2="160" class="solid end_marked_arrow"></line>
+    <text x="74" y="140" >Safe</text>
+    <text x="34" y="156" >straightforward</text>
+    <text x="530" y="156" >Straightforward</text>
+    <polygon points="48,180 40,184 48,188" class="filled"></polygon>
+    <line x1="48" y1="184" x2="144" y2="184" class="solid end_marked_arrow"></line>
+    <polygon points="296,180 288,184 296,188" class="filled"></polygon>
+    <line x1="296" y1="184" x2="408" y2="184" class="broken end_marked_arrow"></line>
+    <polygon points="552,180 544,184 552,188" class="filled"></polygon>
+    <line x1="552" y1="184" x2="648" y2="184" class="solid end_marked_arrow"></line>
+    <text x="2" y="188" >Rust</text>
+    <text x="2" y="204" >code</text>
+    <text x="658" y="204" >code</text>
+    <text x="202" y="108" >Macro expansion</text>
+    <text x="370" y="108" >Code generation</text>
+    <text x="58" y="172" >Rust APIs</text>
+    <text x="298" y="172" >Hidden C ABI</text>
+    <text x="562" y="172" >C++ APIs</text>
+    <text x="658" y="188" >C++</text>
+    <g>
+        <path d="M 228,8 A 8,8 0,0,0 220,16" class="nofill"></path>
+        <line x1="228" y1="8" x2="468" y2="8" class="solid"></line>
+        <path d="M 468,8 A 8,8 0,0,1 476,16" class="nofill"></path>
+        <line x1="220" y1="16" x2="220" y2="48" class="solid"></line>
+        <line x1="476" y1="16" x2="476" y2="48" class="solid"></line>
+        <path d="M 220,48 A 8,8 0,0,0 228,56" class="nofill"></path>
+        <line x1="228" y1="56" x2="468" y2="56" class="solid"></line>
+        <line x1="348" y1="56" x2="348" y2="120" class="solid"></line>
+        <path d="M 476,48 A 8,8 0,0,1 468,56" class="nofill"></path>
+        <line x1="220" y1="120" x2="476" y2="120" class="solid"></line>
+    </g>
+</svg>
diff --git a/book/package-lock.json b/book/package-lock.json
new file mode 100644
index 0000000..dec26ad
--- /dev/null
+++ b/book/package-lock.json
@@ -0,0 +1,207 @@
+{
+  "name": "cxx-book-build",
+  "version": "0.0.0",
+  "lockfileVersion": 1,
+  "requires": true,
+  "dependencies": {
+    "boolbase": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
+      "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24="
+    },
+    "cheerio": {
+      "version": "0.22.0",
+      "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-0.22.0.tgz",
+      "integrity": "sha1-qbqoYKP5tZWmuBsahocxIe06Jp4=",
+      "requires": {
+        "css-select": "~1.2.0",
+        "dom-serializer": "~0.1.0",
+        "entities": "~1.1.1",
+        "htmlparser2": "^3.9.1",
+        "lodash.assignin": "^4.0.9",
+        "lodash.bind": "^4.1.4",
+        "lodash.defaults": "^4.0.1",
+        "lodash.filter": "^4.4.0",
+        "lodash.flatten": "^4.2.0",
+        "lodash.foreach": "^4.3.0",
+        "lodash.map": "^4.4.0",
+        "lodash.merge": "^4.4.0",
+        "lodash.pick": "^4.2.1",
+        "lodash.reduce": "^4.4.0",
+        "lodash.reject": "^4.4.0",
+        "lodash.some": "^4.4.0"
+      }
+    },
+    "css-select": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz",
+      "integrity": "sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg=",
+      "requires": {
+        "boolbase": "~1.0.0",
+        "css-what": "2.1",
+        "domutils": "1.5.1",
+        "nth-check": "~1.0.1"
+      }
+    },
+    "css-what": {
+      "version": "2.1.3",
+      "resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.3.tgz",
+      "integrity": "sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg=="
+    },
+    "dom-serializer": {
+      "version": "0.1.1",
+      "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.1.tgz",
+      "integrity": "sha512-l0IU0pPzLWSHBcieZbpOKgkIn3ts3vAh7ZuFyXNwJxJXk/c4Gwj9xaTJwIDVQCXawWD0qb3IzMGH5rglQaO0XA==",
+      "requires": {
+        "domelementtype": "^1.3.0",
+        "entities": "^1.1.1"
+      }
+    },
+    "domelementtype": {
+      "version": "1.3.1",
+      "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz",
+      "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w=="
+    },
+    "domhandler": {
+      "version": "2.4.2",
+      "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.2.tgz",
+      "integrity": "sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==",
+      "requires": {
+        "domelementtype": "1"
+      }
+    },
+    "domutils": {
+      "version": "1.5.1",
+      "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz",
+      "integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=",
+      "requires": {
+        "dom-serializer": "0",
+        "domelementtype": "1"
+      }
+    },
+    "entities": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz",
+      "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w=="
+    },
+    "html-entities": {
+      "version": "1.3.1",
+      "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-1.3.1.tgz",
+      "integrity": "sha512-rhE/4Z3hIhzHAUKbW8jVcCyuT5oJCXXqhN/6mXXVCpzTmvJnoH2HL/bt3EZ6p55jbFJBeAe1ZNpL5BugLujxNA=="
+    },
+    "htmlparser2": {
+      "version": "3.10.1",
+      "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz",
+      "integrity": "sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==",
+      "requires": {
+        "domelementtype": "^1.3.1",
+        "domhandler": "^2.3.0",
+        "domutils": "^1.5.1",
+        "entities": "^1.1.1",
+        "inherits": "^2.0.1",
+        "readable-stream": "^3.1.1"
+      }
+    },
+    "inherits": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+      "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
+    },
+    "lodash.assignin": {
+      "version": "4.2.0",
+      "resolved": "https://registry.npmjs.org/lodash.assignin/-/lodash.assignin-4.2.0.tgz",
+      "integrity": "sha1-uo31+4QesKPoBEIysOJjqNxqKKI="
+    },
+    "lodash.bind": {
+      "version": "4.2.1",
+      "resolved": "https://registry.npmjs.org/lodash.bind/-/lodash.bind-4.2.1.tgz",
+      "integrity": "sha1-euMBfpOWIqwxt9fX3LGzTbFpDTU="
+    },
+    "lodash.defaults": {
+      "version": "4.2.0",
+      "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz",
+      "integrity": "sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw="
+    },
+    "lodash.filter": {
+      "version": "4.6.0",
+      "resolved": "https://registry.npmjs.org/lodash.filter/-/lodash.filter-4.6.0.tgz",
+      "integrity": "sha1-ZosdSYFgOuHMWm+nYBQ+SAtMSs4="
+    },
+    "lodash.flatten": {
+      "version": "4.4.0",
+      "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz",
+      "integrity": "sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8="
+    },
+    "lodash.foreach": {
+      "version": "4.5.0",
+      "resolved": "https://registry.npmjs.org/lodash.foreach/-/lodash.foreach-4.5.0.tgz",
+      "integrity": "sha1-Gmo16s5AEoDH8G3d7DUWWrJ+PlM="
+    },
+    "lodash.map": {
+      "version": "4.6.0",
+      "resolved": "https://registry.npmjs.org/lodash.map/-/lodash.map-4.6.0.tgz",
+      "integrity": "sha1-dx7Hg540c9nEzeKLGTlMNWL09tM="
+    },
+    "lodash.merge": {
+      "version": "4.6.2",
+      "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
+      "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="
+    },
+    "lodash.pick": {
+      "version": "4.4.0",
+      "resolved": "https://registry.npmjs.org/lodash.pick/-/lodash.pick-4.4.0.tgz",
+      "integrity": "sha1-UvBWEP/53tQiYRRB7R/BI6AwAbM="
+    },
+    "lodash.reduce": {
+      "version": "4.6.0",
+      "resolved": "https://registry.npmjs.org/lodash.reduce/-/lodash.reduce-4.6.0.tgz",
+      "integrity": "sha1-8atrg5KZrUj3hKu/R2WW8DuRTTs="
+    },
+    "lodash.reject": {
+      "version": "4.6.0",
+      "resolved": "https://registry.npmjs.org/lodash.reject/-/lodash.reject-4.6.0.tgz",
+      "integrity": "sha1-gNZJLcFHCGS79YNTO2UfQqn1JBU="
+    },
+    "lodash.some": {
+      "version": "4.6.0",
+      "resolved": "https://registry.npmjs.org/lodash.some/-/lodash.some-4.6.0.tgz",
+      "integrity": "sha1-G7nzFO9ri63tE7VJFpsqlF62jk0="
+    },
+    "nth-check": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz",
+      "integrity": "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==",
+      "requires": {
+        "boolbase": "~1.0.0"
+      }
+    },
+    "readable-stream": {
+      "version": "3.6.0",
+      "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
+      "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
+      "requires": {
+        "inherits": "^2.0.3",
+        "string_decoder": "^1.1.1",
+        "util-deprecate": "^1.0.1"
+      }
+    },
+    "safe-buffer": {
+      "version": "5.2.1",
+      "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+      "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="
+    },
+    "string_decoder": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
+      "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
+      "requires": {
+        "safe-buffer": "~5.2.0"
+      }
+    },
+    "util-deprecate": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+      "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
+    }
+  }
+}
diff --git a/book/package.json b/book/package.json
new file mode 100644
index 0000000..092cea2
--- /dev/null
+++ b/book/package.json
@@ -0,0 +1,12 @@
+{
+  "name": "cxx-book-build",
+  "version": "0.0.0",
+  "main": "build.js",
+  "dependencies": {
+    "cheerio": "^0.22.0",
+    "html-entities": "^1.3.1"
+  },
+  "prettier": {
+    "singleQuote": true
+  }
+}
diff --git a/book/src/404.md b/book/src/404.md
new file mode 100644
index 0000000..c5b71f2
--- /dev/null
+++ b/book/src/404.md
@@ -0,0 +1,5 @@
+### Whoops, this page doesn’t exist :-(
+
+<br>
+
+<img src="https://www.rust-lang.org/static/images/ferris-error.png" alt="ferris" width="325">
diff --git a/book/src/SUMMARY.md b/book/src/SUMMARY.md
index f3fa924..a8f89bf 100644
--- a/book/src/SUMMARY.md
+++ b/book/src/SUMMARY.md
@@ -1,3 +1,37 @@
 # Summary
 
-[Rust ❤️ C++](about.md)
+- [Rust ❤️ C++](index.md)
+
+- [Core concepts](concepts.md)
+
+- [Tutorial](tutorial.md)
+
+- [Other Rust&ndash;C++ interop tools](context.md)
+
+- [Multi-language build system options](building.md)
+    - [Cargo](build/cargo.md)
+    - [Bazel](build/bazel.md)
+    - [CMake](build/cmake.md)
+    - [More...](build/other.md)
+
+- [Reference: the bridge module](reference.md)
+    - [extern "Rust"](extern-rust.md)
+    - [extern "C++"](extern-c++.md)
+    - [Shared types](shared.md)
+    - [Attributes](attributes.md)
+    - [Async functions](async.md)
+    - [Error handling](binding/result.md)
+
+- [Reference: built-in bindings](bindings.md)
+    - [String &mdash; rust::String](binding/string.md)
+    - [&str &mdash; rust::Str](binding/str.md)
+    - [&&#91;T&#93;, &mut &#91;T&#93; &mdash; rust::Slice\<T\>](binding/slice.md)
+    - [CxxString &mdash; std::string](binding/cxxstring.md)
+    - [Box\<T\> &mdash; rust::Box\<T\>](binding/box.md)
+    - [UniquePtr\<T\> &mdash; std::unique\_ptr\<T\>](binding/uniqueptr.md)
+    - [SharedPtr\<T\> &mdash; std::shared\_ptr\<T\>](binding/sharedptr.md)
+    - [Vec\<T\> &mdash; rust::Vec\<T\>](binding/vec.md)
+    - [CxxVector\<T\> &mdash; std::vector\<T\>](binding/cxxvector.md)
+    - [*mut T, *const T raw pointers](binding/rawptr.md)
+    - [Function pointers](binding/fn.md)
+    - [Result\<T\>](binding/result.md)
diff --git a/book/src/about.md b/book/src/about.md
deleted file mode 100644
index 6e63bf5..0000000
--- a/book/src/about.md
+++ /dev/null
@@ -1 +0,0 @@
-### Coming soon
diff --git a/book/src/async.md b/book/src/async.md
new file mode 100644
index 0000000..b4c696a
--- /dev/null
+++ b/book/src/async.md
@@ -0,0 +1,86 @@
+{{#title Async functions — Rust ♡ C++}}
+# Async functions
+
+Direct FFI of async functions is absolutely in scope for CXX (on C++20 and up)
+but is not implemented yet in the current release. We are aiming for an
+implementation that is as easy as:
+
+```rust,noplayground
+#[cxx::bridge]
+mod ffi {
+    unsafe extern "C++" {
+        async fn doThing(arg: Arg) -> Ret;
+    }
+}
+```
+
+```cpp,hidelines
+rust::Future<Ret> doThing(Arg arg) {
+  auto v1 = co_await f();
+  auto v2 = co_await g(arg);
+  co_return v1 + v2;
+}
+```
+
+## Workaround
+
+For now the recommended approach is to handle the return codepath over a oneshot
+channel (such as [`futures::channel::oneshot`]) represented in an opaque Rust
+type on the FFI.
+
+[`futures::channel::oneshot`]: https://docs.rs/futures/0.3.8/futures/channel/oneshot/index.html
+
+```rust,noplayground
+// bridge.rs
+
+use futures::channel::oneshot;
+
+#[cxx::bridge]
+mod ffi {
+    extern "Rust" {
+        type DoThingContext;
+    }
+
+    unsafe extern "C++" {
+        include!("path/to/bridge_shim.h");
+
+        fn shim_doThing(
+            arg: Arg,
+            done: fn(Box<DoThingContext>, ret: Ret),
+            ctx: Box<DoThingContext>,
+        );
+    }
+}
+
+struct DoThingContext(oneshot::Sender<Ret>);
+
+pub async fn do_thing(arg: Arg) -> Ret {
+    let (tx, rx) = oneshot::channel();
+    let context = Box::new(DoThingContext(tx));
+
+    ffi::shim_doThing(
+        arg,
+        |context, ret| { let _ = context.0.send(ret); },
+        context,
+    );
+
+    rx.await.unwrap()
+}
+```
+
+```cpp
+// bridge_shim.cc
+
+#include "path/to/bridge.rs.h"
+#include "rust/cxx.h"
+
+void shim_doThing(
+    Arg arg,
+    rust::Fn<void(rust::Box<DoThingContext> ctx, Ret ret)> done,
+    rust::Box<DoThingContext> ctx) noexcept {
+  doThing(arg)
+      .then([done, ctx(std::move(ctx))](auto &&res) mutable {
+        (*done)(std::move(ctx), std::move(res));
+      });
+}
+```
diff --git a/book/src/attributes.md b/book/src/attributes.md
new file mode 100644
index 0000000..9c33b77
--- /dev/null
+++ b/book/src/attributes.md
@@ -0,0 +1,75 @@
+{{#title Attributes — Rust ♡ C++}}
+# Attributes
+
+## namespace
+
+The top-level cxx::bridge attribute macro takes an optional `namespace` argument
+to control the C++ namespace into which to emit extern Rust items and the
+namespace in which to expect to find the extern C++ items.
+
+```rust,noplayground
+#[cxx::bridge(namespace = "path::of::my::company")]
+mod ffi {
+    extern "Rust" {
+        type MyType;  // emitted to path::of::my::company::MyType
+    }
+
+    extern "C++" {
+        type TheirType;  // refers to path::of::my::company::TheirType
+    }
+}
+```
+
+Additionally, a `#[namespace = "..."]` attribute may be used inside the bridge
+module on any extern block or individual item. An item will inherit the
+namespace specified on its surrounding extern block if any, otherwise the
+namespace specified with the top level cxx::bridge attribute if any, otherwise
+the global namespace.
+
+```rust,noplayground
+#[cxx::bridge(namespace = "third_priority")]
+mod ffi {
+    #[namespace = "second_priority"]
+    extern "Rust" {
+        fn f();
+
+        #[namespace = "first_priority"]
+        fn g();
+    }
+
+    extern "Rust" {
+        fn h();
+    }
+}
+```
+
+The above would result in functions `::second_priority::f`,
+`::first_priority::g`, `::third_priority::h`.
+
+## rust\_name, cxx\_name
+
+Sometimes you want the Rust name of a function or type to differ from its C++
+name. Importantly, this enables binding multiple overloads of the same C++
+function name using distinct Rust names.
+
+```rust,noplayground
+#[cxx::bridge]
+mod ffi {
+    unsafe extern "C++" {
+        #[rust_name = "i32_overloaded_function"]
+        fn cOverloadedFunction(x: i32) -> String;
+        #[rust_name = "str_overloaded_function"]
+        fn cOverloadedFunction(x: &str) -> String;
+    }
+}
+```
+
+The `#[rust_name = "..."]` attribute replaces the name that Rust should use for
+this function, and an analogous `#[cxx_name = "..."]` attribute replaces the
+name that C++ should use.
+
+Either of the two attributes may be used on extern "Rust" as well as extern
+"C++" functions, according to which one you find clearer in context.
+
+The same attribute works for renaming functions, opaque types, shared
+structs and enums, and enum variants.
diff --git a/book/src/binding/box.md b/book/src/binding/box.md
new file mode 100644
index 0000000..7df1959
--- /dev/null
+++ b/book/src/binding/box.md
@@ -0,0 +1,120 @@
+{{#title rust::Box<T> — Rust ♡ C++}}
+# rust::Box\<T\>
+
+### Public API:
+
+```cpp,hidelines
+// rust/cxx.h
+#
+# #include <type_traits>
+#
+# namespace rust {
+
+template <typename T>
+class Box final {
+public:
+  using element_type = T;
+  using const_pointer =
+      typename std::add_pointer<typename std::add_const<T>::type>::type;
+  using pointer = typename std::add_pointer<T>::type;
+
+  Box(Box &&) noexcept;
+  ~Box() noexcept;
+
+  explicit Box(const T &);
+  explicit Box(T &&);
+
+  Box &operator=(Box &&) noexcept;
+
+  const T *operator->() const noexcept;
+  const T &operator*() const noexcept;
+  T *operator->() noexcept;
+  T &operator*() noexcept;
+
+  template <typename... Fields>
+  static Box in_place(Fields &&...);
+
+  void swap(Box &) noexcept;
+
+  // Important: requires that `raw` came from an into_raw call. Do not
+  // pass a pointer from `new` or any other source.
+  static Box from_raw(T *) noexcept;
+
+  T *into_raw() noexcept;
+};
+#
+# } // namespace rust
+```
+
+### Restrictions:
+
+Box\<T\> does not support T being an opaque C++ type. You should use
+[UniquePtr\<T\>](uniqueptr.md) or [SharedPtr\<T\>](sharedptr.md) instead for
+transferring ownership of opaque C++ types on the language boundary.
+
+If T is an opaque Rust type, the Rust type is required to be [Sized] i.e. size
+known at compile time. In the future we may introduce support for dynamically
+sized opaque Rust types.
+
+[Sized]: https://doc.rust-lang.org/std/marker/trait.Sized.html
+
+## Example
+
+This program uses a Box to pass ownership of some opaque piece of Rust state
+over to C++ and then back to a Rust callback, which is a useful pattern for
+implementing [async functions over FFI](../async.md).
+
+```rust,noplayground
+// src/main.rs
+
+use std::io::Write;
+
+#[cxx::bridge]
+mod ffi {
+    extern "Rust" {
+        type File;
+    }
+
+    unsafe extern "C++" {
+        include!("example/include/example.h");
+
+        fn f(
+            callback: fn(Box<File>, fst: &str, snd: &str),
+            out: Box<File>,
+        );
+    }
+}
+
+pub struct File(std::fs::File);
+
+fn main() {
+    let out = std::fs::File::create("example.log").unwrap();
+
+    ffi::f(
+        |mut out, fst, snd| { let _ = write!(out.0, "{}{}\n", fst, snd); },
+        Box::new(File(out)),
+    );
+}
+```
+
+```cpp
+// include/example.h
+
+#pragma once
+#include "example/src/main.rs.h"
+#include "rust/cxx.h"
+
+void f(rust::Fn<void(rust::Box<File>, rust::Str, rust::Str)> callback,
+       rust::Box<File> out);
+```
+
+```cpp
+// include/example.cc
+
+#include "example/include/example.h"
+
+void f(rust::Fn<void(rust::Box<File>, rust::Str, rust::Str)> callback,
+       rust::Box<File> out) {
+  callback(std::move(out), "fearless", "concurrency");
+}
+```
diff --git a/book/src/binding/cxxstring.md b/book/src/binding/cxxstring.md
new file mode 100644
index 0000000..0b1d7bb
--- /dev/null
+++ b/book/src/binding/cxxstring.md
@@ -0,0 +1,139 @@
+{{#title std::string — Rust ♡ C++}}
+# std::string
+
+The Rust binding of std::string is called **[`CxxString`]**. See the link for
+documentation of the Rust API.
+
+[`CxxString`]: https://docs.rs/cxx/*/cxx/struct.CxxString.html
+
+### Restrictions:
+
+Rust code can never obtain a CxxString by value. C++'s string requires a move
+constructor and may hold internal pointers, which is not compatible with Rust's
+move behavior. Instead in Rust code we will only ever look at a CxxString
+through a reference or smart pointer, as in &CxxString or Pin\<&mut CxxString\>
+or UniquePtr\<CxxString\>.
+
+In order to construct a CxxString on the stack from Rust, you must use the
+[`let_cxx_string!`] macro which will pin the string properly. The code below
+uses this in one place, and the link covers the syntax.
+
+[`let_cxx_string!`]: https://docs.rs/cxx/*/cxx/macro.let_cxx_string.html
+
+## Example
+
+This example uses C++17's std::variant to build a toy JSON type. JSON can hold
+various types including strings, and JSON's object type is a map with string
+keys. The example demonstrates Rust indexing into one of those maps.
+
+```rust,noplayground
+// src/main.rs
+
+use cxx::let_cxx_string;
+
+#[cxx::bridge]
+mod ffi {
+    unsafe extern "C++" {
+        include!("example/include/json.h");
+
+        #[cxx_name = "json"]
+        type Json;
+        #[cxx_name = "object"]
+        type Object;
+
+        fn isNull(self: &Json) -> bool;
+        fn isNumber(self: &Json) -> bool;
+        fn isString(self: &Json) -> bool;
+        fn isArray(self: &Json) -> bool;
+        fn isObject(self: &Json) -> bool;
+
+        fn getNumber(self: &Json) -> f64;
+        fn getString(self: &Json) -> &CxxString;
+        fn getArray(self: &Json) -> &CxxVector<Json>;
+        fn getObject(self: &Json) -> &Object;
+
+        #[cxx_name = "at"]
+        fn get<'a>(self: &'a Object, key: &CxxString) -> &'a Json;
+
+        fn load_config() -> UniquePtr<Json>;
+    }
+}
+
+fn main() {
+    let config = ffi::load_config();
+
+    let_cxx_string!(key = "name");
+    println!("{}", config.getObject().get(&key).getString());
+}
+```
+
+```cpp
+// include/json.h
+
+#pragma once
+#include <map>
+#include <memory>
+#include <variant>
+#include <vector>
+
+class json final {
+public:
+  static const json null;
+  using number = double;
+  using string = std::string;
+  using array = std::vector<json>;
+  using object = std::map<string, json>;
+
+  json() noexcept = default;
+  json(const json &) = default;
+  json(json &&) = default;
+  template <typename... T>
+  json(T &&...value) : value(std::forward<T>(value)...) {}
+
+  bool isNull() const;
+  bool isNumber() const;
+  bool isString() const;
+  bool isArray() const;
+  bool isObject() const;
+
+  number getNumber() const;
+  const string &getString() const;
+  const array &getArray() const;
+  const object &getObject() const;
+
+private:
+  std::variant<std::monostate, number, string, array, object> value;
+};
+
+using object = json::object;
+
+std::unique_ptr<json> load_config();
+```
+
+```cpp
+// include/json.cc
+
+#include "example/include/json.h"
+#include <initializer_list>
+#include <utility>
+
+const json json::null{};
+bool json::isNull() const { return std::holds_alternative<std::monostate>(value); }
+bool json::isNumber() const { return std::holds_alternative<number>(value); }
+bool json::isString() const { return std::holds_alternative<string>(value); }
+bool json::isArray() const { return std::holds_alternative<array>(value); }
+bool json::isObject() const { return std::holds_alternative<object>(value); }
+json::number json::getNumber() const { return std::get<number>(value); }
+const json::string &json::getString() const { return std::get<string>(value); }
+const json::array &json::getArray() const { return std::get<array>(value); }
+const json::object &json::getObject() const { return std::get<object>(value); }
+
+std::unique_ptr<json> load_config() {
+  return std::make_unique<json>(
+      std::in_place_type<json::object>,
+      std::initializer_list<std::pair<const std::string, json>>{
+          {"name", "cxx-example"},
+          {"edition", 2018.},
+          {"repository", json::null}});
+}
+```
diff --git a/book/src/binding/cxxvector.md b/book/src/binding/cxxvector.md
new file mode 100644
index 0000000..fd95a2d
--- /dev/null
+++ b/book/src/binding/cxxvector.md
@@ -0,0 +1,62 @@
+{{#title std::vector<T> — Rust ♡ C++}}
+# std::vector\<T\>
+
+The Rust binding of std::vector\<T\> is called **[`CxxVector<T>`]**. See the
+link for documentation of the Rust API.
+
+[`CxxVector<T>`]: https://docs.rs/cxx/*/cxx/struct.CxxVector.html
+
+### Restrictions:
+
+Rust code can never obtain a CxxVector by value. Instead in Rust code we will
+only ever look at a vector behind a reference or smart pointer, as in
+&CxxVector\<T\> or UniquePtr\<CxxVector\<T\>\>.
+
+CxxVector\<T\> does not support T being an opaque Rust type. You should use a
+Vec\<T\> (C++ rust::Vec\<T\>) instead for collections of opaque Rust types on
+the language boundary.
+
+## Example
+
+This program involves Rust code converting a `CxxVector<CxxString>` (i.e.
+`std::vector<std::string>`) into a Rust `Vec<String>`.
+
+```rust,noplayground
+// src/main.rs
+
+#![no_main] // main defined in C++ by main.cc
+
+use cxx::{CxxString, CxxVector};
+
+#[cxx::bridge]
+mod ffi {
+    extern "Rust" {
+        fn f(vec: &CxxVector<CxxString>);
+    }
+}
+
+fn f(vec: &CxxVector<CxxString>) {
+    let vec: Vec<String> = vec
+        .iter()
+        .map(|s| s.to_string_lossy().into_owned())
+        .collect();
+    g(&vec);
+}
+
+fn g(vec: &[String]) {
+    println!("{:?}", vec);
+}
+```
+
+```cpp
+// src/main.cc
+
+#include "example/src/main.rs.h"
+#include <string>
+#include <vector>
+
+int main() {
+  std::vector<std::string> vec{"fearless", "concurrency"};
+  f(vec);
+}
+```
diff --git a/book/src/binding/fn.md b/book/src/binding/fn.md
new file mode 100644
index 0000000..2934b06
--- /dev/null
+++ b/book/src/binding/fn.md
@@ -0,0 +1,34 @@
+{{#title Function pointers — Rust ♡ C++}}
+# Function pointers
+
+### Public API:
+
+```cpp,hidelines
+// rust/cxx.h
+#
+# namespace rust {
+
+template <typename Signature>
+class Fn;
+
+template <typename Ret, typename... Args>
+class Fn<Ret(Args...)> final {
+public:
+  Ret operator()(Args... args) const noexcept;
+  Fn operator*() const noexcept;
+};
+#
+# } // namespace rust
+```
+
+### Restrictions:
+
+Function pointers with a Result return type are not implemented yet.
+
+Passing a function pointer from C++ to Rust is not implemented yet, only from
+Rust to an `extern "C++"` function is implemented.
+
+## Example
+
+Function pointers are commonly useful for implementing [async functions over
+FFI](../async.md). See the example code on that page.
diff --git a/book/src/binding/rawptr.md b/book/src/binding/rawptr.md
new file mode 100644
index 0000000..1794211
--- /dev/null
+++ b/book/src/binding/rawptr.md
@@ -0,0 +1,100 @@
+{{#title *mut T, *const T — Rust ♡ C++}}
+# *mut T,&ensp;*const T
+
+Generally you should use references (`&mut T`, `&T`) or [std::unique_ptr\<T\>]
+where possible over raw pointers, but raw pointers are available too as an
+unsafe fallback option.
+
+[std::unique_ptr\<T\>]: uniqueptr.md
+
+### Restrictions:
+
+Extern functions and function pointers taking a raw pointer as an argument must
+be declared `unsafe fn` i.e. unsafe to call. The same does not apply to
+functions which only *return* a raw pointer, though presumably doing anything
+useful with the returned pointer is going to involve unsafe code elsewhere
+anyway.
+
+## Example
+
+This example illustrates making a Rust call to a canonical C-style `main`
+signature involving `char *argv[]`.
+
+```cpp
+// include/args.h
+
+#pragma once
+
+void parseArgs(int argc, char *argv[]);
+```
+
+```cpp
+// src/args.cc
+
+#include "example/include/args.h"
+#include <iostream>
+
+void parseArgs(int argc, char *argv[]) {
+  std::cout << argc << std::endl;
+  for (int i = 0; i < argc; i++) {
+    std::cout << '"' << argv[i] << '"' << std::endl;
+  }
+}
+```
+
+```rust,noplayground
+// src/main.rs
+
+use std::env;
+use std::ffi::CString;
+use std::os::raw::c_char;
+use std::os::unix::ffi::OsStrExt;
+use std::ptr;
+
+#[cxx::bridge]
+mod ffi {
+    extern "C++" {
+        include!("example/include/args.h");
+
+        unsafe fn parseArgs(argc: i32, argv: *mut *mut c_char);
+    }
+}
+
+fn main() {
+    // Convert from OsString to nul-terminated CString, truncating each argument
+    // at the first inner nul byte if present.
+    let args: Vec<CString> = env::args_os()
+        .map(|os_str| {
+            let bytes = os_str.as_bytes();
+            CString::new(bytes).unwrap_or_else(|nul_error| {
+                let nul_position = nul_error.nul_position();
+                let mut bytes = nul_error.into_vec();
+                bytes.truncate(nul_position);
+                CString::new(bytes).unwrap()
+            })
+        })
+        .collect();
+
+    // Convert from Vec<CString> of owned strings to Vec<*mut c_char> of
+    // borrowed string pointers.
+    //
+    // Once extern type stabilizes (https://github.com/rust-lang/rust/issues/43467)
+    // and https://internals.rust-lang.org/t/pre-rfc-make-cstr-a-thin-pointer/6258
+    // is implemented, and CStr pointers become thin, we can sidestep this step
+    // by accumulating the args as Vec<Box<CStr>> up front, then simply casting
+    // from *mut [Box<CStr>] to *mut [*mut CStr] to *mut *mut c_char.
+    let argc = args.len();
+    let mut argv: Vec<*mut c_char> = Vec::with_capacity(argc + 1);
+    for arg in &args {
+        argv.push(arg.as_ptr() as *mut c_char);
+    }
+    argv.push(ptr::null_mut()); // Nul terminator.
+
+    unsafe {
+        ffi::parseArgs(argc as i32, argv.as_mut_ptr());
+    }
+
+    // The CStrings go out of scope here. C function must not have held on to
+    // the pointers beyond this point.
+}
+```
diff --git a/book/src/binding/result.md b/book/src/binding/result.md
new file mode 100644
index 0000000..e49dcf4
--- /dev/null
+++ b/book/src/binding/result.md
@@ -0,0 +1,148 @@
+{{#title Result<T> — Rust ♡ C++}}
+# Result\<T\>
+
+Result\<T\> is allowed as the return type of an extern function in either
+direction. Its behavior is to translate to/from C++ exceptions. If your codebase
+does not use C++ exceptions, or prefers to represent fallibility using something
+like outcome\<T\>, leaf::result\<T\>, StatusOr\<T\>, etc then you'll need to
+handle the translation of those to Rust Result\<T\> using your own shims for
+now. Better support for this is planned.
+
+If an exception is thrown from an `extern "C++"` function that is *not* declared
+by the CXX bridge to return Result, the program calls C++'s `std::terminate`.
+The behavior is equivalent to the same exception being thrown through a
+`noexcept` C++ function.
+
+If a panic occurs in *any* `extern "Rust"` function, regardless of whether it is
+declared by the CXX bridge to return Result, a message is logged and the program
+calls Rust's `std::process::abort`.
+
+## Returning Result from Rust to C++
+
+An `extern "Rust"` function returning a Result turns into a `throw` in C++ if
+the Rust side produces an error.
+
+Note that the return type written inside of cxx::bridge must be written without
+a second type parameter. Only the Ok type is specified for the purpose of the
+FFI. The Rust *implementation* (outside of the bridge module) may pick any error
+type as long as it has a std::fmt::Display impl.
+
+```rust,noplayground
+# use std::io;
+#
+#[cxx::bridge]
+mod ffi {
+    extern "Rust" {
+        fn fallible1(depth: usize) -> Result<String>;
+        fn fallible2() -> Result<()>;
+    }
+}
+
+fn fallible1(depth: usize) -> anyhow::Result<String> {
+    if depth == 0 {
+        return Err(anyhow::Error::msg("fallible1 requires depth > 0"));
+    }
+    ...
+}
+
+fn fallible2() -> Result<(), io::Error> {
+    ...
+    Ok(())
+}
+```
+
+The exception that gets thrown by CXX on the C++ side is always of type
+`rust::Error` and has the following C++ public API. The `what()` member function
+gives the error message according to the Rust error's std::fmt::Display impl.
+
+```cpp,hidelines
+// rust/cxx.h
+#
+# namespace rust {
+
+class Error final : public std::exception {
+public:
+  Error(const Error &);
+  Error(Error &&) noexcept;
+  ~Error() noexcept;
+
+  Error &operator=(const Error &);
+  Error &operator=(Error &&) noexcept;
+
+  const char *what() const noexcept override;
+};
+#
+# } // namespace rust
+```
+
+## Returning Result from C++ to Rust
+
+An `extern "C++"` function returning a Result turns into a `catch` in C++ that
+converts the exception into an Err for Rust.
+
+Note that the return type written inside of cxx::bridge must be written without
+a second type parameter. Only the Ok type is specified for the purpose of the
+FFI. The resulting error type created by CXX when an `extern "C++"` function
+throws will always be of type **[`cxx::Exception`]**.
+
+[`cxx::Exception`]: https://docs.rs/cxx/*/cxx/struct.Exception.html
+
+```rust,noplayground
+# use std::process;
+#
+#[cxx::bridge]
+mod ffi {
+    unsafe extern "C++" {
+        include!("example/include/example.h");
+        fn fallible1(depth: usize) -> Result<String>;
+        fn fallible2() -> Result<()>;
+    }
+}
+
+fn main() {
+    if let Err(err) = ffi::fallible1(99) {
+        eprintln!("Error: {}", err);
+        process::exit(1);
+    }
+}
+```
+
+The specific set of caught exceptions and the conversion to error message are
+both customizable. The way you do this is by defining a template function
+`rust::behavior::trycatch` with a suitable signature inside any one of the
+headers `include!`'d by your cxx::bridge.
+
+The template signature is required to be:
+
+```cpp,hidelines
+namespace rust {
+namespace behavior {
+
+template <typename Try, typename Fail>
+static void trycatch(Try &&func, Fail &&fail) noexcept;
+
+} // namespace behavior
+} // namespace rust
+```
+
+The default `trycatch` used by CXX if you have not provided your own is the
+following. You must follow the same pattern: invoke `func` with no arguments,
+catch whatever exception(s) you want, and invoke `fail` with the error message
+you'd like for the Rust error to have.
+
+```cpp,hidelines
+# #include <exception>
+#
+# namespace rust {
+# namespace behavior {
+#
+template <typename Try, typename Fail>
+static void trycatch(Try &&func, Fail &&fail) noexcept try {
+  func();
+} catch (const std::exception &e) {
+  fail(e.what());
+}
+#
+# } // namespace behavior
+# } // namespace rust
+```
diff --git a/book/src/binding/sharedptr.md b/book/src/binding/sharedptr.md
new file mode 100644
index 0000000..a3b7070
--- /dev/null
+++ b/book/src/binding/sharedptr.md
@@ -0,0 +1,80 @@
+{{#title std::shared_ptr<T> — Rust ♡ C++}}
+# std::shared\_ptr\<T\>
+
+The Rust binding of std::shared\_ptr\<T\> is called **[`SharedPtr<T>`]**. See
+the link for documentation of the Rust API.
+
+[`SharedPtr<T>`]: https://docs.rs/cxx/*/cxx/struct.SharedPtr.html
+
+### Restrictions:
+
+SharedPtr\<T\> does not support T being an opaque Rust type. You should use a
+Box\<T\> (C++ [rust::Box\<T\>](box.md)) instead for transferring ownership of
+opaque Rust types on the language boundary.
+
+## Example
+
+```rust,noplayground
+// src/main.rs
+
+use std::ops::Deref;
+use std::ptr;
+
+#[cxx::bridge]
+mod ffi {
+    unsafe extern "C++" {
+        include!("example/include/example.h");
+
+        type Object;
+
+        fn create_shared_ptr() -> SharedPtr<Object>;
+    }
+}
+
+fn main() {
+    let ptr1 = ffi::create_shared_ptr();
+
+    {
+        // Create a second shared_ptr holding shared ownership of the same
+        // object. There is still only one Object but two SharedPtr<Object>.
+        // Both pointers point to the same object on the heap.
+        let ptr2 = ptr1.clone();
+        assert!(ptr::eq(ptr1.deref(), ptr2.deref()));
+
+        // ptr2 goes out of scope, but Object is not destroyed yet.
+    }
+
+    println!("say goodbye to Object");
+
+    // ptr1 goes out of scope and Object is destroyed.
+}
+```
+
+```cpp
+// include/example.h
+
+#pragma once
+#include <memory>
+
+class Object {
+public:
+  Object();
+  ~Object();
+};
+
+std::shared_ptr<Object> create_shared_ptr();
+```
+
+```cpp
+// src/example.cc
+
+#include "example/include/example.h"
+#include <iostream>
+
+Object::Object() { std::cout << "construct Object" << std::endl; }
+Object::~Object() { std::cout << "~Object" << std::endl; }
+
+std::shared_ptr<Object> create_shared_ptr() {
+  return std::make_shared<Object>();
+}
+```
diff --git a/book/src/binding/slice.md b/book/src/binding/slice.md
new file mode 100644
index 0000000..803277b
--- /dev/null
+++ b/book/src/binding/slice.md
@@ -0,0 +1,171 @@
+{{#title rust::Slice<T> — Rust ♡ C++}}
+# rust::Slice\<const T\>,&ensp;rust::Slice\<T\>
+
+- Rust `&[T]` is written `rust::Slice<const T>` in C++
+- Rust `&mut [T]` is written `rust::Slice<T>` in C++
+
+### Public API:
+
+```cpp,hidelines
+// rust/cxx.h
+#
+# #include <iterator>
+# #include <type_traits>
+#
+# namespace rust {
+
+template <typename T>
+class Slice final {
+public:
+  using value_type = T;
+
+  Slice() noexcept;
+  Slice(const Slice<T> &) noexcept;
+  Slice(T *, size_t count) noexcept;
+
+  Slice &operator=(Slice<T> &&) noexcept;
+  Slice &operator=(const Slice<T> &) noexcept
+    requires std::is_const_v<T>;
+
+  T *data() const noexcept;
+  size_t size() const noexcept;
+  size_t length() const noexcept;
+  bool empty() const noexcept;
+
+  T &operator[](size_t n) const noexcept;
+  T &at(size_t n) const;
+  T &front() const noexcept;
+  T &back() const noexcept;
+
+  class iterator;
+  iterator begin() const noexcept;
+  iterator end() const noexcept;
+
+  void swap(Slice &) noexcept;
+};
+#
+# template <typename T>
+# class Slice<T>::iterator final {
+# public:
+#   using iterator_category = std::random_access_iterator_tag;
+#   using value_type = T;
+#   using pointer = T *;
+#   using reference = T &;
+#
+#   T &operator*() const noexcept;
+#   T *operator->() const noexcept;
+#   T &operator[](ptrdiff_t) const noexcept;
+#
+#   iterator &operator++() noexcept;
+#   iterator operator++(int) noexcept;
+#   iterator &operator--() noexcept;
+#   iterator operator--(int) noexcept;
+#
+#   iterator &operator+=(ptrdiff_t) noexcept;
+#   iterator &operator-=(ptrdiff_t) noexcept;
+#   iterator operator+(ptrdiff_t) const noexcept;
+#   iterator operator-(ptrdiff_t) const noexcept;
+#   ptrdiff_t operator-(const iterator &) const noexcept;
+#
+#   bool operator==(const iterator &) const noexcept;
+#   bool operator!=(const iterator &) const noexcept;
+#   bool operator<(const iterator &) const noexcept;
+#   bool operator>(const iterator &) const noexcept;
+#   bool operator<=(const iterator &) const noexcept;
+#   bool operator>=(const iterator &) const noexcept;
+# };
+#
+# } // namespace rust
+```
+
+### Restrictions:
+
+T must not be an opaque Rust type or opaque C++ type. Support for opaque Rust
+types in slices is coming.
+
+Allowed as function argument or return value. Not supported in shared structs.
+
+Only rust::Slice\<const T\> is copy-assignable, not rust::Slice\<T\>. (Both are
+move-assignable.) You'll need to write std::move occasionally as a reminder that
+accidentally exposing overlapping &amp;mut \[T\] to Rust is UB.
+
+## Example
+
+This example is a C++ program that constructs a slice containing JSON data (by
+reading from stdin, but it could be from anywhere), then calls into Rust to
+pretty-print that JSON data into a std::string via the [serde_json] and
+[serde_transcode] crates.
+
+[serde_json]: https://github.com/serde-rs/json
+[serde_transcode]: https://github.com/sfackler/serde-transcode
+
+```rust,noplayground
+// src/main.rs
+
+#![no_main] // main defined in C++ by main.cc
+
+use cxx::CxxString;
+use std::io::{self, Write};
+use std::pin::Pin;
+
+#[cxx::bridge]
+mod ffi {
+    extern "Rust" {
+        fn prettify_json(input: &[u8], output: Pin<&mut CxxString>) -> Result<()>;
+    }
+}
+
+struct WriteToCxxString<'a>(Pin<&'a mut CxxString>);
+
+impl<'a> Write for WriteToCxxString<'a> {
+    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+        self.0.as_mut().push_bytes(buf);
+        Ok(buf.len())
+    }
+    fn flush(&mut self) -> io::Result<()> {
+        Ok(())
+    }
+}
+
+fn prettify_json(input: &[u8], output: Pin<&mut CxxString>) -> serde_json::Result<()> {
+    let writer = WriteToCxxString(output);
+    let mut deserializer = serde_json::Deserializer::from_slice(input);
+    let mut serializer = serde_json::Serializer::pretty(writer);
+    serde_transcode::transcode(&mut deserializer, &mut serializer)
+}
+```
+
+```cpp
+// src/main.cc
+
+#include "example/src/main.rs.h"
+#include <iostream>
+#include <iterator>
+#include <string>
+#include <vector>
+
+int main() {
+  // Read json from stdin.
+  std::istreambuf_iterator<char> begin{std::cin}, end;
+  std::vector<unsigned char> input{begin, end};
+  rust::Slice<const uint8_t> slice{input.data(), input.size()};
+
+  // Prettify using serde_json and serde_transcode.
+  std::string output;
+  prettify_json(slice, output);
+
+  // Write to stdout.
+  std::cout << output << std::endl;
+}
+```
+
+Testing the example:
+
+```console
+$  echo '{"fearless":"concurrency"}' | cargo run
+    Finished dev [unoptimized + debuginfo] target(s) in 0.02s
+     Running `target/debug/example`
+{
+  "fearless": "concurrency"
+}
+```
diff --git a/book/src/binding/str.md b/book/src/binding/str.md
new file mode 100644
index 0000000..a4820aa
--- /dev/null
+++ b/book/src/binding/str.md
@@ -0,0 +1,117 @@
+{{#title rust::Str — Rust ♡ C++}}
+# rust::Str
+
+### Public API:
+
+```cpp,hidelines
+// rust/cxx.h
+#
+# #include <iosfwd>
+# #include <string>
+#
+# namespace rust {
+
+class Str final {
+public:
+  Str() noexcept;
+  Str(const Str &) noexcept;
+  Str(const String &) noexcept;
+
+  // Throws std::invalid_argument if not utf-8.
+  Str(const std::string &);
+  Str(const char *);
+  Str(const char *, size_t);
+
+  Str &operator=(const Str &) noexcept;
+
+  explicit operator std::string() const;
+
+  // Note: no null terminator.
+  const char *data() const noexcept;
+  size_t size() const noexcept;
+  size_t length() const noexcept;
+
+  using iterator = const char *;
+  using const_iterator = const char *;
+  const_iterator begin() const noexcept;
+  const_iterator end() const noexcept;
+  const_iterator cbegin() const noexcept;
+  const_iterator cend() const noexcept;
+
+  bool operator==(const Str &) const noexcept;
+  bool operator!=(const Str &) const noexcept;
+  bool operator<(const Str &) const noexcept;
+  bool operator<=(const Str &) const noexcept;
+  bool operator>(const Str &) const noexcept;
+  bool operator>=(const Str &) const noexcept;
+
+  void swap(Str &) noexcept;
+};
+
+std::ostream &operator<<(std::ostream &, const Str &);
+#
+# } // namespace rust
+```
+
+### Notes:
+
+**Be aware that rust::Str behaves like &amp;str i.e. it is a borrow!**&ensp;C++
+needs to be mindful of the lifetimes at play.
+
+Just to reiterate: &amp;str is rust::Str. Do not try to write &amp;str as `const
+rust::Str &`. A language-level C++ reference is not able to capture the fat
+pointer nature of &amp;str.
+
+### Restrictions:
+
+Allowed as function argument or return value. Not supported in shared structs
+yet. `&mut str` is not supported yet, but is also extremely obscure so this is
+fine.
+
+## Example
+
+```rust,noplayground
+// src/main.rs
+
+#[cxx::bridge]
+mod ffi {
+    extern "Rust" {
+        fn r(greeting: &str);
+    }
+
+    unsafe extern "C++" {
+        include!("example/include/greeting.h");
+        fn c(greeting: &str);
+    }
+}
+
+fn r(greeting: &str) {
+    println!("{}", greeting);
+}
+
+fn main() {
+    ffi::c("hello from Rust");
+}
+```
+
+```cpp
+// include/greeting.h
+
+#pragma once
+#include "example/src/main.rs.h"
+#include "rust/cxx.h"
+
+void c(rust::Str greeting);
+```
+
+```cpp
+// src/greeting.cc
+
+#include "example/include/greeting.h"
+#include <iostream>
+
+void c(rust::Str greeting) {
+  std::cout << greeting << std::endl;
+  r("hello from C++");
+}
+```
diff --git a/book/src/binding/string.md b/book/src/binding/string.md
new file mode 100644
index 0000000..a7d0790
--- /dev/null
+++ b/book/src/binding/string.md
@@ -0,0 +1,115 @@
+{{#title rust::String — Rust ♡ C++}}
+# rust::String
+
+### Public API:
+
+```cpp,hidelines
+// rust/cxx.h
+#
+# #include <iosfwd>
+# #include <string>
+#
+# namespace rust {
+
+class String final {
+public:
+  String() noexcept;
+  String(const String &) noexcept;
+  String(String &&) noexcept;
+  ~String() noexcept;
+
+  // Throws std::invalid_argument if not utf-8.
+  String(const std::string &);
+  String(const char *);
+  String(const char *, size_t);
+
+  String &operator=(const String &) noexcept;
+  String &operator=(String &&) noexcept;
+
+  explicit operator std::string() const;
+
+  // Note: no null terminator.
+  const char *data() const noexcept;
+  size_t size() const noexcept;
+  size_t length() const noexcept;
+
+  const char *c_str() noexcept;
+
+  using iterator = char *;
+  iterator begin() noexcept;
+  iterator end() noexcept;
+
+  using const_iterator = const char *;
+  const_iterator begin() const noexcept;
+  const_iterator end() const noexcept;
+  const_iterator cbegin() const noexcept;
+  const_iterator cend() const noexcept;
+
+  bool operator==(const String &) const noexcept;
+  bool operator!=(const String &) const noexcept;
+  bool operator<(const String &) const noexcept;
+  bool operator<=(const String &) const noexcept;
+  bool operator>(const String &) const noexcept;
+  bool operator>=(const String &) const noexcept;
+
+  void swap(String &) noexcept;
+};
+
+std::ostream &operator<<(std::ostream &, const String &);
+#
+# } // namespace rust
+```
+
+### Restrictions:
+
+None. Strings may be used as function arguments and function return values, by
+value or by reference, as well as fields of shared structs.
+
+## Example
+
+```rust,noplayground
+// src/main.rs
+
+#[cxx::bridge]
+mod ffi {
+    struct ConcatRequest {
+        fst: String,
+        snd: String,
+    }
+
+    unsafe extern "C++" {
+        include!("example/include/concat.h");
+        fn concat(r: ConcatRequest) -> String;
+    }
+}
+
+fn main() {
+    let concatenated = ffi::concat(ffi::ConcatRequest {
+        fst: "fearless".to_owned(),
+        snd: "concurrency".to_owned(),
+    });
+    println!("concatenated: {:?}", concatenated);
+}
+```
+
+```cpp
+// include/concat.h
+
+#pragma once
+#include "example/src/main.rs.h"
+#include "rust/cxx.h"
+
+rust::String concat(ConcatRequest r);
+```
+
+```cpp
+// src/concat.cc
+
+#include "example/include/concat.h"
+
+rust::String concat(ConcatRequest r) {
+  // The full suite of operator overloads hasn't been added
+  // yet on rust::String, but we can get it done like this:
+  return std::string(r.fst) + std::string(r.snd);
+}
+```
diff --git a/book/src/binding/uniqueptr.md b/book/src/binding/uniqueptr.md
new file mode 100644
index 0000000..eefbc34
--- /dev/null
+++ b/book/src/binding/uniqueptr.md
@@ -0,0 +1,63 @@
+{{#title std::unique_ptr<T> — Rust ♡ C++}}
+# std::unique\_ptr\<T\>
+
+The Rust binding of std::unique\_ptr\<T\> is called **[`UniquePtr<T>`]**. See
+the link for documentation of the Rust API.
+
+[`UniquePtr<T>`]: https://docs.rs/cxx/*/cxx/struct.UniquePtr.html
+
+### Restrictions:
+
+Only `std::unique_ptr<T, std::default_delete<T>>` is currently supported. Custom
+deleters may be supported in the future.
+
+UniquePtr\<T\> does not support T being an opaque Rust type. You should use a
+Box\<T\> (C++ [rust::Box\<T\>](box.md)) instead for transferring ownership of
+opaque Rust types on the language boundary.
+
+## Example
+
+UniquePtr is commonly useful for returning opaque C++ objects to Rust. This use
+case was featured in the [*blobstore tutorial*](../tutorial.md).
+
+```rust,noplayground
+// src/main.rs
+
+#[cxx::bridge]
+mod ffi {
+    unsafe extern "C++" {
+        include!("example/include/blobstore.h");
+
+        type BlobstoreClient;
+
+        fn new_blobstore_client() -> UniquePtr<BlobstoreClient>;
+        // ...
+    }
+}
+
+fn main() {
+    let client = ffi::new_blobstore_client();
+    // ...
+}
+```
+
+```cpp
+// include/blobstore.h
+
+#pragma once
+#include <memory>
+
+class BlobstoreClient;
+
+std::unique_ptr<BlobstoreClient> new_blobstore_client();
+```
+
+```cpp
+// src/blobstore.cc
+
+#include "example/include/blobstore.h"
+
+std::unique_ptr<BlobstoreClient> new_blobstore_client() {
+  return std::make_unique<BlobstoreClient>();
+}
+```
diff --git a/book/src/binding/vec.md b/book/src/binding/vec.md
new file mode 100644
index 0000000..948fa67
--- /dev/null
+++ b/book/src/binding/vec.md
@@ -0,0 +1,190 @@
+{{#title rust::Vec<T> — Rust ♡ C++}}
+# rust::Vec\<T\>
+
+### Public API:
+
+```cpp,hidelines
+// rust/cxx.h
+#
+# #include <initializer_list>
+# #include <iterator>
+# #include <type_traits>
+#
+# namespace rust {
+
+template <typename T>
+class Vec final {
+public:
+  using value_type = T;
+
+  Vec() noexcept;
+  Vec(std::initializer_list<T>);
+  Vec(const Vec &);
+  Vec(Vec &&) noexcept;
+  ~Vec() noexcept;
+
+  Vec &operator=(Vec &&) noexcept;
+  Vec &operator=(const Vec &);
+
+  size_t size() const noexcept;
+  bool empty() const noexcept;
+  const T *data() const noexcept;
+  T *data() noexcept;
+  size_t capacity() const noexcept;
+
+  const T &operator[](size_t n) const noexcept;
+  const T &at(size_t n) const;
+  const T &front() const;
+  const T &back() const;
+
+  T &operator[](size_t n) noexcept;
+  T &at(size_t n);
+  T &front();
+  T &back();
+
+  void reserve(size_t new_cap);
+  void push_back(const T &value);
+  void push_back(T &&value);
+  template <typename... Args>
+  void emplace_back(Args &&...args);
+
+  class iterator;
+  iterator begin() noexcept;
+  iterator end() noexcept;
+
+  class const_iterator;
+  const_iterator begin() const noexcept;
+  const_iterator end() const noexcept;
+  const_iterator cbegin() const noexcept;
+  const_iterator cend() const noexcept;
+
+  void swap(Vec &) noexcept;
+};
+#
+# template <typename T>
+# class Vec<T>::iterator final {
+# public:
+#   using iterator_category = std::random_access_iterator_tag;
+#   using value_type = T;
+#   using pointer = T *;
+#   using reference = T &;
+#
+#   T &operator*() const noexcept;
+#   T *operator->() const noexcept;
+#   T &operator[](ptrdiff_t) const noexcept;
+#
+#   iterator &operator++() noexcept;
+#   iterator operator++(int) noexcept;
+#   iterator &operator--() noexcept;
+#   iterator operator--(int) noexcept;
+#
+#   iterator &operator+=(ptrdiff_t) noexcept;
+#   iterator &operator-=(ptrdiff_t) noexcept;
+#   iterator operator+(ptrdiff_t) const noexcept;
+#   iterator operator-(ptrdiff_t) const noexcept;
+#   ptrdiff_t operator-(const iterator &) const noexcept;
+#
+#   bool operator==(const iterator &) const noexcept;
+#   bool operator!=(const iterator &) const noexcept;
+#   bool operator<(const iterator &) const noexcept;
+#   bool operator<=(const iterator &) const noexcept;
+#   bool operator>(const iterator &) const noexcept;
+#   bool operator>=(const iterator &) const noexcept;
+# };
+#
+# template <typename T>
+# class Vec<T>::const_iterator final {
+# public:
+#   using iterator_category = std::random_access_iterator_tag;
+#   using value_type = const T;
+#   using pointer = const T *;
+#   using reference = const T &;
+#
+#   const T &operator*() const noexcept;
+#   const T *operator->() const noexcept;
+#   const T &operator[](ptrdiff_t) const noexcept;
+#
+#   const_iterator &operator++() noexcept;
+#   const_iterator operator++(int) noexcept;
+#   const_iterator &operator--() noexcept;
+#   const_iterator operator--(int) noexcept;
+#
+#   const_iterator &operator+=(ptrdiff_t) noexcept;
+#   const_iterator &operator-=(ptrdiff_t) noexcept;
+#   const_iterator operator+(ptrdiff_t) const noexcept;
+#   const_iterator operator-(ptrdiff_t) const noexcept;
+#   ptrdiff_t operator-(const const_iterator &) const noexcept;
+#
+#   bool operator==(const const_iterator &) const noexcept;
+#   bool operator!=(const const_iterator &) const noexcept;
+#   bool operator<(const const_iterator &) const noexcept;
+#   bool operator<=(const const_iterator &) const noexcept;
+#   bool operator>(const const_iterator &) const noexcept;
+#   bool operator>=(const const_iterator &) const noexcept;
+# };
+#
+# } // namespace rust
+```
+
+### Restrictions:
+
+Vec\<T\> does not support T being an opaque C++ type. You should use
+CxxVector\<T\> (C++ std::vector\<T\>) instead for collections of opaque C++
+types on the language boundary.
+
+## Example
+
+```rust,noplayground
+// src/main.rs
+
+#[cxx::bridge]
+mod ffi {
+    struct Shared {
+        v: u32,
+    }
+
+    unsafe extern "C++" {
+        include!("example/include/example.h");
+
+        fn f(elements: Vec<Shared>);
+    }
+}
+
+fn main() {
+    let shared = |v| ffi::Shared { v };
+    let elements = vec![shared(3), shared(2), shared(1)];
+    ffi::f(elements);
+}
+```
+
+```cpp
+// include/example.h
+
+#pragma once
+#include "example/src/main.rs.h"
+#include "rust/cxx.h"
+
+void f(rust::Vec<Shared> elements);
+```
+
+```cpp
+// src/example.cc
+
+#include "example/include/example.h"
+#include <algorithm>
+#include <cassert>
+#include <iostream>
+#include <iterator>
+#include <vector>
+
+void f(rust::Vec<Shared> v) {
+  for (auto shared : v) {
+    std::cout << shared.v << std::endl;
+  }
+
+  // Copy the elements to a C++ std::vector using STL algorithm.
+  std::vector<Shared> stdv;
+  std::copy(v.begin(), v.end(), std::back_inserter(stdv));
+  assert(v.size() == stdv.size());
+}
+```
diff --git a/book/src/bindings.md b/book/src/bindings.md
new file mode 100644
index 0000000..bcb51d8
--- /dev/null
+++ b/book/src/bindings.md
@@ -0,0 +1,56 @@
+{{#title Built-in bindings — Rust ♡ C++}}
+# Built-in bindings reference
+
+In addition to all the primitive types (i32 &lt;=&gt; int32_t), the following
+common types may be used in the fields of shared structs and the arguments and
+returns of extern functions.
+
+<br>
+
+<table>
+<tr><th>name in Rust</th><th>name in C++</th><th>restrictions</th></tr>
+<tr><td style="padding:3px 6px">String</td><td style="padding:3px 6px"><b><a href="binding/string.md">rust::String</a></b></td><td style="padding:3px 6px"></td></tr>
+<tr><td style="padding:3px 6px">&amp;str</td><td style="padding:3px 6px"><b><a href="binding/str.md">rust::Str</a></b></td><td style="padding:3px 6px"></td></tr>
+<tr><td style="padding:3px 6px">&amp;[T]</td><td style="padding:3px 6px"><b><a href="binding/slice.md">rust::Slice&lt;const&nbsp;T&gt;</a></b></td><td style="padding:3px 6px"><sup><i>cannot hold opaque C++ type</i></sup></td></tr>
+<tr><td style="padding:3px 6px">&amp;mut [T]</td><td style="padding:3px 6px"><b><a href="binding/slice.md">rust::Slice&lt;T&gt;</a></b></td><td style="padding:3px 6px"><sup><i>cannot hold opaque C++ type</i></sup></td></tr>
+<tr><td style="padding:3px 6px"><b><a href="binding/cxxstring.md">CxxString</a></b></td><td style="padding:3px 6px">std::string</td><td style="padding:3px 6px"><sup><i>cannot be passed by value</i></sup></td></tr>
+<tr><td style="padding:3px 6px">Box&lt;T&gt;</td><td style="padding:3px 6px"><b><a href="binding/box.md">rust::Box&lt;T&gt;</a></b></td><td style="padding:3px 6px"><sup><i>cannot hold opaque C++ type</i></sup></td></tr>
+<tr><td style="padding:3px 6px"><b><a href="binding/uniqueptr.md">UniquePtr&lt;T&gt;</a></b></td><td style="padding:3px 6px">std::unique_ptr&lt;T&gt;</td><td style="padding:3px 6px"><sup><i>cannot hold opaque Rust type</i></sup></td></tr>
+<tr><td style="padding:3px 6px"><b><a href="binding/sharedptr.md">SharedPtr&lt;T&gt;</a></b></td><td style="padding:3px 6px">std::shared_ptr&lt;T&gt;</td><td style="padding:3px 6px"><sup><i>cannot hold opaque Rust type</i></sup></td></tr>
+<tr><td style="padding:3px 6px">[T; N]</td><td style="padding:3px 6px">std::array&lt;T, N&gt;</td><td style="padding:3px 6px"><sup><i>cannot hold opaque C++ type</i></sup></td></tr>
+<tr><td style="padding:3px 6px">Vec&lt;T&gt;</td><td style="padding:3px 6px"><b><a href="binding/vec.md">rust::Vec&lt;T&gt;</a></b></td><td style="padding:3px 6px"><sup><i>cannot hold opaque C++ type</i></sup></td></tr>
+<tr><td style="padding:3px 6px"><b><a href="binding/cxxvector.md">CxxVector&lt;T&gt;</a></b></td><td style="padding:3px 6px">std::vector&lt;T&gt;</td><td style="padding:3px 6px"><sup><i>cannot be passed by value, cannot hold opaque Rust type</i></sup></td></tr>
+<tr><td style="padding:3px 6px"><b><a href="binding/rawptr.md">*mut T, *const T</a></b></td><td style="padding:3px 6px">T*, const T*</td><td style="padding:3px 6px"><sup><i>fn with a raw pointer argument must be declared unsafe to call</i></sup></td></tr>
+<tr><td style="padding:3px 6px">fn(T, U) -&gt; V</td><td style="padding:3px 6px"><b><a href="binding/fn.md">rust::Fn&lt;V(T, U)&gt;</a></b></td><td style="padding:3px 6px"><sup><i>only passing from Rust to C++ is implemented so far</i></sup></td></tr>
+<tr><td style="padding:3px 6px"><b><a href="binding/result.md">Result&lt;T&gt;</a></b></td><td style="padding:3px 6px">throw/catch</td><td style="padding:3px 6px"><sup><i>allowed as return type only</i></sup></td></tr>
+</table>
+
+<br>
+
+The C++ API of the `rust` namespace is defined by the *include/cxx.h* file in
+the CXX GitHub repo. You will need to include this header in your C++ code when
+working with those types. **When using Cargo and the cxx-build crate, the header
+is made available to you at `#include "rust/cxx.h"`.**
+
+The `rust` namespace additionally provides lowercase type aliases of all the
+types mentioned in the table, for use in codebases preferring that style. For
+example `rust::String`, `rust::Vec` may alternatively be written `rust::string`,
+`rust::vec` etc.
+
+## Pending bindings
+
+The following types are intended to be supported "soon" but are just not
+implemented yet. I don't expect any of these to be hard to make work but it's a
+matter of designing a nice API for each in its non-native language.
+
+<br>
+
+<table>
+<tr><th>name in Rust</th><th>name in C++</th></tr>
+<tr><td>BTreeMap&lt;K, V&gt;</td><td><sup><i>tbd</i></sup></td></tr>
+<tr><td>HashMap&lt;K, V&gt;</td><td><sup><i>tbd</i></sup></td></tr>
+<tr><td>Arc&lt;T&gt;</td><td><sup><i>tbd</i></sup></td></tr>
+<tr><td>Option&lt;T&gt;</td><td><sup><i>tbd</i></sup></td></tr>
+<tr><td><sup><i>tbd</i></sup></td><td>std::map&lt;K, V&gt;</td></tr>
+<tr><td><sup><i>tbd</i></sup></td><td>std::unordered_map&lt;K, V&gt;</td></tr>
+</table>
diff --git a/book/src/build/bazel.md b/book/src/build/bazel.md
new file mode 100644
index 0000000..08edb19
--- /dev/null
+++ b/book/src/build/bazel.md
@@ -0,0 +1,106 @@
+{{#title Bazel, Buck — Rust ♡ C++}}
+## Bazel, Buck, potentially other similar environments
+
+Starlark-based build systems with the ability to compile a code generator and
+invoke it as a `genrule` will run CXX's C++ code generator via its `cxxbridge`
+command line interface.
+
+The tool is packaged as the `cxxbridge-cmd` crate on crates.io or can be built
+from the *gen/cmd/* directory of the CXX GitHub repo.
+
+```console
+$  cargo install cxxbridge-cmd
+
+$  cxxbridge src/bridge.rs --header > path/to/bridge.rs.h
+$  cxxbridge src/bridge.rs > path/to/bridge.rs.cc
+```
+
+The CXX repo maintains working Bazel `BUILD` and Buck `BUCK` targets for the
+complete blobstore tutorial (chapter 3) for your reference, tested in CI. These
+aren't meant to be directly what you use in your codebase, but serve as an
+illustration of one possible working pattern.
+
+```python
+# tools/bazel/rust_cxx_bridge.bzl
+
+load("@bazel_skylib//rules:run_binary.bzl", "run_binary")
+load("@rules_cc//cc:defs.bzl", "cc_library")
+
+def rust_cxx_bridge(name, src, deps = []):
+    native.alias(
+        name = "%s/header" % name,
+        actual = src + ".h",
+    )
+
+    native.alias(
+        name = "%s/source" % name,
+        actual = src + ".cc",
+    )
+
+    run_binary(
+        name = "%s/generated" % name,
+        srcs = [src],
+        outs = [
+            src + ".h",
+            src + ".cc",
+        ],
+        args = [
+            "$(location %s)" % src,
+            "-o",
+            "$(location %s.h)" % src,
+            "-o",
+            "$(location %s.cc)" % src,
+        ],
+        tool = "//:codegen",
+    )
+
+    cc_library(
+        name = name,
+        srcs = [src + ".cc"],
+        deps = deps + [":%s/include" % name],
+    )
+
+    cc_library(
+        name = "%s/include" % name,
+        hdrs = [src + ".h"],
+    )
+```
+
+```python
+# demo/BUILD
+
+load("@rules_cc//cc:defs.bzl", "cc_library")
+load("//tools/bazel:rust.bzl", "rust_binary")
+load("//tools/bazel:rust_cxx_bridge.bzl", "rust_cxx_bridge")
+
+rust_binary(
+    name = "demo",
+    srcs = glob(["src/**/*.rs"]),
+    deps = [
+        ":blobstore-sys",
+        ":bridge",
+        "//:cxx",
+    ],
+)
+
+rust_cxx_bridge(
+    name = "bridge",
+    src = "src/main.rs",
+    deps = [":blobstore-include"],
+)
+
+cc_library(
+    name = "blobstore-sys",
+    srcs = ["src/blobstore.cc"],
+    deps = [
+        ":blobstore-include",
+        ":bridge/include",
+    ],
+)
+
+cc_library(
+    name = "blobstore-include",
+    hdrs = ["include/blobstore.h"],
+    deps = ["//:core"],
+)
+```
diff --git a/book/src/build/cargo.md b/book/src/build/cargo.md
new file mode 100644
index 0000000..82ccfb5
--- /dev/null
+++ b/book/src/build/cargo.md
@@ -0,0 +1,306 @@
+{{#title Cargo-based setup — Rust ♡ C++}}
+# Cargo-based builds
+
+As one aspect of delivering a good Rust&ndash;C++ interop experience, CXX turns
+Cargo into a quite usable build system for C++ projects published as a
+collection of crates.io packages, including a consistent and frictionless
+experience `#include`-ing C++ headers across dependencies.
+
+## Canonical setup
+
+CXX's integration with Cargo is handled through the [cxx-build] crate.
+
+[cxx-build]: https://docs.rs/cxx-build
+
+```toml,hidelines
+## Cargo.toml
+# [package]
+# name = "..."
+# version = "..."
+# edition = "2018"
+
+[dependencies]
+cxx = "1.0"
+
+[build-dependencies]
+cxx-build = "1.0"
+```
+
+The canonical build script is as follows. The indicated line returns a
+[`cc::Build`] instance (from the usual widely used `cc` crate) on which you can
+set up any additional source files and compiler flags as normal.
+
+[`cc::Build`]: https://docs.rs/cc/1.0/cc/struct.Build.html
+
+```rust,noplayground
+// build.rs
+
+fn main() {
+    cxx_build::bridge("src/main.rs")  // returns a cc::Build
+        .file("src/demo.cc")
+        .flag_if_supported("-std=c++11")
+        .compile("cxxbridge-demo");
+
+    println!("cargo:rerun-if-changed=src/main.rs");
+    println!("cargo:rerun-if-changed=src/demo.cc");
+    println!("cargo:rerun-if-changed=include/demo.h");
+}
+```
+
+The `rerun-if-changed` lines are optional but make it so that Cargo does not
+spend time recompiling your C++ code when only non-C++ code has changed since
+the previous Cargo build. By default without any `rerun-if-changed`, Cargo will
+re-execute the build script after *any* file changed in the project.
+
+If stuck, try comparing what you have against the *demo/* directory of the CXX
+GitHub repo, which maintains a working Cargo-based setup for the blobstore
+tutorial (chapter 3).
+
+## Header include paths
+
+With cxx-build, by default your include paths always start with the crate name.
+This applies to both `#include` within your C++ code, and `include!` in the
+`extern "C++"` section of your Rust cxx::bridge.
+
+Your crate name is determined by the `name` entry in Cargo.toml.
+
+For example if your crate is named `yourcratename` and contains a C++ header
+file `path/to/header.h` relative to Cargo.toml, that file will be includable as:
+
+```cpp
+#include "yourcratename/path/to/header.h"
+```
+
+A crate can choose a prefix for its headers that is different from the crate
+name by modifying **[`CFG.include_prefix`][CFG]** from build.rs:
+
+[CFG]: https://docs.rs/cxx-build/*/cxx_build/static.CFG.html
+
+```rust,noplayground
+// build.rs
+
+use cxx_build::CFG;
+
+fn main() {
+    CFG.include_prefix = "my/project";
+
+    cxx_build::bridge(...)...
+}
+```
+
+Subsequently the header located at `path/to/header.h` would now be includable
+as:
+
+```cpp
+#include "my/project/path/to/header.h"
+```
+
+The empty string `""` is a valid include prefix and will make it possible to
+have `#include "path/to/header.h"`. However, if your crate is a library, be
+considerate of possible name collisions that may occur in downstream crates. If
+using an empty include prefix, you'll want to make sure your headers' local path
+within the crate is sufficiently namespaced or unique.
+
+## Including generated code
+
+If your `#[cxx::bridge]` module contains an `extern "Rust"` block i.e. types or
+functions exposed from Rust to C++, or any shared data structures, the
+CXX-generated C++ header declaring those things is available using a `.rs.h`
+extension on the Rust source file's name.
+
+```cpp
+// the header generated from path/to/lib.rs
+#include "yourcratename/path/to/lib.rs.h"
+```
+
+For giggles, it's also available using just a plain `.rs` extension as if you
+were including the Rust file directly. Use whichever you find more palatable.
+
+```cpp
+#include "yourcratename/path/to/lib.rs"
+```
+
+## Including headers from dependencies
+
+You get to include headers from your dependencies, both handwritten ones
+contained as `.h` files in their Cargo package, as well as CXX-generated ones.
+
+It works the same as an include of a local header: use the crate name (or their
+include\_prefix if their crate changed it) followed by the relative path of the
+header within the crate.
+
+```cpp
+#include "dependencycratename/path/to/their/header.h`
+```
+
+Note that cross-crate imports are only made available between **direct
+dependencies**. You must directly depend on the other crate in order to #include
+its headers; a transitive dependency is not sufficient.
+
+Additionally, headers from a direct dependency are only importable if the
+dependency's Cargo.toml manifest contains a `links` key. If not, its headers
+will not be importable from outside of the same crate. See *[the `links`
+manifest key][links]* in the Cargo reference.
+
+[links]: https://doc.rust-lang.org/cargo/reference/build-scripts.html#the-links-manifest-key
+
+<br><br><br>
+
+# Advanced features
+
+The following CFG settings are only relevant to you if you are writing a library
+that needs to support downstream crates `#include`-ing its C++ public headers.
+
+## Publicly exporting header directories
+
+**[`CFG.exported_header_dirs`][CFG]** (vector of absolute paths) defines a set
+of additional directories from which the current crate, directly dependent
+crates, and further crates to which this crate's headers are exported (more
+below) will be able to `#include` headers.
+
+Adding a directory to `exported_header_dirs` is similar to adding it to the
+current build via the `cc` crate's [`Build::include`], but *also* makes the
+directory available to downstream crates that want to `#include` one of the
+headers from your crate. If the dir were added only using `Build::include`, the
+downstream crate including your header would need to manually add the same
+directory to their own build as well.
+
+[`Build::include`]: https://docs.rs/cc/1/cc/struct.Build.html#method.include
+
+When using `exported_header_dirs`, your crate must also set a `links` key for
+itself in Cargo.toml. See [*the `links` manifest key*][links]. The reason is
+that Cargo imposes no ordering on the execution of build scripts without a
+`links` key, which means the downstream crate's build script might otherwise
+execute before yours decides what to put into `exported_header_dirs`.
+
+### Example
+
+One of your crate's headers wants to include a system library, such as `#include
+"Python.h"`.
+
+```rust,noplayground
+// build.rs
+
+use cxx_build::CFG;
+use std::path::PathBuf;
+
+fn main() {
+    let python3 = pkg_config::probe_library("python3").unwrap();
+    let python_include_paths = python3.include_paths.iter().map(PathBuf::as_path);
+    CFG.exported_header_dirs.extend(python_include_paths);
+
+    cxx_build::bridge("src/bridge.rs").compile("demo");
+}
+```
+
+### Example
+
+Your crate wants to rearrange the headers that it exports vs how they're laid
+out locally inside the crate's source directory.
+
+Suppose the crate as published contains a file at `./include/myheader.h` but
+wants it available to downstream crates as `#include "foo/v1/public.h"`.
+
+```rust,noplayground
+// build.rs
+
+use cxx_build::CFG;
+use std::path::Path;
+use std::{env, fs};
+
+fn main() {
+    let out_dir = env::var_os("OUT_DIR").unwrap();
+    let headers = Path::new(&out_dir).join("headers");
+    CFG.exported_header_dirs.push(&headers);
+
+    // We contain `include/myheader.h` locally, but
+    // downstream will use `#include "foo/v1/public.h"`
+    let foo = headers.join("foo").join("v1");
+    fs::create_dir_all(&foo).unwrap();
+    fs::copy("include/myheader.h", foo.join("public.h")).unwrap();
+
+    cxx_build::bridge("src/bridge.rs").compile("demo");
+}
+```
+
+## Publicly exporting dependencies
+
+**[`CFG.exported_header_prefixes`][CFG]** (vector of strings) each refer to the
+`include_prefix` of one of your direct dependencies, or a prefix thereof. They
+describe which of your dependencies participate in your crate's C++ public API,
+as opposed to private use by your crate's implementation.
+
+As a general rule, if one of your headers `#include`s something from one of your
+dependencies, you need to put that dependency's `include_prefix` into
+`CFG.exported_header_prefixes` (*or* their `links` key into
+`CFG.exported_header_links`; see below). On the other hand if only your C++
+implementation files and *not* your headers are importing from the dependency,
+you do not export that dependency.
+
+The significance of exported headers is that if downstream code (crate **𝒜**)
+contains an `#include` of a header from your crate (**ℬ**) and your header
+contains an `#include` of something from your dependency (**𝒞**), the exported
+dependency **𝒞** becomes available during the downstream crate **𝒜**'s build.
+Otherwise the downstream crate **𝒜** doesn't know about **𝒞** and wouldn't be
+able to find what header your header is referring to, and would fail to build.
+
+When using `exported_header_prefixes`, your crate must also set a `links` key
+for itself in Cargo.toml.
+
+### Example
+
+Suppose you have a crate with 5 direct dependencies and the `include_prefix` for
+each one are:
+
+- "crate0"
+- "group/api/crate1"
+- "group/api/crate2"
+- "group/api/contrib/crate3"
+- "detail/crate4"
+
+Your header involves types from the first four so we re-export those as part of
+your public API, while crate4 is only used internally by your cc file not your
+header, so we do not export:
+
+```rust,noplayground
+// build.rs
+
+use cxx_build::CFG;
+
+fn main() {
+    CFG.exported_header_prefixes = vec!["crate0", "group/api"];
+
+    cxx_build::bridge("src/bridge.rs")
+        .file("src/impl.cc")
+        .compile("demo");
+}
+```
+
+<br>
+
+For more fine grained control, there is **[`CFG.exported_header_links`][CFG]**
+(vector of strings) which each refer to the `links` attribute ([*the `links`
+manifest key*][links]) of one of your crate's direct dependencies.
+
+This achieves an equivalent result to `CFG.exported_header_prefixes` by
+re-exporting a C++ dependency as part of your crate's public API, except with
+finer control for cases when multiple crates might be sharing the same
+`include_prefix` and you'd like to export some but not others. Links attributes
+are guaranteed to be unique identifiers by Cargo.
+
+When using `exported_header_links`, your crate must also set a `links` key for
+itself in Cargo.toml.
+
+### Example
+
+```rust,noplayground
+// build.rs
+
+use cxx_build::CFG;
+
+fn main() {
+    CFG.exported_header_links.push("git2");
+
+    cxx_build::bridge("src/bridge.rs").compile("demo");
+}
+```
diff --git a/book/src/build/cmake.md b/book/src/build/cmake.md
new file mode 100644
index 0000000..ed3cb61
--- /dev/null
+++ b/book/src/build/cmake.md
@@ -0,0 +1,24 @@
+{{#title CMake — Rust ♡ C++}}
+# CMake
+
+There is not an officially endorsed CMake setup for CXX, but a few developers
+have shared one that they got working. You can try one of these as a starting
+point. If you feel that you have arrived at a CMake setup that is superior to
+what is available in these links, feel free to make a PR adding it to this list.
+
+<br>
+
+---
+
+- **<https://github.com/XiangpengHao/cxx-cmake-example>**
+
+  - Supports cross-language link time optimization (LTO)
+
+---
+
+- **<https://github.com/david-cattermole/cxx-demo-example>**
+
+  - Includes a cbindgen component
+  - Tested on Windows 10 with MSVC, and on Linux
+
+---
diff --git a/book/src/build/other.md b/book/src/build/other.md
new file mode 100644
index 0000000..8933c41
--- /dev/null
+++ b/book/src/build/other.md
@@ -0,0 +1,75 @@
+{{#title Other build systems — Rust ♡ C++}}
+# Some other build system
+
+You will need to achieve at least these three things:
+
+- Produce the CXX-generated C++ bindings code.
+- Compile the generated C++ code.
+- Link the resulting objects together with your other C++ and Rust objects.
+
+### Producing the generated code
+
+CXX's Rust code generation automatically happens when the `#[cxx::bridge]`
+procedural macro is expanded during the normal Rust compilation process, so no
+special build steps are required there.
+
+But the C++ side of the bindings needs to be generated. Your options are:
+
+- Use the `cxxbridge` command, which is a standalone command line interface to
+  the CXX C++ code generator. Wire up your build system to compile and invoke
+  this tool.
+
+  ```console
+  $  cxxbridge src/bridge.rs --header > path/to/bridge.rs.h
+  $  cxxbridge src/bridge.rs > path/to/bridge.rs.cc
+  ```
+
+  It's packaged as the `cxxbridge-cmd` crate on crates.io or can be built from
+  the *gen/cmd/* directory of the CXX GitHub repo.
+
+- Or, build your own code generator frontend on top of the [cxx-gen] crate. This
+  is currently unofficial and unsupported.
+
+[cxx-gen]: https://docs.rs/cxx-gen
+
+### Compiling C++
+
+However you like. We can provide no guidance.
+
+### Linking the C++ and Rust together
+
+When linking a binary which contains mixed Rust and C++ code, you will have to
+choose between using the Rust toolchain (`rustc`) or the C++ toolchain which you
+may already have extensively tuned.
+
+Rust does not generate simple standalone `.o` files, so you can't just throw the
+Rust-generated code into your existing C++ toolchain linker. Instead you need to
+choose one of these options:
+
+* Use `rustc` as the final linker. Pass any non-Rust libraries using `-L
+  <directory>` and `-l<library>` rustc arguments, and/or `#[link]` directives in
+  your Rust code. If you need to link against C/C++ `.o` files you can use
+  `-Clink-arg=file.o`.
+
+* Use your C++ linker. In this case, you first need to use `rustc` and/or
+  `cargo` to generate a _single_ Rust `staticlib` target and pass that into your
+  foreign linker invocation.
+
+  * If you need to link multiple Rust subsystems, you will need to generate a
+    _single_ `staticlib` perhaps using lots of `extern crate` statements to
+    include multiple Rust `rlib`s.  Multiple Rust `staticlib` files are likely
+    to conflict.
+
+Passing Rust `rlib`s directly into your non-Rust linker is not supported (but
+apparently sometimes works).
+
+See the [Rust reference's *Linkage*][linkage] page for some general information
+here.
+
+[linkage]: https://doc.rust-lang.org/reference/linkage.html
+
+The following open rust-lang issues might hold more recent guidance or
+inspiration: [rust-lang/rust#73632], [rust-lang/rust#73295].
+
+[rust-lang/rust#73632]: https://github.com/rust-lang/rust/issues/73632
+[rust-lang/rust#73295]: https://github.com/rust-lang/rust/issues/73295
diff --git a/book/src/building.md b/book/src/building.md
new file mode 100644
index 0000000..c75939e
--- /dev/null
+++ b/book/src/building.md
@@ -0,0 +1,20 @@
+{{#title Multi-language build system options — Rust ♡ C++}}
+# Multi-language build system options
+
+CXX is designed to be convenient to integrate into a variety of build systems.
+
+If you are working in a project that does not already have a preferred build
+system for its C++ code *or* which will be relying heavily on open source
+libraries from the Rust package registry, you're likely to have the easiest
+experience with Cargo which is the build system commonly used by open source
+Rust projects. Refer to the ***[Cargo](build/cargo.md)*** chapter about CXX's
+Cargo support.
+
+Among build systems designed for first class multi-language support, Bazel is a
+solid choice. Refer to the ***[Bazel](build/bazel.md)*** chapter.
+
+If your codebase is already invested in CMake, refer to the
+***[CMake](build/cmake.md)*** chapter.
+
+If you have some other build system that you'd like to try to make work with
+CXX, see [this page](build/other.md) for notes.
diff --git a/book/src/concepts.md b/book/src/concepts.md
new file mode 100644
index 0000000..75daedd
--- /dev/null
+++ b/book/src/concepts.md
@@ -0,0 +1,85 @@
+{{#title Core concepts — Rust ♡ C++}}
+# Core concepts
+
+This page is a brief overview of the major concepts of CXX, enough so that you
+recognize the shape of things as you read the tutorial and following chapters.
+
+In CXX, the language of the FFI boundary involves 3 kinds of items:
+
+- **Shared structs** &mdash; data structures whose fields are made visible to
+  both languages. The definition written within cxx::bridge in Rust is usually
+  the single source of truth, though there are ways to do sharing based on a
+  bindgen-generated definition with C++ as source of truth.
+
+- **Opaque types** &mdash; their fields are secret from the other language.
+  These cannot be passed across the FFI by value but only behind an indirection,
+  such as a reference `&`, a Rust `Box`, or a C++ `unique_ptr`. Can be a type
+  alias for an arbitrarily complicated generic language-specific type depending
+  on your use case.
+
+- **Functions** &mdash; implemented in either language, callable from the other
+  language.
+
+```rust,noplayground,focuscomment
+# #[cxx::bridge]
+# mod ffi {
+    // Any shared structs, whose fields will be visible to both languages.
+#     struct BlobMetadata {
+#         size: usize,
+#         tags: Vec<String>,
+#     }
+#
+#     extern "Rust" {
+        // Zero or more opaque types which both languages can pass around
+        // but only Rust can see the fields.
+#         type MultiBuf;
+#
+        // Functions implemented in Rust.
+#         fn next_chunk(buf: &mut MultiBuf) -> &[u8];
+#     }
+#
+#     unsafe extern "C++" {
+        // One or more headers with the matching C++ declarations for the
+        // enclosing extern "C++" block. Our code generators don't read it
+        // but it gets #include'd and used in static assertions to ensure
+        // our picture of the FFI boundary is accurate.
+#         include!("demo/include/blobstore.h");
+#
+        // Zero or more opaque types which both languages can pass around
+        // but only C++ can see the fields.
+#         type BlobstoreClient;
+#
+        // Functions implemented in C++.
+#         fn new_blobstore_client() -> UniquePtr<BlobstoreClient>;
+#         fn put(&self, parts: &mut MultiBuf) -> u64;
+#         fn tag(&self, blobid: u64, tag: &str);
+#         fn metadata(&self, blobid: u64) -> BlobMetadata;
+#     }
+# }
+```
+
+Within the `extern "Rust"` part of the CXX bridge we list the types and
+functions for which Rust is the source of truth. These all implicitly refer to
+the `super` module, the parent module of the CXX bridge. You can think of the
+two items listed in the example above as being like `use super::MultiBuf` and
+`use super::next_chunk` except re-exported to C++. The parent module will either
+contain the definitions directly for simple things, or contain the relevant
+`use` statements to bring them into scope from elsewhere.
+
+Within the `extern "C++"` part, we list types and functions for which C++ is the
+source of truth, as well as the header(s) that declare those APIs. In the future
+it's possible that this section could be generated bindgen-style from the
+headers but for now we need the signatures written out; static assertions verify
+that they are accurate.
+
+<br><br>
+
+Be aware that the design of this library is intentionally restrictive and
+opinionated! It isn't a goal to be flexible enough to handle an arbitrary
+signature in either language. Instead this project is about carving out a highly
+expressive set of functionality about which we can make powerful safety
+guarantees today and extend over time. You may find that it takes some practice
+to use CXX bridge effectively as it won't work in all the ways that you may be
+used to.
+
+<br>
diff --git a/book/src/context.md b/book/src/context.md
new file mode 100644
index 0000000..516ee91
--- /dev/null
+++ b/book/src/context.md
@@ -0,0 +1,118 @@
+{{#title Other Rust–C++ interop tools — Rust ♡ C++}}
+# Context: other Rust&ndash;C++ interop tools
+
+When it comes to interacting with an idiomatic Rust API or idiomatic C++ API
+from the other language, the generally applicable approaches outside of the CXX
+crate are:
+
+- Build a C-compatible wrapper around the code (expressed using `extern "C"`
+  signatures, primitives, C-compatible structs, raw pointers). Translate that
+  manually to equivalent `extern "C"` declarations in the other language and
+  keep them in sync. Preferably, build a safe/idiomatic wrapper around the
+  translated `extern "C"` signatures for callers to use.
+
+- Build a C wrapper around the C++ code and use **[bindgen]** to translate that
+  programmatically to `extern "C"` Rust signatures. Preferably, build a
+  safe/idiomatic Rust wrapper on top.
+
+- Build a C-compatible Rust wrapper around the Rust code and use **[cbindgen]**
+  to translate that programmatically to an `extern "C"` C++ header. Preferably,
+  build an idiomatic C++ wrapper.
+
+**If the code you are binding is already *"effectively C"*, the above has you
+covered.** You should use bindgen or cbindgen, or manually translated C
+signatures if there aren't too many and they seldom change.
+
+[bindgen]: https://github.com/rust-lang/rust-bindgen
+[cbindgen]: https://github.com/eqrion/cbindgen
+
+## C++ vs C
+
+Bindgen has some basic support for C++. It can reason about classes, member
+functions, and the layout of templated types. However, everything it does
+related to C++ is best-effort only. Bindgen starts from a point of wanting to
+generate declarations for everything, so any C++ detail that it hasn't
+implemented will cause a crash if you are lucky ([bindgen#388]) or more likely
+silently emit an incompatible signature ([bindgen#380], [bindgen#607],
+[bindgen#652], [bindgen#778], [bindgen#1194]) which will do arbitrary
+memory-unsafe things at runtime whenever called.
+
+[bindgen#388]: https://github.com/rust-lang/rust-bindgen/issues/388
+[bindgen#380]: https://github.com/rust-lang/rust-bindgen/issues/380
+[bindgen#607]: https://github.com/rust-lang/rust-bindgen/issues/607
+[bindgen#652]: https://github.com/rust-lang/rust-bindgen/issues/652
+[bindgen#778]: https://github.com/rust-lang/rust-bindgen/issues/778
+[bindgen#1194]: https://github.com/rust-lang/rust-bindgen/issues/1194
+
+Thus using bindgen correctly requires not just juggling all your pointers
+correctly at the language boundary, but also understanding ABI details and their
+workarounds and reliably applying them. For example, the programmer will
+discover that their program sometimes segfaults if they call a function that
+returns std::unique\_ptr\<T\> through bindgen. Why? Because unique\_ptr, despite
+being "just a pointer", has a different ABI than a pointer or a C struct
+containing a pointer ([bindgen#778]) and is not directly expressible in Rust.
+Bindgen emitted something that *looks* reasonable and you will have a hell of a
+time in gdb working out what went wrong. Eventually people learn to avoid
+anything involving a non-trivial copy constructor, destructor, or inheritance,
+and instead stick to raw pointers and primitives and trivial structs only
+&mdash; in other words C.
+
+## Geometric intuition for why there is so much opportunity for improvement
+
+The CXX project attempts a different approach to C++ FFI.
+
+Imagine Rust and C and C++ as three vertices of a scalene triangle, with length
+of the edges being related to similarity of the languages when it comes to
+library design.
+
+The most similar pair (the shortest edge) is Rust&ndash;C++. These languages
+have largely compatible concepts of things like ownership, vectors, strings,
+fallibility, etc that translate clearly from signatures in either language to
+signatures in the other language.
+
+When we make a binding for an idiomatic C++ API using bindgen, and we fall down
+to raw pointers and primitives and trivial structs as described above, what we
+are really doing is coding the two longest edges of the triangle: getting from
+C++ down to C, and C back up to Rust. The Rust&ndash;C edge always involves a
+great deal of `unsafe` code, and the C++&ndash;C edge similarly requires care
+just for basic memory safety. Something as basic as "how do I pass ownership of
+a string to the other language?" becomes a strap-yourself-in moment,
+particularly for someone not already an expert in one or both sides.
+
+You should think of the `cxx` crate as being the midpoint of the Rust&ndash;C++
+edge. Rather than coding the two long edges, you will code half the short edge
+in Rust and half the short edge in C++, in both cases with the library playing
+to the strengths of the Rust type system *and* the C++ type system to help
+assure correctness.
+
+If you've already been through the tutorial in the previous chapter, take a
+moment to appreciate that the C++ side *really* looks like we are just writing
+C++ and the Rust side *really* looks like we are just writing Rust. Anything you
+could do wrong in Rust, and almost anything you could reasonably do wrong in
+C++, will be caught by the compiler. This highlights that we are on the "short
+edge of the triangle".
+
+But it all still boils down to the same things: it's still FFI from one piece of
+native code to another, nothing is getting serialized or allocated or
+runtime-checked in between.
+
+## Role of CXX
+
+The role of CXX is to capture the language boundary with more fidelity than what
+`extern "C"` is able to represent. You can think of CXX as being a replacement
+for `extern "C"` in a sense.
+
+From this perspective, CXX is a lower level tool than the bindgens. Just as
+bindgen and cbindgen are built on top of `extern "C"`, it makes sense to think
+about higher level tools built on top of CXX. Such a tool might consume a C++
+header and/or Rust module (and/or IDL like Thrift) and emit the corresponding
+safe cxx::bridge language boundary, leveraging CXX's static analysis and
+underlying implementation of that boundary. We are beginning to see this space
+explored by the [autocxx] tool, though nothing yet ready for broad use in the
+way that CXX on its own is.
+
+[autocxx]: https://github.com/google/autocxx
+
+But note in other ways CXX is higher level than the bindgens, with rich support
+for common standard library types. CXX's types serve as an intuitive vocabulary
+for designing a good boundary between components in different languages.
diff --git a/book/src/cxx.png b/book/src/cxx.png
new file mode 100644
index 0000000..aeb7ca9
--- /dev/null
+++ b/book/src/cxx.png
Binary files differ
diff --git a/book/src/extern-c++.md b/book/src/extern-c++.md
new file mode 100644
index 0000000..11ed7b5
--- /dev/null
+++ b/book/src/extern-c++.md
@@ -0,0 +1,352 @@
+{{#title extern "C++" — Rust ♡ C++}}
+# extern "C++"
+
+```rust,noplayground
+#[cxx::bridge]
+mod ffi {
+    extern "C++" {
+        include!("path/to/header.h");
+        include!("path/to/another.h");
+
+        ...
+    }
+}
+```
+
+The `extern "C++"` section of a CXX bridge declares C++ types and signatures to
+be made available to Rust, and gives the paths of the header(s) which contain
+the corresponding C++ declarations.
+
+A bridge module may contain zero or more extern "C++" blocks.
+
+## Opaque C++ types
+
+Type defined in C++ that are made available to Rust, but only behind an
+indirection.
+
+```rust,noplayground
+# #[cxx::bridge]
+# mod ffi {
+    extern "C++" {
+        # include!("path/to/header.h");
+        #
+        type MyType;
+        type MyOtherType;
+    }
+# }
+```
+
+For example in the ***[Tutorial](tutorial.md)*** we saw `BlobstoreClient`
+implemented as an opaque C++ type. The blobstore client was created in C++ and
+returned to Rust by way of a UniquePtr.
+
+**Mutability:** Unlike extern Rust types and shared types, an extern C++ type is
+not permitted to be passed by plain mutable reference `&mut MyType` across the
+FFI bridge. For mutation support, the bridge is required to use `Pin<&mut
+MyType>`. This is to safeguard against things like mem::swap-ing the contents of
+two mutable references, given that Rust doesn't have information about the size
+of the underlying object and couldn't invoke an appropriate C++ move constructor
+anyway.
+
+**Thread safety:** Be aware that CXX does not assume anything about the thread
+safety of your extern C++ types. In other words the `MyType` etc bindings which
+CXX produces for you in Rust *do not* come with `Send` and `Sync` impls. If you
+are sure that your C++ type satisfies the requirements of `Send` and/or `Sync`
+and need to leverage that fact from Rust, you must provide your own unsafe
+marker trait impls.
+
+```rust,noplayground
+# #[cxx::bridge]
+# mod ffi {
+#     extern "C++" {
+#         include!("path/to/header.h");
+#
+#         type MyType;
+#     }
+# }
+#
+/// The C++ implementation of MyType is thread safe.
+unsafe impl Send for ffi::MyType {}
+unsafe impl Sync for ffi::MyType {}
+```
+
+Take care in doing this because thread safety in C++ can be extremely tricky to
+assess if you are coming from a Rust background. For example the
+`BlobstoreClient` type in the tutorial is *not thread safe* despite doing only
+completely innocuous things in its implementation. Concurrent calls to the `tag`
+member function trigger a data race on the `blobs` map.
+
+## Functions and member functions
+
+This largely follows the same principles as ***[extern
+"Rust"](extern-rust.md)*** functions and methods. In particular, any signature
+with a `self` parameter is interpreted as a C++ non-static member function and
+exposed to Rust as a method.
+
+The programmer **does not** need to promise that the signatures they have typed
+in are accurate; that would be unreasonable. CXX performs static assertions that
+the signatures exactly correspond with what is declared in C++. Rather, the
+programmer is only on the hook for things that C++'s static information is not
+precise enough to capture, i.e. things that would only be represented at most by
+comments in the C++ code unintelligible to a static assertion: namely whether
+the C++ function is safe or unsafe to be called from Rust.
+
+**Safety:** the extern "C++" block is responsible for deciding whether to expose
+each signature inside as safe-to-call or unsafe-to-call. If an extern block
+contains at least one safe-to-call signature, it must be written as an `unsafe
+extern` block, which serves as an item level unsafe block to indicate that an
+unchecked safety claim is being made about the contents of the block.
+
+```rust,noplayground
+#[cxx::bridge]
+mod ffi {
+    unsafe extern "C++" {
+        # include!("path/to/header.h");
+        #
+        fn f();  // safe to call
+    }
+
+    extern "C++" {
+        unsafe fn g();  // unsafe to call
+    }
+}
+```
+
+## Lifetimes
+
+C++ types holding borrowed data may be described naturally in Rust by an extern
+type with a generic lifetime parameter. For example in the case of the following
+pair of types:
+
+```cpp
+// header.h
+
+class Resource;
+
+class TypeContainingBorrow {
+  TypeContainingBorrow(const Resource &res) : res(res) {}
+  const Resource &res;
+};
+
+std::shared_ptr<TypeContainingBorrow> create(const Resource &res);
+```
+
+we'd want to expose this to Rust as:
+
+```rust,noplayground
+#[cxx::bridge]
+mod ffi {
+    unsafe extern "C++" {
+        # include!("path/to/header.h");
+        #
+        type Resource;
+        type TypeContainingBorrow<'a>;
+
+        fn create<'a>(res: &'a Resource) -> SharedPtr<TypeContainingBorrow<'a>>;
+
+        // or with lifetime elision:
+        fn create(res: &Resource) -> SharedPtr<TypeContainingBorrow>;
+    }
+}
+```
+
+## Reusing existing binding types
+
+Extern C++ types support a syntax for declaring that a Rust binding of the
+correct C++ type already exists outside of the current bridge module. This
+avoids generating a fresh new binding which Rust's type system would consider
+non-interchangeable with the first.
+
+```rust,noplayground
+#[cxx::bridge(namespace = "path::to")]
+mod ffi {
+    extern "C++" {
+        type MyType = crate::existing::MyType;
+    }
+
+    extern "Rust" {
+        fn f(x: &MyType) -> usize;
+    }
+}
+```
+
+In this case rather than producing a unique new Rust type `ffi::MyType` for the
+Rust binding of C++'s `::path::to::MyType`, CXX will reuse the already existing
+binding at `crate::existing::MyType` in expressing the signature of `f` and any
+other uses of `MyType` within the bridge module.
+
+CXX safely validates that `crate::existing::MyType` is in fact a binding for the
+right C++ type `::path::to::MyType` by generating a static assertion based on
+`crate::existing::MyType`'s implementation of [`ExternType`], which is a trait
+automatically implemented by CXX for bindings that it generates but can also be
+manually implemented as described below.
+
+[`ExternType`]: https://docs.rs/cxx/*/cxx/trait.ExternType.html
+
+`ExternType` serves the following two related use cases.
+
+#### Safely unifying occurrences of an extern type across bridges
+
+In the following snippet, two #\[cxx::bridge\] invocations in different files
+(possibly different crates) both contain function signatures involving the same
+C++ type `example::Demo`. If both were written just containing `type Demo;`,
+then both macro expansions would produce their own separate Rust type called
+`Demo` and thus the compiler wouldn't allow us to take the `Demo` returned by
+`file1::ffi::create_demo` and pass it as the `Demo` argument accepted by
+`file2::ffi::take_ref_demo`. Instead, one of the two `Demo`s has been defined as
+an extern type alias of the other, making them the same type in Rust.
+
+```rust,noplayground
+// file1.rs
+#[cxx::bridge(namespace = "example")]
+pub mod ffi {
+    unsafe extern "C++" {
+        type Demo;
+
+        fn create_demo() -> UniquePtr<Demo>;
+    }
+}
+```
+
+```rust,noplayground
+// file2.rs
+#[cxx::bridge(namespace = "example")]
+pub mod ffi {
+    unsafe extern "C++" {
+        type Demo = crate::file1::ffi::Demo;
+
+        fn take_ref_demo(demo: &Demo);
+    }
+}
+```
+
+#### Integrating with bindgen-generated or handwritten unsafe bindings
+
+Handwritten `ExternType` impls make it possible to plug in a data structure
+emitted by bindgen as the definition of a C++ type emitted by CXX.
+
+By writing the unsafe `ExternType` impl, the programmer asserts that the C++
+namespace and type name given in the type id refers to a C++ type that is
+equivalent to Rust type that is the `Self` type of the impl.
+
+```rust,noplayground
+mod folly_sys;  // the bindgen-generated bindings
+
+use cxx::{type_id, ExternType};
+
+unsafe impl ExternType for folly_sys::StringPiece {
+    type Id = type_id!("folly::StringPiece");
+    type Kind = cxx::kind::Opaque;
+}
+
+#[cxx::bridge(namespace = "folly")]
+pub mod ffi {
+    unsafe extern "C++" {
+        include!("rust_cxx_bindings.h");
+
+        type StringPiece = crate::folly_sys::StringPiece;
+
+        fn print_string_piece(s: &StringPiece);
+    }
+}
+
+// Now if we construct a StringPiece or obtain one through one
+// of the bindgen-generated signatures, we are able to pass it
+// along to ffi::print_string_piece.
+```
+
+The `ExternType::Id` associated type encodes a type-level representation of the
+type's C++ namespace and type name. It will always be defined using the
+`type_id!` macro exposed in the cxx crate.
+
+The `ExternType::Kind` associated type will always be either
+[`cxx::kind::Opaque`] or [`cxx::kind::Trivial`] identifying whether a C++ type
+is soundly relocatable by Rust's move semantics. A C++ type is only okay to hold
+and pass around by value in Rust if its [move constructor is trivial] and it has
+no destructor. In CXX, these are called Trivial extern C++ types, while types
+with nontrivial move behavior or a destructor must be considered Opaque and
+handled by Rust only behind an indirection, such as a reference or UniquePtr.
+
+[`cxx::kind::Opaque`]: https://docs.rs/cxx/*/cxx/kind/enum.Opaque.html
+[`cxx::kind::Trivial`]: https://docs.rs/cxx/*/cxx/kind/enum.Trivial.html
+[move constructor is trivial]: https://en.cppreference.com/w/cpp/types/is_move_constructible
+
+If you believe your C++ type reflected by the ExternType impl is indeed fine to
+hold by value and move in Rust, you can specify:
+
+```rust,noplayground
+# unsafe impl cxx::ExternType for TypeName {
+#     type Id = cxx::type_id!("name::space::of::TypeName");
+    type Kind = cxx::kind::Trivial;
+# }
+```
+
+which will enable you to pass it into C++ functions by value, return it by
+value, and include it in `struct`s that you have declared to `cxx::bridge`. Your
+claim about the triviality of the C++ type will be checked by a `static_assert`
+in the generated C++ side of the binding.
+
+## Explicit shim trait impls
+
+This is a somewhat niche feature, but important when you need it.
+
+CXX's support for C++'s std::unique\_ptr and std::vector is built on a set of
+internal trait impls connecting the Rust API of UniquePtr and CxxVector to
+underlying template instantiations performed by the C++ compiler.
+
+When reusing a binding type across multiple bridge modules as described in the
+previous section, you may find that your code needs some trait impls which CXX
+hasn't decided to generate.
+
+```rust,noplayground
+#[cxx::bridge]
+mod ffi1 {
+    extern "C++" {
+        include!("path/to/header.h");
+
+        type A;
+        type B;
+
+        // Okay: CXX sees UniquePtr<B> using a type B defined within the same
+        // bridge, and automatically emits the right template instantiations
+        // corresponding to std::unique_ptr<B>.
+        fn get_b() -> UniquePtr<B>;
+    }
+}
+
+#[cxx::bridge]
+mod ffi2 {
+    extern "C++" {
+        type A = crate::ffi1::A;
+
+        // Rust trait error: CXX processing this module has no visibility into
+        // whether template instantiations corresponding to std::unique_ptr<A>
+        // have already been emitted by the upstream library, so it does not
+        // emit them here. If the upstream library does not have any signatures
+        // involving UniquePtr<A>, an explicit instantiation of the template
+        // needs to be requested in one module or the other.
+        fn get_a() -> UniquePtr<A>;
+    }
+}
+```
+
+You can request a specific template instantiation at a particular location in
+the Rust crate hierarchy by writing `impl UniquePtr<A> {}` inside of the bridge
+module which defines `A` but does not otherwise contain any use of
+`UniquePtr<A>`.
+
+```rust,noplayground
+#[cxx::bridge]
+mod ffi1 {
+    extern "C++" {
+        include!("path/to/header.h");
+
+        type A;
+        type B;
+
+        fn get_b() -> UniquePtr<B>;
+    }
+
+    impl UniquePtr<A> {}  // explicit instantiation
+}
+```
diff --git a/book/src/extern-rust.md b/book/src/extern-rust.md
new file mode 100644
index 0000000..ab9dcb6
--- /dev/null
+++ b/book/src/extern-rust.md
@@ -0,0 +1,165 @@
+{{#title extern "Rust" — Rust ♡ C++}}
+# extern "Rust"
+
+```rust,noplayground
+#[cxx::bridge]
+mod ffi {
+    extern "Rust" {
+
+    }
+}
+```
+
+The `extern "Rust"` section of a CXX bridge declares Rust types and signatures
+to be made available to C++.
+
+The CXX code generator uses your extern "Rust" section(s) to produce a C++
+header file containing the corresponding C++ declarations. The generated header
+has the same path as the Rust source file containing the bridge, except with a
+`.rs.h` file extension.
+
+A bridge module may contain zero or more extern "Rust" blocks.
+
+## Opaque Rust types
+
+Types defined in Rust that are made available to C++, but only behind an
+indirection.
+
+```rust,noplayground
+# #[cxx::bridge]
+# mod ffi {
+    extern "Rust" {
+        type MyType;
+        type MyOtherType;
+        type OneMoreType<'a>;
+    }
+# }
+```
+
+For example in the ***[Tutorial](tutorial.md)*** we saw `MultiBuf` used in this
+way. Rust code created the `MultiBuf`, passed a `&mut MultiBuf` to C++, and C++
+later passed a `&mut MultiBuf` back across the bridge to Rust.
+
+Another example is the one on the ***[Box\<T\>](binding/box.md)*** page, which
+exposes the Rust standard library's `std::fs::File` to C++ as an opaque type in
+a similar way but with Box as the indirection rather than &mut.
+
+The types named as opaque types (`MyType` etc) refer to types in the `super`
+module, the parent module of the CXX bridge. You can think of an opaque type `T`
+as being like a re-export `use super::T` made available to C++ via the generated
+header.
+
+Opaque types are currently required to be [`Sized`] and [`Unpin`]. In
+particular, a trait object `dyn MyTrait` or slice `[T]` may not be used for an
+opaque Rust type. These restrictions may be lifted in the future.
+
+[`Sized`]: https://doc.rust-lang.org/std/marker/trait.Sized.html
+[`Unpin`]: https://doc.rust-lang.org/std/marker/trait.Unpin.html
+
+For now, types used as extern Rust types are required to be defined by the same
+crate that contains the bridge using them. This restriction may be lifted in the
+future.
+
+The bridge's parent module will contain the appropriate imports or definitions
+for these types.
+
+```rust,noplayground
+use path::to::MyType;
+
+pub struct MyOtherType {
+    ...
+}
+#
+# #[cxx::bridge]
+# mod ffi {
+#     extern "Rust" {
+#         type MyType;
+#         type MyOtherType;
+#     }
+# }
+```
+
+## Functions
+
+Rust functions made callable to C++.
+
+Just like for opaque types, these functions refer implicitly to something in
+scope in the `super` module, whether defined there or imported by some `use`
+statement.
+
+```rust,noplayground
+#[cxx::bridge]
+mod ffi {
+    extern "Rust" {
+        struct MyType;
+        fn f() -> Box<MyType>;
+    }
+}
+
+struct MyType(i32);
+
+fn f() -> Box<MyType> {
+    return Box::new(MyType(1));
+}
+```
+
+Extern Rust function signature may consist of types defined in the bridge,
+primitives, and [any of these additional bindings](bindings.md).
+
+## Methods
+
+Any signature with a `self` parameter is interpreted as a Rust method and
+exposed to C++ as a non-static member function.
+
+```rust,noplayground
+# #[cxx::bridge]
+# mod ffi {
+    extern "Rust" {
+        type MyType;
+        fn f(&self) -> usize;
+    }
+# }
+```
+
+The `self` parameter may be a shared reference `&self`, an exclusive reference
+`&mut self`, or a pinned reference `self: Pin<&mut Self>`. A by-value `self` is
+not currently supported.
+
+If the surrounding `extern "Rust"` block contains exactly one extern type, that
+type is implicitly the receiver for a `&self` or `&mut self` method. If the
+surrounding block contains *more than one* extern type, a receiver type must be
+provided explicitly for the self parameter, or you can consider splitting into
+multiple extern blocks.
+
+```rust,noplayground
+# #[cxx::bridge]
+# mod ffi {
+    extern "Rust" {
+        type First;
+        type Second;
+        fn bar(self: &First);
+        fn foo(self: &mut Second);
+    }
+# }
+```
+
+## Functions with explicit lifetimes
+
+An extern Rust function signature is allowed to contain explicit lifetimes but
+in this case the function must be declared unsafe-to-call. This is pretty
+meaningless given we're talking about calls from C++, but at least it draws some
+extra attention from the caller that they may be responsible for upholding some
+atypical lifetime relationship.
+
+```rust,noplayground
+#[cxx::bridge]
+mod ffi {
+    extern "Rust" {
+        type MyType;
+        unsafe fn f<'a>(&'a self, s: &str) -> &'a str;
+    }
+}
+```
+
+Bounds on a lifetime (like `<'a, 'b: 'a>`) are not currently supported. Nor are
+type parameters or where-clauses.
diff --git a/book/src/index.md b/book/src/index.md
new file mode 100644
index 0000000..7b03e7e
--- /dev/null
+++ b/book/src/index.md
@@ -0,0 +1,83 @@
+<div class="badges">
+<a href="https://github.com/dtolnay/cxx"><img src="https://img.shields.io/badge/github-8da0cb?style=for-the-badge&labelColor=555555&logo=github" alt="github" height="28" class="badge"></a><a href="https://crates.io/crates/cxx"><img src="https://img.shields.io/badge/crates.io-fc8d62?style=for-the-badge&labelColor=555555&logo=rust" alt="crates-io" height="28" class="badge"></a><a href="https://docs.rs/cxx"><img src="https://img.shields.io/badge/docs.rs-66c2a5?style=for-the-badge&labelColor=555555&logoColor=white&logo=data:image/svg+xml;base64,PHN2ZyByb2xlPSJpbWciIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgdmlld0JveD0iMCAwIDUxMiA1MTIiPjxwYXRoIGZpbGw9IiNmNWY1ZjUiIGQ9Ik00ODguNiAyNTAuMkwzOTIgMjE0VjEwNS41YzAtMTUtOS4zLTI4LjQtMjMuNC0zMy43bC0xMDAtMzcuNWMtOC4xLTMuMS0xNy4xLTMuMS0yNS4zIDBsLTEwMCAzNy41Yy0xNC4xIDUuMy0yMy40IDE4LjctMjMuNCAzMy43VjIxNGwtOTYuNiAzNi4yQzkuMyAyNTUuNSAwIDI2OC45IDAgMjgzLjlWMzk0YzAgMTMuNiA3LjcgMjYuMSAxOS45IDMyLjJsMTAwIDUwYzEwLjEgNS4xIDIyLjEgNS4xIDMyLjIgMGwxMDMuOS01MiAxMDMuOSA1MmMxMC4xIDUuMSAyMi4xIDUuMSAzMi4yIDBsMTAwLTUwYzEyLjItNi4xIDE5LjktMTguNiAxOS45LTMyLjJWMjgzLjljMC0xNS05LjMtMjguNC0yMy40LTMzLjd6TTM1OCAyMTQuOGwtODUgMzEuOXYtNjguMmw4NS0zN3Y3My4zek0xNTQgMTA0LjFsMTAyLTM4LjIgMTAyIDM4LjJ2LjZsLTEwMiA0MS40LTEwMi00MS40di0uNnptODQgMjkxLjFsLTg1IDQyLjV2LTc5LjFsODUtMzguOHY3NS40em0wLTExMmwtMTAyIDQxLjQtMTAyLTQxLjR2LS42bDEwMi0zOC4yIDEwMiAzOC4ydi42em0yNDAgMTEybC04NSA0Mi41di03OS4xbDg1LTM4Ljh2NzUuNHptMC0xMTJsLTEwMiA0MS40LTEwMi00MS40di0uNmwxMDItMzguMiAxMDIgMzguMnYuNnoiPjwvcGF0aD48L3N2Zz4K" alt="docs-rs" height="28" class="badge"></a>
+</div>
+
+# CXX — safe interop between Rust and C++
+
+This library provides a safe mechanism for calling C++ code from Rust and Rust
+code from C++. It carves out a regime of commonality where Rust and C++ are
+semantically very similar and guides the programmer to express their language
+boundary effectively within this regime. CXX fills in the low level stuff so
+that you get a safe binding, preventing the pitfalls of doing a foreign function
+interface over unsafe C-style signatures.
+
+<div style="height:226px;padding:34px 0 24px">
+<object type="image/svg+xml" data="overview.svg"></object>
+</div>
+
+From a high level description of the language boundary, CXX uses static analysis
+of the types and function signatures to protect both Rust's and C++'s
+invariants. Then it uses a pair of code generators to implement the boundary
+efficiently on both sides together with any necessary static assertions for
+later in the build process to verify correctness.
+
+The resulting FFI bridge operates at zero or negligible overhead, i.e. no
+copying, no serialization, no memory allocation, no runtime checks needed.
+
+The FFI signatures are able to use native data structures from whichever side
+they please. In addition, CXX provides builtin bindings for key standard library
+types like strings, vectors, Box, unique\_ptr, etc to expose an idiomatic API on
+those types to the other language.
+
+## Example
+
+In this example we are writing a Rust application that calls a C++ client of a
+large-file blobstore service. The blobstore supports a `put` operation for a
+discontiguous buffer upload. For example we might be uploading snapshots of a
+circular buffer which would tend to consist of 2 pieces, or fragments of a file
+spread across memory for some other reason (like a rope data structure).
+
+```rust,noplayground
+#[cxx::bridge]
+mod ffi {
+    extern "Rust" {
+        type MultiBuf;
+
+        fn next_chunk(buf: &mut MultiBuf) -> &[u8];
+    }
+
+    unsafe extern "C++" {
+        include!("example/include/blobstore.h");
+
+        type BlobstoreClient;
+
+        fn new_blobstore_client() -> UniquePtr<BlobstoreClient>;
+        fn put(self: &BlobstoreClient, buf: &mut MultiBuf) -> Result<u64>;
+    }
+}
+```
+
+Now we simply provide Rust definitions of all the things in the `extern "Rust"`
+block and C++ definitions of all the things in the `extern "C++"` block, and get
+to call back and forth safely.
+
+The [***Tutorial***](tutorial.md) chapter walks through a fleshed out version of
+this blobstore example in full detail, including all of the Rust code and all of
+the C++ code. The code is also provided in runnable form in the *demo* directory
+of <https://github.com/dtolnay/cxx>. To try it out, run `cargo run` from that
+directory.
+
+- [demo/src/main.rs](https://github.com/dtolnay/cxx/blob/master/demo/src/main.rs)
+- [demo/include/blobstore.h](https://github.com/dtolnay/cxx/blob/master/demo/include/blobstore.h)
+- [demo/src/blobstore.cc](https://github.com/dtolnay/cxx/blob/master/demo/src/blobstore.cc)
+
+The key takeaway, which is enabled by the CXX library, is that the Rust code in
+main.rs is 100% ordinary safe Rust code working idiomatically with Rust types
+while the C++ code in blobstore.cc is 100% ordinary C++ code working
+idiomatically with C++ types. The Rust code feels like Rust and the C++ code
+feels like C++, not like C-style "FFI glue".
+
+<br>
+
+***Chapter outline:** See the hamburger menu in the top left if you are on a
+small screen and it didn't open with a sidebar by default.*
diff --git a/book/src/overview.svg b/book/src/overview.svg
new file mode 100644
index 0000000..6590547
--- /dev/null
+++ b/book/src/overview.svg
@@ -0,0 +1,99 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="696" height="224">
+    <style>line, path, circle,rect,polygon {
+                          stroke: black;
+                          stroke-width: 2;
+                          stroke-opacity: 1;
+                          fill-opacity: 1;
+                          stroke-linecap: round;
+                          stroke-linejoin: miter;
+                        }
+
+                    text {
+                        fill: black;
+                        }
+                        rect.backdrop{
+                            stroke: none;
+                            fill: white;
+                            fill-opacity: 0;
+                        }
+                        .broken{
+                            stroke-dasharray: 8;
+                        }
+                        .filled{
+                            fill: black;
+                        }
+                        .bg_filled{
+                            fill: white;
+                        }
+                        .nofill{
+                            fill: white;
+                            fill-opacity: 0;
+                        }
+
+                        text {
+                         font-family: monospace;
+                         font-size: 14px;
+                        }
+                        </style>
+    <defs>
+        <marker id="arrow" viewBox="-2 -2 8 8" refX="4" refY="2" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
+            <polygon points="0,0 0,4 4,2 0,0"></polygon>
+        </marker>
+        <marker id="diamond" viewBox="-2 -2 8 8" refX="4" refY="2" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
+            <polygon points="0,2 2,0 4,2 2,4 0,2"></polygon>
+        </marker>
+        <marker id="circle" viewBox="0 0 8 8" refX="4" refY="4" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
+            <circle cx="4" cy="4" r="2" class="filled"></circle>
+        </marker>
+        <marker id="open_circle" viewBox="0 0 8 8" refX="4" refY="4" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
+            <circle cx="4" cy="4" r="2" class="bg_filled"></circle>
+        </marker>
+        <marker id="big_open_circle" viewBox="0 0 8 8" refX="4" refY="4" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
+            <circle cx="4" cy="4" r="3" class="bg_filled"></circle>
+        </marker>
+    </defs>
+    <rect class="backdrop" x="0" y="0" width="696" height="224"></rect>
+    <rect x="148" y="168" width="136" height="32" class="solid nofill" rx="4"></rect>
+    <text x="162" y="188" >Rust bindings</text>
+    <rect x="412" y="168" width="128" height="32" class="solid nofill" rx="4"></rect>
+    <text x="426" y="188" >C++ bindings</text>
+    <text x="272" y="28" >#[cxx::bridge] mod</text>
+    <text x="250" y="44" >description of boundary</text>
+    <line x1="220" y1="120" x2="220" y2="160" class="solid"></line>
+    <polygon points="216,156 224,156 220,164" class="filled"></polygon>
+    <line x1="476" y1="120" x2="476" y2="160" class="solid"></line>
+    <polygon points="472,156 480,156 476,164" class="filled"></polygon>
+    <text x="74" y="140" >Safe</text>
+    <text x="34" y="156" >straightforward</text>
+    <text x="530" y="156" >Straightforward</text>
+    <polygon points="48,180 40,184 48,188" class="filled"></polygon>
+    <line x1="48" y1="184" x2="144" y2="184" class="solid"></line>
+    <polygon points="136,180 144,184 136,188" class="filled"></polygon>
+    <polygon points="296,180 288,184 296,188" class="filled"></polygon>
+    <line x1="296" y1="184" x2="408" y2="184" class="broken"></line>
+    <polygon points="400,180 408,184 400,188" class="filled"></polygon>
+    <polygon points="552,180 544,184 552,188" class="filled"></polygon>
+    <line x1="552" y1="184" x2="648" y2="184" class="solid"></line>
+    <polygon points="644,180 652,184 644,188" class="filled"></polygon>
+    <text x="2" y="188" >Rust</text>
+    <text x="2" y="204" >code</text>
+    <text x="658" y="204" >code</text>
+    <text x="202" y="108" >Macro expansion</text>
+    <text x="370" y="108" >Code generation</text>
+    <text x="58" y="172" >Rust APIs</text>
+    <text x="298" y="172" >Hidden C ABI</text>
+    <text x="562" y="172" >C++ APIs</text>
+    <text x="658" y="188" >C++</text>
+    <g>
+        <path d="M 228,8 A 8,8 0,0,0 220,16" class="nofill"></path>
+        <line x1="228" y1="8" x2="468" y2="8" class="solid"></line>
+        <path d="M 468,8 A 8,8 0,0,1 476,16" class="nofill"></path>
+        <line x1="220" y1="16" x2="220" y2="48" class="solid"></line>
+        <line x1="476" y1="16" x2="476" y2="48" class="solid"></line>
+        <path d="M 220,48 A 8,8 0,0,0 228,56" class="nofill"></path>
+        <line x1="228" y1="56" x2="468" y2="56" class="solid"></line>
+        <line x1="348" y1="56" x2="348" y2="120" class="solid"></line>
+        <path d="M 476,48 A 8,8 0,0,1 468,56" class="nofill"></path>
+        <line x1="220" y1="120" x2="476" y2="120" class="solid"></line>
+    </g>
+</svg>
diff --git a/book/src/reference.md b/book/src/reference.md
new file mode 100644
index 0000000..62ca35c
--- /dev/null
+++ b/book/src/reference.md
@@ -0,0 +1,29 @@
+{{#title The bridge module — Rust ♡ C++}}
+# The bridge module reference
+
+The ***[Core concepts](concepts.md)*** in chapter 2 covered the high level model
+that CXX uses to represent a language boundary. This chapter builds on that one
+to document an exhaustive reference on the syntax and functionality of
+\#\[cxx::bridge\].
+
+- ***[extern "Rust"](extern-rust.md)*** &mdash; exposing opaque Rust types, Rust
+  functions, Rust methods to C++; functions with lifetimes.
+
+- ***[extern "C++"](extern-c++.md)*** &mdash; binding opaque C++ types, C++
+  functions, C++ member functions; sharing an opaque type definition across
+  multiple bridge modules or different crates; using bindgen-generated data
+  structures across a CXX bridge; Rust orphan-rule-compatible way to request
+  that particular glue code be emitted in a specific bridge module.
+
+- ***[Shared types](shared.md)*** &mdash; shared structs; shared enums; using
+  Rust as source of truth vs C++ as source of truth.
+
+- ***[Attributes](attributes.md)*** &mdash; working with namespaces; giving
+  functions a different name in their non-native language.
+
+- ***[Async functions](async.md)*** &mdash; integrating async C++ with async
+  Rust.
+
+- ***[Error handling](binding/result.md)*** &mdash; representing fallibility on
+  the language boundary; accessing a Rust error message from C++; customizing
+  the set of caught exceptions and their conversion to a Rust error message.
diff --git a/book/src/shared.md b/book/src/shared.md
new file mode 100644
index 0000000..4043db1
--- /dev/null
+++ b/book/src/shared.md
@@ -0,0 +1,246 @@
+{{#title Shared types — Rust ♡ C++}}
+# Shared types
+
+Shared types enable *both* languages to have visibility into the internals of a
+type. This is in contrast to opaque Rust types and opaque C++ types, for which
+only one side gets to manipulate the internals.
+
+Unlike opaque types, the FFI bridge is allowed to pass and return shared types
+by value.
+
+The order in which shared types are written is not important. C++ is order
+sensitive but CXX will topologically sort and forward-declare your types as
+necessary.
+
+## Shared structs and enums
+
+For enums, only C-like a.k.a. unit variants are currently supported.
+
+```rust,noplayground
+#[cxx::bridge]
+mod ffi {
+    struct PlayingCard {
+        suit: Suit,
+        value: u8,  // A=1, J=11, Q=12, K=13
+    }
+
+    enum Suit {
+        Clubs,
+        Diamonds,
+        Hearts,
+        Spades,
+    }
+
+    unsafe extern "C++" {
+        fn deck() -> Vec<PlayingCard>;
+        fn sort(cards: &mut Vec<PlayingCard>);
+    }
+}
+```
+
+## The generated data structures
+
+Shared structs compile to an aggregate-initialization compatible C++ struct.
+
+Shared enums compile to a C++ `enum class` with a sufficiently sized integral
+base type decided by CXX.
+
+```cpp
+// generated header
+
+struct PlayingCard final {
+  Suit suit;
+  uint8_t value;
+};
+
+enum class Suit : uint8_t {
+  Clubs = 0,
+  Diamonds = 1,
+  Hearts = 2,
+  Spades = 3,
+};
+```
+
+Because it is not UB in C++ for an `enum class` to hold a value different from
+all of the listed variants, we use a Rust representation for shared enums that
+is compatible with this. The API you'll get is something like:
+
+```rust,noplayground
+#[derive(Copy, Clone, PartialEq, Eq)]
+#[repr(transparent)]
+pub struct Suit {
+    pub repr: u8,
+}
+#[allow(non_upper_case_globals)]
+impl Suit {
+    pub const Clubs: Self = Suit { repr: 0 };
+    pub const Diamonds: Self = Suit { repr: 1 };
+    pub const Hearts: Self = Suit { repr: 2 };
+    pub const Spades: Self = Suit { repr: 3 };
+}
+```
+
+Notice you're free to treat the enum as an integer in Rust code via the public
+`repr` field.
+
+Pattern matching with `match` still works but will require you to write wildcard
+arms to handle the situation of an enum value that is not one of the listed
+variants.
+
+```rust,noplayground
+fn main() {
+    let suit: Suit = /*...*/;
+    match suit {
+        Suit::Clubs => ...,
+        Suit::Diamonds => ...,
+        Suit::Hearts => ...,
+        Suit::Spades => ...,
+        _ => ...,  // fallback arm
+    }
+}
+```
+
+If a shared struct has generic lifetime parameters, the lifetimes are simply not
+represented on the C++ side. C++ code will need care when working with borrowed
+data (as usual in C++).
+
+```rust,noplayground
+#[cxx::bridge]
+mod ffi {
+    struct Borrowed<'a> {
+        flags: &'a [&'a str],
+    }
+}
+```
+
+```cpp
+// generated header
+
+struct Borrowed final {
+  rust::Slice<const rust::Str> flags;
+};
+```
+
+## Enum discriminants
+
+You may provide explicit discriminants for some or all of the enum variants, in
+which case those numbers will be propagated into the generated C++ `enum class`.
+
+```rust,noplayground
+#[cxx::bridge]
+mod ffi {
+    enum SmallPrime {
+        Two = 2,
+        Three = 3,
+        Five = 5,
+        Seven = 7,
+    }
+}
+```
+
+Variants without an explicit discriminant are assigned the previous discriminant
+plus 1. If the first variant has not been given an explicit discriminant, it is
+assigned discriminant 0.
+
+By default CXX represents your enum using the smallest integer type capable of
+fitting all the discriminants (whether explicit or implicit). If you need a
+different representation for reasons, provide a `repr` attribute.
+
+```rust,noplayground
+#[cxx::bridge]
+mod ffi {
+    #[repr(i32)]
+    enum Enum {
+        Zero,
+        One,
+        Five = 5,
+        Six,
+    }
+}
+```
+
+```cpp
+// generated header
+
+enum class Enum : int32_t {
+  Zero = 0,
+  One = 1,
+  Five = 5,
+  Six = 6,
+};
+```
+
+## Extern enums
+
+If you need to interoperate with an already existing enum for which an existing
+C++ definition is the source of truth, make sure that definition is provided by
+some header in the bridge and then declare your enum *additionally* as an extern
+C++ type.
+
+```rust,noplayground
+#[cxx::bridge]
+mod ffi {
+    enum Enum {
+        Yes,
+        No,
+    }
+
+    extern "C++" {
+        include!("path/to/the/header.h");
+        type Enum;
+    }
+}
+```
+
+CXX will recognize this pattern and, instead of generating a C++ definition of
+the enum, will instead generate C++ static assertions asserting that the
+variants and discriminant values and integer representation written in Rust all
+correctly match the existing C++ enum definition.
+
+Extern enums support all the same features as ordinary shared enums (explicit
+discriminants, repr). Again, CXX will static assert that all of those things you
+wrote are correct.
+
+## Derives
+
+The following standard traits are supported in `derive(...)` within the CXX
+bridge module.
+
+- `Clone`
+- `Copy`
+- `Debug`
+- `Default`
+- `Eq`
+- `Hash`
+- `Ord`
+- `PartialEq`
+- `PartialOrd`
+
+Note that shared enums automatically always come with impls of `Copy`, `Clone`,
+`Eq`, and `PartialEq`, so you're free to omit those derives on an enum.
+
+```rust,noplayground
+#[cxx::bridge]
+mod ffi {
+    #[derive(Clone, Debug, Hash)]
+    struct ExampleStruct {
+        x: u32,
+        s: String,
+    }
+
+    #[derive(Hash, Ord, PartialOrd)]
+    enum ExampleEnum {
+        Yes,
+        No,
+    }
+}
+```
+
+The derives naturally apply to *both* the Rust data type *and* the corresponding
+C++ data type:
+
+- `Hash` gives you a specialization of [`template <> struct std::hash<T>`][hash] in C++
+- `PartialEq` produces `operator==` and `operator!=`
+- `PartialOrd` produces `operator<`, `operator<=`, `operator>`, `operator>=`
+
+[hash]: https://en.cppreference.com/w/cpp/utility/hash
diff --git a/book/src/tutorial.md b/book/src/tutorial.md
new file mode 100644
index 0000000..c9fa00c
--- /dev/null
+++ b/book/src/tutorial.md
@@ -0,0 +1,687 @@
+{{#title Tutorial — Rust ♡ C++}}
+# Tutorial: CXX blobstore client
+
+This example walks through a Rust application that calls into a C++ client of a
+blobstore service. In fact we'll see calls going in both directions: Rust to C++
+as well as C++ to Rust. For your own use case it may be that you need just one
+of these directions.
+
+All of the code involved in the example is shown on this page, but it's also
+provided in runnable form in the *demo* directory of
+<https://github.com/dtolnay/cxx>. To try it out directly, run `cargo run` from
+that directory.
+
+This tutorial assumes you've read briefly about **shared structs**, **opaque
+types**, and **functions** in the [*Core concepts*](concepts.md) page.
+
+## Creating the project
+
+We'll use Cargo, which is the build system commonly used by open source Rust
+projects. (CXX works with other build systems too; refer to chapter 5.)
+
+Create a blank Cargo project: `mkdir cxx-demo`; `cd cxx-demo`; `cargo init`.
+
+Edit the Cargo.toml to add a dependency on the `cxx` crate:
+
+```toml,hidelines
+## Cargo.toml
+# [package]
+# name = "cxx-demo"
+# version = "0.1.0"
+# edition = "2018"
+
+[dependencies]
+cxx = "1.0"
+```
+
+We'll revisit this Cargo.toml later when we get to compiling some C++ code.
+
+## Defining the language boundary
+
+CXX relies on a description of the function signatures that will be exposed from
+each language to the other. You provide this description using `extern` blocks
+in a Rust module annotated with the `#[cxx::bridge]` attribute macro.
+
+We'll open with just the following at the top of src/main.rs and walk through
+each item in detail.
+
+```rust,noplayground
+// src/main.rs
+
+#[cxx::bridge]
+mod ffi {
+
+}
+#
+# fn main() {}
+```
+
+The contents of this module will be everything that needs to be agreed upon by
+both sides of the FFI boundary.
+
+## Calling a C++ function from Rust
+
+Let's obtain an instance of the C++ blobstore client, a class `BlobstoreClient`
+defined in C++.
+
+We'll treat `BlobstoreClient` as an *opaque type* in CXX's classification so
+that Rust does not need to assume anything about its implementation, not even
+its size or alignment. In general, a C++ type might have a move-constructor
+which is incompatible with Rust's move semantics, or may hold internal
+references which cannot be modeled by Rust's borrowing system. Though there are
+alternatives, the easiest way to not care about any such thing on an FFI
+boundary is to require no knowledge about a type by treating it as opaque.
+
+Opaque types may only be manipulated behind an indirection such as a reference
+`&`, a Rust `Box`, or a `UniquePtr` (Rust binding of `std::unique_ptr`). We'll
+add a function through which C++ can return a `std::unique_ptr<BlobstoreClient>`
+to Rust.
+
+```rust,noplayground
+// src/main.rs
+
+#[cxx::bridge]
+mod ffi {
+    unsafe extern "C++" {
+        include!("cxx-demo/include/blobstore.h");
+
+        type BlobstoreClient;
+
+        fn new_blobstore_client() -> UniquePtr<BlobstoreClient>;
+    }
+}
+
+fn main() {
+    let client = ffi::new_blobstore_client();
+}
+```
+
+The nature of `unsafe` extern blocks is clarified in more detail in the
+[*extern "C++"*](extern-c++.md) chapter. In brief: the programmer is **not**
+promising that the signatures they have typed in are accurate; that would be
+unreasonable. CXX performs static assertions that the signatures exactly match
+what is declared in C++. Rather, the programmer is only on the hook for things
+that C++'s semantics are not precise enough to capture, i.e. things that would
+only be represented at most by comments in the C++ code. In this case, it's
+whether `new_blobstore_client` is safe or unsafe to call. If that function said
+something like "must be called at most once or we'll stomp yer memery", Rust
+would instead want to expose it as `unsafe fn new_blobstore_client`, this time
+inside a safe `extern "C++"` block because the programmer is no longer on the
+hook for any safety claim about the signature.
+
+If you build this file right now with `cargo build`, it won't build because we
+haven't written a C++ implementation of `new_blobstore_client` nor instructed
+Cargo about how to link it into the resulting binary. You'll see an error from
+the linker like this:
+
+```console
+error: linking with `cc` failed: exit code: 1
+ |
+ = /bin/ld: target/debug/deps/cxx-demo-7cb7fddf3d67d880.rcgu.o: in function `cxx_demo::ffi::new_blobstore_client':
+   src/main.rs:1: undefined reference to `cxxbridge1$new_blobstore_client'
+   collect2: error: ld returned 1 exit status
+```
+
+## Adding in the C++ code
+
+In CXX's integration with Cargo, all #include paths begin with a crate name by
+default (when not explicitly selected otherwise by a crate; see
+`CFG.include_prefix` in chapter 5). That's why we see
+`include!("cxx-demo/include/blobstore.h")` above &mdash; we'll be putting the
+C++ header at relative path `include/blobstore.h` within the Rust crate. If your
+crate is named something other than `cxx-demo` according to the `name` field in
+Cargo.toml, you will need to use that name everywhere in place of `cxx-demo`
+throughout this tutorial.
+
+```cpp
+// include/blobstore.h
+
+#pragma once
+#include <memory>
+
+class BlobstoreClient {
+public:
+  BlobstoreClient();
+};
+
+std::unique_ptr<BlobstoreClient> new_blobstore_client();
+```
+
+```cpp
+// src/blobstore.cc
+
+#include "cxx-demo/include/blobstore.h"
+
+BlobstoreClient::BlobstoreClient() {}
+
+std::unique_ptr<BlobstoreClient> new_blobstore_client() {
+  return std::unique_ptr<BlobstoreClient>(new BlobstoreClient());
+}
+```
+
+Using `std::make_unique` would work too, as long as you pass `-std=c++14` to the
+C++ compiler as described later on.
+
+The placement in *include/* and *src/* is not significant; you can place C++
+code anywhere else in the crate as long as you use the right paths throughout
+the tutorial.
+
+Be aware that *CXX does not look at any of these files.* You're free to put
+arbitrary C++ code in here, #include your own libraries, etc. All we do is emit
+static assertions against what you provide in the headers.
+
+## Compiling the C++ code with Cargo
+
+Cargo has a [build scripts] feature suitable for compiling non-Rust code.
+
+We need to introduce a new build-time dependency on CXX's C++ code generator in
+Cargo.toml:
+
+```toml,hidelines
+## Cargo.toml
+# [package]
+# name = "cxx-demo"
+# version = "0.1.0"
+# edition = "2018"
+
+[dependencies]
+cxx = "1.0"
+
+[build-dependencies]
+cxx-build = "1.0"
+```
+
+Then add a build.rs build script adjacent to Cargo.toml to run the cxx-build
+code generator and C++ compiler. The relevant arguments are the path to the Rust
+source file containing the cxx::bridge language boundary definition, and the
+paths to any additional C++ source files to be compiled during the Rust crate's
+build.
+
+```rust,noplayground
+// build.rs
+
+fn main() {
+    cxx_build::bridge("src/main.rs")
+        .file("src/blobstore.cc")
+        .compile("cxx-demo");
+}
+```
+
+This build.rs would also be where you set up C++ compiler flags, for example if
+you'd like to have access to `std::make_unique` from C++14. See the page on
+***[Cargo-based builds](build/cargo.md)*** for more details about CXX's Cargo
+integration.
+
+```rust,noplayground
+# // build.rs
+#
+# fn main() {
+    cxx_build::bridge("src/main.rs")
+        .file("src/blobstore.cc")
+        .flag_if_supported("-std=c++14")
+        .compile("cxx-demo");
+# }
+```
+
+[build scripts]: https://doc.rust-lang.org/cargo/reference/build-scripts.html
+
+The project should now build and run successfully, though not do anything useful
+yet.
+
+```console
+cxx-demo$  cargo run
+  Compiling cxx-demo v0.1.0
+  Finished dev [unoptimized + debuginfo] target(s) in 0.34s
+  Running `target/debug/cxx-demo`
+
+cxx-demo$
+```
+
+## Calling a Rust function from C++
+
+Our C++ blobstore supports a `put` operation for a discontiguous buffer upload.
+For example we might be uploading snapshots of a circular buffer which would
+tend to consist of 2 pieces, or fragments of a file spread across memory for
+some other reason (like a rope data structure).
+
+We'll express this by handing off an iterator over contiguous borrowed chunks.
+This loosely resembles the API of the widely used `bytes` crate's `Buf` trait.
+During a `put`, we'll make C++ call back into Rust to obtain contiguous chunks
+of the upload (all with no copying or allocation on the language boundary). In
+reality the C++ client might contain some sophisticated batching of chunks
+and/or parallel uploading that all of this ties into.
+
+```rust,noplayground
+// src/main.rs
+
+#[cxx::bridge]
+mod ffi {
+    extern "Rust" {
+        type MultiBuf;
+
+        fn next_chunk(buf: &mut MultiBuf) -> &[u8];
+    }
+
+    unsafe extern "C++" {
+        include!("cxx-demo/include/blobstore.h");
+
+        type BlobstoreClient;
+
+        fn new_blobstore_client() -> UniquePtr<BlobstoreClient>;
+        fn put(&self, parts: &mut MultiBuf) -> u64;
+    }
+}
+#
+# fn main() {
+#     let client = ffi::new_blobstore_client();
+# }
+```
+
+Any signature having a `self` parameter (the Rust name for C++'s `this`) is
+considered a method / non-static member function. If there is only one `type` in
+the surrounding extern block, it'll be a method of that type. If there is more
+than one `type`, you can disambiguate which one a method belongs to by writing
+`self: &BlobstoreClient` in the argument list.
+
+As usual, now we need to provide Rust definitions of everything declared by the
+`extern "Rust"` block and a C++ definition of the new signature declared by the
+`extern "C++"` block.
+
+```rust,noplayground
+// src/main.rs
+#
+# #[cxx::bridge]
+# mod ffi {
+#     extern "Rust" {
+#         type MultiBuf;
+#
+#         fn next_chunk(buf: &mut MultiBuf) -> &[u8];
+#     }
+#
+#     unsafe extern "C++" {
+#         include!("cxx-demo/include/blobstore.h");
+#
+#         type BlobstoreClient;
+#
+#         fn new_blobstore_client() -> UniquePtr<BlobstoreClient>;
+#         fn put(&self, parts: &mut MultiBuf) -> u64;
+#     }
+# }
+
+// An iterator over contiguous chunks of a discontiguous file object. Toy
+// implementation uses a Vec<Vec<u8>> but in reality this might be iterating
+// over some more complex Rust data structure like a rope, or maybe loading
+// chunks lazily from somewhere.
+pub struct MultiBuf {
+    chunks: Vec<Vec<u8>>,
+    pos: usize,
+}
+
+pub fn next_chunk(buf: &mut MultiBuf) -> &[u8] {
+    let next = buf.chunks.get(buf.pos);
+    buf.pos += 1;
+    next.map_or(&[], Vec::as_slice)
+}
+#
+# fn main() {
+#     let client = ffi::new_blobstore_client();
+# }
+```
+
+```cpp,hidelines
+// include/blobstore.h
+
+# #pragma once
+# #include <memory>
+#
+struct MultiBuf;
+
+class BlobstoreClient {
+public:
+  BlobstoreClient();
+  uint64_t put(MultiBuf &buf) const;
+};
+#
+#std::unique_ptr<BlobstoreClient> new_blobstore_client();
+```
+
+In blobstore.cc we're able to call the Rust `next_chunk` function, exposed to
+C++ by a header `main.rs.h` generated by the CXX code generator. In CXX's Cargo
+integration this generated header has a path containing the crate name, the
+relative path of the Rust source file within the crate, and a `.rs.h` extension.
+
+```cpp,hidelines
+// src/blobstore.cc
+
+##include "cxx-demo/include/blobstore.h"
+##include "cxx-demo/src/main.rs.h"
+##include <functional>
+#
+# BlobstoreClient::BlobstoreClient() {}
+#
+# std::unique_ptr<BlobstoreClient> new_blobstore_client() {
+#   return std::make_unique<BlobstoreClient>();
+# }
+
+// Upload a new blob and return a blobid that serves as a handle to the blob.
+uint64_t BlobstoreClient::put(MultiBuf &buf) const {
+  // Traverse the caller's chunk iterator.
+  std::string contents;
+  while (true) {
+    auto chunk = next_chunk(buf);
+    if (chunk.size() == 0) {
+      break;
+    }
+    contents.append(reinterpret_cast<const char *>(chunk.data()), chunk.size());
+  }
+
+  // Pretend we did something useful to persist the data.
+  auto blobid = std::hash<std::string>{}(contents);
+  return blobid;
+}
+```
+
+This is now ready to use. :)
+
+```rust,noplayground
+// src/main.rs
+#
+# #[cxx::bridge]
+# mod ffi {
+#     extern "Rust" {
+#         type MultiBuf;
+#
+#         fn next_chunk(buf: &mut MultiBuf) -> &[u8];
+#     }
+#
+#     unsafe extern "C++" {
+#         include!("cxx-demo/include/blobstore.h");
+#
+#         type BlobstoreClient;
+#
+#         fn new_blobstore_client() -> UniquePtr<BlobstoreClient>;
+#         fn put(&self, parts: &mut MultiBuf) -> u64;
+#     }
+# }
+#
+# pub struct MultiBuf {
+#     chunks: Vec<Vec<u8>>,
+#     pos: usize,
+# }
+# pub fn next_chunk(buf: &mut MultiBuf) -> &[u8] {
+#     let next = buf.chunks.get(buf.pos);
+#     buf.pos += 1;
+#     next.map_or(&[], Vec::as_slice)
+# }
+
+fn main() {
+    let client = ffi::new_blobstore_client();
+
+    // Upload a blob.
+    let chunks = vec![b"fearless".to_vec(), b"concurrency".to_vec()];
+    let mut buf = MultiBuf { chunks, pos: 0 };
+    let blobid = client.put(&mut buf);
+    println!("blobid = {}", blobid);
+}
+```
+
+```console
+cxx-demo$  cargo run
+  Compiling cxx-demo v0.1.0
+  Finished dev [unoptimized + debuginfo] target(s) in 0.41s
+  Running `target/debug/cxx-demo`
+
+blobid = 9851996977040795552
+```
+
+## Interlude: What gets generated?
+
+For the curious, it's easy to look behind the scenes at what CXX has done to
+make these function calls work. You shouldn't need to do this during normal
+usage of CXX, but for the purpose of this tutorial it can be educative.
+
+CXX comprises *two* code generators: a Rust one (which is the cxx::bridge
+attribute procedural macro) and a C++ one.
+
+### Rust generated code
+
+It's easiest to view the output of the procedural macro by installing
+[cargo-expand]. Then run `cargo expand ::ffi` to macro-expand the `mod ffi`
+module.
+
+[cargo-expand]: https://github.com/dtolnay/cargo-expand
+
+```console
+cxx-demo$  cargo install cargo-expand
+cxx-demo$  cargo expand ::ffi
+```
+
+You'll see some deeply unpleasant code involving `#[repr(C)]`, `#[link_name]`,
+and `#[export_name]`.
+
+### C++ generated code
+
+For debugging convenience, `cxx_build` links all generated C++ code into Cargo's
+target directory under *target/cxxbridge/*.
+
+```console
+cxx-demo$  exa -T target/cxxbridge/
+target/cxxbridge
+├── cxx-demo
+│  └── src
+│     ├── main.rs.cc -> ../../../debug/build/cxx-demo-11c6f678ce5c3437/out/cxxbridge/sources/cxx-demo/src/main.rs.cc
+│     └── main.rs.h -> ../../../debug/build/cxx-demo-11c6f678ce5c3437/out/cxxbridge/include/cxx-demo/src/main.rs.h
+└── rust
+   └── cxx.h -> ~/.cargo/registry/src/github.com-1ecc6299db9ec823/cxx-1.0.0/include/cxx.h
+```
+
+In those files you'll see declarations or templates of any CXX Rust types
+present in your language boundary (like `rust::Slice<T>` for `&[T]`) and `extern
+"C"` signatures corresponding to your extern functions.
+
+If it fits your workflow better, the CXX C++ code generator is also available as
+a standalone executable which outputs generated code to stdout.
+
+```console
+cxx-demo$  cargo install cxxbridge-cmd
+cxx-demo$  cxxbridge src/main.rs
+```
+
+## Shared data structures
+
+So far the calls in both directions above only used **opaque types**, not
+**shared structs**.
+
+Shared structs are data structures whose complete definition is visible to both
+languages, making it possible to pass them by value across the language
+boundary. Shared structs translate to a C++ aggregate-initialization compatible
+struct exactly matching the layout of the Rust one.
+
+As the last step of this demo, we'll use a shared struct `BlobMetadata` to pass
+metadata about blobs between our Rust application and C++ blobstore client.
+
+```rust,noplayground
+// src/main.rs
+
+#[cxx::bridge]
+mod ffi {
+    struct BlobMetadata {
+        size: usize,
+        tags: Vec<String>,
+    }
+
+    extern "Rust" {
+        // ...
+#         type MultiBuf;
+#
+#         fn next_chunk(buf: &mut MultiBuf) -> &[u8];
+    }
+
+    unsafe extern "C++" {
+        // ...
+#         include!("cxx-demo/include/blobstore.h");
+#
+#         type BlobstoreClient;
+#
+#         fn new_blobstore_client() -> UniquePtr<BlobstoreClient>;
+#         fn put(&self, parts: &mut MultiBuf) -> u64;
+        fn tag(&self, blobid: u64, tag: &str);
+        fn metadata(&self, blobid: u64) -> BlobMetadata;
+    }
+}
+#
+# pub struct MultiBuf {
+#     chunks: Vec<Vec<u8>>,
+#     pos: usize,
+# }
+# pub fn next_chunk(buf: &mut MultiBuf) -> &[u8] {
+#     let next = buf.chunks.get(buf.pos);
+#     buf.pos += 1;
+#     next.map_or(&[], Vec::as_slice)
+# }
+
+fn main() {
+    let client = ffi::new_blobstore_client();
+
+    // Upload a blob.
+    let chunks = vec![b"fearless".to_vec(), b"concurrency".to_vec()];
+    let mut buf = MultiBuf { chunks, pos: 0 };
+    let blobid = client.put(&mut buf);
+    println!("blobid = {}", blobid);
+
+    // Add a tag.
+    client.tag(blobid, "rust");
+
+    // Read back the tags.
+    let metadata = client.metadata(blobid);
+    println!("tags = {:?}", metadata.tags);
+}
+```
+
+```cpp,hidelines
+// include/blobstore.h
+#
+# #pragma once
+# #include "rust/cxx.h"
+# #include <memory>
+
+struct MultiBuf;
+struct BlobMetadata;
+
+class BlobstoreClient {
+public:
+  BlobstoreClient();
+  uint64_t put(MultiBuf &buf) const;
+  void tag(uint64_t blobid, rust::Str tag) const;
+  BlobMetadata metadata(uint64_t blobid) const;
+
+private:
+  class impl;
+  std::shared_ptr<impl> impl;
+};
+#
+# std::unique_ptr<BlobstoreClient> new_blobstore_client();
+```
+
+```cpp,hidelines
+// src/blobstore.cc
+
+##include "cxx-demo/include/blobstore.h"
+##include "cxx-demo/src/main.rs.h"
+##include <algorithm>
+##include <functional>
+##include <set>
+##include <string>
+##include <unordered_map>
+
+// Toy implementation of an in-memory blobstore.
+//
+// In reality the implementation of BlobstoreClient could be a large
+// complex C++ library.
+class BlobstoreClient::impl {
+  friend BlobstoreClient;
+  using Blob = struct {
+    std::string data;
+    std::set<std::string> tags;
+  };
+  std::unordered_map<uint64_t, Blob> blobs;
+};
+
+BlobstoreClient::BlobstoreClient() : impl(new class BlobstoreClient::impl) {}
+#
+# // Upload a new blob and return a blobid that serves as a handle to the blob.
+# uint64_t BlobstoreClient::put(MultiBuf &buf) const {
+#   // Traverse the caller's chunk iterator.
+#   std::string contents;
+#   while (true) {
+#     auto chunk = next_chunk(buf);
+#     if (chunk.size() == 0) {
+#       break;
+#     }
+#     contents.append(reinterpret_cast<const char *>(chunk.data()), chunk.size());
+#   }
+#
+#   // Insert into map and provide caller the handle.
+#   auto blobid = std::hash<std::string>{}(contents);
+#   impl->blobs[blobid] = {std::move(contents), {}};
+#   return blobid;
+# }
+
+// Add tag to an existing blob.
+void BlobstoreClient::tag(uint64_t blobid, rust::Str tag) const {
+  impl->blobs[blobid].tags.emplace(tag);
+}
+
+// Retrieve metadata about a blob.
+BlobMetadata BlobstoreClient::metadata(uint64_t blobid) const {
+  BlobMetadata metadata{};
+  auto blob = impl->blobs.find(blobid);
+  if (blob != impl->blobs.end()) {
+    metadata.size = blob->second.data.size();
+    std::for_each(blob->second.tags.cbegin(), blob->second.tags.cend(),
+                  [&](auto &t) { metadata.tags.emplace_back(t); });
+  }
+  return metadata;
+}
+#
+# std::unique_ptr<BlobstoreClient> new_blobstore_client() {
+#   return std::make_unique<BlobstoreClient>();
+# }
+```
+
+```console
+cxx-demo$  cargo run
+  Running `target/debug/cxx-demo`
+
+blobid = 9851996977040795552
+tags = ["rust"]
+```
+
+*You've now seen all the code involved in the tutorial. It's available all
+together in runnable form in the* demo *directory of
+<https://github.com/dtolnay/cxx>. You can run it directly without stepping
+through the steps above by running `cargo run` from that directory.*
+
+<br>
+
+# Takeaways
+
+The key contribution of CXX is it gives you Rust&ndash;C++ interop in which
+*all* of the Rust side of the code you write *really* looks like you are just
+writing normal Rust, and the C++ side *really* looks like you are just writing
+normal C++.
+
+You've seen in this tutorial that none of the code involved feels like C or like
+the usual perilous "FFI glue" prone to leaks or memory safety flaws.
+
+An expressive system of opaque types, shared types, and key standard library
+type bindings enables API design on the language boundary that captures the
+proper ownership and borrowing contracts of the interface.
+
+CXX plays to the strengths of the Rust type system *and* C++ type system *and*
+the programmer's intuitions. An individual working on the C++ side without a
+Rust background, or the Rust side without a C++ background, will be able to
+apply all their usual intuitions and best practices about development in their
+language to maintain a correct FFI.
+
+<br><br>
diff --git a/build.rs b/build.rs
index 57b3e52..f799441 100644
--- a/build.rs
+++ b/build.rs
@@ -1,5 +1,6 @@
 use std::env;
 use std::path::Path;
+use std::process::Command;
 
 fn main() {
     cc::Build::new()
@@ -7,12 +8,42 @@
         .cpp(true)
         .cpp_link_stdlib(None) // linked via link-cplusplus crate
         .flag_if_supported(cxxbridge_flags::STD)
-        .compile("cxxbridge05");
+        .warnings_into_errors(cfg!(deny_warnings))
+        .compile("cxxbridge1");
+
     println!("cargo:rerun-if-changed=src/cxx.cc");
     println!("cargo:rerun-if-changed=include/cxx.h");
     println!("cargo:rustc-cfg=built_with_cargo");
+
     if let Some(manifest_dir) = env::var_os("CARGO_MANIFEST_DIR") {
         let cxx_h = Path::new(&manifest_dir).join("include").join("cxx.h");
         println!("cargo:HEADER={}", cxx_h.to_string_lossy());
     }
+
+    if let Some(rustc) = rustc_version() {
+        if rustc.minor < 48 {
+            println!("cargo:warning=The cxx crate requires a rustc version 1.48.0 or newer.");
+            println!(
+                "cargo:warning=You appear to be building with: {}",
+                rustc.version,
+            );
+        }
+    }
+}
+
+struct RustVersion {
+    version: String,
+    minor: u32,
+}
+
+fn rustc_version() -> Option<RustVersion> {
+    let rustc = env::var_os("RUSTC")?;
+    let output = Command::new(rustc).arg("--version").output().ok()?;
+    let version = String::from_utf8(output.stdout).ok()?;
+    let mut pieces = version.split('.');
+    if pieces.next() != Some("rustc 1") {
+        return None;
+    }
+    let minor = pieces.next()?.parse().ok()?;
+    Some(RustVersion { version, minor })
 }
diff --git a/compile_flags.txt b/compile_flags.txt
new file mode 100644
index 0000000..c24e3b5
--- /dev/null
+++ b/compile_flags.txt
@@ -0,0 +1 @@
+-std=c++11
diff --git a/demo/Cargo.toml b/demo/Cargo.toml
index dc94861..8115b1e 100644
--- a/demo/Cargo.toml
+++ b/demo/Cargo.toml
@@ -2,11 +2,14 @@
 name = "demo"
 version = "0.0.0"
 authors = ["David Tolnay <dtolnay@gmail.com>"]
+license = "MIT OR Apache-2.0"
+description = "Toy project from https://github.com/dtolnay/cxx"
+repository = "https://github.com/dtolnay/cxx"
 edition = "2018"
 publish = false
 
 [dependencies]
-cxx = { path = ".." }
+cxx = "1.0"
 
 [build-dependencies]
-cxx-build = { path = "../gen/build" }
+cxx-build = "1.0"
diff --git a/demo/include/blobstore.h b/demo/include/blobstore.h
index 68a7fc2..d89583a 100644
--- a/demo/include/blobstore.h
+++ b/demo/include/blobstore.h
@@ -16,8 +16,8 @@
   BlobMetadata metadata(uint64_t blobid) const;
 
 private:
-  class Impl;
-  std::shared_ptr<Impl> impl;
+  class impl;
+  std::shared_ptr<impl> impl;
 };
 
 std::unique_ptr<BlobstoreClient> new_blobstore_client();
diff --git a/demo/src/blobstore.cc b/demo/src/blobstore.cc
index 0036a32..7cf40df 100644
--- a/demo/src/blobstore.cc
+++ b/demo/src/blobstore.cc
@@ -13,7 +13,7 @@
 //
 // In reality the implementation of BlobstoreClient could be a large complex C++
 // library.
-class BlobstoreClient::Impl {
+class BlobstoreClient::impl {
   friend BlobstoreClient;
   using Blob = struct {
     std::string data;
@@ -22,7 +22,7 @@
   std::unordered_map<uint64_t, Blob> blobs;
 };
 
-BlobstoreClient::BlobstoreClient() : impl(new BlobstoreClient::Impl) {}
+BlobstoreClient::BlobstoreClient() : impl(new class BlobstoreClient::impl) {}
 
 // Upload a new blob and return a blobid that serves as a handle to the blob.
 uint64_t BlobstoreClient::put(MultiBuf &buf) const {
@@ -57,7 +57,7 @@
   auto blob = impl->blobs.find(blobid);
   if (blob != impl->blobs.end()) {
     metadata.size = blob->second.data.size();
-    std::for_each(blob->second.tags.begin(), blob->second.tags.end(),
+    std::for_each(blob->second.tags.cbegin(), blob->second.tags.cend(),
                   [&](auto &t) { metadata.tags.emplace_back(t); });
   }
   return metadata;
diff --git a/demo/src/main.rs b/demo/src/main.rs
index 10f57e5..458f1f2 100644
--- a/demo/src/main.rs
+++ b/demo/src/main.rs
@@ -14,7 +14,7 @@
     }
 
     // C++ types and signatures exposed to Rust.
-    extern "C++" {
+    unsafe extern "C++" {
         include!("demo/include/blobstore.h");
 
         type BlobstoreClient;
@@ -38,7 +38,7 @@
 pub fn next_chunk(buf: &mut MultiBuf) -> &[u8] {
     let next = buf.chunks.get(buf.pos);
     buf.pos += 1;
-    next.map(Vec::as_slice).unwrap_or(&[])
+    next.map_or(&[], Vec::as_slice)
 }
 
 fn main() {
diff --git a/flags/Cargo.toml b/flags/Cargo.toml
index cc5f230..87c796c 100644
--- a/flags/Cargo.toml
+++ b/flags/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "cxxbridge-flags"
-version = "0.5.9"
+version = "1.0.42"
 authors = ["David Tolnay <dtolnay@gmail.com>"]
 edition = "2018"
 license = "MIT OR Apache-2.0"
diff --git a/flags/LICENSE-APACHE b/flags/LICENSE-APACHE
new file mode 120000
index 0000000..965b606
--- /dev/null
+++ b/flags/LICENSE-APACHE
@@ -0,0 +1 @@
+../LICENSE-APACHE
\ No newline at end of file
diff --git a/flags/LICENSE-MIT b/flags/LICENSE-MIT
new file mode 120000
index 0000000..76219eb
--- /dev/null
+++ b/flags/LICENSE-MIT
@@ -0,0 +1 @@
+../LICENSE-MIT
\ No newline at end of file
diff --git a/gen/build/Cargo.toml b/gen/build/Cargo.toml
index 3f7b0ea..33f17df 100644
--- a/gen/build/Cargo.toml
+++ b/gen/build/Cargo.toml
@@ -1,26 +1,30 @@
 [package]
 name = "cxx-build"
-version = "0.5.9"
+version = "1.0.42"
 authors = ["David Tolnay <dtolnay@gmail.com>"]
 edition = "2018"
 license = "MIT OR Apache-2.0"
 description = "C++ code generator for integrating `cxx` crate into a Cargo build."
 repository = "https://github.com/dtolnay/cxx"
+homepage = "https://cxx.rs"
 exclude = ["build.rs"]
 keywords = ["ffi"]
 categories = ["development-tools::ffi"]
 
+[features]
+parallel = ["cc/parallel"]
+
 [dependencies]
 cc = "1.0.49"
-codespan-reporting = "0.9"
+codespan-reporting = "0.11"
 lazy_static = "1.4"
-proc-macro2 = { version = "1.0.17", default-features = false, features = ["span-locations"] }
+proc-macro2 = { version = "1.0.26", default-features = false, features = ["span-locations"] }
 quote = { version = "1.0", default-features = false }
 scratch = "1.0"
-syn = { version = "1.0.20", default-features = false, features = ["parsing", "printing", "clone-impls", "full"] }
+syn = { version = "1.0.68", default-features = false, features = ["parsing", "printing", "clone-impls", "full"] }
 
 [dev-dependencies]
-cxx-gen = { version = "0.6", path = "../lib" }
+cxx-gen = { version = "0.7", path = "../lib" }
 pkg-config = "0.3"
 
 [package.metadata.docs.rs]
diff --git a/gen/build/src/cfg.rs b/gen/build/src/cfg.rs
index c15a173..6818bda 100644
--- a/gen/build/src/cfg.rs
+++ b/gen/build/src/cfg.rs
@@ -266,10 +266,10 @@
 #[cfg(not(doc))]
 mod r#impl {
     use crate::intern::{intern, InternedString};
+    use crate::syntax::map::UnorderedMap as Map;
     use crate::vec::{self, InternedVec as _};
     use lazy_static::lazy_static;
     use std::cell::RefCell;
-    use std::collections::HashMap;
     use std::fmt::{self, Debug};
     use std::marker::PhantomData;
     use std::ops::{Deref, DerefMut};
@@ -316,7 +316,7 @@
         //         cfg: AtomicPtr<super::Cfg>,
         //     }
         //
-        static CONST_DEREFS: RefCell<HashMap<Handle, Box<super::Cfg<'static>>>> = RefCell::default();
+        static CONST_DEREFS: RefCell<Map<Handle, Box<super::Cfg<'static>>>> = RefCell::default();
     }
 
     #[derive(Eq, PartialEq, Hash)]
diff --git a/gen/build/src/intern.rs b/gen/build/src/intern.rs
index 51bc07c..25b8706 100644
--- a/gen/build/src/intern.rs
+++ b/gen/build/src/intern.rs
@@ -1,5 +1,5 @@
+use crate::syntax::set::UnorderedSet as Set;
 use lazy_static::lazy_static;
-use std::collections::HashSet;
 use std::sync::{Mutex, PoisonError};
 
 #[derive(Copy, Clone, Default)]
@@ -13,7 +13,7 @@
 
 pub fn intern(s: &str) -> InternedString {
     lazy_static! {
-        static ref INTERN: Mutex<HashSet<&'static str>> = Mutex::new(HashSet::new());
+        static ref INTERN: Mutex<Set<&'static str>> = Mutex::new(Set::new());
     }
 
     let mut set = INTERN.lock().unwrap_or_else(PoisonError::into_inner);
diff --git a/gen/build/src/lib.rs b/gen/build/src/lib.rs
index db15335..63b2cf6 100644
--- a/gen/build/src/lib.rs
+++ b/gen/build/src/lib.rs
@@ -46,12 +46,35 @@
 //! ```
 
 #![allow(
+    clippy::cast_sign_loss,
+    clippy::default_trait_access,
+    clippy::doc_markdown,
     clippy::drop_copy,
+    clippy::enum_glob_use,
     clippy::inherent_to_string,
+    clippy::items_after_statements,
+    clippy::let_underscore_drop,
+    clippy::match_bool,
+    clippy::match_on_vec_items,
+    clippy::match_same_arms,
+    clippy::module_name_repetitions,
     clippy::needless_doctest_main,
+    clippy::needless_pass_by_value,
     clippy::new_without_default,
+    clippy::nonminimal_bool,
+    clippy::option_if_let_else,
     clippy::or_fun_call,
-    clippy::toplevel_ref_arg
+    clippy::redundant_else,
+    clippy::shadow_unrelated,
+    clippy::similar_names,
+    clippy::single_match_else,
+    clippy::struct_excessive_bools,
+    clippy::too_many_arguments,
+    clippy::too_many_lines,
+    clippy::toplevel_ref_arg,
+    clippy::upper_case_acronyms,
+    // clippy bug: https://github.com/rust-lang/rust-clippy/issues/6983
+    clippy::wrong_self_convention
 )]
 
 mod cfg;
@@ -70,10 +93,10 @@
 use crate::gen::error::report;
 use crate::gen::Opt;
 use crate::paths::PathExt;
+use crate::syntax::map::{Entry, UnorderedMap};
 use crate::target::TargetDir;
 use cc::Build;
-use std::collections::btree_map::Entry;
-use std::collections::{BTreeMap, BTreeSet};
+use std::collections::BTreeSet;
 use std::env;
 use std::ffi::{OsStr, OsString};
 use std::io::{self, Write};
@@ -186,7 +209,6 @@
     let ref prj = Project::init()?;
     validate_cfg(prj)?;
     let this_crate = make_this_crate(prj)?;
-    this_crate.print_to_cargo();
 
     let mut build = Build::new();
     build.cpp(true);
@@ -196,6 +218,7 @@
         generate_bridge(prj, &mut build, path.as_ref())?;
     }
 
+    this_crate.print_to_cargo();
     eprintln!("\nCXX include path:");
     for header_dir in this_crate.header_dirs {
         build.include(&header_dir.path);
@@ -256,12 +279,10 @@
         path: include_dir,
     });
 
-    if let Some(crate_dir) = crate_dir {
-        this_crate.header_dirs.push(HeaderDir {
-            exported: true,
-            path: crate_dir,
-        });
-    }
+    this_crate.header_dirs.push(HeaderDir {
+        exported: true,
+        path: crate_dir,
+    });
 
     for exported_dir in &CFG.exported_header_dirs {
         this_crate.header_dirs.push(HeaderDir {
@@ -270,7 +291,7 @@
         });
     }
 
-    let mut header_dirs_index = BTreeMap::new();
+    let mut header_dirs_index = UnorderedMap::new();
     let mut used_header_links = BTreeSet::new();
     let mut used_header_prefixes = BTreeSet::new();
     for krate in deps::direct_dependencies() {
@@ -335,24 +356,30 @@
     Ok(this_crate)
 }
 
-fn make_crate_dir(prj: &Project) -> Option<PathBuf> {
+fn make_crate_dir(prj: &Project) -> PathBuf {
     if prj.include_prefix.as_os_str().is_empty() {
-        return Some(prj.manifest_dir.clone());
+        return prj.manifest_dir.clone();
     }
     let crate_dir = prj.out_dir.join("cxxbridge").join("crate");
-    let link = crate_dir.join(&prj.include_prefix);
-    if out::symlink_dir(&prj.manifest_dir, link).is_ok() {
-        Some(crate_dir)
-    } else {
-        None
+    let ref link = crate_dir.join(&prj.include_prefix);
+    let ref manifest_dir = prj.manifest_dir;
+    if out::symlink_dir(manifest_dir, link).is_err() && cfg!(not(unix)) {
+        let cachedir_tag = "\
+        Signature: 8a477f597d28d172789f06886806bc55\n\
+        # This file is a cache directory tag created by cxx.\n\
+        # For information about cache directory tags see https://bford.info/cachedir/\n";
+        let _ = out::write(crate_dir.join("CACHEDIR.TAG"), cachedir_tag.as_bytes());
+        let max_depth = 6;
+        best_effort_copy_headers(manifest_dir, link, max_depth);
     }
+    crate_dir
 }
 
 fn make_include_dir(prj: &Project) -> Result<PathBuf> {
     let include_dir = prj.out_dir.join("cxxbridge").join("include");
     let cxx_h = include_dir.join("rust").join("cxx.h");
     let ref shared_cxx_h = prj.shared_dir.join("rust").join("cxx.h");
-    if let Some(ref original) = env::var_os("DEP_CXXBRIDGE05_HEADER") {
+    if let Some(ref original) = env::var_os("DEP_CXXBRIDGE1_HEADER") {
         out::symlink_file(original, cxx_h)?;
         out::symlink_file(original, shared_cxx_h)?;
     } else {
@@ -393,6 +420,49 @@
     Ok(())
 }
 
+fn best_effort_copy_headers(src: &Path, dst: &Path, max_depth: usize) {
+    // Not using crate::gen::fs because we aren't reporting the errors.
+    use std::fs;
+
+    let mut dst_created = false;
+    let mut entries = match fs::read_dir(src) {
+        Ok(entries) => entries,
+        Err(_) => return,
+    };
+
+    while let Some(Ok(entry)) = entries.next() {
+        let file_name = entry.file_name();
+        if file_name.to_string_lossy().starts_with('.') {
+            continue;
+        }
+        match entry.file_type() {
+            Ok(file_type) if file_type.is_dir() && max_depth > 0 => {
+                let src = entry.path();
+                if src.join("Cargo.toml").exists() || src.join("CACHEDIR.TAG").exists() {
+                    continue;
+                }
+                let dst = dst.join(file_name);
+                best_effort_copy_headers(&src, &dst, max_depth - 1);
+            }
+            Ok(file_type) if file_type.is_file() => {
+                let src = entry.path();
+                match src.extension().and_then(OsStr::to_str) {
+                    Some("h") | Some("hh") | Some("hpp") => {}
+                    _ => continue,
+                }
+                if !dst_created && fs::create_dir_all(dst).is_err() {
+                    return;
+                }
+                dst_created = true;
+                let dst = dst.join(file_name);
+                let _ = fs::remove_file(&dst);
+                let _ = fs::copy(src, dst);
+            }
+            _ => {}
+        }
+    }
+}
+
 fn env_os(key: impl AsRef<OsStr>) -> Result<OsString> {
     let key = key.as_ref();
     env::var_os(key).ok_or_else(|| Error::NoEnv(key.to_owned()))
diff --git a/gen/build/src/out.rs b/gen/build/src/out.rs
index b97e992..e275049 100644
--- a/gen/build/src/out.rs
+++ b/gen/build/src/out.rs
@@ -14,7 +14,7 @@
                 return Ok(());
             }
         }
-        let _ = fs::remove_file(path);
+        best_effort_remove(path);
     } else {
         let parent = path.parent().unwrap();
         create_dir_error = fs::create_dir_all(parent).err();
@@ -28,19 +28,19 @@
     }
 }
 
-pub(crate) fn symlink_file(src: impl AsRef<Path>, dst: impl AsRef<Path>) -> Result<()> {
-    let src = src.as_ref();
-    let dst = dst.as_ref();
+pub(crate) fn symlink_file(original: impl AsRef<Path>, link: impl AsRef<Path>) -> Result<()> {
+    let original = original.as_ref();
+    let link = link.as_ref();
 
     let mut create_dir_error = None;
-    if dst.exists() {
-        let _ = fs::remove_file(dst).unwrap();
+    if link.exists() {
+        best_effort_remove(link);
     } else {
-        let parent = dst.parent().unwrap();
+        let parent = link.parent().unwrap();
         create_dir_error = fs::create_dir_all(parent).err();
     }
 
-    match paths::symlink_or_copy(src, dst) {
+    match paths::symlink_or_copy(original, link) {
         // As long as symlink_or_copy succeeded, ignore any create_dir_all error.
         Ok(()) => Ok(()),
         // If create_dir_all and symlink_or_copy both failed, prefer the first error.
@@ -48,22 +48,47 @@
     }
 }
 
-pub(crate) fn symlink_dir(src: impl AsRef<Path>, dst: impl AsRef<Path>) -> Result<()> {
-    let src = src.as_ref();
-    let dst = dst.as_ref();
+pub(crate) fn symlink_dir(original: impl AsRef<Path>, link: impl AsRef<Path>) -> Result<()> {
+    let original = original.as_ref();
+    let link = link.as_ref();
 
     let mut create_dir_error = None;
-    if dst.exists() {
-        let _ = paths::remove_symlink_dir(dst).unwrap();
+    if link.exists() {
+        best_effort_remove(link);
     } else {
-        let parent = dst.parent().unwrap();
+        let parent = link.parent().unwrap();
         create_dir_error = fs::create_dir_all(parent).err();
     }
 
-    match paths::symlink_dir(src, dst) {
+    match fs::symlink_dir(original, link) {
         // As long as symlink_dir succeeded, ignore any create_dir_all error.
         Ok(()) => Ok(()),
         // If create_dir_all and symlink_dir both failed, prefer the first error.
         Err(err) => Err(Error::Fs(create_dir_error.unwrap_or(err))),
     }
 }
+
+fn best_effort_remove(path: &Path) {
+    use std::fs;
+
+    let file_type = match if cfg!(windows) {
+        // On Windows, the correct choice of remove_file vs remove_dir needs to
+        // be used according to what the symlink *points to*. Trying to use
+        // remove_file to remove a symlink which points to a directory fails
+        // with "Access is denied".
+        fs::metadata(path)
+    } else {
+        // On non-Windows, we check metadata not following symlinks. All
+        // symlinks are removed using remove_file.
+        fs::symlink_metadata(path)
+    } {
+        Ok(metadata) => metadata.file_type(),
+        Err(_) => return,
+    };
+
+    if file_type.is_dir() {
+        let _ = fs::remove_dir_all(path);
+    } else {
+        let _ = fs::remove_file(path);
+    }
+}
diff --git a/gen/build/src/paths.rs b/gen/build/src/paths.rs
index 4459363..c514a57 100644
--- a/gen/build/src/paths.rs
+++ b/gen/build/src/paths.rs
@@ -43,32 +43,25 @@
 pub(crate) use self::fs::symlink_file as symlink_or_copy;
 
 #[cfg(windows)]
-pub(crate) fn symlink_or_copy(src: impl AsRef<Path>, dst: impl AsRef<Path>) -> fs::Result<()> {
+pub(crate) fn symlink_or_copy(
+    original: impl AsRef<Path>,
+    link: impl AsRef<Path>,
+) -> fs::Result<()> {
     // Pre-Windows 10, symlinks require admin privileges. Since Windows 10, they
     // require Developer Mode. If it fails, fall back to copying the file.
-    let src = src.as_ref();
-    let dst = dst.as_ref();
-    if fs::symlink_file(src, dst).is_err() {
-        fs::copy(src, dst)?;
+    let original = original.as_ref();
+    let link = link.as_ref();
+    if fs::symlink_file(original, link).is_err() {
+        fs::copy(original, link)?;
     }
     Ok(())
 }
 
 #[cfg(not(any(unix, windows)))]
-pub(crate) use self::fs::copy as symlink_or_copy;
-
-#[cfg(any(unix, windows))]
-pub(crate) use self::fs::symlink_dir;
-
-#[cfg(not(any(unix, windows)))]
-pub(crate) fn symlink_dir(_src: impl AsRef<Path>, _dst: impl AsRef<Path>) -> fs::Result<()> {
+pub(crate) fn symlink_or_copy(
+    original: impl AsRef<Path>,
+    copy: impl AsRef<Path>,
+) -> fs::Result<()> {
+    fs::copy(original, copy)?;
     Ok(())
 }
-
-#[cfg(not(windows))]
-pub(crate) use self::fs::remove_file as remove_symlink_dir;
-
-// On Windows, trying to use remove_file to remove a symlink which points to a
-// directory fails with "Access is denied".
-#[cfg(windows)]
-pub(crate) use self::fs::remove_dir as remove_symlink_dir;
diff --git a/gen/build/src/target.rs b/gen/build/src/target.rs
index 58ada3a..4c9a9f3 100644
--- a/gen/build/src/target.rs
+++ b/gen/build/src/target.rs
@@ -1,4 +1,5 @@
 use std::env;
+use std::ffi::OsStr;
 use std::path::{Path, PathBuf};
 
 pub(crate) enum TargetDir {
@@ -24,7 +25,13 @@
 
     let mut dir = out_dir.to_owned();
     loop {
-        if dir.join(".rustc_info.json").exists() || dir.join("CACHEDIR.TAG").exists() {
+        if dir.join(".rustc_info.json").exists()
+            || dir.join("CACHEDIR.TAG").exists()
+            || dir.file_name() == Some(OsStr::new("target"))
+                && dir
+                    .parent()
+                    .map_or(false, |parent| parent.join("Cargo.toml").exists())
+        {
             return TargetDir::Path(dir);
         }
         if dir.pop() {
diff --git a/gen/cmd/Cargo.toml b/gen/cmd/Cargo.toml
index f3bdebf..7db0206 100644
--- a/gen/cmd/Cargo.toml
+++ b/gen/cmd/Cargo.toml
@@ -1,11 +1,12 @@
 [package]
 name = "cxxbridge-cmd"
-version = "0.5.9"
+version = "1.0.42"
 authors = ["David Tolnay <dtolnay@gmail.com>"]
 edition = "2018"
 license = "MIT OR Apache-2.0"
 description = "C++ code generator for integrating `cxx` crate into a non-Cargo build."
 repository = "https://github.com/dtolnay/cxx"
+homepage = "https://cxx.rs"
 exclude = ["build.rs"]
 keywords = ["ffi"]
 categories = ["development-tools::ffi"]
@@ -16,10 +17,10 @@
 
 [dependencies]
 clap = "2.33"
-codespan-reporting = "0.9"
-proc-macro2 = { version = "1.0.17", default-features = false, features = ["span-locations"] }
+codespan-reporting = "0.11"
+proc-macro2 = { version = "1.0.26", default-features = false, features = ["span-locations"] }
 quote = { version = "1.0", default-features = false }
-syn = { version = "1.0.20", default-features = false, features = ["parsing", "printing", "clone-impls", "full"] }
+syn = { version = "1.0.68", default-features = false, features = ["parsing", "printing", "clone-impls", "full"] }
 
 [package.metadata.docs.rs]
 targets = ["x86_64-unknown-linux-gnu"]
diff --git a/gen/cmd/src/app.rs b/gen/cmd/src/app.rs
index e2945a1..c233ad7 100644
--- a/gen/cmd/src/app.rs
+++ b/gen/cmd/src/app.rs
@@ -94,8 +94,8 @@
 
     Opt {
         input,
-        cxx_impl_annotations,
         header,
+        cxx_impl_annotations,
         include,
         outputs,
     }
diff --git a/gen/cmd/src/main.rs b/gen/cmd/src/main.rs
index c723039..f419dad 100644
--- a/gen/cmd/src/main.rs
+++ b/gen/cmd/src/main.rs
@@ -1,10 +1,30 @@
 #![allow(
+    clippy::cast_sign_loss,
     clippy::cognitive_complexity,
+    clippy::default_trait_access,
+    clippy::enum_glob_use,
     clippy::inherent_to_string,
+    clippy::items_after_statements,
     clippy::large_enum_variant,
+    clippy::match_bool,
+    clippy::match_on_vec_items,
+    clippy::match_same_arms,
+    clippy::module_name_repetitions,
+    clippy::needless_pass_by_value,
     clippy::new_without_default,
+    clippy::nonminimal_bool,
+    clippy::option_if_let_else,
     clippy::or_fun_call,
-    clippy::toplevel_ref_arg
+    clippy::redundant_else,
+    clippy::shadow_unrelated,
+    clippy::similar_names,
+    clippy::single_match_else,
+    clippy::struct_excessive_bools,
+    clippy::too_many_arguments,
+    clippy::too_many_lines,
+    clippy::toplevel_ref_arg,
+    // clippy bug: https://github.com/rust-lang/rust-clippy/issues/6983
+    clippy::wrong_self_convention
 )]
 
 mod app;
@@ -51,7 +71,11 @@
     for output in opt.outputs {
         let kind = if opt.input.is_none() {
             Kind::Header
-        } else if opt.header || output.ends_with(".h") {
+        } else if opt.header
+            || output.ends_with(".h")
+            || output.ends_with(".hh")
+            || output.ends_with(".hpp")
+        {
             gen_header = true;
             Kind::GeneratedHeader
         } else {
diff --git a/gen/lib/Cargo.toml b/gen/lib/Cargo.toml
index 2de0f28..ff33d97 100644
--- a/gen/lib/Cargo.toml
+++ b/gen/lib/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "cxx-gen"
-version = "0.6.6"
+version = "0.7.42"
 authors = ["Adrian Taylor <adetaylor@chromium.org>"]
 edition = "2018"
 license = "MIT OR Apache-2.0"
@@ -12,10 +12,10 @@
 
 [dependencies]
 cc = "1.0.49"
-codespan-reporting = "0.9"
-proc-macro2 = { version = "1.0.17", default-features = false, features = ["span-locations"] }
+codespan-reporting = "0.11"
+proc-macro2 = { version = "1.0.26", default-features = false, features = ["span-locations"] }
 quote = { version = "1.0", default-features = false }
-syn = { version = "1.0.20", default-features = false, features = ["parsing", "printing", "clone-impls", "full"] }
+syn = { version = "1.0.68", default-features = false, features = ["parsing", "printing", "clone-impls", "full"] }
 
 [package.metadata.docs.rs]
 targets = ["x86_64-unknown-linux-gnu"]
diff --git a/gen/lib/src/lib.rs b/gen/lib/src/lib.rs
index 963e870..e3eca5e 100644
--- a/gen/lib/src/lib.rs
+++ b/gen/lib/src/lib.rs
@@ -9,10 +9,31 @@
 
 #![allow(dead_code)]
 #![allow(
+    clippy::cast_sign_loss,
+    clippy::default_trait_access,
+    clippy::enum_glob_use,
     clippy::inherent_to_string,
+    clippy::items_after_statements,
+    clippy::match_bool,
+    clippy::match_on_vec_items,
+    clippy::match_same_arms,
+    clippy::missing_errors_doc,
+    clippy::module_name_repetitions,
+    clippy::needless_pass_by_value,
     clippy::new_without_default,
+    clippy::nonminimal_bool,
+    clippy::option_if_let_else,
     clippy::or_fun_call,
-    clippy::toplevel_ref_arg
+    clippy::redundant_else,
+    clippy::shadow_unrelated,
+    clippy::similar_names,
+    clippy::single_match_else,
+    clippy::struct_excessive_bools,
+    clippy::too_many_arguments,
+    clippy::too_many_lines,
+    clippy::toplevel_ref_arg,
+    // clippy bug: https://github.com/rust-lang/rust-clippy/issues/6983
+    clippy::wrong_self_convention
 )]
 
 mod error;
diff --git a/gen/lib/tests/test.rs b/gen/lib/tests/test.rs
index 73a25f3..d035b52 100644
--- a/gen/lib/tests/test.rs
+++ b/gen/lib/tests/test.rs
@@ -6,7 +6,7 @@
     let rs = quote! {
         #[cxx::bridge]
         mod ffi {
-            extern "C" {
+            unsafe extern "C++" {
                 fn in_C();
             }
             extern "Rust" {
diff --git a/gen/src/builtin.rs b/gen/src/builtin.rs
index 41d037a..eaaa08d 100644
--- a/gen/src/builtin.rs
+++ b/gen/src/builtin.rs
@@ -12,12 +12,16 @@
     pub rust_vec: bool,
     pub rust_fn: bool,
     pub rust_isize: bool,
+    pub opaque: bool,
+    pub layout: bool,
     pub unsafe_bitcopy: bool,
+    pub unsafe_bitcopy_t: bool,
     pub rust_error: bool,
     pub manually_drop: bool,
     pub maybe_uninit: bool,
     pub trycatch: bool,
     pub ptr_len: bool,
+    pub repr_fat: bool,
     pub rust_str_new_unchecked: bool,
     pub rust_str_repr: bool,
     pub rust_slice_new: bool,
@@ -25,6 +29,8 @@
     pub exception: bool,
     pub relocatable: bool,
     pub friend_impl: bool,
+    pub is_complete: bool,
+    pub deleter_if: bool,
     pub content: Content<'a>,
 }
 
@@ -50,13 +56,40 @@
     }
 
     if builtin.rust_str {
+        include.array = true;
         include.cstdint = true;
         include.string = true;
         builtin.friend_impl = true;
     }
 
+    if builtin.rust_vec {
+        include.algorithm = true;
+        include.array = true;
+        include.cassert = true;
+        include.cstddef = true;
+        include.cstdint = true;
+        include.initializer_list = true;
+        include.iterator = true;
+        include.new = true;
+        include.stdexcept = true;
+        include.type_traits = true;
+        include.utility = true;
+        builtin.panic = true;
+        builtin.rust_slice = true;
+        builtin.unsafe_bitcopy_t = true;
+    }
+
     if builtin.rust_slice {
+        include.array = true;
+        include.cassert = true;
+        include.cstddef = true;
+        include.cstdint = true;
+        include.iterator = true;
+        include.stdexcept = true;
+        include.type_traits = true;
         builtin.friend_impl = true;
+        builtin.layout = true;
+        builtin.panic = true;
     }
 
     if builtin.rust_box {
@@ -65,15 +98,6 @@
         include.utility = true;
     }
 
-    if builtin.rust_vec {
-        include.array = true;
-        include.new = true;
-        include.type_traits = true;
-        include.utility = true;
-        builtin.panic = true;
-        builtin.unsafe_bitcopy = true;
-    }
-
     if builtin.rust_fn {
         include.utility = true;
     }
@@ -85,40 +109,122 @@
 
     if builtin.rust_isize {
         include.basetsd = true;
+        include.sys_types = true;
     }
 
     if builtin.relocatable {
         include.type_traits = true;
     }
 
+    if builtin.layout {
+        include.type_traits = true;
+        include.cstddef = true;
+        builtin.is_complete = true;
+    }
+
+    if builtin.is_complete {
+        include.cstddef = true;
+        include.type_traits = true;
+    }
+
+    if builtin.unsafe_bitcopy {
+        builtin.unsafe_bitcopy_t = true;
+    }
+
     out.begin_block(Block::Namespace("rust"));
-    out.begin_block(Block::InlineNamespace("cxxbridge05"));
-    writeln!(out, "// #include \"rust/cxx.h\"");
+    out.begin_block(Block::InlineNamespace("cxxbridge1"));
 
-    ifndef::write(out, builtin.panic, "CXXBRIDGE05_PANIC");
+    let cxx_header = include.has_cxx_header();
+    if !cxx_header {
+        writeln!(out, "// #include \"rust/cxx.h\"");
 
-    if builtin.rust_string {
+        ifndef::write(out, builtin.panic, "CXXBRIDGE1_PANIC");
+
+        if builtin.rust_string {
+            out.next_section();
+            writeln!(out, "struct unsafe_bitcopy_t;");
+        }
+
+        if builtin.friend_impl {
+            out.begin_block(Block::AnonymousNamespace);
+            writeln!(out, "template <typename T>");
+            writeln!(out, "class impl;");
+            out.end_block(Block::AnonymousNamespace);
+        }
+
         out.next_section();
-        writeln!(out, "struct unsafe_bitcopy_t;");
+        if builtin.rust_str && !builtin.rust_string {
+            writeln!(out, "class String;");
+        }
+        if builtin.layout && !builtin.opaque {
+            writeln!(out, "class Opaque;");
+        }
+
+        if builtin.rust_slice {
+            out.next_section();
+            writeln!(out, "template <typename T>");
+            writeln!(out, "::std::size_t size_of();");
+            writeln!(out, "template <typename T>");
+            writeln!(out, "::std::size_t align_of();");
+        }
+
+        ifndef::write(out, builtin.rust_string, "CXXBRIDGE1_RUST_STRING");
+        ifndef::write(out, builtin.rust_str, "CXXBRIDGE1_RUST_STR");
+        ifndef::write(out, builtin.rust_slice, "CXXBRIDGE1_RUST_SLICE");
+        ifndef::write(out, builtin.rust_box, "CXXBRIDGE1_RUST_BOX");
+        ifndef::write(out, builtin.unsafe_bitcopy_t, "CXXBRIDGE1_RUST_BITCOPY_T");
+        ifndef::write(out, builtin.unsafe_bitcopy, "CXXBRIDGE1_RUST_BITCOPY");
+        ifndef::write(out, builtin.rust_vec, "CXXBRIDGE1_RUST_VEC");
+        ifndef::write(out, builtin.rust_fn, "CXXBRIDGE1_RUST_FN");
+        ifndef::write(out, builtin.rust_error, "CXXBRIDGE1_RUST_ERROR");
+        ifndef::write(out, builtin.rust_isize, "CXXBRIDGE1_RUST_ISIZE");
+        ifndef::write(out, builtin.opaque, "CXXBRIDGE1_RUST_OPAQUE");
+        ifndef::write(out, builtin.is_complete, "CXXBRIDGE1_IS_COMPLETE");
+        ifndef::write(out, builtin.layout, "CXXBRIDGE1_LAYOUT");
+        ifndef::write(out, builtin.relocatable, "CXXBRIDGE1_RELOCATABLE");
     }
 
-    if builtin.friend_impl {
-        out.begin_block(Block::AnonymousNamespace);
+    if builtin.rust_str_new_unchecked {
+        out.next_section();
+        writeln!(out, "class Str::uninit {{}};");
+        writeln!(out, "inline Str::Str(uninit) noexcept {{}}");
+    }
+
+    if builtin.rust_slice_new {
+        out.next_section();
         writeln!(out, "template <typename T>");
-        writeln!(out, "class impl;");
-        out.end_block(Block::AnonymousNamespace);
+        writeln!(out, "class Slice<T>::uninit {{}};");
+        writeln!(out, "template <typename T>");
+        writeln!(out, "inline Slice<T>::Slice(uninit) noexcept {{}}");
     }
 
-    ifndef::write(out, builtin.rust_string, "CXXBRIDGE05_RUST_STRING");
-    ifndef::write(out, builtin.rust_str, "CXXBRIDGE05_RUST_STR");
-    ifndef::write(out, builtin.rust_slice, "CXXBRIDGE05_RUST_SLICE");
-    ifndef::write(out, builtin.rust_box, "CXXBRIDGE05_RUST_BOX");
-    ifndef::write(out, builtin.unsafe_bitcopy, "CXXBRIDGE05_RUST_BITCOPY");
-    ifndef::write(out, builtin.rust_vec, "CXXBRIDGE05_RUST_VEC");
-    ifndef::write(out, builtin.rust_fn, "CXXBRIDGE05_RUST_FN");
-    ifndef::write(out, builtin.rust_error, "CXXBRIDGE05_RUST_ERROR");
-    ifndef::write(out, builtin.rust_isize, "CXXBRIDGE05_RUST_ISIZE");
-    ifndef::write(out, builtin.relocatable, "CXXBRIDGE05_RELOCATABLE");
+    out.begin_block(Block::Namespace("detail"));
+
+    if builtin.maybe_uninit {
+        include.cstddef = true;
+        include.new = true;
+        out.next_section();
+        writeln!(out, "template <typename T, typename = void *>");
+        writeln!(out, "struct operator_new {{");
+        writeln!(
+            out,
+            "  void *operator()(::std::size_t sz) {{ return ::operator new(sz); }}",
+        );
+        writeln!(out, "}};");
+        out.next_section();
+        writeln!(out, "template <typename T>");
+        writeln!(
+            out,
+            "struct operator_new<T, decltype(T::operator new(sizeof(T)))> {{",
+        );
+        writeln!(
+            out,
+            "  void *operator()(::std::size_t sz) {{ return T::operator new(sz); }}",
+        );
+        writeln!(out, "}};");
+    }
+
+    out.end_block(Block::Namespace("detail"));
 
     if builtin.manually_drop {
         out.next_section();
@@ -135,10 +241,15 @@
     }
 
     if builtin.maybe_uninit {
+        include.cstddef = true;
         out.next_section();
         writeln!(out, "template <typename T>");
         writeln!(out, "union MaybeUninit {{");
         writeln!(out, "  T value;");
+        writeln!(
+            out,
+            "  void *operator new(::std::size_t sz) {{ return detail::operator_new<T>{{}}(sz); }}",
+        );
         writeln!(out, "  MaybeUninit() {{}}");
         writeln!(out, "  ~MaybeUninit() {{}}");
         writeln!(out, "}};");
@@ -146,11 +257,22 @@
 
     out.begin_block(Block::AnonymousNamespace);
 
+    if builtin.repr_fat {
+        include.array = true;
+        include.cstdint = true;
+        out.next_section();
+        out.begin_block(Block::Namespace("repr"));
+        writeln!(out, "using Fat = ::std::array<::std::uintptr_t, 2>;");
+        out.end_block(Block::Namespace("repr"));
+    }
+
     if builtin.ptr_len {
+        include.cstddef = true;
+        out.next_section();
         out.begin_block(Block::Namespace("repr"));
         writeln!(out, "struct PtrLen final {{");
-        writeln!(out, "  const void *ptr;");
-        writeln!(out, "  size_t len;");
+        writeln!(out, "  void *ptr;");
+        writeln!(out, "  ::std::size_t len;");
         writeln!(out, "}};");
         out.end_block(Block::Namespace("repr"));
     }
@@ -163,17 +285,16 @@
         if builtin.rust_str_new_unchecked {
             writeln!(
                 out,
-                "  static Str new_unchecked(repr::PtrLen repr) noexcept {{",
+                "  static Str new_unchecked(repr::Fat repr) noexcept {{",
             );
-            writeln!(out, "    Str str;");
-            writeln!(out, "    str.ptr = static_cast<const char *>(repr.ptr);");
-            writeln!(out, "    str.len = repr.len;");
+            writeln!(out, "    Str str = Str::uninit{{}};");
+            writeln!(out, "    str.repr = repr;");
             writeln!(out, "    return str;");
             writeln!(out, "  }}");
         }
         if builtin.rust_str_repr {
-            writeln!(out, "  static repr::PtrLen repr(Str str) noexcept {{");
-            writeln!(out, "    return repr::PtrLen{{str.ptr, str.len}};");
+            writeln!(out, "  static repr::Fat repr(Str str) noexcept {{");
+            writeln!(out, "    return str.repr;");
             writeln!(out, "  }}");
         }
         writeln!(out, "}};");
@@ -185,22 +306,15 @@
         writeln!(out, "class impl<Slice<T>> final {{");
         writeln!(out, "public:");
         if builtin.rust_slice_new {
-            writeln!(
-                out,
-                "  static Slice<T> slice(repr::PtrLen repr) noexcept {{",
-            );
-            writeln!(
-                out,
-                "    return {{static_cast<const T *>(repr.ptr), repr.len}};",
-            );
+            writeln!(out, "  static Slice<T> slice(repr::Fat repr) noexcept {{");
+            writeln!(out, "    Slice<T> slice = typename Slice<T>::uninit{{}};");
+            writeln!(out, "    slice.repr = repr;");
+            writeln!(out, "    return slice;");
             writeln!(out, "  }}");
         }
         if builtin.rust_slice_repr {
-            writeln!(
-                out,
-                "  static repr::PtrLen repr(Slice<T> slice) noexcept {{",
-            );
-            writeln!(out, "    return repr::PtrLen{{slice.ptr, slice.len}};");
+            writeln!(out, "  static repr::Fat repr(Slice<T> slice) noexcept {{");
+            writeln!(out, "    return slice.repr;");
             writeln!(out, "  }}");
         }
         writeln!(out, "}};");
@@ -220,8 +334,22 @@
         writeln!(out, "}};");
     }
 
+    if builtin.deleter_if {
+        out.next_section();
+        writeln!(out, "template <bool> struct deleter_if {{");
+        writeln!(out, "  template <typename T> void operator()(T *) {{}}");
+        writeln!(out, "}};");
+        out.next_section();
+        writeln!(out, "template <> struct deleter_if<true> {{");
+        writeln!(
+            out,
+            "  template <typename T> void operator()(T *ptr) {{ ptr->~T(); }}",
+        );
+        writeln!(out, "}};");
+    }
+
     out.end_block(Block::AnonymousNamespace);
-    out.end_block(Block::InlineNamespace("cxxbridge05"));
+    out.end_block(Block::InlineNamespace("cxxbridge1"));
 
     if builtin.trycatch {
         out.begin_block(Block::Namespace("behavior"));
@@ -249,10 +377,11 @@
     out.end_block(Block::Namespace("rust"));
 
     if builtin.exception {
+        include.cstddef = true;
         out.begin_block(Block::ExternC);
         writeln!(
             out,
-            "const char *cxxbridge05$exception(const char *, size_t);",
+            "const char *cxxbridge1$exception(const char *, ::std::size_t);",
         );
         out.end_block(Block::ExternC);
     }
diff --git a/gen/src/error.rs b/gen/src/error.rs
index 2335d43..2c8287f 100644
--- a/gen/src/error.rs
+++ b/gen/src/error.rs
@@ -68,6 +68,13 @@
                 display_syn_error(stderr, path, source, error);
             }
         }
+        Error::NoBridgeMod => {
+            let _ = writeln!(
+                io::stderr(),
+                "cxxbridge: no #[cxx::bridge] module found in {}",
+                path.display(),
+            );
+        }
         _ => {
             let _ = writeln!(io::stderr(), "cxxbridge: {}", report(error));
         }
diff --git a/gen/src/fs.rs b/gen/src/fs.rs
index 8f94f00..7053cc4 100644
--- a/gen/src/fs.rs
+++ b/gen/src/fs.rs
@@ -9,7 +9,7 @@
 
 #[derive(Debug)]
 pub(crate) struct Error {
-    source: io::Error,
+    source: Option<io::Error>,
     message: String,
 }
 
@@ -21,14 +21,15 @@
 
 impl StdError for Error {
     fn source(&self) -> Option<&(dyn StdError + 'static)> {
-        Some(&self.source)
+        let source = self.source.as_ref()?;
+        Some(source)
     }
 }
 
 macro_rules! err {
     ($io_error:expr, $fmt:expr $(, $path:expr)* $(,)?) => {
         Err(Error {
-            source: $io_error,
+            source: Option::from($io_error),
             message: format!($fmt $(, $path.display())*),
         })
     }
@@ -91,44 +92,57 @@
 }
 
 fn symlink<'a>(
-    src: &'a Path,
-    dst: &'a Path,
+    original: &'a Path,
+    link: &'a Path,
     fun: fn(&'a Path, &'a Path) -> io::Result<()>,
 ) -> Result<()> {
-    match fun(src, dst) {
+    match fun(original, link) {
         Ok(()) => Ok(()),
         Err(e) => err!(
             e,
             "Failed to create symlink `{}` pointing to `{}`",
-            dst,
-            src,
+            link,
+            original,
         ),
     }
 }
 
+pub(crate) fn symlink_fail(original: impl AsRef<Path>, link: impl AsRef<Path>) -> Result<()> {
+    err!(
+        None,
+        "Failed to create symlink `{}` pointing to `{}`",
+        link.as_ref(),
+        original.as_ref(),
+    )
+}
+
 #[cfg(unix)]
 #[allow(unused_imports)]
 pub(crate) use self::symlink_file as symlink_dir;
 
+#[cfg(not(any(unix, windows)))]
+#[allow(unused_imports)]
+pub(crate) use self::symlink_fail as symlink_dir;
+
 #[cfg(unix)]
-pub(crate) fn symlink_file(src: impl AsRef<Path>, dst: impl AsRef<Path>) -> Result<()> {
-    symlink(src.as_ref(), dst.as_ref(), std::os::unix::fs::symlink)
+pub(crate) fn symlink_file(original: impl AsRef<Path>, link: impl AsRef<Path>) -> Result<()> {
+    symlink(original.as_ref(), link.as_ref(), std::os::unix::fs::symlink)
 }
 
 #[cfg(windows)]
-pub(crate) fn symlink_file(src: impl AsRef<Path>, dst: impl AsRef<Path>) -> Result<()> {
+pub(crate) fn symlink_file(original: impl AsRef<Path>, link: impl AsRef<Path>) -> Result<()> {
     symlink(
-        src.as_ref(),
-        dst.as_ref(),
+        original.as_ref(),
+        link.as_ref(),
         std::os::windows::fs::symlink_file,
     )
 }
 
 #[cfg(windows)]
-pub(crate) fn symlink_dir(src: impl AsRef<Path>, dst: impl AsRef<Path>) -> Result<()> {
+pub(crate) fn symlink_dir(original: impl AsRef<Path>, link: impl AsRef<Path>) -> Result<()> {
     symlink(
-        src.as_ref(),
-        dst.as_ref(),
+        original.as_ref(),
+        link.as_ref(),
         std::os::windows::fs::symlink_dir,
     )
 }
diff --git a/gen/src/include.rs b/gen/src/include.rs
index cf718da..62c9232 100644
--- a/gen/src/include.rs
+++ b/gen/src/include.rs
@@ -21,18 +21,25 @@
 #[derive(Default, PartialEq)]
 pub struct Includes<'a> {
     pub custom: Vec<Include>,
+    pub algorithm: bool,
     pub array: bool,
+    pub cassert: bool,
     pub cstddef: bool,
     pub cstdint: bool,
     pub cstring: bool,
     pub exception: bool,
+    pub functional: bool,
+    pub initializer_list: bool,
+    pub iterator: bool,
     pub memory: bool,
     pub new: bool,
+    pub stdexcept: bool,
     pub string: bool,
     pub type_traits: bool,
     pub utility: bool,
     pub vector: bool,
     pub basetsd: bool,
+    pub sys_types: bool,
     pub content: Content<'a>,
 }
 
@@ -44,11 +51,18 @@
     pub fn insert(&mut self, include: impl Into<Include>) {
         self.custom.push(include.into());
     }
+
+    pub fn has_cxx_header(&self) -> bool {
+        self.custom
+            .iter()
+            .any(|header| header.path == "rust/cxx.h" || header.path == "rust\\cxx.h")
+    }
 }
 
 pub(super) fn write(out: &mut OutFile) {
     let header = out.header;
     let include = &mut out.include;
+    let cxx_header = include.has_cxx_header();
     let out = &mut include.content;
 
     if header {
@@ -66,42 +80,96 @@
         }
     }
 
-    if include.array {
+    let Includes {
+        custom: _,
+        algorithm,
+        array,
+        cassert,
+        cstddef,
+        cstdint,
+        cstring,
+        exception,
+        functional,
+        initializer_list,
+        iterator,
+        memory,
+        new,
+        stdexcept,
+        string,
+        type_traits,
+        utility,
+        vector,
+        basetsd,
+        sys_types,
+        content: _,
+    } = *include;
+
+    if algorithm && !cxx_header {
+        writeln!(out, "#include <algorithm>");
+    }
+    if array && !cxx_header {
         writeln!(out, "#include <array>");
     }
-    if include.cstddef {
+    if cassert && !cxx_header {
+        writeln!(out, "#include <cassert>");
+    }
+    if cstddef && !cxx_header {
         writeln!(out, "#include <cstddef>");
     }
-    if include.cstdint {
+    if cstdint && !cxx_header {
         writeln!(out, "#include <cstdint>");
     }
-    if include.cstring {
+    if cstring {
         writeln!(out, "#include <cstring>");
     }
-    if include.exception {
+    if exception && !cxx_header {
         writeln!(out, "#include <exception>");
     }
-    if include.memory {
+    if functional {
+        writeln!(out, "#include <functional>");
+    }
+    if initializer_list && !cxx_header {
+        writeln!(out, "#include <initializer_list>");
+    }
+    if iterator && !cxx_header {
+        writeln!(out, "#include <iterator>");
+    }
+    if memory {
         writeln!(out, "#include <memory>");
     }
-    if include.new {
+    if new && !cxx_header {
         writeln!(out, "#include <new>");
     }
-    if include.string {
+    if stdexcept && !cxx_header {
+        writeln!(out, "#include <stdexcept>");
+    }
+    if string && !cxx_header {
         writeln!(out, "#include <string>");
     }
-    if include.type_traits {
+    if type_traits && !cxx_header {
         writeln!(out, "#include <type_traits>");
     }
-    if include.utility {
+    if utility && !cxx_header {
         writeln!(out, "#include <utility>");
     }
-    if include.vector {
+    if vector && !cxx_header {
         writeln!(out, "#include <vector>");
     }
-    if include.basetsd {
+    if basetsd && !cxx_header {
         writeln!(out, "#if defined(_WIN32)");
         writeln!(out, "#include <basetsd.h>");
+    }
+    if sys_types && !cxx_header {
+        if basetsd {
+            writeln!(out, "#else");
+        } else {
+            writeln!(out, "#if not defined(_WIN32)");
+        }
+    }
+    if sys_types && !cxx_header {
+        writeln!(out, "#include <sys/types.h>");
+    }
+    if (basetsd || sys_types) && !cxx_header {
         writeln!(out, "#endif");
     }
 }
diff --git a/gen/src/nested.rs b/gen/src/nested.rs
index 22b0c9f..2129d10 100644
--- a/gen/src/nested.rs
+++ b/gen/src/nested.rs
@@ -1,6 +1,6 @@
+use crate::syntax::map::UnorderedMap as Map;
 use crate::syntax::Api;
 use proc_macro2::Ident;
-use std::collections::HashMap as Map;
 
 pub struct NamespaceEntries<'a> {
     direct: Vec<&'a Api>,
@@ -51,10 +51,12 @@
 #[cfg(test)]
 mod tests {
     use super::NamespaceEntries;
+    use crate::syntax::attrs::OtherAttrs;
     use crate::syntax::namespace::Namespace;
-    use crate::syntax::{Api, Doc, ExternType, Pair};
+    use crate::syntax::{Api, Doc, ExternType, ForeignName, Lang, Lifetimes, Pair};
     use proc_macro2::{Ident, Span};
     use std::iter::FromIterator;
+    use syn::punctuated::Punctuated;
     use syn::Token;
 
     #[test]
@@ -117,7 +119,7 @@
 
     fn assert_ident(api: &Api, expected: &str) {
         if let Api::CxxType(cxx_type) = api {
-            assert_eq!(cxx_type.name.cxx, expected);
+            assert_eq!(cxx_type.name.cxx.to_string(), expected);
         } else {
             unreachable!()
         }
@@ -126,9 +128,24 @@
     fn make_api(ns: Option<&str>, ident: &str) -> Api {
         let ns = ns.map_or(Namespace::ROOT, |ns| syn::parse_str(ns).unwrap());
         Api::CxxType(ExternType {
+            lang: Lang::Rust,
             doc: Doc::new(),
+            derives: Vec::new(),
+            attrs: OtherAttrs::none(),
+            visibility: Token![pub](Span::call_site()),
             type_token: Token![type](Span::call_site()),
-            name: Pair::new(ns, Ident::new(ident, Span::call_site())),
+            name: Pair {
+                namespace: ns,
+                cxx: ForeignName::parse(ident, Span::call_site()).unwrap(),
+                rust: Ident::new(ident, Span::call_site()),
+            },
+            generics: Lifetimes {
+                lt_token: None,
+                lifetimes: Punctuated::new(),
+                gt_token: None,
+            },
+            colon_token: None,
+            bounds: Vec::new(),
             semi_token: Token![;](Span::call_site()),
             trusted: false,
         })
diff --git a/gen/src/out.rs b/gen/src/out.rs
index bf880cc..3b4d739 100644
--- a/gen/src/out.rs
+++ b/gen/src/out.rs
@@ -102,7 +102,7 @@
 }
 
 impl<'a> PartialEq for Content<'a> {
-    fn eq(&self, _other: &Content) -> bool {
+    fn eq(&self, _other: &Self) -> bool {
         true
     }
 }
diff --git a/gen/src/write.rs b/gen/src/write.rs
index 5fe8ee3..9f9c039 100644
--- a/gen/src/write.rs
+++ b/gen/src/write.rs
@@ -3,13 +3,16 @@
 use crate::gen::out::OutFile;
 use crate::gen::{builtin, include, Opt};
 use crate::syntax::atom::Atom::{self, *};
+use crate::syntax::instantiate::{ImplKey, NamedImplKey};
+use crate::syntax::map::UnorderedMap as Map;
+use crate::syntax::set::UnorderedSet;
 use crate::syntax::symbol::Symbol;
+use crate::syntax::trivial::{self, TrivialReason};
 use crate::syntax::{
-    mangle, Api, Enum, ExternFn, ExternType, Pair, ResolvableName, Signature, Struct, Type, Types,
-    Var,
+    derive, mangle, Api, Enum, ExternFn, ExternType, Pair, Signature, Struct, Trait, Type,
+    TypeAlias, Types, Var,
 };
 use proc_macro2::Ident;
-use std::collections::{HashMap, HashSet};
 
 pub(super) fn gen(apis: &[Api], types: &Types, opt: &Opt, header: bool) -> Vec<u8> {
     let mut out_file = OutFile::new(header, opt, types);
@@ -47,10 +50,10 @@
         for api in apis {
             write!(out, "{:1$}", "", indent);
             match api {
-                Api::Struct(strct) => write_struct_decl(out, &strct.name.cxx),
+                Api::Struct(strct) => write_struct_decl(out, &strct.name),
                 Api::Enum(enm) => write_enum_decl(out, enm),
                 Api::CxxType(ety) => write_struct_using(out, &ety.name),
-                Api::RustType(ety) => write_struct_decl(out, &ety.name.cxx),
+                Api::RustType(ety) => write_struct_decl(out, &ety.name),
                 _ => unreachable!(),
             }
         }
@@ -64,7 +67,7 @@
 }
 
 fn write_data_structures<'a>(out: &mut OutFile<'a>, apis: &'a [Api]) {
-    let mut methods_for_type = HashMap::new();
+    let mut methods_for_type = Map::new();
     for api in apis {
         if let Api::CxxFunction(efn) | Api::RustFunction(efn) = api {
             if let Some(receiver) = &efn.sig.receiver {
@@ -76,7 +79,7 @@
         }
     }
 
-    let mut structs_written = HashSet::new();
+    let mut structs_written = UnorderedSet::new();
     let mut toposorted_structs = out.types.toposorted_structs.iter();
     for api in apis {
         match api {
@@ -105,20 +108,28 @@
                 }
             }
             Api::RustType(ety) => {
-                if let Some(methods) = methods_for_type.get(&ety.name.rust) {
-                    out.next_section();
-                    write_struct_with_methods(out, ety, methods);
-                }
+                out.next_section();
+                let methods = methods_for_type
+                    .get(&ety.name.rust)
+                    .map(Vec::as_slice)
+                    .unwrap_or_default();
+                write_opaque_type(out, ety, methods);
             }
             _ => {}
         }
     }
 
+    if out.header {
+        return;
+    }
+
+    out.set_namespace(Default::default());
+
     out.next_section();
     for api in apis {
         if let Api::TypeAlias(ety) = api {
-            if out.types.required_trivial.contains_key(&ety.name.rust) {
-                check_trivial_extern_type(out, &ety.name)
+            if let Some(reasons) = out.types.required_trivial.get(&ety.name.rust) {
+                check_trivial_extern_type(out, ety, reasons)
             }
         }
     }
@@ -128,21 +139,62 @@
     if !out.header {
         for api in apis {
             match api {
+                Api::Struct(strct) => write_struct_operator_decls(out, strct),
+                Api::RustType(ety) => write_opaque_type_layout_decls(out, ety),
                 Api::CxxFunction(efn) => write_cxx_function_shim(out, efn),
                 Api::RustFunction(efn) => write_rust_function_decl(out, efn),
                 _ => {}
             }
         }
+
+        write_std_specializations(out, apis);
     }
 
     for api in apis {
-        if let Api::RustFunction(efn) = api {
-            out.next_section();
-            write_rust_function_shim(out, efn);
+        match api {
+            Api::Struct(strct) => write_struct_operators(out, strct),
+            Api::RustType(ety) => write_opaque_type_layout(out, ety),
+            Api::RustFunction(efn) => {
+                out.next_section();
+                write_rust_function_shim(out, efn);
+            }
+            _ => {}
         }
     }
 }
 
+fn write_std_specializations(out: &mut OutFile, apis: &[Api]) {
+    out.set_namespace(Default::default());
+    out.begin_block(Block::Namespace("std"));
+
+    for api in apis {
+        if let Api::Struct(strct) = api {
+            if derive::contains(&strct.derives, Trait::Hash) {
+                out.next_section();
+                out.include.cstddef = true;
+                out.include.functional = true;
+                let qualified = strct.name.to_fully_qualified();
+                writeln!(out, "template <> struct hash<{}> {{", qualified);
+                writeln!(
+                    out,
+                    "  ::std::size_t operator()(const {} &self) const noexcept {{",
+                    qualified,
+                );
+                let link_name = mangle::operator(&strct.name, "hash");
+                write!(out, "    return ::");
+                for name in &strct.name.namespace {
+                    write!(out, "{}::", name);
+                }
+                writeln!(out, "{}(self);", link_name);
+                writeln!(out, "  }}");
+                writeln!(out, "}};");
+            }
+        }
+    }
+
+    out.end_block(Block::Namespace("std"));
+}
+
 fn pick_includes_and_builtins(out: &mut OutFile, apis: &[Api]) {
     for api in apis {
         if let Api::Include(include) = api {
@@ -159,41 +211,46 @@
                 Some(Isize) => out.builtin.rust_isize = true,
                 Some(CxxString) => out.include.string = true,
                 Some(RustString) => out.builtin.rust_string = true,
-                Some(Bool) | Some(F32) | Some(F64) | None => {}
+                Some(Bool) | Some(Char) | Some(F32) | Some(F64) | None => {}
             },
             Type::RustBox(_) => out.builtin.rust_box = true,
             Type::RustVec(_) => out.builtin.rust_vec = true,
             Type::UniquePtr(_) => out.include.memory = true,
+            Type::SharedPtr(_) | Type::WeakPtr(_) => out.include.memory = true,
             Type::Str(_) => out.builtin.rust_str = true,
             Type::CxxVector(_) => out.include.vector = true,
             Type::Fn(_) => out.builtin.rust_fn = true,
-            Type::Slice(_) => out.builtin.rust_slice = true,
-            Type::SliceRefU8(_) => {
-                out.include.cstdint = true;
-                out.builtin.rust_slice = true;
-            }
-            Type::Ref(_) | Type::Void(_) => {}
+            Type::SliceRef(_) => out.builtin.rust_slice = true,
+            Type::Array(_) => out.include.array = true,
+            Type::Ref(_) | Type::Void(_) | Type::Ptr(_) => {}
         }
     }
 }
 
 fn write_struct<'a>(out: &mut OutFile<'a>, strct: &'a Struct, methods: &[&ExternFn]) {
+    let operator_eq = derive::contains(&strct.derives, Trait::PartialEq);
+    let operator_ord = derive::contains(&strct.derives, Trait::PartialOrd);
+
     out.set_namespace(&strct.name.namespace);
-    let guard = format!("CXXBRIDGE05_STRUCT_{}", strct.name.to_symbol());
+    let guard = format!("CXXBRIDGE1_STRUCT_{}", strct.name.to_symbol());
     writeln!(out, "#ifndef {}", guard);
     writeln!(out, "#define {}", guard);
     for line in strct.doc.to_string().lines() {
         writeln!(out, "//{}", line);
     }
     writeln!(out, "struct {} final {{", strct.name.cxx);
+
     for field in &strct.fields {
+        for line in field.doc.to_string().lines() {
+            writeln!(out, "  //{}", line);
+        }
         write!(out, "  ");
         write_type_space(out, &field.ty);
-        writeln!(out, "{};", field.ident);
+        writeln!(out, "{};", field.name.cxx);
     }
-    if !methods.is_empty() {
-        writeln!(out);
-    }
+
+    writeln!(out);
+
     for method in methods {
         write!(out, "  ");
         let sig = &method.sig;
@@ -201,12 +258,52 @@
         write_rust_function_shim_decl(out, &local_name, sig, false);
         writeln!(out, ";");
     }
+
+    if operator_eq {
+        writeln!(
+            out,
+            "  bool operator==(const {} &) const noexcept;",
+            strct.name.cxx,
+        );
+        writeln!(
+            out,
+            "  bool operator!=(const {} &) const noexcept;",
+            strct.name.cxx,
+        );
+    }
+
+    if operator_ord {
+        writeln!(
+            out,
+            "  bool operator<(const {} &) const noexcept;",
+            strct.name.cxx,
+        );
+        writeln!(
+            out,
+            "  bool operator<=(const {} &) const noexcept;",
+            strct.name.cxx,
+        );
+        writeln!(
+            out,
+            "  bool operator>(const {} &) const noexcept;",
+            strct.name.cxx,
+        );
+        writeln!(
+            out,
+            "  bool operator>=(const {} &) const noexcept;",
+            strct.name.cxx,
+        );
+    }
+
+    out.include.type_traits = true;
+    writeln!(out, "  using IsRelocatable = ::std::true_type;");
+
     writeln!(out, "}};");
     writeln!(out, "#endif // {}", guard);
 }
 
-fn write_struct_decl(out: &mut OutFile, ident: &Ident) {
-    writeln!(out, "struct {};", ident);
+fn write_struct_decl(out: &mut OutFile, ident: &Pair) {
+    writeln!(out, "struct {};", ident.cxx);
 }
 
 fn write_enum_decl(out: &mut OutFile, enm: &Enum) {
@@ -219,25 +316,22 @@
     writeln!(out, "using {} = {};", ident.cxx, ident.to_fully_qualified());
 }
 
-fn write_struct_with_methods<'a>(
-    out: &mut OutFile<'a>,
-    ety: &'a ExternType,
-    methods: &[&ExternFn],
-) {
+fn write_opaque_type<'a>(out: &mut OutFile<'a>, ety: &'a ExternType, methods: &[&ExternFn]) {
     out.set_namespace(&ety.name.namespace);
-    let guard = format!("CXXBRIDGE05_STRUCT_{}", ety.name.to_symbol());
+    let guard = format!("CXXBRIDGE1_STRUCT_{}", ety.name.to_symbol());
     writeln!(out, "#ifndef {}", guard);
     writeln!(out, "#define {}", guard);
     for line in ety.doc.to_string().lines() {
         writeln!(out, "//{}", line);
     }
-    writeln!(out, "struct {} final {{", ety.name.cxx);
-    writeln!(out, "  {}() = delete;", ety.name.cxx);
+
+    out.builtin.opaque = true;
     writeln!(
         out,
-        "  {}(const {} &) = delete;",
-        ety.name.cxx, ety.name.cxx,
+        "struct {} final : public ::rust::Opaque {{",
+        ety.name.cxx,
     );
+
     for method in methods {
         write!(out, "  ");
         let sig = &method.sig;
@@ -245,13 +339,25 @@
         write_rust_function_shim_decl(out, &local_name, sig, false);
         writeln!(out, ";");
     }
+
+    writeln!(out, "  ~{}() = delete;", ety.name.cxx);
+    writeln!(out);
+
+    out.builtin.layout = true;
+    out.include.cstddef = true;
+    writeln!(out, "private:");
+    writeln!(out, "  friend ::rust::layout;");
+    writeln!(out, "  struct layout {{");
+    writeln!(out, "    static ::std::size_t size() noexcept;");
+    writeln!(out, "    static ::std::size_t align() noexcept;");
+    writeln!(out, "  }};");
     writeln!(out, "}};");
     writeln!(out, "#endif // {}", guard);
 }
 
 fn write_enum<'a>(out: &mut OutFile<'a>, enm: &'a Enum) {
     out.set_namespace(&enm.name.namespace);
-    let guard = format!("CXXBRIDGE05_ENUM_{}", enm.name.to_symbol());
+    let guard = format!("CXXBRIDGE1_ENUM_{}", enm.name.to_symbol());
     writeln!(out, "#ifndef {}", guard);
     writeln!(out, "#define {}", guard);
     for line in enm.doc.to_string().lines() {
@@ -261,7 +367,10 @@
     write_atom(out, enm.repr);
     writeln!(out, " {{");
     for variant in &enm.variants {
-        writeln!(out, "  {} = {},", variant.ident, variant.discriminant);
+        for line in variant.doc.to_string().lines() {
+            writeln!(out, "  //{}", line);
+        }
+        writeln!(out, "  {} = {},", variant.name.cxx, variant.discriminant);
     }
     writeln!(out, "}};");
     writeln!(out, "#endif // {}", guard);
@@ -269,6 +378,12 @@
 
 fn check_enum<'a>(out: &mut OutFile<'a>, enm: &'a Enum) {
     out.set_namespace(&enm.name.namespace);
+    out.include.type_traits = true;
+    writeln!(
+        out,
+        "static_assert(::std::is_enum<{}>::value, \"expected enum\");",
+        enm.name.cxx,
+    );
     write!(out, "static_assert(sizeof({}) == sizeof(", enm.name.cxx);
     write_atom(out, enm.repr);
     writeln!(out, "), \"incorrect size\");");
@@ -278,12 +393,12 @@
         writeln!(
             out,
             ">({}::{}) == {}, \"disagrees with the value in #[cxx::bridge]\");",
-            enm.name.cxx, variant.ident, variant.discriminant,
+            enm.name.cxx, variant.name.cxx, variant.discriminant,
         );
     }
 }
 
-fn check_trivial_extern_type(out: &mut OutFile, id: &Pair) {
+fn check_trivial_extern_type(out: &mut OutFile, alias: &TypeAlias, reasons: &[TrivialReason]) {
     // NOTE: The following static assertion is just nice-to-have and not
     // necessary for soundness. That's because triviality is always declared by
     // the user in the form of an unsafe impl of cxx::ExternType:
@@ -317,24 +432,221 @@
     //    + struct rust::IsRelocatable<MyType> : std::true_type {};
     //
 
-    let id = id.to_fully_qualified();
+    let id = alias.name.to_fully_qualified();
     out.builtin.relocatable = true;
     writeln!(out, "static_assert(");
     writeln!(out, "    ::rust::IsRelocatable<{}>::value,", id);
     writeln!(
         out,
-        "    \"type {} marked as Trivial in Rust is not trivially move constructible and trivially destructible in C++\");",
-        id,
+        "    \"type {} should be trivially move constructible and trivially destructible in C++ to be used as {} in Rust\");",
+        id.trim_start_matches("::"),
+        trivial::as_what(&alias.name, reasons),
     );
 }
 
+fn write_struct_operator_decls<'a>(out: &mut OutFile<'a>, strct: &'a Struct) {
+    out.set_namespace(&strct.name.namespace);
+    out.begin_block(Block::ExternC);
+
+    if derive::contains(&strct.derives, Trait::PartialEq) {
+        let link_name = mangle::operator(&strct.name, "eq");
+        writeln!(
+            out,
+            "bool {}(const {1} &, const {1} &) noexcept;",
+            link_name, strct.name.cxx,
+        );
+
+        if !derive::contains(&strct.derives, Trait::Eq) {
+            let link_name = mangle::operator(&strct.name, "ne");
+            writeln!(
+                out,
+                "bool {}(const {1} &, const {1} &) noexcept;",
+                link_name, strct.name.cxx,
+            );
+        }
+    }
+
+    if derive::contains(&strct.derives, Trait::PartialOrd) {
+        let link_name = mangle::operator(&strct.name, "lt");
+        writeln!(
+            out,
+            "bool {}(const {1} &, const {1} &) noexcept;",
+            link_name, strct.name.cxx,
+        );
+
+        let link_name = mangle::operator(&strct.name, "le");
+        writeln!(
+            out,
+            "bool {}(const {1} &, const {1} &) noexcept;",
+            link_name, strct.name.cxx,
+        );
+
+        if !derive::contains(&strct.derives, Trait::Ord) {
+            let link_name = mangle::operator(&strct.name, "gt");
+            writeln!(
+                out,
+                "bool {}(const {1} &, const {1} &) noexcept;",
+                link_name, strct.name.cxx,
+            );
+
+            let link_name = mangle::operator(&strct.name, "ge");
+            writeln!(
+                out,
+                "bool {}(const {1} &, const {1} &) noexcept;",
+                link_name, strct.name.cxx,
+            );
+        }
+    }
+
+    if derive::contains(&strct.derives, Trait::Hash) {
+        out.include.cstddef = true;
+        let link_name = mangle::operator(&strct.name, "hash");
+        writeln!(
+            out,
+            "::std::size_t {}(const {} &) noexcept;",
+            link_name, strct.name.cxx,
+        );
+    }
+
+    out.end_block(Block::ExternC);
+}
+
+fn write_struct_operators<'a>(out: &mut OutFile<'a>, strct: &'a Struct) {
+    if out.header {
+        return;
+    }
+
+    out.set_namespace(&strct.name.namespace);
+
+    if derive::contains(&strct.derives, Trait::PartialEq) {
+        out.next_section();
+        writeln!(
+            out,
+            "bool {0}::operator==(const {0} &rhs) const noexcept {{",
+            strct.name.cxx,
+        );
+        let link_name = mangle::operator(&strct.name, "eq");
+        writeln!(out, "  return {}(*this, rhs);", link_name);
+        writeln!(out, "}}");
+
+        out.next_section();
+        writeln!(
+            out,
+            "bool {0}::operator!=(const {0} &rhs) const noexcept {{",
+            strct.name.cxx,
+        );
+        if derive::contains(&strct.derives, Trait::Eq) {
+            writeln!(out, "  return !(*this == rhs);");
+        } else {
+            let link_name = mangle::operator(&strct.name, "ne");
+            writeln!(out, "  return {}(*this, rhs);", link_name);
+        }
+        writeln!(out, "}}");
+    }
+
+    if derive::contains(&strct.derives, Trait::PartialOrd) {
+        out.next_section();
+        writeln!(
+            out,
+            "bool {0}::operator<(const {0} &rhs) const noexcept {{",
+            strct.name.cxx,
+        );
+        let link_name = mangle::operator(&strct.name, "lt");
+        writeln!(out, "  return {}(*this, rhs);", link_name);
+        writeln!(out, "}}");
+
+        out.next_section();
+        writeln!(
+            out,
+            "bool {0}::operator<=(const {0} &rhs) const noexcept {{",
+            strct.name.cxx,
+        );
+        let link_name = mangle::operator(&strct.name, "le");
+        writeln!(out, "  return {}(*this, rhs);", link_name);
+        writeln!(out, "}}");
+
+        out.next_section();
+        writeln!(
+            out,
+            "bool {0}::operator>(const {0} &rhs) const noexcept {{",
+            strct.name.cxx,
+        );
+        if derive::contains(&strct.derives, Trait::Ord) {
+            writeln!(out, "  return !(*this <= rhs);");
+        } else {
+            let link_name = mangle::operator(&strct.name, "gt");
+            writeln!(out, "  return {}(*this, rhs);", link_name);
+        }
+        writeln!(out, "}}");
+
+        out.next_section();
+        writeln!(
+            out,
+            "bool {0}::operator>=(const {0} &rhs) const noexcept {{",
+            strct.name.cxx,
+        );
+        if derive::contains(&strct.derives, Trait::Ord) {
+            writeln!(out, "  return !(*this < rhs);");
+        } else {
+            let link_name = mangle::operator(&strct.name, "ge");
+            writeln!(out, "  return {}(*this, rhs);", link_name);
+        }
+        writeln!(out, "}}");
+    }
+}
+
+fn write_opaque_type_layout_decls<'a>(out: &mut OutFile<'a>, ety: &'a ExternType) {
+    out.set_namespace(&ety.name.namespace);
+    out.begin_block(Block::ExternC);
+
+    let link_name = mangle::operator(&ety.name, "sizeof");
+    writeln!(out, "::std::size_t {}() noexcept;", link_name);
+
+    let link_name = mangle::operator(&ety.name, "alignof");
+    writeln!(out, "::std::size_t {}() noexcept;", link_name);
+
+    out.end_block(Block::ExternC);
+}
+
+fn write_opaque_type_layout<'a>(out: &mut OutFile<'a>, ety: &'a ExternType) {
+    if out.header {
+        return;
+    }
+
+    out.set_namespace(&ety.name.namespace);
+
+    out.next_section();
+    let link_name = mangle::operator(&ety.name, "sizeof");
+    writeln!(
+        out,
+        "::std::size_t {}::layout::size() noexcept {{",
+        ety.name.cxx,
+    );
+    writeln!(out, "  return {}();", link_name);
+    writeln!(out, "}}");
+
+    out.next_section();
+    let link_name = mangle::operator(&ety.name, "alignof");
+    writeln!(
+        out,
+        "::std::size_t {}::layout::align() noexcept {{",
+        ety.name.cxx,
+    );
+    writeln!(out, "  return {}();", link_name);
+    writeln!(out, "}}");
+}
+
+fn begin_function_definition(out: &mut OutFile) {
+    if let Some(annotation) = &out.opt.cxx_impl_annotations {
+        write!(out, "{} ", annotation);
+    }
+}
+
 fn write_cxx_function_shim<'a>(out: &mut OutFile<'a>, efn: &'a ExternFn) {
     out.next_section();
     out.set_namespace(&efn.name.namespace);
     out.begin_block(Block::ExternC);
-    if let Some(annotation) = &out.opt.cxx_impl_annotations {
-        write!(out, "{} ", annotation);
-    }
+    begin_function_definition(out);
     if efn.throws {
         out.builtin.ptr_len = true;
         write!(out, "::rust::repr::PtrLen ");
@@ -344,13 +656,13 @@
     let mangled = mangle::extern_fn(efn, out.types);
     write!(out, "{}(", mangled);
     if let Some(receiver) = &efn.receiver {
-        if receiver.mutability.is_none() {
+        if !receiver.mutable {
             write!(out, "const ");
         }
         write!(
             out,
             "{} &self",
-            out.types.resolve(&receiver.ty).to_fully_qualified(),
+            out.types.resolve(&receiver.ty).name.to_fully_qualified(),
         );
     }
     for (i, arg) in efn.args.iter().enumerate() {
@@ -380,7 +692,7 @@
         Some(receiver) => write!(
             out,
             "({}::*{}$)(",
-            out.types.resolve(&receiver.ty).to_fully_qualified(),
+            out.types.resolve(&receiver.ty).name.to_fully_qualified(),
             efn.name.rust,
         ),
     }
@@ -392,7 +704,7 @@
     }
     write!(out, ")");
     if let Some(receiver) = &efn.receiver {
-        if receiver.mutability.is_none() {
+        if !receiver.mutable {
             write!(out, " const");
         }
     }
@@ -402,7 +714,7 @@
         Some(receiver) => write!(
             out,
             "&{}::{}",
-            out.types.resolve(&receiver.ty).to_fully_qualified(),
+            out.types.resolve(&receiver.ty).name.to_fully_qualified(),
             efn.name.cxx,
         ),
     }
@@ -430,9 +742,11 @@
             out.builtin.rust_str_repr = true;
             write!(out, "::rust::impl<::rust::Str>::repr(");
         }
-        Some(Type::SliceRefU8(_)) if !indirect_return => {
+        Some(ty @ Type::SliceRef(_)) if !indirect_return => {
             out.builtin.rust_slice_repr = true;
-            write!(out, "::rust::impl<::rust::Slice<uint8_t>>::repr(")
+            write!(out, "::rust::impl<");
+            write_type(out, ty);
+            write!(out, ">::repr(");
         }
         _ => {}
     }
@@ -446,46 +760,33 @@
         }
         if let Type::RustBox(_) = &arg.ty {
             write_type(out, &arg.ty);
-            write!(out, "::from_raw({})", arg.ident);
+            write!(out, "::from_raw({})", arg.name.cxx);
         } else if let Type::UniquePtr(_) = &arg.ty {
             write_type(out, &arg.ty);
-            write!(out, "({})", arg.ident);
-        } else if let Type::Str(_) = arg.ty {
-            out.builtin.rust_str_new_unchecked = true;
-            write!(
-                out,
-                "::rust::impl<::rust::Str>::new_unchecked({})",
-                arg.ident,
-            );
+            write!(out, "({})", arg.name.cxx);
         } else if arg.ty == RustString {
             out.builtin.unsafe_bitcopy = true;
             write!(
                 out,
                 "::rust::String(::rust::unsafe_bitcopy, *{})",
-                arg.ident,
+                arg.name.cxx,
             );
         } else if let Type::RustVec(_) = arg.ty {
             out.builtin.unsafe_bitcopy = true;
             write_type(out, &arg.ty);
-            write!(out, "(::rust::unsafe_bitcopy, *{})", arg.ident);
-        } else if let Type::SliceRefU8(_) = arg.ty {
-            write!(
-                out,
-                "::rust::Slice<uint8_t>(static_cast<const uint8_t *>({0}.ptr), {0}.len)",
-                arg.ident,
-            );
+            write!(out, "(::rust::unsafe_bitcopy, *{})", arg.name.cxx);
         } else if out.types.needs_indirect_abi(&arg.ty) {
             out.include.utility = true;
-            write!(out, "::std::move(*{})", arg.ident);
+            write!(out, "::std::move(*{})", arg.name.cxx);
         } else {
-            write!(out, "{}", arg.ident);
+            write!(out, "{}", arg.name.cxx);
         }
     }
     write!(out, ")");
     match &efn.ret {
         Some(Type::RustBox(_)) => write!(out, ".into_raw()"),
         Some(Type::UniquePtr(_)) => write!(out, ".release()"),
-        Some(Type::Str(_)) | Some(Type::SliceRefU8(_)) if !indirect_return => write!(out, ")"),
+        Some(Type::Str(_)) | Some(Type::SliceRef(_)) if !indirect_return => write!(out, ")"),
         _ => {}
     }
     if indirect_return {
@@ -501,7 +802,7 @@
         writeln!(out, "        throw$.len = ::std::strlen(catch$);");
         writeln!(
             out,
-            "        throw$.ptr = ::cxxbridge05$exception(catch$, throw$.len);",
+            "        throw$.ptr = const_cast<char *>(::cxxbridge1$exception(catch$, throw$.len));",
         );
         writeln!(out, "      }});");
         writeln!(out, "  return throw$;");
@@ -509,19 +810,14 @@
     writeln!(out, "}}");
     for arg in &efn.args {
         if let Type::Fn(f) = &arg.ty {
-            let var = &arg.ident;
+            let var = &arg.name;
             write_function_pointer_trampoline(out, efn, var, f);
         }
     }
     out.end_block(Block::ExternC);
 }
 
-fn write_function_pointer_trampoline(
-    out: &mut OutFile,
-    efn: &ExternFn,
-    var: &Ident,
-    f: &Signature,
-) {
+fn write_function_pointer_trampoline(out: &mut OutFile, efn: &ExternFn, var: &Pair, f: &Signature) {
     let r_trampoline = mangle::r_trampoline(efn, var, out.types);
     let indirect_call = true;
     write_rust_function_decl_impl(out, &r_trampoline, f, indirect_call);
@@ -556,13 +852,13 @@
     write!(out, "{}(", link_name);
     let mut needs_comma = false;
     if let Some(receiver) = &sig.receiver {
-        if receiver.mutability.is_none() {
+        if !receiver.mutable {
             write!(out, "const ");
         }
         write!(
             out,
             "{} &self",
-            out.types.resolve(&receiver.ty).to_fully_qualified(),
+            out.types.resolve(&receiver.ty).name.to_fully_qualified(),
         );
         needs_comma = true;
     }
@@ -577,7 +873,13 @@
         if needs_comma {
             write!(out, ", ");
         }
-        write_return_type(out, &sig.ret);
+        match sig.ret.as_ref().unwrap() {
+            Type::Ref(ret) => {
+                write_pointee_type(out, &ret.inner, ret.mutable);
+                write!(out, " *");
+            }
+            ret => write_type_space(out, ret),
+        }
         write!(out, "*return$");
         needs_comma = true;
     }
@@ -597,7 +899,11 @@
     }
     let local_name = match &efn.sig.receiver {
         None => efn.name.cxx.to_string(),
-        Some(receiver) => format!("{}::{}", out.types.resolve(&receiver.ty).cxx, efn.name.cxx),
+        Some(receiver) => format!(
+            "{}::{}",
+            out.types.resolve(&receiver.ty).name.cxx,
+            efn.name.cxx,
+        ),
     };
     let invoke = mangle::extern_fn(efn, out.types);
     let indirect_call = false;
@@ -610,6 +916,7 @@
     sig: &Signature,
     indirect_call: bool,
 ) {
+    begin_function_definition(out);
     write_return_type(out, &sig.ret);
     write!(out, "{}(", local_name);
     for (i, arg) in sig.args.iter().enumerate() {
@@ -617,7 +924,7 @@
             write!(out, ", ");
         }
         write_type_space(out, &arg.ty);
-        write!(out, "{}", arg.ident);
+        write!(out, "{}", arg.name.cxx);
     }
     if indirect_call {
         if !sig.args.is_empty() {
@@ -627,7 +934,7 @@
     }
     write!(out, ")");
     if let Some(receiver) = &sig.receiver {
-        if receiver.mutability.is_none() {
+        if !receiver.mutable {
             write!(out, " const");
         }
     }
@@ -659,7 +966,7 @@
             out.builtin.manually_drop = true;
             write!(out, "  ::rust::ManuallyDrop<");
             write_type(out, &arg.ty);
-            writeln!(out, "> {}$(::std::move({0}));", arg.ident);
+            writeln!(out, "> {}$(::std::move({0}));", arg.name.cxx);
         }
     }
     write!(out, "  ");
@@ -667,7 +974,13 @@
     if indirect_return {
         out.builtin.maybe_uninit = true;
         write!(out, "::rust::MaybeUninit<");
-        write_type(out, sig.ret.as_ref().unwrap());
+        match sig.ret.as_ref().unwrap() {
+            Type::Ref(ret) => {
+                write_pointee_type(out, &ret.inner, ret.mutable);
+                write!(out, " *");
+            }
+            ret => write_type(out, ret),
+        }
         writeln!(out, "> return$;");
         write!(out, "  ");
     } else if let Some(ret) = &sig.ret {
@@ -686,9 +999,11 @@
                 out.builtin.rust_str_new_unchecked = true;
                 write!(out, "::rust::impl<::rust::Str>::new_unchecked(");
             }
-            Type::SliceRefU8(_) => {
+            Type::SliceRef(_) => {
                 out.builtin.rust_slice_new = true;
-                write!(out, "::rust::impl<::rust::Slice<uint8_t>>::slice(");
+                write!(out, "::rust::impl<");
+                write_type(out, ret);
+                write!(out, ">::slice(");
             }
             _ => {}
         }
@@ -707,23 +1022,13 @@
         if needs_comma {
             write!(out, ", ");
         }
-        match &arg.ty {
-            Type::Str(_) => {
-                out.builtin.rust_str_repr = true;
-                write!(out, "::rust::impl<::rust::Str>::repr(");
-            }
-            Type::SliceRefU8(_) => {
-                out.builtin.rust_slice_repr = true;
-                write!(out, "::rust::impl<::rust::Slice<uint8_t>>::repr(");
-            }
-            ty if out.types.needs_indirect_abi(ty) => write!(out, "&"),
-            _ => {}
+        if out.types.needs_indirect_abi(&arg.ty) {
+            write!(out, "&");
         }
-        write!(out, "{}", arg.ident);
+        write!(out, "{}", arg.name.cxx);
         match &arg.ty {
             Type::RustBox(_) => write!(out, ".into_raw()"),
             Type::UniquePtr(_) => write!(out, ".release()"),
-            Type::Str(_) | Type::SliceRefU8(_) => write!(out, ")"),
             ty if ty != RustString && out.types.needs_indirect_abi(ty) => write!(out, "$.value"),
             _ => {}
         }
@@ -745,8 +1050,7 @@
     write!(out, ")");
     if !indirect_return {
         if let Some(ret) = &sig.ret {
-            if let Type::RustBox(_) | Type::UniquePtr(_) | Type::Str(_) | Type::SliceRefU8(_) = ret
-            {
+            if let Type::RustBox(_) | Type::UniquePtr(_) | Type::Str(_) | Type::SliceRef(_) = ret {
                 write!(out, ")");
             }
         }
@@ -759,8 +1063,15 @@
         writeln!(out, "  }}");
     }
     if indirect_return {
-        out.include.utility = true;
-        writeln!(out, "  return ::std::move(return$.value);");
+        write!(out, "  return ");
+        match sig.ret.as_ref().unwrap() {
+            Type::Ref(_) => write!(out, "*return$.value"),
+            _ => {
+                out.include.utility = true;
+                write!(out, "::std::move(return$.value)");
+            }
+        }
+        writeln!(out, ";");
     }
     writeln!(out, "}}");
 }
@@ -785,7 +1096,7 @@
             write!(out, "*");
         }
         Type::Ref(ty) => {
-            if ty.mutability.is_none() {
+            if !ty.mutable {
                 write!(out, "const ");
             }
             write_type(out, &ty.inner);
@@ -799,7 +1110,7 @@
     write_indirect_return_type(out, ty);
     match ty {
         Type::RustBox(_) | Type::UniquePtr(_) | Type::Ref(_) => {}
-        Type::Str(_) | Type::SliceRefU8(_) => write!(out, " "),
+        Type::Str(_) | Type::SliceRef(_) => write!(out, " "),
         _ => write_space_after_type(out, ty),
     }
 }
@@ -811,15 +1122,15 @@
             write!(out, "*");
         }
         Some(Type::Ref(ty)) => {
-            if ty.mutability.is_none() {
+            if !ty.mutable {
                 write!(out, "const ");
             }
             write_type(out, &ty.inner);
             write!(out, " *");
         }
-        Some(Type::Str(_)) | Some(Type::SliceRefU8(_)) => {
-            out.builtin.ptr_len = true;
-            write!(out, "::rust::repr::PtrLen ");
+        Some(Type::Str(_)) | Some(Type::SliceRef(_)) => {
+            out.builtin.repr_fat = true;
+            write!(out, "::rust::repr::Fat ");
         }
         Some(ty) if out.types.needs_indirect_abi(ty) => write!(out, "void "),
         _ => write_return_type(out, ty),
@@ -832,23 +1143,23 @@
             write_type_space(out, &ty.inner);
             write!(out, "*");
         }
-        Type::Str(_) | Type::SliceRefU8(_) => {
-            out.builtin.ptr_len = true;
-            write!(out, "::rust::repr::PtrLen ");
-        }
         _ => write_type_space(out, &arg.ty),
     }
     if out.types.needs_indirect_abi(&arg.ty) {
         write!(out, "*");
     }
-    write!(out, "{}", arg.ident);
+    write!(out, "{}", arg.name.cxx);
 }
 
 fn write_type(out: &mut OutFile, ty: &Type) {
     match ty {
         Type::Ident(ident) => match Atom::from(&ident.rust) {
             Some(atom) => write_atom(out, atom),
-            None => write!(out, "{}", out.types.resolve(ident).to_fully_qualified()),
+            None => write!(
+                out,
+                "{}",
+                out.types.resolve(ident).name.to_fully_qualified(),
+            ),
         },
         Type::RustBox(ty) => {
             write!(out, "::rust::Box<");
@@ -865,30 +1176,42 @@
             write_type(out, &ptr.inner);
             write!(out, ">");
         }
+        Type::SharedPtr(ptr) => {
+            write!(out, "::std::shared_ptr<");
+            write_type(out, &ptr.inner);
+            write!(out, ">");
+        }
+        Type::WeakPtr(ptr) => {
+            write!(out, "::std::weak_ptr<");
+            write_type(out, &ptr.inner);
+            write!(out, ">");
+        }
         Type::CxxVector(ty) => {
             write!(out, "::std::vector<");
             write_type(out, &ty.inner);
             write!(out, ">");
         }
         Type::Ref(r) => {
-            if r.mutability.is_none() {
-                write!(out, "const ");
-            }
-            write_type(out, &r.inner);
+            write_pointee_type(out, &r.inner, r.mutable);
             write!(out, " &");
         }
-        Type::Slice(_) => {
-            // For now, only U8 slices are supported, which are covered separately below
-            unreachable!()
+        Type::Ptr(p) => {
+            write_pointee_type(out, &p.inner, p.mutable);
+            write!(out, " *");
         }
         Type::Str(_) => {
             write!(out, "::rust::Str");
         }
-        Type::SliceRefU8(_) => {
-            write!(out, "::rust::Slice<uint8_t>");
+        Type::SliceRef(slice) => {
+            write!(out, "::rust::Slice<");
+            if slice.mutability.is_none() {
+                write!(out, "const ");
+            }
+            write_type(out, &slice.inner);
+            write!(out, ">");
         }
         Type::Fn(f) => {
-            write!(out, "::rust::{}<", if f.throws { "TryFn" } else { "Fn" });
+            write!(out, "::rust::Fn<");
             match &f.ret {
                 Some(ret) => write_type(out, ret),
                 None => write!(out, "void"),
@@ -902,22 +1225,43 @@
             }
             write!(out, ")>");
         }
+        Type::Array(a) => {
+            write!(out, "::std::array<");
+            write_type(out, &a.inner);
+            write!(out, ", {}>", &a.len);
+        }
         Type::Void(_) => unreachable!(),
     }
 }
 
+// Write just the T type behind a &T or &mut T or *const T or *mut T.
+fn write_pointee_type(out: &mut OutFile, inner: &Type, mutable: bool) {
+    if let Type::Ptr(_) = inner {
+        write_type_space(out, inner);
+        if !mutable {
+            write!(out, "const");
+        }
+    } else {
+        if !mutable {
+            write!(out, "const ");
+        }
+        write_type(out, inner);
+    }
+}
+
 fn write_atom(out: &mut OutFile, atom: Atom) {
     match atom {
         Bool => write!(out, "bool"),
-        U8 => write!(out, "uint8_t"),
-        U16 => write!(out, "uint16_t"),
-        U32 => write!(out, "uint32_t"),
-        U64 => write!(out, "uint64_t"),
-        Usize => write!(out, "size_t"),
-        I8 => write!(out, "int8_t"),
-        I16 => write!(out, "int16_t"),
-        I32 => write!(out, "int32_t"),
-        I64 => write!(out, "int64_t"),
+        Char => write!(out, "char"),
+        U8 => write!(out, "::std::uint8_t"),
+        U16 => write!(out, "::std::uint16_t"),
+        U32 => write!(out, "::std::uint32_t"),
+        U64 => write!(out, "::std::uint64_t"),
+        Usize => write!(out, "::std::size_t"),
+        I8 => write!(out, "::std::int8_t"),
+        I16 => write!(out, "::std::int16_t"),
+        I32 => write!(out, "::std::int32_t"),
+        I64 => write!(out, "::std::int64_t"),
         Isize => write!(out, "::rust::isize"),
         F32 => write!(out, "float"),
         F64 => write!(out, "double"),
@@ -936,33 +1280,62 @@
         Type::Ident(_)
         | Type::RustBox(_)
         | Type::UniquePtr(_)
+        | Type::SharedPtr(_)
+        | Type::WeakPtr(_)
         | Type::Str(_)
         | Type::CxxVector(_)
         | Type::RustVec(_)
-        | Type::SliceRefU8(_)
-        | Type::Fn(_) => write!(out, " "),
-        Type::Ref(_) => {}
-        Type::Void(_) | Type::Slice(_) => unreachable!(),
+        | Type::SliceRef(_)
+        | Type::Fn(_)
+        | Type::Array(_) => write!(out, " "),
+        Type::Ref(_) | Type::Ptr(_) => {}
+        Type::Void(_) => unreachable!(),
     }
 }
 
-// Only called for legal referent types of unique_ptr and element types of
-// std::vector and Vec.
-fn to_typename(ty: &Type, types: &Types) -> String {
-    match ty {
-        Type::Ident(ident) => types.resolve(&ident).to_fully_qualified(),
-        Type::CxxVector(ptr) => format!("::std::vector<{}>", to_typename(&ptr.inner, types)),
-        _ => unreachable!(),
+#[derive(Copy, Clone)]
+enum UniquePtr<'a> {
+    Ident(&'a Ident),
+    CxxVector(&'a Ident),
+}
+
+trait ToTypename {
+    fn to_typename(&self, types: &Types) -> String;
+}
+
+impl ToTypename for Ident {
+    fn to_typename(&self, types: &Types) -> String {
+        types.resolve(self).name.to_fully_qualified()
     }
 }
 
-// Only called for legal referent types of unique_ptr and element types of
-// std::vector and Vec.
-fn to_mangled(ty: &Type, types: &Types) -> Symbol {
-    match ty {
-        Type::Ident(ident) => ident.to_symbol(types),
-        Type::CxxVector(ptr) => to_mangled(&ptr.inner, types).prefix_with("std$vector$"),
-        _ => unreachable!(),
+impl<'a> ToTypename for UniquePtr<'a> {
+    fn to_typename(&self, types: &Types) -> String {
+        match self {
+            UniquePtr::Ident(ident) => ident.to_typename(types),
+            UniquePtr::CxxVector(element) => {
+                format!("::std::vector<{}>", element.to_typename(types))
+            }
+        }
+    }
+}
+
+trait ToMangled {
+    fn to_mangled(&self, types: &Types) -> Symbol;
+}
+
+impl ToMangled for Ident {
+    fn to_mangled(&self, types: &Types) -> Symbol {
+        types.resolve(self).name.to_symbol()
+    }
+}
+
+impl<'a> ToMangled for UniquePtr<'a> {
+    fn to_mangled(&self, types: &Types) -> Symbol {
+        match self {
+            UniquePtr::Ident(ident) => ident.to_mangled(types),
+            UniquePtr::CxxVector(element) => element.to_mangled(types).prefix_with("std$vector$"),
+        }
     }
 }
 
@@ -974,233 +1347,252 @@
     out.next_section();
     out.set_namespace(Default::default());
     out.begin_block(Block::ExternC);
-    for ty in out.types {
-        if let Type::RustBox(ty) = ty {
-            if let Type::Ident(inner) = &ty.inner {
-                out.next_section();
-                write_rust_box_extern(out, &out.types.resolve(&inner));
-            }
-        } else if let Type::RustVec(ty) = ty {
-            if let Type::Ident(inner) = &ty.inner {
-                if Atom::from(&inner.rust).is_none() {
-                    out.next_section();
-                    write_rust_vec_extern(out, inner);
-                }
-            }
-        } else if let Type::UniquePtr(ptr) = ty {
-            if let Type::Ident(inner) = &ptr.inner {
-                if Atom::from(&inner.rust).is_none()
-                    && (!out.types.aliases.contains_key(&inner.rust)
-                        || out.types.explicit_impls.contains(ty))
-                {
-                    out.next_section();
-                    write_unique_ptr(out, inner);
-                }
-            }
-        } else if let Type::CxxVector(ptr) = ty {
-            if let Type::Ident(inner) = &ptr.inner {
-                if Atom::from(&inner.rust).is_none()
-                    && (!out.types.aliases.contains_key(&inner.rust)
-                        || out.types.explicit_impls.contains(ty))
-                {
-                    out.next_section();
-                    write_cxx_vector(out, ty, inner);
-                }
-            }
+    for impl_key in out.types.impls.keys() {
+        out.next_section();
+        match *impl_key {
+            ImplKey::RustBox(ident) => write_rust_box_extern(out, ident),
+            ImplKey::RustVec(ident) => write_rust_vec_extern(out, ident),
+            ImplKey::UniquePtr(ident) => write_unique_ptr(out, ident),
+            ImplKey::SharedPtr(ident) => write_shared_ptr(out, ident),
+            ImplKey::WeakPtr(ident) => write_weak_ptr(out, ident),
+            ImplKey::CxxVector(ident) => write_cxx_vector(out, ident),
         }
     }
     out.end_block(Block::ExternC);
 
     out.begin_block(Block::Namespace("rust"));
-    out.begin_block(Block::InlineNamespace("cxxbridge05"));
-    for ty in out.types {
-        if let Type::RustBox(ty) = ty {
-            if let Type::Ident(inner) = &ty.inner {
-                write_rust_box_impl(out, &out.types.resolve(&inner));
-            }
-        } else if let Type::RustVec(ty) = ty {
-            if let Type::Ident(inner) = &ty.inner {
-                if Atom::from(&inner.rust).is_none() {
-                    write_rust_vec_impl(out, inner);
-                }
-            }
+    out.begin_block(Block::InlineNamespace("cxxbridge1"));
+    for impl_key in out.types.impls.keys() {
+        match *impl_key {
+            ImplKey::RustBox(ident) => write_rust_box_impl(out, ident),
+            ImplKey::RustVec(ident) => write_rust_vec_impl(out, ident),
+            _ => {}
         }
     }
-    out.end_block(Block::InlineNamespace("cxxbridge05"));
+    out.end_block(Block::InlineNamespace("cxxbridge1"));
     out.end_block(Block::Namespace("rust"));
 }
 
-fn write_rust_box_extern(out: &mut OutFile, ident: &Pair) {
-    let inner = ident.to_fully_qualified();
-    let instance = ident.to_symbol();
+fn write_rust_box_extern(out: &mut OutFile, key: NamedImplKey) {
+    let resolve = out.types.resolve(&key);
+    let inner = resolve.name.to_fully_qualified();
+    let instance = resolve.name.to_symbol();
 
-    writeln!(out, "#ifndef CXXBRIDGE05_RUST_BOX_{}", instance);
-    writeln!(out, "#define CXXBRIDGE05_RUST_BOX_{}", instance);
     writeln!(
         out,
-        "void cxxbridge05$box${}$uninit(::rust::Box<{}> *ptr) noexcept;",
-        instance, inner,
-    );
-    writeln!(
-        out,
-        "void cxxbridge05$box${}$drop(::rust::Box<{}> *ptr) noexcept;",
-        instance, inner,
-    );
-    writeln!(out, "#endif // CXXBRIDGE05_RUST_BOX_{}", instance);
-}
-
-fn write_rust_vec_extern(out: &mut OutFile, element: &ResolvableName) {
-    let element = Type::Ident(element.clone());
-    let inner = to_typename(&element, out.types);
-    let instance = to_mangled(&element, out.types);
-
-    writeln!(out, "#ifndef CXXBRIDGE05_RUST_VEC_{}", instance);
-    writeln!(out, "#define CXXBRIDGE05_RUST_VEC_{}", instance);
-    writeln!(
-        out,
-        "void cxxbridge05$rust_vec${}$new(const ::rust::Vec<{}> *ptr) noexcept;",
-        instance, inner,
-    );
-    writeln!(
-        out,
-        "void cxxbridge05$rust_vec${}$drop(::rust::Vec<{}> *ptr) noexcept;",
-        instance, inner,
-    );
-    writeln!(
-        out,
-        "size_t cxxbridge05$rust_vec${}$len(const ::rust::Vec<{}> *ptr) noexcept;",
-        instance, inner,
-    );
-    writeln!(
-        out,
-        "const {} *cxxbridge05$rust_vec${}$data(const ::rust::Vec<{0}> *ptr) noexcept;",
+        "{} *cxxbridge1$box${}$alloc() noexcept;",
         inner, instance,
     );
     writeln!(
         out,
-        "void cxxbridge05$rust_vec${}$reserve_total(::rust::Vec<{}> *ptr, size_t cap) noexcept;",
+        "void cxxbridge1$box${}$dealloc({} *) noexcept;",
         instance, inner,
     );
     writeln!(
         out,
-        "void cxxbridge05$rust_vec${}$set_len(::rust::Vec<{}> *ptr, size_t len) noexcept;",
+        "void cxxbridge1$box${}$drop(::rust::Box<{}> *ptr) noexcept;",
         instance, inner,
     );
-    writeln!(
-        out,
-        "size_t cxxbridge05$rust_vec${}$stride() noexcept;",
-        instance,
-    );
-    writeln!(out, "#endif // CXXBRIDGE05_RUST_VEC_{}", instance);
 }
 
-fn write_rust_box_impl(out: &mut OutFile, ident: &Pair) {
-    let inner = ident.to_fully_qualified();
-    let instance = ident.to_symbol();
+fn write_rust_vec_extern(out: &mut OutFile, key: NamedImplKey) {
+    let element = key.rust;
+    let inner = element.to_typename(out.types);
+    let instance = element.to_mangled(out.types);
+
+    out.include.cstddef = true;
+
+    writeln!(
+        out,
+        "void cxxbridge1$rust_vec${}$new(const ::rust::Vec<{}> *ptr) noexcept;",
+        instance, inner,
+    );
+    writeln!(
+        out,
+        "void cxxbridge1$rust_vec${}$drop(::rust::Vec<{}> *ptr) noexcept;",
+        instance, inner,
+    );
+    writeln!(
+        out,
+        "::std::size_t cxxbridge1$rust_vec${}$len(const ::rust::Vec<{}> *ptr) noexcept;",
+        instance, inner,
+    );
+    writeln!(
+        out,
+        "::std::size_t cxxbridge1$rust_vec${}$capacity(const ::rust::Vec<{}> *ptr) noexcept;",
+        instance, inner,
+    );
+    writeln!(
+        out,
+        "const {} *cxxbridge1$rust_vec${}$data(const ::rust::Vec<{0}> *ptr) noexcept;",
+        inner, instance,
+    );
+    writeln!(
+        out,
+        "void cxxbridge1$rust_vec${}$reserve_total(::rust::Vec<{}> *ptr, ::std::size_t cap) noexcept;",
+        instance, inner,
+    );
+    writeln!(
+        out,
+        "void cxxbridge1$rust_vec${}$set_len(::rust::Vec<{}> *ptr, ::std::size_t len) noexcept;",
+        instance, inner,
+    );
+}
+
+fn write_rust_box_impl(out: &mut OutFile, key: NamedImplKey) {
+    let resolve = out.types.resolve(&key);
+    let inner = resolve.name.to_fully_qualified();
+    let instance = resolve.name.to_symbol();
 
     writeln!(out, "template <>");
-    writeln!(out, "void Box<{}>::uninit() noexcept {{", inner);
-    writeln!(out, "  cxxbridge05$box${}$uninit(this);", instance);
+    begin_function_definition(out);
+    writeln!(
+        out,
+        "{} *Box<{}>::allocation::alloc() noexcept {{",
+        inner, inner,
+    );
+    writeln!(out, "  return cxxbridge1$box${}$alloc();", instance);
     writeln!(out, "}}");
 
     writeln!(out, "template <>");
+    begin_function_definition(out);
+    writeln!(
+        out,
+        "void Box<{}>::allocation::dealloc({} *ptr) noexcept {{",
+        inner, inner,
+    );
+    writeln!(out, "  cxxbridge1$box${}$dealloc(ptr);", instance);
+    writeln!(out, "}}");
+
+    writeln!(out, "template <>");
+    begin_function_definition(out);
     writeln!(out, "void Box<{}>::drop() noexcept {{", inner);
-    writeln!(out, "  cxxbridge05$box${}$drop(this);", instance);
+    writeln!(out, "  cxxbridge1$box${}$drop(this);", instance);
     writeln!(out, "}}");
 }
 
-fn write_rust_vec_impl(out: &mut OutFile, element: &ResolvableName) {
-    let element = Type::Ident(element.clone());
-    let inner = to_typename(&element, out.types);
-    let instance = to_mangled(&element, out.types);
+fn write_rust_vec_impl(out: &mut OutFile, key: NamedImplKey) {
+    let element = key.rust;
+    let inner = element.to_typename(out.types);
+    let instance = element.to_mangled(out.types);
+
+    out.include.cstddef = true;
 
     writeln!(out, "template <>");
+    begin_function_definition(out);
     writeln!(out, "Vec<{}>::Vec() noexcept {{", inner);
-    writeln!(out, "  cxxbridge05$rust_vec${}$new(this);", instance);
+    writeln!(out, "  cxxbridge1$rust_vec${}$new(this);", instance);
     writeln!(out, "}}");
 
     writeln!(out, "template <>");
+    begin_function_definition(out);
     writeln!(out, "void Vec<{}>::drop() noexcept {{", inner);
+    writeln!(out, "  return cxxbridge1$rust_vec${}$drop(this);", instance);
+    writeln!(out, "}}");
+
+    writeln!(out, "template <>");
+    begin_function_definition(out);
     writeln!(
         out,
-        "  return cxxbridge05$rust_vec${}$drop(this);",
-        instance,
+        "::std::size_t Vec<{}>::size() const noexcept {{",
+        inner,
     );
+    writeln!(out, "  return cxxbridge1$rust_vec${}$len(this);", instance);
     writeln!(out, "}}");
 
     writeln!(out, "template <>");
-    writeln!(out, "size_t Vec<{}>::size() const noexcept {{", inner);
-    writeln!(out, "  return cxxbridge05$rust_vec${}$len(this);", instance);
-    writeln!(out, "}}");
-
-    writeln!(out, "template <>");
-    writeln!(out, "const {} *Vec<{0}>::data() const noexcept {{", inner);
+    begin_function_definition(out);
     writeln!(
         out,
-        "  return cxxbridge05$rust_vec${}$data(this);",
-        instance,
-    );
-    writeln!(out, "}}");
-
-    writeln!(out, "template <>");
-    writeln!(
-        out,
-        "void Vec<{}>::reserve_total(size_t cap) noexcept {{",
+        "::std::size_t Vec<{}>::capacity() const noexcept {{",
         inner,
     );
     writeln!(
         out,
-        "  return cxxbridge05$rust_vec${}$reserve_total(this, cap);",
+        "  return cxxbridge1$rust_vec${}$capacity(this);",
         instance,
     );
     writeln!(out, "}}");
 
     writeln!(out, "template <>");
-    writeln!(out, "void Vec<{}>::set_len(size_t len) noexcept {{", inner);
+    begin_function_definition(out);
+    writeln!(out, "const {} *Vec<{0}>::data() const noexcept {{", inner);
+    writeln!(out, "  return cxxbridge1$rust_vec${}$data(this);", instance);
+    writeln!(out, "}}");
+
+    writeln!(out, "template <>");
+    begin_function_definition(out);
     writeln!(
         out,
-        "  return cxxbridge05$rust_vec${}$set_len(this, len);",
+        "void Vec<{}>::reserve_total(::std::size_t cap) noexcept {{",
+        inner,
+    );
+    writeln!(
+        out,
+        "  return cxxbridge1$rust_vec${}$reserve_total(this, cap);",
         instance,
     );
     writeln!(out, "}}");
 
     writeln!(out, "template <>");
-    writeln!(out, "size_t Vec<{}>::stride() noexcept {{", inner);
-    writeln!(out, "  return cxxbridge05$rust_vec${}$stride();", instance);
+    begin_function_definition(out);
+    writeln!(
+        out,
+        "void Vec<{}>::set_len(::std::size_t len) noexcept {{",
+        inner,
+    );
+    writeln!(
+        out,
+        "  return cxxbridge1$rust_vec${}$set_len(this, len);",
+        instance,
+    );
     writeln!(out, "}}");
 }
 
-fn write_unique_ptr(out: &mut OutFile, ident: &ResolvableName) {
-    let ty = Type::Ident(ident.clone());
-    let instance = to_mangled(&ty, out.types);
-
-    writeln!(out, "#ifndef CXXBRIDGE05_UNIQUE_PTR_{}", instance);
-    writeln!(out, "#define CXXBRIDGE05_UNIQUE_PTR_{}", instance);
-
-    write_unique_ptr_common(out, &ty);
-
-    writeln!(out, "#endif // CXXBRIDGE05_UNIQUE_PTR_{}", instance);
+fn write_unique_ptr(out: &mut OutFile, key: NamedImplKey) {
+    let ty = UniquePtr::Ident(key.rust);
+    write_unique_ptr_common(out, ty);
 }
 
 // Shared by UniquePtr<T> and UniquePtr<CxxVector<T>>.
-fn write_unique_ptr_common(out: &mut OutFile, ty: &Type) {
+fn write_unique_ptr_common(out: &mut OutFile, ty: UniquePtr) {
     out.include.new = true;
     out.include.utility = true;
-    let inner = to_typename(ty, out.types);
-    let instance = to_mangled(ty, out.types);
+    let inner = ty.to_typename(out.types);
+    let instance = ty.to_mangled(out.types);
 
     let can_construct_from_value = match ty {
         // Some aliases are to opaque types; some are to trivial types. We can't
         // know at code generation time, so we generate both C++ and Rust side
         // bindings for a "new" method anyway. But the Rust code can't be called
         // for Opaque types because the 'new' method is not implemented.
-        Type::Ident(ident) => {
-            out.types.structs.contains_key(&ident.rust)
-                || out.types.aliases.contains_key(&ident.rust)
+        UniquePtr::Ident(ident) => {
+            out.types.structs.contains_key(ident)
+                || out.types.enums.contains_key(ident)
+                || out.types.aliases.contains_key(ident)
         }
-        _ => false,
+        UniquePtr::CxxVector(_) => false,
     };
 
+    let conditional_delete = match ty {
+        UniquePtr::Ident(ident) => {
+            !out.types.structs.contains_key(ident) && !out.types.enums.contains_key(ident)
+        }
+        UniquePtr::CxxVector(_) => false,
+    };
+
+    if conditional_delete {
+        out.builtin.is_complete = true;
+        let definition = match ty {
+            UniquePtr::Ident(ty) => &out.types.resolve(ty).name.cxx,
+            UniquePtr::CxxVector(_) => unreachable!(),
+        };
+        writeln!(
+            out,
+            "static_assert(::rust::detail::is_complete<{}>::value, \"definition of {} is required\");",
+            inner, definition,
+        );
+    }
     writeln!(
         out,
         "static_assert(sizeof(::std::unique_ptr<{}>) == sizeof(void *), \"\");",
@@ -1213,77 +1605,220 @@
     );
     writeln!(
         out,
-        "void cxxbridge05$unique_ptr${}$null(::std::unique_ptr<{}> *ptr) noexcept {{",
+        "void cxxbridge1$unique_ptr${}$null(::std::unique_ptr<{}> *ptr) noexcept {{",
         instance, inner,
     );
-    writeln!(out, "  new (ptr) ::std::unique_ptr<{}>();", inner);
+    writeln!(out, "  ::new (ptr) ::std::unique_ptr<{}>();", inner);
     writeln!(out, "}}");
     if can_construct_from_value {
+        out.builtin.maybe_uninit = true;
         writeln!(
             out,
-            "void cxxbridge05$unique_ptr${}$new(::std::unique_ptr<{}> *ptr, {} *value) noexcept {{",
-            instance, inner, inner,
+            "{} *cxxbridge1$unique_ptr${}$uninit(::std::unique_ptr<{}> *ptr) noexcept {{",
+            inner, instance, inner,
         );
         writeln!(
             out,
-            "  new (ptr) ::std::unique_ptr<{}>(new {}(::std::move(*value)));",
-            inner, inner,
+            "  {} *uninit = reinterpret_cast<{} *>(new ::rust::MaybeUninit<{}>);",
+            inner, inner, inner,
         );
+        writeln!(out, "  ::new (ptr) ::std::unique_ptr<{}>(uninit);", inner);
+        writeln!(out, "  return uninit;");
         writeln!(out, "}}");
     }
     writeln!(
         out,
-        "void cxxbridge05$unique_ptr${}$raw(::std::unique_ptr<{}> *ptr, {} *raw) noexcept {{",
+        "void cxxbridge1$unique_ptr${}$raw(::std::unique_ptr<{}> *ptr, {} *raw) noexcept {{",
         instance, inner, inner,
     );
-    writeln!(out, "  new (ptr) ::std::unique_ptr<{}>(raw);", inner);
+    writeln!(out, "  ::new (ptr) ::std::unique_ptr<{}>(raw);", inner);
     writeln!(out, "}}");
     writeln!(
         out,
-        "const {} *cxxbridge05$unique_ptr${}$get(const ::std::unique_ptr<{}>& ptr) noexcept {{",
+        "const {} *cxxbridge1$unique_ptr${}$get(const ::std::unique_ptr<{}>& ptr) noexcept {{",
         inner, instance, inner,
     );
     writeln!(out, "  return ptr.get();");
     writeln!(out, "}}");
     writeln!(
         out,
-        "{} *cxxbridge05$unique_ptr${}$release(::std::unique_ptr<{}>& ptr) noexcept {{",
+        "{} *cxxbridge1$unique_ptr${}$release(::std::unique_ptr<{}>& ptr) noexcept {{",
         inner, instance, inner,
     );
     writeln!(out, "  return ptr.release();");
     writeln!(out, "}}");
     writeln!(
         out,
-        "void cxxbridge05$unique_ptr${}$drop(::std::unique_ptr<{}> *ptr) noexcept {{",
+        "void cxxbridge1$unique_ptr${}$drop(::std::unique_ptr<{}> *ptr) noexcept {{",
         instance, inner,
     );
-    writeln!(out, "  ptr->~unique_ptr();");
+    if conditional_delete {
+        out.builtin.deleter_if = true;
+        writeln!(
+            out,
+            "  ::rust::deleter_if<::rust::detail::is_complete<{}>::value>{{}}(ptr);",
+            inner,
+        );
+    } else {
+        writeln!(out, "  ptr->~unique_ptr();");
+    }
     writeln!(out, "}}");
 }
 
-fn write_cxx_vector(out: &mut OutFile, vector_ty: &Type, element: &ResolvableName) {
-    let element = Type::Ident(element.clone());
-    let inner = to_typename(&element, out.types);
-    let instance = to_mangled(&element, out.types);
+fn write_shared_ptr(out: &mut OutFile, key: NamedImplKey) {
+    let ident = key.rust;
+    let resolve = out.types.resolve(ident);
+    let inner = resolve.name.to_fully_qualified();
+    let instance = resolve.name.to_symbol();
 
-    writeln!(out, "#ifndef CXXBRIDGE05_VECTOR_{}", instance);
-    writeln!(out, "#define CXXBRIDGE05_VECTOR_{}", instance);
+    out.include.new = true;
+    out.include.utility = true;
+
+    // Some aliases are to opaque types; some are to trivial types. We can't
+    // know at code generation time, so we generate both C++ and Rust side
+    // bindings for a "new" method anyway. But the Rust code can't be called for
+    // Opaque types because the 'new' method is not implemented.
+    let can_construct_from_value = out.types.structs.contains_key(ident)
+        || out.types.enums.contains_key(ident)
+        || out.types.aliases.contains_key(ident);
+
     writeln!(
         out,
-        "size_t cxxbridge05$std$vector${}$size(const ::std::vector<{}> &s) noexcept {{",
+        "static_assert(sizeof(::std::shared_ptr<{}>) == 2 * sizeof(void *), \"\");",
+        inner,
+    );
+    writeln!(
+        out,
+        "static_assert(alignof(::std::shared_ptr<{}>) == alignof(void *), \"\");",
+        inner,
+    );
+    writeln!(
+        out,
+        "void cxxbridge1$shared_ptr${}$null(::std::shared_ptr<{}> *ptr) noexcept {{",
+        instance, inner,
+    );
+    writeln!(out, "  ::new (ptr) ::std::shared_ptr<{}>();", inner);
+    writeln!(out, "}}");
+    if can_construct_from_value {
+        out.builtin.maybe_uninit = true;
+        writeln!(
+            out,
+            "{} *cxxbridge1$shared_ptr${}$uninit(::std::shared_ptr<{}> *ptr) noexcept {{",
+            inner, instance, inner,
+        );
+        writeln!(
+            out,
+            "  {} *uninit = reinterpret_cast<{} *>(new ::rust::MaybeUninit<{}>);",
+            inner, inner, inner,
+        );
+        writeln!(out, "  ::new (ptr) ::std::shared_ptr<{}>(uninit);", inner);
+        writeln!(out, "  return uninit;");
+        writeln!(out, "}}");
+    }
+    writeln!(
+        out,
+        "void cxxbridge1$shared_ptr${}$clone(const ::std::shared_ptr<{}>& self, ::std::shared_ptr<{}> *ptr) noexcept {{",
+        instance, inner, inner,
+    );
+    writeln!(out, "  ::new (ptr) ::std::shared_ptr<{}>(self);", inner);
+    writeln!(out, "}}");
+    writeln!(
+        out,
+        "const {} *cxxbridge1$shared_ptr${}$get(const ::std::shared_ptr<{}>& self) noexcept {{",
+        inner, instance, inner,
+    );
+    writeln!(out, "  return self.get();");
+    writeln!(out, "}}");
+    writeln!(
+        out,
+        "void cxxbridge1$shared_ptr${}$drop(::std::shared_ptr<{}> *self) noexcept {{",
+        instance, inner,
+    );
+    writeln!(out, "  self->~shared_ptr();");
+    writeln!(out, "}}");
+}
+
+fn write_weak_ptr(out: &mut OutFile, key: NamedImplKey) {
+    let resolve = out.types.resolve(&key);
+    let inner = resolve.name.to_fully_qualified();
+    let instance = resolve.name.to_symbol();
+
+    out.include.new = true;
+    out.include.utility = true;
+
+    writeln!(
+        out,
+        "static_assert(sizeof(::std::weak_ptr<{}>) == 2 * sizeof(void *), \"\");",
+        inner,
+    );
+    writeln!(
+        out,
+        "static_assert(alignof(::std::weak_ptr<{}>) == alignof(void *), \"\");",
+        inner,
+    );
+    writeln!(
+        out,
+        "void cxxbridge1$weak_ptr${}$null(::std::weak_ptr<{}> *ptr) noexcept {{",
+        instance, inner,
+    );
+    writeln!(out, "  ::new (ptr) ::std::weak_ptr<{}>();", inner);
+    writeln!(out, "}}");
+    writeln!(
+        out,
+        "void cxxbridge1$weak_ptr${}$clone(const ::std::weak_ptr<{}>& self, ::std::weak_ptr<{}> *ptr) noexcept {{",
+        instance, inner, inner,
+    );
+    writeln!(out, "  ::new (ptr) ::std::weak_ptr<{}>(self);", inner);
+    writeln!(out, "}}");
+    writeln!(
+        out,
+        "void cxxbridge1$weak_ptr${}$downgrade(const ::std::shared_ptr<{}>& shared, ::std::weak_ptr<{}> *weak) noexcept {{",
+        instance, inner, inner,
+    );
+    writeln!(out, "  ::new (weak) ::std::weak_ptr<{}>(shared);", inner);
+    writeln!(out, "}}");
+    writeln!(
+        out,
+        "void cxxbridge1$weak_ptr${}$upgrade(const ::std::weak_ptr<{}>& weak, ::std::shared_ptr<{}> *shared) noexcept {{",
+        instance, inner, inner,
+    );
+    writeln!(
+        out,
+        "  ::new (shared) ::std::shared_ptr<{}>(weak.lock());",
+        inner,
+    );
+    writeln!(out, "}}");
+    writeln!(
+        out,
+        "void cxxbridge1$weak_ptr${}$drop(::std::weak_ptr<{}> *self) noexcept {{",
+        instance, inner,
+    );
+    writeln!(out, "  self->~weak_ptr();");
+    writeln!(out, "}}");
+}
+
+fn write_cxx_vector(out: &mut OutFile, key: NamedImplKey) {
+    let element = key.rust;
+    let inner = element.to_typename(out.types);
+    let instance = element.to_mangled(out.types);
+
+    out.include.cstddef = true;
+
+    writeln!(
+        out,
+        "::std::size_t cxxbridge1$std$vector${}$size(const ::std::vector<{}> &s) noexcept {{",
         instance, inner,
     );
     writeln!(out, "  return s.size();");
     writeln!(out, "}}");
     writeln!(
         out,
-        "const {} *cxxbridge05$std$vector${}$get_unchecked(const ::std::vector<{}> &s, size_t pos) noexcept {{",
+        "{} *cxxbridge1$std$vector${}$get_unchecked(::std::vector<{}> *s, ::std::size_t pos) noexcept {{",
         inner, instance, inner,
     );
-    writeln!(out, "  return &s[pos];");
+    writeln!(out, "  return &(*s)[pos];");
     writeln!(out, "}}");
 
-    write_unique_ptr_common(out, vector_ty);
-
-    writeln!(out, "#endif // CXXBRIDGE05_VECTOR_{}", instance);
+    out.include.memory = true;
+    write_unique_ptr_common(out, UniquePtr::CxxVector(element));
 }
diff --git a/include/cxx.h b/include/cxx.h
index 7dfbbca..cdc63fb 100644
--- a/include/cxx.h
+++ b/include/cxx.h
@@ -1,9 +1,13 @@
 #pragma once
+#include <algorithm>
 #include <array>
+#include <cassert>
 #include <cstddef>
 #include <cstdint>
 #include <exception>
+#include <initializer_list>
 #include <iosfwd>
+#include <iterator>
 #include <new>
 #include <stdexcept>
 #include <string>
@@ -12,10 +16,12 @@
 #include <vector>
 #if defined(_WIN32)
 #include <basetsd.h>
+#else
+#include <sys/types.h>
 #endif
 
 namespace rust {
-inline namespace cxxbridge05 {
+inline namespace cxxbridge1 {
 
 struct unsafe_bitcopy_t;
 
@@ -24,8 +30,9 @@
 class impl;
 }
 
-#ifndef CXXBRIDGE05_RUST_STRING
-#define CXXBRIDGE05_RUST_STRING
+#ifndef CXXBRIDGE1_RUST_STRING
+#define CXXBRIDGE1_RUST_STRING
+// https://cxx.rs/binding/string.html
 class String final {
 public:
   String() noexcept;
@@ -35,102 +42,213 @@
 
   String(const std::string &);
   String(const char *);
-  String(const char *, size_t);
+  String(const char *, std::size_t);
 
-  String &operator=(const String &) noexcept;
-  String &operator=(String &&) noexcept;
+  String &operator=(const String &) &noexcept;
+  String &operator=(String &&) &noexcept;
 
   explicit operator std::string() const;
 
   // Note: no null terminator.
   const char *data() const noexcept;
-  size_t size() const noexcept;
-  size_t length() const noexcept;
+  std::size_t size() const noexcept;
+  std::size_t length() const noexcept;
+
+  const char *c_str() noexcept;
+
+  using iterator = char *;
+  iterator begin() noexcept;
+  iterator end() noexcept;
+
+  using const_iterator = const char *;
+  const_iterator begin() const noexcept;
+  const_iterator end() const noexcept;
+  const_iterator cbegin() const noexcept;
+  const_iterator cend() const noexcept;
+
+  bool operator==(const String &) const noexcept;
+  bool operator!=(const String &) const noexcept;
+  bool operator<(const String &) const noexcept;
+  bool operator<=(const String &) const noexcept;
+  bool operator>(const String &) const noexcept;
+  bool operator>=(const String &) const noexcept;
+
+  void swap(String &) noexcept;
 
   // Internal API only intended for the cxxbridge code generator.
   String(unsafe_bitcopy_t, const String &) noexcept;
 
 private:
-  // Size and alignment statically verified by rust_string.rs.
-  std::array<uintptr_t, 3> repr;
-};
-#endif // CXXBRIDGE05_RUST_STRING
+  friend void swap(String &lhs, String &rhs) noexcept { lhs.swap(rhs); }
 
-#ifndef CXXBRIDGE05_RUST_STR
+  // 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 *, size_t);
-  Str(std::string &&) = delete;
+  Str(const char *, std::size_t);
 
-  Str &operator=(const Str &) noexcept = default;
+  Str &operator=(const Str &) &noexcept = default;
 
   explicit operator std::string() const;
 
   // Note: no null terminator.
   const char *data() const noexcept;
-  size_t size() const noexcept;
-  size_t length() const noexcept;
+  std::size_t size() const noexcept;
+  std::size_t length() const noexcept;
 
   // Important in order for System V ABI to pass in registers.
   Str(const Str &) noexcept = default;
   ~Str() noexcept = default;
 
+  using iterator = const char *;
+  using const_iterator = const char *;
+  const_iterator begin() const noexcept;
+  const_iterator end() const noexcept;
+  const_iterator cbegin() const noexcept;
+  const_iterator cend() const noexcept;
+
+  bool operator==(const Str &) const noexcept;
+  bool operator!=(const Str &) const noexcept;
+  bool operator<(const Str &) const noexcept;
+  bool operator<=(const Str &) const noexcept;
+  bool operator>(const Str &) const noexcept;
+  bool operator>=(const Str &) const noexcept;
+
+  void swap(Str &) noexcept;
+
 private:
+  class uninit;
+  Str(uninit) noexcept;
   friend impl<Str>;
-  // Not necessarily ABI compatible with &str. Codegen will translate to
-  // cxx::rust_str::RustStr which matches this layout.
-  const char *ptr;
-  size_t len;
+
+  std::array<std::uintptr_t, 2> repr;
 };
-#endif // CXXBRIDGE05_RUST_STR
+#endif // CXXBRIDGE1_RUST_STR
 
-#ifndef CXXBRIDGE05_RUST_SLICE
+#ifndef CXXBRIDGE1_RUST_SLICE
+namespace detail {
+template <bool>
+struct copy_assignable_if {};
+
+template <>
+struct copy_assignable_if<false> {
+  copy_assignable_if() noexcept = default;
+  copy_assignable_if(const copy_assignable_if &) noexcept = default;
+  copy_assignable_if &operator=(const copy_assignable_if &) &noexcept = delete;
+  copy_assignable_if &operator=(copy_assignable_if &&) &noexcept = default;
+};
+} // namespace detail
+
+// https://cxx.rs/binding/slice.html
 template <typename T>
-class Slice final {
+class Slice final
+    : private detail::copy_assignable_if<std::is_const<T>::value> {
 public:
+  using value_type = T;
+
   Slice() noexcept;
-  Slice(const T *, size_t count) noexcept;
+  Slice(T *, std::size_t count) noexcept;
 
-  Slice &operator=(const Slice<T> &) noexcept = default;
+  Slice &operator=(const Slice<T> &) &noexcept = default;
+  Slice &operator=(Slice<T> &&) &noexcept = default;
 
-  const T *data() const noexcept;
-  size_t size() const noexcept;
-  size_t length() const noexcept;
+  T *data() const noexcept;
+  std::size_t size() const noexcept;
+  std::size_t length() const noexcept;
+  bool empty() const noexcept;
+
+  T &operator[](std::size_t n) const noexcept;
+  T &at(std::size_t n) const;
+  T &front() const noexcept;
+  T &back() const noexcept;
 
   // Important in order for System V ABI to pass in registers.
   Slice(const Slice<T> &) noexcept = default;
   ~Slice() noexcept = default;
 
-private:
-  friend impl<Slice>;
-  // Not necessarily ABI compatible with &[T]. Codegen will translate to
-  // cxx::rust_sliceu8::RustSliceU8 which matches this layout.
-  const T *ptr;
-  size_t len;
-};
-#endif // CXXBRIDGE05_RUST_SLICE
+  class iterator;
+  iterator begin() const noexcept;
+  iterator end() const noexcept;
 
-#ifndef CXXBRIDGE05_RUST_BOX
+  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;
+};
+
+template <typename T>
+class Slice<T>::iterator final {
+public:
+  using iterator_category = std::random_access_iterator_tag;
+  using value_type = T;
+  using difference_type = std::ptrdiff_t;
+  using pointer = typename std::add_pointer<T>::type;
+  using reference = typename std::add_lvalue_reference<T>::type;
+
+  reference operator*() const noexcept;
+  pointer operator->() const noexcept;
+  reference operator[](difference_type) const noexcept;
+
+  iterator &operator++() noexcept;
+  iterator operator++(int) noexcept;
+  iterator &operator--() noexcept;
+  iterator operator--(int) noexcept;
+
+  iterator &operator+=(difference_type) noexcept;
+  iterator &operator-=(difference_type) noexcept;
+  iterator operator+(difference_type) const noexcept;
+  iterator operator-(difference_type) const noexcept;
+  difference_type operator-(const iterator &) const noexcept;
+
+  bool operator==(const iterator &) const noexcept;
+  bool operator!=(const iterator &) const noexcept;
+  bool operator<(const iterator &) const noexcept;
+  bool operator<=(const iterator &) const noexcept;
+  bool operator>(const iterator &) const noexcept;
+  bool operator>=(const iterator &) const noexcept;
+
+private:
+  friend class Slice;
+  void *pos;
+  std::size_t stride;
+};
+#endif // CXXBRIDGE1_RUST_SLICE
+
+#ifndef CXXBRIDGE1_RUST_BOX
+// https://cxx.rs/binding/box.html
 template <typename T>
 class Box final {
 public:
-  using value_type = T;
+  using element_type = T;
   using const_pointer =
       typename std::add_pointer<typename std::add_const<T>::type>::type;
   using pointer = typename std::add_pointer<T>::type;
 
-  Box(const Box &);
+  Box() = delete;
   Box(Box &&) noexcept;
   ~Box() noexcept;
 
   explicit Box(const T &);
   explicit Box(T &&);
 
-  Box &operator=(const Box &);
-  Box &operator=(Box &&) noexcept;
+  Box &operator=(Box &&) &noexcept;
 
   const T *operator->() const noexcept;
   const T &operator*() const noexcept;
@@ -140,118 +258,121 @@
   template <typename... Fields>
   static Box in_place(Fields &&...);
 
+  void swap(Box &) noexcept;
+
   // Important: requires that `raw` came from an into_raw call. Do not pass a
   // pointer from `new` or any other source.
   static Box from_raw(T *) noexcept;
 
   T *into_raw() noexcept;
 
+  /* Deprecated */ using value_type = element_type;
+
 private:
-  Box() noexcept;
-  void uninit() noexcept;
+  class uninit;
+  class allocation;
+  Box(uninit) noexcept;
   void drop() noexcept;
+
+  friend void swap(Box &lhs, Box &rhs) noexcept { lhs.swap(rhs); }
+
   T *ptr;
 };
-#endif // CXXBRIDGE05_RUST_BOX
+#endif // CXXBRIDGE1_RUST_BOX
 
-#ifndef CXXBRIDGE05_RUST_VEC
+#ifndef CXXBRIDGE1_RUST_VEC
+// https://cxx.rs/binding/vec.html
 template <typename T>
 class Vec final {
 public:
   using value_type = T;
 
   Vec() noexcept;
+  Vec(std::initializer_list<T>);
+  Vec(const Vec &);
   Vec(Vec &&) noexcept;
   ~Vec() noexcept;
 
-  Vec &operator=(Vec &&) noexcept;
+  Vec &operator=(Vec &&) &noexcept;
+  Vec &operator=(const Vec &) &;
 
-  size_t size() const noexcept;
+  std::size_t size() const noexcept;
   bool empty() const noexcept;
   const T *data() const noexcept;
   T *data() noexcept;
+  std::size_t capacity() const noexcept;
 
-  const T &operator[](size_t n) const noexcept;
-  const T &at(size_t n) const;
+  const T &operator[](std::size_t n) const noexcept;
+  const T &at(std::size_t n) const;
+  const T &front() const noexcept;
+  const T &back() const noexcept;
 
-  const T &front() const;
-  const T &back() const;
+  T &operator[](std::size_t n) noexcept;
+  T &at(std::size_t n);
+  T &front() noexcept;
+  T &back() noexcept;
 
-  void reserve(size_t new_cap);
+  void reserve(std::size_t new_cap);
   void push_back(const T &value);
   void push_back(T &&value);
-  template <class... Args>
-  void emplace_back(Args &&... args);
+  template <typename... Args>
+  void emplace_back(Args &&...args);
 
-  class const_iterator final {
-  public:
-    using difference_type = ptrdiff_t;
-    using value_type = typename std::add_const<T>::type;
-    using pointer =
-        typename std::add_pointer<typename std::add_const<T>::type>::type;
-    using reference = typename std::add_lvalue_reference<
-        typename std::add_const<T>::type>::type;
-    using iterator_category = std::forward_iterator_tag;
+  using iterator = typename Slice<T>::iterator;
+  iterator begin() noexcept;
+  iterator end() noexcept;
 
-    const T &operator*() const noexcept;
-    const T *operator->() const noexcept;
-    const_iterator &operator++() noexcept;
-    const_iterator operator++(int) noexcept;
-    bool operator==(const const_iterator &) const noexcept;
-    bool operator!=(const const_iterator &) const noexcept;
-
-  private:
-    friend class Vec;
-    const void *pos;
-    size_t stride;
-  };
-
+  using const_iterator = typename Slice<const T>::iterator;
   const_iterator begin() const noexcept;
   const_iterator end() const noexcept;
+  const_iterator cbegin() const noexcept;
+  const_iterator cend() const noexcept;
+
+  void swap(Vec &) noexcept;
 
   // Internal API only intended for the cxxbridge code generator.
   Vec(unsafe_bitcopy_t, const Vec &) noexcept;
 
 private:
-  static size_t stride() noexcept;
-  void reserve_total(size_t cap) noexcept;
-  void set_len(size_t len) noexcept;
+  void reserve_total(std::size_t cap) noexcept;
+  void set_len(std::size_t len) noexcept;
   void drop() noexcept;
 
-  // Size and alignment statically verified by rust_vec.rs.
-  std::array<uintptr_t, 3> repr;
-};
-#endif // CXXBRIDGE05_RUST_VEC
+  friend void swap(Vec &lhs, Vec &rhs) noexcept { lhs.swap(rhs); }
 
-#ifndef CXXBRIDGE05_RUST_FN
-template <typename Signature, bool Throws = false>
+  // 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, bool Throws>
-class Fn<Ret(Args...), Throws> final {
+template <typename Ret, typename... Args>
+class Fn<Ret(Args...)> final {
 public:
-  Ret operator()(Args... args) const noexcept(!Throws);
+  Ret operator()(Args... args) const noexcept;
   Fn operator*() const noexcept;
 
 private:
-  Ret (*trampoline)(Args..., void *fn) noexcept(!Throws);
+  Ret (*trampoline)(Args..., void *fn) noexcept;
   void *fn;
 };
+#endif // CXXBRIDGE1_RUST_FN
 
-template <typename Signature>
-using TryFn = Fn<Signature, true>;
-#endif // CXXBRIDGE05_RUST_FN
-
-#ifndef CXXBRIDGE05_RUST_ERROR
-#define CXXBRIDGE05_RUST_ERROR
+#ifndef CXXBRIDGE1_RUST_ERROR
+#define CXXBRIDGE1_RUST_ERROR
+// https://cxx.rs/binding/result.html
 class Error final : public std::exception {
 public:
   Error(const Error &);
   Error(Error &&) noexcept;
-  ~Error() noexcept;
+  ~Error() noexcept override;
 
-  Error &operator=(const Error &);
-  Error &operator=(Error &&) noexcept;
+  Error &operator=(const Error &) &;
+  Error &operator=(Error &&) &noexcept;
 
   const char *what() const noexcept override;
 
@@ -259,22 +380,38 @@
   Error() noexcept = default;
   friend impl<Error>;
   const char *msg;
-  size_t len;
+  std::size_t len;
 };
-#endif // CXXBRIDGE05_RUST_ERROR
+#endif // CXXBRIDGE1_RUST_ERROR
 
-#ifndef CXXBRIDGE05_RUST_ISIZE
-#define CXXBRIDGE05_RUST_ISIZE
+#ifndef CXXBRIDGE1_RUST_ISIZE
+#define CXXBRIDGE1_RUST_ISIZE
 #if defined(_WIN32)
 using isize = SSIZE_T;
 #else
 using isize = ssize_t;
 #endif
-#endif // CXXBRIDGE05_RUST_ISIZE
+#endif // CXXBRIDGE1_RUST_ISIZE
 
 std::ostream &operator<<(std::ostream &, const String &);
 std::ostream &operator<<(std::ostream &, const Str &);
 
+#ifndef CXXBRIDGE1_RUST_OPAQUE
+#define CXXBRIDGE1_RUST_OPAQUE
+// Base class of generated opaque Rust types.
+class Opaque {
+public:
+  Opaque() = delete;
+  Opaque(const Opaque &) = delete;
+  ~Opaque() = delete;
+};
+#endif // CXXBRIDGE1_RUST_OPAQUE
+
+template <typename T>
+std::size_t size_of();
+template <typename T>
+std::size_t align_of();
+
 // IsRelocatable<T> is used in assertions that a C++ type passed by value
 // between Rust and C++ is soundly relocatable by Rust.
 //
@@ -296,20 +433,30 @@
 template <typename T>
 struct IsRelocatable;
 
+using u8 = std::uint8_t;
+using u16 = std::uint16_t;
+using u32 = std::uint32_t;
+using u64 = std::uint64_t;
+using usize = std::size_t; // see static asserts in cxx.cc
+using i8 = std::int8_t;
+using i16 = std::int16_t;
+using i32 = std::int32_t;
+using i64 = std::int64_t;
+using f32 = float;
+using f64 = double;
+
 // Snake case aliases for use in code that uses this style for type names.
 using string = String;
 using str = Str;
-template <class T>
+template <typename T>
 using slice = Slice<T>;
-template <class T>
+template <typename T>
 using box = Box<T>;
-template <class T>
+template <typename T>
 using vec = Vec<T>;
 using error = Error;
-template <typename Signature, bool Throws = false>
-using fn = Fn<Signature, Throws>;
 template <typename Signature>
-using try_fn = TryFn<Signature>;
+using fn = Fn<Signature>;
 template <typename T>
 using is_relocatable = IsRelocatable<T>;
 
@@ -318,71 +465,254 @@
 ////////////////////////////////////////////////////////////////////////////////
 /// end public API, begin implementation details
 
-#ifndef CXXBRIDGE05_PANIC
-#define CXXBRIDGE05_PANIC
+#ifndef CXXBRIDGE1_PANIC
+#define CXXBRIDGE1_PANIC
 template <typename Exception>
 void panic [[noreturn]] (const char *msg);
-#endif // CXXBRIDGE05_PANIC
+#endif // CXXBRIDGE1_PANIC
 
-#ifndef CXXBRIDGE05_RUST_FN
-#define CXXBRIDGE05_RUST_FN
-template <typename Ret, typename... Args, bool Throws>
-Ret Fn<Ret(Args...), Throws>::operator()(Args... args) const noexcept(!Throws) {
+#ifndef CXXBRIDGE1_RUST_FN
+#define CXXBRIDGE1_RUST_FN
+template <typename Ret, typename... Args>
+Ret Fn<Ret(Args...)>::operator()(Args... args) const noexcept {
   return (*this->trampoline)(std::move(args)..., this->fn);
 }
 
-template <typename Ret, typename... Args, bool Throws>
-Fn<Ret(Args...), Throws> Fn<Ret(Args...), Throws>::operator*() const noexcept {
+template <typename Ret, typename... Args>
+Fn<Ret(Args...)> Fn<Ret(Args...)>::operator*() const noexcept {
   return *this;
 }
-#endif // CXXBRIDGE05_RUST_FN
+#endif // CXXBRIDGE1_RUST_FN
 
-#ifndef CXXBRIDGE05_RUST_BITCOPY
-#define CXXBRIDGE05_RUST_BITCOPY
+#ifndef CXXBRIDGE1_RUST_BITCOPY_T
+#define CXXBRIDGE1_RUST_BITCOPY_T
 struct unsafe_bitcopy_t final {
   explicit unsafe_bitcopy_t() = default;
 };
+#endif // CXXBRIDGE1_RUST_BITCOPY_T
 
+#ifndef CXXBRIDGE1_RUST_BITCOPY
+#define CXXBRIDGE1_RUST_BITCOPY
 constexpr unsafe_bitcopy_t unsafe_bitcopy{};
-#endif // CXXBRIDGE05_RUST_BITCOPY
+#endif // CXXBRIDGE1_RUST_BITCOPY
 
-#ifndef CXXBRIDGE05_RUST_STR
-#define CXXBRIDGE05_RUST_STR
-inline const char *Str::data() const noexcept { return this->ptr; }
-
-inline size_t Str::size() const noexcept { return this->len; }
-
-inline size_t Str::length() const noexcept { return this->len; }
-#endif // CXXBRIDGE05_RUST_STR
-
-#ifndef CXXBRIDGE05_RUST_SLICE
-#define CXXBRIDGE05_RUST_SLICE
+#ifndef CXXBRIDGE1_RUST_SLICE
+#define CXXBRIDGE1_RUST_SLICE
 template <typename T>
-Slice<T>::Slice() noexcept : ptr(reinterpret_cast<const T *>(this)), len(0) {}
-
-template <typename T>
-Slice<T>::Slice(const T *s, size_t count) noexcept : ptr(s), len(count) {}
-
-template <typename T>
-const T *Slice<T>::data() const noexcept {
-  return this->ptr;
+Slice<T>::Slice() noexcept {
+  sliceInit(this, reinterpret_cast<void *>(align_of<T>()), 0);
 }
 
 template <typename T>
-size_t Slice<T>::size() const noexcept {
-  return this->len;
+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>
-size_t Slice<T>::length() const noexcept {
-  return this->len;
+T *Slice<T>::data() const noexcept {
+  return reinterpret_cast<T *>(slicePtr(this));
 }
-#endif // CXXBRIDGE05_RUST_SLICE
 
-#ifndef CXXBRIDGE05_RUST_BOX
-#define CXXBRIDGE05_RUST_BOX
 template <typename T>
-Box<T>::Box(const Box &other) : Box(*other) {}
+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 pos = static_cast<char *>(slicePtr(this)) + size_of<T>() * n;
+  return *reinterpret_cast<T *>(pos);
+}
+
+template <typename T>
+T &Slice<T>::at(std::size_t n) const {
+  if (n >= this->size()) {
+    panic<std::out_of_range>("rust::Slice index out of range");
+  }
+  return (*this)[n];
+}
+
+template <typename T>
+T &Slice<T>::front() const noexcept {
+  assert(!this->empty());
+  return (*this)[0];
+}
+
+template <typename T>
+T &Slice<T>::back() const noexcept {
+  assert(!this->empty());
+  return (*this)[this->size() - 1];
+}
+
+template <typename T>
+typename Slice<T>::iterator::reference
+Slice<T>::iterator::operator*() const noexcept {
+  return *static_cast<T *>(this->pos);
+}
+
+template <typename T>
+typename Slice<T>::iterator::pointer
+Slice<T>::iterator::operator->() const noexcept {
+  return static_cast<T *>(this->pos);
+}
+
+template <typename T>
+typename Slice<T>::iterator::reference Slice<T>::iterator::operator[](
+    typename Slice<T>::iterator::difference_type n) const noexcept {
+  auto pos = static_cast<char *>(this->pos) + this->stride * n;
+  return *reinterpret_cast<T *>(pos);
+}
+
+template <typename T>
+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 / this->stride;
+}
+
+template <typename T>
+bool Slice<T>::iterator::operator==(const iterator &other) const noexcept {
+  return this->pos == other.pos;
+}
+
+template <typename T>
+bool Slice<T>::iterator::operator!=(const iterator &other) const noexcept {
+  return this->pos != other.pos;
+}
+
+template <typename T>
+bool Slice<T>::iterator::operator<(const iterator &other) const noexcept {
+  return this->pos < other.pos;
+}
+
+template <typename T>
+bool Slice<T>::iterator::operator<=(const iterator &other) const noexcept {
+  return this->pos <= other.pos;
+}
+
+template <typename T>
+bool Slice<T>::iterator::operator>(const iterator &other) const noexcept {
+  return this->pos > other.pos;
+}
+
+template <typename T>
+bool Slice<T>::iterator::operator>=(const iterator &other) const noexcept {
+  return this->pos >= other.pos;
+}
+
+template <typename T>
+typename Slice<T>::iterator Slice<T>::begin() const noexcept {
+  iterator it;
+  it.pos = slicePtr(this);
+  it.stride = size_of<T>();
+  return it;
+}
+
+template <typename T>
+typename Slice<T>::iterator Slice<T>::end() const noexcept {
+  iterator it = this->begin();
+  it.pos = static_cast<char *>(it.pos) + it.stride * this->size();
+  return it;
+}
+
+template <typename T>
+void Slice<T>::swap(Slice &rhs) noexcept {
+  std::swap(*this, rhs);
+}
+#endif // CXXBRIDGE1_RUST_SLICE
+
+#ifndef CXXBRIDGE1_RUST_BOX
+#define CXXBRIDGE1_RUST_BOX
+template <typename T>
+class Box<T>::uninit {};
+
+template <typename T>
+class Box<T>::allocation {
+  static T *alloc() noexcept;
+  static void dealloc(T *) noexcept;
+
+public:
+  allocation() noexcept : ptr(alloc()) {}
+  ~allocation() noexcept {
+    if (this->ptr) {
+      dealloc(this->ptr);
+    }
+  }
+  T *ptr;
+};
 
 template <typename T>
 Box<T>::Box(Box &&other) noexcept : ptr(other.ptr) {
@@ -391,14 +721,18 @@
 
 template <typename T>
 Box<T>::Box(const T &val) {
-  this->uninit();
-  ::new (this->ptr) T(val);
+  allocation alloc;
+  ::new (alloc.ptr) T(val);
+  this->ptr = alloc.ptr;
+  alloc.ptr = nullptr;
 }
 
 template <typename T>
 Box<T>::Box(T &&val) {
-  this->uninit();
-  ::new (this->ptr) T(std::move(val));
+  allocation alloc;
+  ::new (alloc.ptr) T(std::move(val));
+  this->ptr = alloc.ptr;
+  alloc.ptr = nullptr;
 }
 
 template <typename T>
@@ -409,20 +743,7 @@
 }
 
 template <typename T>
-Box<T> &Box<T>::operator=(const Box &other) {
-  if (this != &other) {
-    if (this->ptr) {
-      **this = *other;
-    } else {
-      this->uninit();
-      ::new (this->ptr) T(*other);
-    }
-  }
-  return *this;
-}
-
-template <typename T>
-Box<T> &Box<T>::operator=(Box &&other) noexcept {
+Box<T> &Box<T>::operator=(Box &&other) &noexcept {
   if (this->ptr) {
     this->drop();
   }
@@ -453,16 +774,23 @@
 
 template <typename T>
 template <typename... Fields>
-Box<T> Box<T>::in_place(Fields &&... fields) {
-  Box box;
-  box.uninit();
-  ::new (box.ptr) T{std::forward<Fields>(fields)...};
-  return box;
+Box<T> Box<T>::in_place(Fields &&...fields) {
+  allocation alloc;
+  auto ptr = alloc.ptr;
+  ::new (ptr) T{std::forward<Fields>(fields)...};
+  alloc.ptr = nullptr;
+  return from_raw(ptr);
+}
+
+template <typename T>
+void Box<T>::swap(Box &rhs) noexcept {
+  using std::swap;
+  swap(this->ptr, rhs.ptr);
 }
 
 template <typename T>
 Box<T> Box<T>::from_raw(T *raw) noexcept {
-  Box box;
+  Box box = uninit{};
   box.ptr = raw;
   return box;
 }
@@ -475,14 +803,25 @@
 }
 
 template <typename T>
-Box<T>::Box() noexcept {}
-#endif // CXXBRIDGE05_RUST_BOX
+Box<T>::Box(uninit) noexcept {}
+#endif // CXXBRIDGE1_RUST_BOX
 
-#ifndef CXXBRIDGE05_RUST_VEC
-#define CXXBRIDGE05_RUST_VEC
+#ifndef CXXBRIDGE1_RUST_VEC
+#define CXXBRIDGE1_RUST_VEC
 template <typename T>
-Vec<T>::Vec(Vec &&other) noexcept {
-  this->repr = other.repr;
+Vec<T>::Vec(std::initializer_list<T> init) : Vec{} {
+  this->reserve_total(init.size());
+  std::move(init.begin(), init.end(), std::back_inserter(*this));
+}
+
+template <typename T>
+Vec<T>::Vec(const Vec &other) : Vec() {
+  this->reserve_total(other.size());
+  std::copy(other.begin(), other.end(), std::back_inserter(*this));
+}
+
+template <typename T>
+Vec<T>::Vec(Vec &&other) noexcept : repr(other.repr) {
   new (&other) Vec();
 }
 
@@ -492,18 +831,25 @@
 }
 
 template <typename T>
-Vec<T> &Vec<T>::operator=(Vec &&other) noexcept {
+Vec<T> &Vec<T>::operator=(Vec &&other) &noexcept {
+  this->drop();
+  this->repr = other.repr;
+  new (&other) Vec();
+  return *this;
+}
+
+template <typename T>
+Vec<T> &Vec<T>::operator=(const Vec &other) & {
   if (this != &other) {
     this->drop();
-    this->repr = other.repr;
-    new (&other) Vec();
+    new (this) Vec(other);
   }
   return *this;
 }
 
 template <typename T>
 bool Vec<T>::empty() const noexcept {
-  return size() == 0;
+  return this->size() == 0;
 }
 
 template <typename T>
@@ -512,13 +858,14 @@
 }
 
 template <typename T>
-const T &Vec<T>::operator[](size_t n) const noexcept {
+const T &Vec<T>::operator[](std::size_t n) const noexcept {
+  assert(n < this->size());
   auto data = reinterpret_cast<const char *>(this->data());
-  return *reinterpret_cast<const T *>(data + n * this->stride());
+  return *reinterpret_cast<const T *>(data + n * size_of<T>());
 }
 
 template <typename T>
-const T &Vec<T>::at(size_t n) const {
+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");
   }
@@ -526,17 +873,46 @@
 }
 
 template <typename T>
-const T &Vec<T>::front() const {
+const T &Vec<T>::front() const noexcept {
+  assert(!this->empty());
   return (*this)[0];
 }
 
 template <typename T>
-const T &Vec<T>::back() const {
+const T &Vec<T>::back() const noexcept {
+  assert(!this->empty());
   return (*this)[this->size() - 1];
 }
 
 template <typename T>
-void Vec<T>::reserve(size_t new_cap) {
+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);
 }
 
@@ -552,73 +928,126 @@
 
 template <typename T>
 template <typename... Args>
-void Vec<T>::emplace_back(Args &&... args) {
+void Vec<T>::emplace_back(Args &&...args) {
   auto size = this->size();
   this->reserve_total(size + 1);
   ::new (reinterpret_cast<T *>(reinterpret_cast<char *>(this->data()) +
-                               size * this->stride()))
+                               size * size_of<T>()))
       T(std::forward<Args>(args)...);
   this->set_len(size + 1);
 }
 
 template <typename T>
-const T &Vec<T>::const_iterator::operator*() const noexcept {
-  return *static_cast<const T *>(this->pos);
+typename Vec<T>::iterator Vec<T>::begin() noexcept {
+  return Slice<T>(this->data(), this->size()).begin();
 }
 
 template <typename T>
-const T *Vec<T>::const_iterator::operator->() const noexcept {
-  return static_cast<const T *>(this->pos);
-}
-
-template <typename T>
-typename Vec<T>::const_iterator &Vec<T>::const_iterator::operator++() noexcept {
-  this->pos = static_cast<const uint8_t *>(this->pos) + this->stride;
-  return *this;
-}
-
-template <typename T>
-typename Vec<T>::const_iterator
-Vec<T>::const_iterator::operator++(int) noexcept {
-  auto ret = const_iterator(*this);
-  this->pos = static_cast<const uint8_t *>(this->pos) + this->stride;
-  return ret;
-}
-
-template <typename T>
-bool Vec<T>::const_iterator::operator==(
-    const const_iterator &other) const noexcept {
-  return this->pos == other.pos;
-}
-
-template <typename T>
-bool Vec<T>::const_iterator::operator!=(
-    const const_iterator &other) const noexcept {
-  return this->pos != other.pos;
+typename Vec<T>::iterator Vec<T>::end() noexcept {
+  return Slice<T>(this->data(), this->size()).end();
 }
 
 template <typename T>
 typename Vec<T>::const_iterator Vec<T>::begin() const noexcept {
-  const_iterator it;
-  it.pos = this->data();
-  it.stride = this->stride();
-  return it;
+  return this->cbegin();
 }
 
 template <typename T>
 typename Vec<T>::const_iterator Vec<T>::end() const noexcept {
-  const_iterator it = this->begin();
-  it.pos = static_cast<const uint8_t *>(it.pos) + it.stride * this->size();
-  return it;
+  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 // CXXBRIDGE05_RUST_VEC
+#endif // CXXBRIDGE1_RUST_VEC
 
-#ifndef CXXBRIDGE05_RELOCATABLE
-#define CXXBRIDGE05_RELOCATABLE
+#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 {
@@ -652,7 +1081,7 @@
           std::integral_constant<
               bool, std::is_trivially_move_constructible<T>::value &&
                         std::is_trivially_destructible<T>::value>>::type {};
-#endif // CXXBRIDGE05_RELOCATABLE
+#endif // CXXBRIDGE1_RELOCATABLE
 
-} // namespace cxxbridge05
+} // namespace cxxbridge1
 } // namespace rust
diff --git a/macro/Cargo.toml b/macro/Cargo.toml
index 6b07a07..a3df101 100644
--- a/macro/Cargo.toml
+++ b/macro/Cargo.toml
@@ -1,11 +1,12 @@
 [package]
 name = "cxxbridge-macro"
-version = "0.5.9"
+version = "1.0.42"
 authors = ["David Tolnay <dtolnay@gmail.com>"]
 edition = "2018"
 license = "MIT OR Apache-2.0"
 description = "Implementation detail of the `cxx` crate."
 repository = "https://github.com/dtolnay/cxx"
+homepage = "https://cxx.rs"
 exclude = ["build.rs", "README.md"]
 keywords = ["ffi"]
 categories = ["development-tools::ffi"]
@@ -16,10 +17,10 @@
 [dependencies]
 proc-macro2 = "1.0"
 quote = "1.0.4"
-syn = { version = "1.0.20", features = ["full"] }
+syn = { version = "1.0.68", features = ["full"] }
 
 [dev-dependencies]
-cxx = { version = "0.5", path = ".." }
+cxx = { version = "1.0", path = ".." }
 
 [package.metadata.docs.rs]
 targets = ["x86_64-unknown-linux-gnu"]
diff --git a/macro/src/derive.rs b/macro/src/derive.rs
index 1abc5da..2f77095 100644
--- a/macro/src/derive.rs
+++ b/macro/src/derive.rs
@@ -1,14 +1,281 @@
-use crate::syntax::Derive;
-use proc_macro2::TokenStream;
-use quote::{quote, ToTokens};
+use crate::syntax::{derive, Enum, Struct, Trait};
+use proc_macro2::{Ident, Span, TokenStream};
+use quote::{quote, quote_spanned, ToTokens};
 
-pub struct DeriveAttribute<'a>(pub &'a [Derive]);
+pub use crate::syntax::derive::*;
 
-impl<'a> ToTokens for DeriveAttribute<'a> {
-    fn to_tokens(&self, tokens: &mut TokenStream) {
-        if !self.0.is_empty() {
-            let derives = self.0;
-            tokens.extend(quote!(#[derive(#(#derives),*)]));
+pub fn expand_struct(strct: &Struct, actual_derives: &mut Option<TokenStream>) -> TokenStream {
+    let mut expanded = TokenStream::new();
+    let mut traits = Vec::new();
+
+    for derive in &strct.derives {
+        let span = derive.span;
+        match derive.what {
+            Trait::Copy => expanded.extend(struct_copy(strct, span)),
+            Trait::Clone => expanded.extend(struct_clone(strct, span)),
+            Trait::Debug => expanded.extend(struct_debug(strct, span)),
+            Trait::Default => expanded.extend(struct_default(strct, span)),
+            Trait::Eq => traits.push(quote_spanned!(span=> ::std::cmp::Eq)),
+            Trait::ExternType => unreachable!(),
+            Trait::Hash => traits.push(quote_spanned!(span=> ::std::hash::Hash)),
+            Trait::Ord => expanded.extend(struct_ord(strct, span)),
+            Trait::PartialEq => traits.push(quote_spanned!(span=> ::std::cmp::PartialEq)),
+            Trait::PartialOrd => expanded.extend(struct_partial_ord(strct, span)),
+        }
+    }
+
+    if traits.is_empty() {
+        *actual_derives = None;
+    } else {
+        *actual_derives = Some(quote!(#[derive(#(#traits),*)]));
+    }
+
+    expanded
+}
+
+pub fn expand_enum(enm: &Enum, actual_derives: &mut Option<TokenStream>) -> TokenStream {
+    let mut expanded = TokenStream::new();
+    let mut traits = Vec::new();
+    let mut has_copy = false;
+    let mut has_clone = false;
+    let mut has_eq = false;
+    let mut has_partial_eq = false;
+
+    for derive in &enm.derives {
+        let span = derive.span;
+        match derive.what {
+            Trait::Copy => {
+                expanded.extend(enum_copy(enm, span));
+                has_copy = true;
+            }
+            Trait::Clone => {
+                expanded.extend(enum_clone(enm, span));
+                has_clone = true;
+            }
+            Trait::Debug => expanded.extend(enum_debug(enm, span)),
+            Trait::Default => unreachable!(),
+            Trait::Eq => {
+                traits.push(quote_spanned!(span=> ::std::cmp::Eq));
+                has_eq = true;
+            }
+            Trait::ExternType => unreachable!(),
+            Trait::Hash => traits.push(quote_spanned!(span=> ::std::hash::Hash)),
+            Trait::Ord => expanded.extend(enum_ord(enm, span)),
+            Trait::PartialEq => {
+                traits.push(quote_spanned!(span=> ::std::cmp::PartialEq));
+                has_partial_eq = true;
+            }
+            Trait::PartialOrd => expanded.extend(enum_partial_ord(enm, span)),
+        }
+    }
+
+    let span = enm.name.rust.span();
+    if !has_copy {
+        expanded.extend(enum_copy(enm, span));
+    }
+    if !has_clone {
+        expanded.extend(enum_clone(enm, span));
+    }
+    if !has_eq {
+        // Required to be derived in order for the enum's "variants" to be
+        // usable in patterns.
+        traits.push(quote!(::std::cmp::Eq));
+    }
+    if !has_partial_eq {
+        traits.push(quote!(::std::cmp::PartialEq));
+    }
+
+    *actual_derives = Some(quote!(#[derive(#(#traits),*)]));
+
+    expanded
+}
+
+fn struct_copy(strct: &Struct, span: Span) -> TokenStream {
+    let ident = &strct.name.rust;
+    let generics = &strct.generics;
+
+    quote_spanned! {span=>
+        impl #generics ::std::marker::Copy for #ident #generics {}
+    }
+}
+
+fn struct_clone(strct: &Struct, span: Span) -> TokenStream {
+    let ident = &strct.name.rust;
+    let generics = &strct.generics;
+
+    let body = if derive::contains(&strct.derives, Trait::Copy) {
+        quote!(*self)
+    } else {
+        let fields = strct.fields.iter().map(|field| &field.name.rust);
+        let values = strct.fields.iter().map(|field| {
+            let ident = &field.name.rust;
+            let ty = field.ty.to_token_stream();
+            let span = ty.into_iter().last().unwrap().span();
+            quote_spanned!(span=> &self.#ident)
+        });
+        quote_spanned!(span=> #ident {
+            #(#fields: ::std::clone::Clone::clone(#values),)*
+        })
+    };
+
+    quote_spanned! {span=>
+        impl #generics ::std::clone::Clone for #ident #generics {
+            fn clone(&self) -> Self {
+                #body
+            }
+        }
+    }
+}
+
+fn struct_debug(strct: &Struct, span: Span) -> TokenStream {
+    let ident = &strct.name.rust;
+    let generics = &strct.generics;
+    let struct_name = ident.to_string();
+    let fields = strct.fields.iter().map(|field| &field.name.rust);
+    let field_names = fields.clone().map(Ident::to_string);
+
+    quote_spanned! {span=>
+        impl #generics ::std::fmt::Debug for #ident #generics {
+            fn fmt(&self, formatter: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
+                formatter.debug_struct(#struct_name)
+                    #(.field(#field_names, &self.#fields))*
+                    .finish()
+            }
+        }
+    }
+}
+
+fn struct_default(strct: &Struct, span: Span) -> TokenStream {
+    let ident = &strct.name.rust;
+    let generics = &strct.generics;
+    let fields = strct.fields.iter().map(|field| &field.name.rust);
+
+    quote_spanned! {span=>
+        impl #generics ::std::default::Default for #ident #generics {
+            fn default() -> Self {
+                #ident {
+                    #(
+                        #fields: ::std::default::Default::default(),
+                    )*
+                }
+            }
+        }
+    }
+}
+
+fn struct_ord(strct: &Struct, span: Span) -> TokenStream {
+    let ident = &strct.name.rust;
+    let generics = &strct.generics;
+    let fields = strct.fields.iter().map(|field| &field.name.rust);
+
+    quote_spanned! {span=>
+        impl #generics ::std::cmp::Ord for #ident #generics {
+            fn cmp(&self, other: &Self) -> ::std::cmp::Ordering {
+                #(
+                    match ::std::cmp::Ord::cmp(&self.#fields, &other.#fields) {
+                        ::std::cmp::Ordering::Equal => {}
+                        ordering => return ordering,
+                    }
+                )*
+                ::std::cmp::Ordering::Equal
+            }
+        }
+    }
+}
+
+fn struct_partial_ord(strct: &Struct, span: Span) -> TokenStream {
+    let ident = &strct.name.rust;
+    let generics = &strct.generics;
+
+    let body = if derive::contains(&strct.derives, Trait::Ord) {
+        quote! {
+            ::std::option::Option::Some(::std::cmp::Ord::cmp(self, other))
+        }
+    } else {
+        let fields = strct.fields.iter().map(|field| &field.name.rust);
+        quote! {
+            #(
+                match ::std::cmp::PartialOrd::partial_cmp(&self.#fields, &other.#fields) {
+                    ::std::option::Option::Some(::std::cmp::Ordering::Equal) => {}
+                    ordering => return ordering,
+                }
+            )*
+            ::std::option::Option::Some(::std::cmp::Ordering::Equal)
+        }
+    };
+
+    quote_spanned! {span=>
+        impl #generics ::std::cmp::PartialOrd for #ident #generics {
+            fn partial_cmp(&self, other: &Self) -> ::std::option::Option<::std::cmp::Ordering> {
+                #body
+            }
+        }
+    }
+}
+
+fn enum_copy(enm: &Enum, span: Span) -> TokenStream {
+    let ident = &enm.name.rust;
+
+    quote_spanned! {span=>
+        impl ::std::marker::Copy for #ident {}
+    }
+}
+
+fn enum_clone(enm: &Enum, span: Span) -> TokenStream {
+    let ident = &enm.name.rust;
+
+    quote_spanned! {span=>
+        impl ::std::clone::Clone for #ident {
+            fn clone(&self) -> Self {
+                *self
+            }
+        }
+    }
+}
+
+fn enum_debug(enm: &Enum, span: Span) -> TokenStream {
+    let ident = &enm.name.rust;
+    let variants = enm.variants.iter().map(|variant| {
+        let variant = &variant.name.rust;
+        let name = variant.to_string();
+        quote_spanned! {span=>
+            #ident::#variant => formatter.write_str(#name),
+        }
+    });
+    let fallback = format!("{}({{}})", ident);
+
+    quote_spanned! {span=>
+        impl ::std::fmt::Debug for #ident {
+            fn fmt(&self, formatter: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
+                match *self {
+                    #(#variants)*
+                    _ => ::std::write!(formatter, #fallback, self.repr),
+                }
+            }
+        }
+    }
+}
+
+fn enum_ord(enm: &Enum, span: Span) -> TokenStream {
+    let ident = &enm.name.rust;
+
+    quote_spanned! {span=>
+        impl ::std::cmp::Ord for #ident {
+            fn cmp(&self, other: &Self) -> ::std::cmp::Ordering {
+                ::std::cmp::Ord::cmp(&self.repr, &other.repr)
+            }
+        }
+    }
+}
+
+fn enum_partial_ord(enm: &Enum, span: Span) -> TokenStream {
+    let ident = &enm.name.rust;
+
+    quote_spanned! {span=>
+        impl ::std::cmp::PartialOrd for #ident {
+            fn partial_cmp(&self, other: &Self) -> ::std::option::Option<::std::cmp::Ordering> {
+                ::std::cmp::PartialOrd::partial_cmp(&self.repr, &other.repr)
+            }
         }
     }
 }
diff --git a/macro/src/expand.rs b/macro/src/expand.rs
index 903585e..7c194ad 100644
--- a/macro/src/expand.rs
+++ b/macro/src/expand.rs
@@ -1,19 +1,34 @@
-use crate::derive::DeriveAttribute;
-use crate::syntax::atom::Atom::{self, *};
+use crate::syntax::atom::Atom::*;
+use crate::syntax::attrs::{self, OtherAttrs};
 use crate::syntax::file::Module;
+use crate::syntax::instantiate::{ImplKey, NamedImplKey};
+use crate::syntax::qualified::QualifiedName;
 use crate::syntax::report::Errors;
 use crate::syntax::symbol::Symbol;
 use crate::syntax::{
-    self, check, mangle, Api, Enum, ExternFn, ExternType, Impl, Pair, ResolvableName, Signature,
-    Struct, Type, TypeAlias, Types,
+    self, check, mangle, Api, Doc, Enum, ExternFn, ExternType, Impl, Lifetimes, Pair, Signature,
+    Struct, Trait, Type, TypeAlias, Types,
 };
+use crate::type_id::Crate;
+use crate::{derive, generics};
 use proc_macro2::{Ident, Span, TokenStream};
 use quote::{format_ident, quote, quote_spanned, ToTokens};
 use std::mem;
-use syn::{parse_quote, Result, Token};
+use syn::{parse_quote, punctuated, Lifetime, Result, Token};
 
 pub fn bridge(mut ffi: Module) -> Result<TokenStream> {
     let ref mut errors = Errors::new();
+
+    let mut doc = Doc::new();
+    let attrs = attrs::parse(
+        errors,
+        mem::take(&mut ffi.attrs),
+        attrs::Parser {
+            doc: Some(&mut doc),
+            ..Default::default()
+        },
+    );
+
     let content = mem::take(&mut ffi.content);
     let trusted = ffi.unsafety.is_some();
     let namespace = &ffi.namespace;
@@ -23,34 +38,44 @@
     check::typecheck(errors, apis, types);
     errors.propagate()?;
 
-    Ok(expand(ffi, apis, types))
+    Ok(expand(ffi, doc, attrs, apis, types))
 }
 
-fn expand(ffi: Module, apis: &[Api], types: &Types) -> TokenStream {
+fn expand(ffi: Module, doc: Doc, attrs: OtherAttrs, apis: &[Api], types: &Types) -> TokenStream {
     let mut expanded = TokenStream::new();
     let mut hidden = TokenStream::new();
+    let mut forbid = TokenStream::new();
 
     for api in apis {
         if let Api::RustType(ety) = api {
-            expanded.extend(expand_rust_type(ety));
+            expanded.extend(expand_rust_type_import(ety));
             hidden.extend(expand_rust_type_assert_sized(ety));
         }
     }
 
     for api in apis {
         match api {
-            Api::Include(_) | Api::RustType(_) | Api::Impl(_) => {}
-            Api::Struct(strct) => expanded.extend(expand_struct(strct)),
+            Api::Include(_) | Api::Impl(_) => {}
+            Api::Struct(strct) => {
+                expanded.extend(expand_struct(strct));
+                hidden.extend(expand_struct_operators(strct));
+                forbid.extend(expand_struct_forbid_drop(strct));
+            }
             Api::Enum(enm) => expanded.extend(expand_enum(enm)),
             Api::CxxType(ety) => {
                 let ident = &ety.name.rust;
                 if !types.structs.contains_key(ident) && !types.enums.contains_key(ident) {
                     expanded.extend(expand_cxx_type(ety));
+                    hidden.extend(expand_cxx_type_assert_pinned(ety));
                 }
             }
             Api::CxxFunction(efn) => {
                 expanded.extend(expand_cxx_function_shim(efn, types));
             }
+            Api::RustType(ety) => {
+                expanded.extend(expand_rust_type_impl(ety));
+                hidden.extend(expand_rust_type_layout(ety));
+            }
             Api::RustFunction(efn) => hidden.extend(expand_rust_function_shim(efn, types)),
             Api::TypeAlias(alias) => {
                 expanded.extend(expand_type_alias(alias));
@@ -59,42 +84,33 @@
         }
     }
 
-    for ty in types {
-        let explicit_impl = types.explicit_impls.get(ty);
-        if let Type::RustBox(ty) = ty {
-            if let Type::Ident(ident) = &ty.inner {
-                if Atom::from(&ident.rust).is_none() {
-                    hidden.extend(expand_rust_box(ident, types));
-                }
+    for (impl_key, &explicit_impl) in &types.impls {
+        match *impl_key {
+            ImplKey::RustBox(ident) => {
+                hidden.extend(expand_rust_box(ident, types, explicit_impl));
             }
-        } else if let Type::RustVec(ty) = ty {
-            if let Type::Ident(ident) = &ty.inner {
-                if Atom::from(&ident.rust).is_none() {
-                    hidden.extend(expand_rust_vec(ident, types));
-                }
+            ImplKey::RustVec(ident) => {
+                hidden.extend(expand_rust_vec(ident, types, explicit_impl));
             }
-        } else if let Type::UniquePtr(ptr) = ty {
-            if let Type::Ident(ident) = &ptr.inner {
-                if Atom::from(&ident.rust).is_none()
-                    && (explicit_impl.is_some() || !types.aliases.contains_key(&ident.rust))
-                {
-                    expanded.extend(expand_unique_ptr(ident, types, explicit_impl));
-                }
+            ImplKey::UniquePtr(ident) => {
+                expanded.extend(expand_unique_ptr(ident, types, explicit_impl));
             }
-        } else if let Type::CxxVector(ptr) = ty {
-            if let Type::Ident(ident) = &ptr.inner {
-                if Atom::from(&ident.rust).is_none()
-                    && (explicit_impl.is_some() || !types.aliases.contains_key(&ident.rust))
-                {
-                    // Generate impl for CxxVector<T> if T is a struct or opaque
-                    // C++ type. Impl for primitives is already provided by cxx
-                    // crate.
-                    expanded.extend(expand_cxx_vector(ident, explicit_impl, types));
-                }
+            ImplKey::SharedPtr(ident) => {
+                expanded.extend(expand_shared_ptr(ident, types, explicit_impl));
+            }
+            ImplKey::WeakPtr(ident) => {
+                expanded.extend(expand_weak_ptr(ident, types, explicit_impl));
+            }
+            ImplKey::CxxVector(ident) => {
+                expanded.extend(expand_cxx_vector(ident, explicit_impl, types));
             }
         }
     }
 
+    if !forbid.is_empty() {
+        hidden.extend(expand_forbid(forbid));
+    }
+
     // Work around https://github.com/rust-lang/rust/issues/67851.
     if !hidden.is_empty() {
         expanded.extend(quote! {
@@ -105,70 +121,209 @@
         });
     }
 
-    let attrs = ffi
-        .attrs
-        .into_iter()
-        .filter(|attr| attr.path.is_ident("doc"));
     let vis = &ffi.vis;
+    let mod_token = &ffi.mod_token;
     let ident = &ffi.ident;
+    let span = ffi.brace_token.span;
+    let expanded = quote_spanned!(span=> {#expanded});
 
     quote! {
-        #(#attrs)*
+        #doc
+        #attrs
         #[deny(improper_ctypes)]
-        #[allow(non_snake_case)]
-        #vis mod #ident {
-            #expanded
-        }
+        #[allow(non_camel_case_types, non_snake_case, clippy::upper_case_acronyms, clippy::unknown_clippy_lints)]
+        #vis #mod_token #ident #expanded
     }
 }
 
 fn expand_struct(strct: &Struct) -> TokenStream {
     let ident = &strct.name.rust;
     let doc = &strct.doc;
-    let derives = DeriveAttribute(&strct.derives);
+    let attrs = &strct.attrs;
+    let generics = &strct.generics;
     let type_id = type_id(&strct.name);
     let fields = strct.fields.iter().map(|field| {
+        let doc = &field.doc;
+        let attrs = &field.attrs;
         // This span on the pub makes "private type in public interface" errors
         // appear in the right place.
-        let vis = Token![pub](field.ident.span());
-        quote!(#vis #field)
+        let vis = field.visibility;
+        quote!(#doc #attrs #vis #field)
     });
+    let mut derives = None;
+    let derived_traits = derive::expand_struct(strct, &mut derives);
+
+    let span = ident.span();
+    let visibility = strct.visibility;
+    let struct_token = strct.struct_token;
+    let struct_def = quote_spanned! {span=>
+        #visibility #struct_token #ident #generics {
+            #(#fields,)*
+        }
+    };
 
     quote! {
         #doc
+        #attrs
         #derives
         #[repr(C)]
-        pub struct #ident {
-            #(#fields,)*
-        }
+        #struct_def
 
-        unsafe impl ::cxx::ExternType for #ident {
+        unsafe impl #generics ::cxx::ExternType for #ident #generics {
+            #[doc(hidden)]
             type Id = #type_id;
             type Kind = ::cxx::kind::Trivial;
         }
+
+        #derived_traits
+    }
+}
+
+fn expand_struct_operators(strct: &Struct) -> TokenStream {
+    let ident = &strct.name.rust;
+    let mut operators = TokenStream::new();
+
+    for derive in &strct.derives {
+        let span = derive.span;
+        match derive.what {
+            Trait::PartialEq => {
+                let link_name = mangle::operator(&strct.name, "eq");
+                let local_name = format_ident!("__operator_eq_{}", strct.name.rust);
+                operators.extend(quote_spanned! {span=>
+                    #[doc(hidden)]
+                    #[export_name = #link_name]
+                    extern "C" fn #local_name(lhs: &#ident, rhs: &#ident) -> bool {
+                        *lhs == *rhs
+                    }
+                });
+
+                if !derive::contains(&strct.derives, Trait::Eq) {
+                    let link_name = mangle::operator(&strct.name, "ne");
+                    let local_name = format_ident!("__operator_ne_{}", strct.name.rust);
+                    operators.extend(quote_spanned! {span=>
+                        #[doc(hidden)]
+                        #[export_name = #link_name]
+                        extern "C" fn #local_name(lhs: &#ident, rhs: &#ident) -> bool {
+                            *lhs != *rhs
+                        }
+                    });
+                }
+            }
+            Trait::PartialOrd => {
+                let link_name = mangle::operator(&strct.name, "lt");
+                let local_name = format_ident!("__operator_lt_{}", strct.name.rust);
+                operators.extend(quote_spanned! {span=>
+                    #[doc(hidden)]
+                    #[export_name = #link_name]
+                    extern "C" fn #local_name(lhs: &#ident, rhs: &#ident) -> bool {
+                        *lhs < *rhs
+                    }
+                });
+
+                let link_name = mangle::operator(&strct.name, "le");
+                let local_name = format_ident!("__operator_le_{}", strct.name.rust);
+                operators.extend(quote_spanned! {span=>
+                    #[doc(hidden)]
+                    #[export_name = #link_name]
+                    extern "C" fn #local_name(lhs: &#ident, rhs: &#ident) -> bool {
+                        *lhs <= *rhs
+                    }
+                });
+
+                if !derive::contains(&strct.derives, Trait::Ord) {
+                    let link_name = mangle::operator(&strct.name, "gt");
+                    let local_name = format_ident!("__operator_gt_{}", strct.name.rust);
+                    operators.extend(quote_spanned! {span=>
+                        #[doc(hidden)]
+                        #[export_name = #link_name]
+                        extern "C" fn #local_name(lhs: &#ident, rhs: &#ident) -> bool {
+                            *lhs > *rhs
+                        }
+                    });
+
+                    let link_name = mangle::operator(&strct.name, "ge");
+                    let local_name = format_ident!("__operator_ge_{}", strct.name.rust);
+                    operators.extend(quote_spanned! {span=>
+                        #[doc(hidden)]
+                        #[export_name = #link_name]
+                        extern "C" fn #local_name(lhs: &#ident, rhs: &#ident) -> bool {
+                            *lhs >= *rhs
+                        }
+                    });
+                }
+            }
+            Trait::Hash => {
+                let link_name = mangle::operator(&strct.name, "hash");
+                let local_name = format_ident!("__operator_hash_{}", strct.name.rust);
+                operators.extend(quote_spanned! {span=>
+                    #[doc(hidden)]
+                    #[export_name = #link_name]
+                    #[allow(clippy::cast_possible_truncation)]
+                    extern "C" fn #local_name(this: &#ident) -> usize {
+                        let mut hasher = ::std::collections::hash_map::DefaultHasher::new();
+                        ::std::hash::Hash::hash(this, &mut hasher);
+                        ::std::hash::Hasher::finish(&hasher) as usize
+                    }
+                });
+            }
+            _ => {}
+        }
+    }
+
+    operators
+}
+
+fn expand_struct_forbid_drop(strct: &Struct) -> TokenStream {
+    let ident = &strct.name.rust;
+    let generics = &strct.generics;
+    let span = ident.span();
+    let impl_token = Token![impl](strct.visibility.span);
+
+    quote_spanned! {span=>
+        #impl_token #generics self::Drop for super::#ident #generics {}
     }
 }
 
 fn expand_enum(enm: &Enum) -> TokenStream {
     let ident = &enm.name.rust;
     let doc = &enm.doc;
+    let attrs = &enm.attrs;
     let repr = enm.repr;
     let type_id = type_id(&enm.name);
     let variants = enm.variants.iter().map(|variant| {
-        let variant_ident = &variant.ident;
+        let doc = &variant.doc;
+        let attrs = &variant.attrs;
+        let variant_ident = &variant.name.rust;
         let discriminant = &variant.discriminant;
-        Some(quote! {
+        let span = variant_ident.span();
+        Some(quote_spanned! {span=>
+            #doc
+            #attrs
             pub const #variant_ident: Self = #ident { repr: #discriminant };
         })
     });
+    let mut derives = None;
+    let derived_traits = derive::expand_enum(enm, &mut derives);
+
+    let span = ident.span();
+    let visibility = enm.visibility;
+    let struct_token = Token![struct](enm.enum_token.span);
+    let enum_repr = quote! {
+        #[allow(missing_docs)]
+        pub repr: #repr,
+    };
+    let enum_def = quote_spanned! {span=>
+        #visibility #struct_token #ident {
+            #enum_repr
+        }
+    };
 
     quote! {
         #doc
-        #[derive(Copy, Clone, PartialEq, Eq)]
+        #attrs
+        #derives
         #[repr(transparent)]
-        pub struct #ident {
-            pub repr: #repr,
-        }
+        #enum_def
 
         #[allow(non_upper_case_globals)]
         impl #ident {
@@ -176,38 +331,95 @@
         }
 
         unsafe impl ::cxx::ExternType for #ident {
+            #[doc(hidden)]
             type Id = #type_id;
             type Kind = ::cxx::kind::Trivial;
         }
+
+        #derived_traits
     }
 }
 
 fn expand_cxx_type(ety: &ExternType) -> TokenStream {
     let ident = &ety.name.rust;
     let doc = &ety.doc;
+    let attrs = &ety.attrs;
+    let generics = &ety.generics;
     let type_id = type_id(&ety.name);
 
+    let lifetime_fields = ety.generics.lifetimes.iter().map(|lifetime| {
+        let field = format_ident!("_lifetime_{}", lifetime.ident);
+        quote!(#field: ::std::marker::PhantomData<&#lifetime ()>)
+    });
+    let repr_fields = quote! {
+        _private: ::cxx::private::Opaque,
+        #(#lifetime_fields,)*
+    };
+
+    let span = ident.span();
+    let visibility = &ety.visibility;
+    let struct_token = Token![struct](ety.type_token.span);
+    let extern_type_def = quote_spanned! {span=>
+        #visibility #struct_token #ident #generics {
+            #repr_fields
+        }
+    };
+
     quote! {
         #doc
+        #attrs
         #[repr(C)]
-        pub struct #ident {
-            _private: ::cxx::private::Opaque,
-        }
+        #extern_type_def
 
-        unsafe impl ::cxx::ExternType for #ident {
+        unsafe impl #generics ::cxx::ExternType for #ident #generics {
+            #[doc(hidden)]
             type Id = #type_id;
             type Kind = ::cxx::kind::Opaque;
         }
     }
 }
 
+fn expand_cxx_type_assert_pinned(ety: &ExternType) -> TokenStream {
+    let ident = &ety.name.rust;
+    let infer = Token![_](ident.span());
+
+    quote! {
+        let _ = {
+            // Derived from https://github.com/nvzqz/static-assertions-rs.
+            trait __AmbiguousIfImpl<A> {
+                fn infer() {}
+            }
+
+            impl<T> __AmbiguousIfImpl<()> for T
+            where
+                T: ?::std::marker::Sized
+            {}
+
+            #[allow(dead_code)]
+            struct __Invalid;
+
+            impl<T> __AmbiguousIfImpl<__Invalid> for T
+            where
+                T: ?::std::marker::Sized + ::std::marker::Unpin,
+            {}
+
+            // If there is only one specialized trait impl, type inference with
+            // `_` can be resolved and this can compile. Fails to compile if
+            // user has added a manual Unpin impl for their opaque C++ type as
+            // then `__AmbiguousIfImpl<__Invalid>` also exists.
+            <#ident as __AmbiguousIfImpl<#infer>>::infer
+        };
+    }
+}
+
 fn expand_cxx_function_decl(efn: &ExternFn, types: &Types) -> TokenStream {
+    let generics = &efn.generics;
     let receiver = efn.receiver.iter().map(|receiver| {
         let receiver_type = receiver.ty();
         quote!(_: #receiver_type)
     });
     let args = efn.args.iter().map(|arg| {
-        let ident = &arg.ident;
+        let ident = &arg.name.rust;
         let ty = expand_extern_type(&arg.ty, types, true);
         if arg.ty == RustString {
             quote!(#ident: *const #ty)
@@ -236,18 +448,25 @@
     let local_name = format_ident!("__{}", efn.name.rust);
     quote! {
         #[link_name = #link_name]
-        fn #local_name(#(#all_args,)* #outparam) #ret;
+        fn #local_name #generics(#(#all_args,)* #outparam) #ret;
     }
 }
 
 fn expand_cxx_function_shim(efn: &ExternFn, types: &Types) -> TokenStream {
     let doc = &efn.doc;
+    let attrs = &efn.attrs;
     let decl = expand_cxx_function_decl(efn, types);
     let receiver = efn.receiver.iter().map(|receiver| {
-        let ampersand = receiver.ampersand;
-        let mutability = receiver.mutability;
         let var = receiver.var;
-        quote!(#ampersand #mutability #var)
+        if receiver.pinned {
+            let ty = receiver.ty_self();
+            quote!(#var: #ty)
+        } else {
+            let ampersand = receiver.ampersand;
+            let lifetime = &receiver.lifetime;
+            let mutability = receiver.mutability;
+            quote!(#ampersand #lifetime #mutability #var)
+        }
     });
     let args = efn.args.iter().map(|arg| quote!(#arg));
     let all_args = receiver.chain(args);
@@ -266,7 +485,7 @@
         .iter()
         .map(|receiver| receiver.var.to_token_stream());
     let arg_vars = efn.args.iter().map(|arg| {
-        let var = &arg.ident;
+        let var = &arg.name.rust;
         match &arg.ty {
             Type::Ident(ident) if ident.rust == RustString => {
                 quote!(#var.as_mut_ptr() as *const ::cxx::private::RustString)
@@ -275,26 +494,44 @@
             Type::UniquePtr(_) => quote!(::cxx::UniquePtr::into_raw(#var)),
             Type::RustVec(_) => quote!(#var.as_mut_ptr() as *const ::cxx::private::RustVec<_>),
             Type::Ref(ty) => match &ty.inner {
-                Type::Ident(ident) if ident.rust == RustString => match ty.mutability {
-                    None => quote!(::cxx::private::RustString::from_ref(#var)),
-                    Some(_) => quote!(::cxx::private::RustString::from_mut(#var)),
+                Type::Ident(ident) if ident.rust == RustString => match ty.mutable {
+                    false => quote!(::cxx::private::RustString::from_ref(#var)),
+                    true => quote!(::cxx::private::RustString::from_mut(#var)),
                 },
-                Type::RustVec(vec) if vec.inner == RustString => match ty.mutability {
-                    None => quote!(::cxx::private::RustVec::from_ref_vec_string(#var)),
-                    Some(_) => quote!(::cxx::private::RustVec::from_mut_vec_string(#var)),
+                Type::RustVec(vec) if vec.inner == RustString => match ty.mutable {
+                    false => quote!(::cxx::private::RustVec::from_ref_vec_string(#var)),
+                    true => quote!(::cxx::private::RustVec::from_mut_vec_string(#var)),
                 },
-                Type::RustVec(_) => match ty.mutability {
-                    None => quote!(::cxx::private::RustVec::from_ref(#var)),
-                    Some(_) => quote!(::cxx::private::RustVec::from_mut(#var)),
+                Type::RustVec(_) => match ty.mutable {
+                    false => quote!(::cxx::private::RustVec::from_ref(#var)),
+                    true => quote!(::cxx::private::RustVec::from_mut(#var)),
                 },
-                inner if types.is_considered_improper_ctype(inner) => match ty.mutability {
-                    None => quote!(#var as *const #inner as *const ::std::ffi::c_void),
-                    Some(_) => quote!(#var as *mut #inner as *mut ::std::ffi::c_void),
-                },
+                inner if types.is_considered_improper_ctype(inner) => {
+                    let var = match ty.pinned {
+                        false => quote!(#var),
+                        true => quote!(::std::pin::Pin::into_inner_unchecked(#var)),
+                    };
+                    match ty.mutable {
+                        false => {
+                            quote!(#var as *const #inner as *const ::std::ffi::c_void)
+                        }
+                        true => quote!(#var as *mut #inner as *mut ::std::ffi::c_void),
+                    }
+                }
                 _ => quote!(#var),
             },
+            Type::Ptr(ty) => {
+                if types.is_considered_improper_ctype(&ty.inner) {
+                    quote!(#var.cast())
+                } else {
+                    quote!(#var)
+                }
+            }
             Type::Str(_) => quote!(::cxx::private::RustStr::from(#var)),
-            Type::SliceRefU8(_) => quote!(::cxx::private::RustSliceU8::from(#var)),
+            Type::SliceRef(ty) => match ty.mutable {
+                false => quote!(::cxx::private::RustSlice::from_ref(#var)),
+                true => quote!(::cxx::private::RustSlice::from_mut(#var)),
+            },
             ty if types.needs_indirect_abi(ty) => quote!(#var.as_mut_ptr()),
             _ => quote!(#var),
         }
@@ -305,7 +542,7 @@
         .iter()
         .filter_map(|arg| {
             if let Type::Fn(f) = &arg.ty {
-                let var = &arg.ident;
+                let var = &arg.name;
                 Some(expand_function_pointer_trampoline(efn, var, f, types))
             } else {
                 None
@@ -317,7 +554,7 @@
         .iter()
         .filter(|arg| types.needs_indirect_abi(&arg.ty))
         .map(|arg| {
-            let var = &arg.ident;
+            let var = &arg.name.rust;
             // These are arguments for which C++ has taken ownership of the data
             // behind the mut reference it received.
             quote! {
@@ -368,26 +605,43 @@
                 }
                 Type::UniquePtr(_) => quote!(::cxx::UniquePtr::from_raw(#call)),
                 Type::Ref(ty) => match &ty.inner {
-                    Type::Ident(ident) if ident.rust == RustString => match ty.mutability {
-                        None => quote!(#call.as_string()),
-                        Some(_) => quote!(#call.as_mut_string()),
+                    Type::Ident(ident) if ident.rust == RustString => match ty.mutable {
+                        false => quote!(#call.as_string()),
+                        true => quote!(#call.as_mut_string()),
                     },
-                    Type::RustVec(vec) if vec.inner == RustString => match ty.mutability {
-                        None => quote!(#call.as_vec_string()),
-                        Some(_) => quote!(#call.as_mut_vec_string()),
+                    Type::RustVec(vec) if vec.inner == RustString => match ty.mutable {
+                        false => quote!(#call.as_vec_string()),
+                        true => quote!(#call.as_mut_vec_string()),
                     },
-                    Type::RustVec(_) => match ty.mutability {
-                        None => quote!(#call.as_vec()),
-                        Some(_) => quote!(#call.as_mut_vec()),
+                    Type::RustVec(_) => match ty.mutable {
+                        false => quote!(#call.as_vec()),
+                        true => quote!(#call.as_mut_vec()),
                     },
                     inner if types.is_considered_improper_ctype(inner) => {
                         let mutability = ty.mutability;
-                        quote!(&#mutability *#call.cast())
+                        let deref_mut = quote!(&#mutability *#call.cast());
+                        match ty.pinned {
+                            false => deref_mut,
+                            true => quote!(::std::pin::Pin::new_unchecked(#deref_mut)),
+                        }
                     }
                     _ => call,
                 },
+                Type::Ptr(ty) => {
+                    if types.is_considered_improper_ctype(&ty.inner) {
+                        quote!(#call.cast())
+                    } else {
+                        call
+                    }
+                }
                 Type::Str(_) => quote!(#call.as_str()),
-                Type::SliceRefU8(_) => quote!(#call.as_slice()),
+                Type::SliceRef(slice) => {
+                    let inner = &slice.inner;
+                    match slice.mutable {
+                        false => quote!(#call.as_slice::<#inner>()),
+                        true => quote!(#call.as_mut_slice::<#inner>()),
+                    }
+                }
                 _ => call,
             },
         };
@@ -396,40 +650,74 @@
         }
     };
     let mut dispatch = quote!(#setup #expr);
+    let visibility = efn.visibility;
     let unsafety = &efn.sig.unsafety;
     if unsafety.is_none() {
         dispatch = quote!(unsafe { #dispatch });
     }
+    let fn_token = efn.sig.fn_token;
     let ident = &efn.name.rust;
-    let function_shim = quote! {
-        #doc
-        pub #unsafety fn #ident(#(#all_args,)*) #ret {
-            extern "C" {
-                #decl
-            }
-            #trampolines
-            #dispatch
+    let generics = &efn.generics;
+    let arg_list = quote_spanned!(efn.sig.paren_token.span=> (#(#all_args,)*));
+    let fn_body = quote_spanned!(efn.semi_token.span=> {
+        extern "C" {
+            #decl
         }
-    };
+        #trampolines
+        #dispatch
+    });
     match &efn.receiver {
-        None => function_shim,
+        None => {
+            quote! {
+                #doc
+                #attrs
+                #visibility #unsafety #fn_token #ident #generics #arg_list #ret #fn_body
+            }
+        }
         Some(receiver) => {
-            let receiver_type = &receiver.ty;
-            quote!(impl #receiver_type { #function_shim })
+            let elided_generics;
+            let receiver_ident = &receiver.ty.rust;
+            let resolve = types.resolve(&receiver.ty);
+            let receiver_generics = if receiver.ty.generics.lt_token.is_some() {
+                &receiver.ty.generics
+            } else {
+                elided_generics = Lifetimes {
+                    lt_token: resolve.generics.lt_token,
+                    lifetimes: resolve
+                        .generics
+                        .lifetimes
+                        .pairs()
+                        .map(|pair| {
+                            let lifetime = Lifetime::new("'_", pair.value().apostrophe);
+                            let punct = pair.punct().map(|&&comma| comma);
+                            punctuated::Pair::new(lifetime, punct)
+                        })
+                        .collect(),
+                    gt_token: resolve.generics.gt_token,
+                };
+                &elided_generics
+            };
+            quote! {
+                impl #generics #receiver_ident #receiver_generics {
+                    #doc
+                    #attrs
+                    #visibility #unsafety #fn_token #ident #arg_list #ret #fn_body
+                }
+            }
         }
     }
 }
 
 fn expand_function_pointer_trampoline(
     efn: &ExternFn,
-    var: &Ident,
+    var: &Pair,
     sig: &Signature,
     types: &Types,
 ) -> TokenStream {
     let c_trampoline = mangle::c_trampoline(efn, var, types);
     let r_trampoline = mangle::r_trampoline(efn, var, types);
     let local_name = parse_quote!(__);
-    let catch_unwind_label = format!("::{}::{}", efn.name.rust, var);
+    let catch_unwind_label = format!("::{}::{}", efn.name.rust, var.rust);
     let shim = expand_rust_function_shim_impl(
         sig,
         types,
@@ -438,6 +726,7 @@
         catch_unwind_label,
         None,
     );
+    let var = &var.rust;
 
     quote! {
         let #var = ::cxx::private::FatFunction {
@@ -454,13 +743,43 @@
     }
 }
 
-fn expand_rust_type(ety: &ExternType) -> TokenStream {
+fn expand_rust_type_import(ety: &ExternType) -> TokenStream {
     let ident = &ety.name.rust;
-    quote! {
+    let span = ident.span();
+
+    quote_spanned! {span=>
         use super::#ident;
     }
 }
 
+fn expand_rust_type_impl(ety: &ExternType) -> TokenStream {
+    let ident = &ety.name.rust;
+    let generics = &ety.generics;
+    let span = ident.span();
+    let unsafe_impl = quote_spanned!(ety.type_token.span=> unsafe impl);
+
+    let mut impls = quote_spanned! {span=>
+        #[doc(hidden)]
+        #unsafe_impl #generics ::cxx::private::RustType for #ident #generics {}
+    };
+
+    for derive in &ety.derives {
+        if derive.what == Trait::ExternType {
+            let type_id = type_id(&ety.name);
+            let span = derive.span;
+            impls.extend(quote_spanned! {span=>
+                unsafe impl #generics ::cxx::ExternType for #ident #generics {
+                    #[doc(hidden)]
+                    type Id = #type_id;
+                    type Kind = ::cxx::kind::Opaque;
+                }
+            });
+        }
+    }
+
+    impls
+}
+
 fn expand_rust_type_assert_sized(ety: &ExternType) -> TokenStream {
     // Rustc will render as follows if not sized:
     //
@@ -472,21 +791,71 @@
 
     let ident = &ety.name.rust;
     let begin_span = Token![::](ety.type_token.span);
-    let sized = quote_spanned! {ety.semi_token.span=>
-        #begin_span std::marker::Sized
+    let unpin = quote_spanned! {ety.semi_token.span=>
+        #begin_span std::marker::Unpin
     };
     quote_spanned! {ident.span()=>
         let _ = {
-            fn __AssertSized<T: ?#sized + #sized>() {}
-            __AssertSized::<#ident>
+            fn __AssertUnpin<T: ?::std::marker::Sized + #unpin>() {}
+            __AssertUnpin::<#ident>
         };
     }
 }
 
+fn expand_rust_type_layout(ety: &ExternType) -> TokenStream {
+    let ident = &ety.name.rust;
+    let begin_span = Token![::](ety.type_token.span);
+    let sized = quote_spanned! {ety.semi_token.span=>
+        #begin_span std::marker::Sized
+    };
+
+    let link_sizeof = mangle::operator(&ety.name, "sizeof");
+    let link_alignof = mangle::operator(&ety.name, "alignof");
+
+    let local_sizeof = format_ident!("__sizeof_{}", ety.name.rust);
+    let local_alignof = format_ident!("__alignof_{}", ety.name.rust);
+
+    quote_spanned! {ident.span()=>
+        {
+            #[doc(hidden)]
+            fn __AssertSized<T: ?#sized + #sized>() -> ::std::alloc::Layout {
+                ::std::alloc::Layout::new::<T>()
+            }
+            #[doc(hidden)]
+            #[export_name = #link_sizeof]
+            extern "C" fn #local_sizeof() -> usize {
+                __AssertSized::<#ident>().size()
+            }
+            #[doc(hidden)]
+            #[export_name = #link_alignof]
+            extern "C" fn #local_alignof() -> usize {
+                __AssertSized::<#ident>().align()
+            }
+        }
+    }
+}
+
+fn expand_forbid(impls: TokenStream) -> TokenStream {
+    quote! {
+        mod forbid {
+            pub trait Drop {}
+            #[allow(drop_bounds)]
+            impl<T: ?::std::marker::Sized + ::std::ops::Drop> self::Drop for T {}
+            #impls
+        }
+    }
+}
+
 fn expand_rust_function_shim(efn: &ExternFn, types: &Types) -> TokenStream {
     let link_name = mangle::extern_fn(efn, types);
-    let local_name = format_ident!("__{}", efn.name.rust);
-    let catch_unwind_label = format!("::{}", efn.name.rust);
+    let local_name = match &efn.receiver {
+        None => format_ident!("__{}", efn.name.rust),
+        Some(receiver) => format_ident!("__{}__{}", receiver.ty.rust, efn.name.rust),
+    };
+    let catch_unwind_label = match &efn.receiver {
+        None => format!("::{}", efn.name.rust),
+        Some(receiver) => format!("::{}::{}", receiver.ty.rust, efn.name.rust),
+    };
     let invoke = Some(&efn.name.rust);
     expand_rust_function_shim_impl(
         efn,
@@ -506,6 +875,7 @@
     catch_unwind_label: String,
     invoke: Option<&Ident>,
 ) -> TokenStream {
+    let generics = &sig.generics;
     let receiver_var = sig
         .receiver
         .as_ref()
@@ -515,7 +885,7 @@
         quote!(#receiver_var: #receiver_type)
     });
     let args = sig.args.iter().map(|arg| {
-        let ident = &arg.ident;
+        let ident = &arg.name.rust;
         let ty = expand_extern_type(&arg.ty, types, false);
         if types.needs_indirect_abi(&arg.ty) {
             quote!(#ident: *mut #ty)
@@ -526,7 +896,7 @@
     let all_args = receiver.into_iter().chain(args);
 
     let arg_vars = sig.args.iter().map(|arg| {
-        let ident = &arg.ident;
+        let ident = &arg.name.rust;
         match &arg.ty {
             Type::Ident(i) if i.rust == RustString => {
                 quote!(::std::mem::take((*#ident).as_mut_string()))
@@ -541,22 +911,28 @@
             }
             Type::UniquePtr(_) => quote!(::cxx::UniquePtr::from_raw(#ident)),
             Type::Ref(ty) => match &ty.inner {
-                Type::Ident(i) if i.rust == RustString => match ty.mutability {
-                    None => quote!(#ident.as_string()),
-                    Some(_) => quote!(#ident.as_mut_string()),
+                Type::Ident(i) if i.rust == RustString => match ty.mutable {
+                    false => quote!(#ident.as_string()),
+                    true => quote!(#ident.as_mut_string()),
                 },
-                Type::RustVec(vec) if vec.inner == RustString => match ty.mutability {
-                    None => quote!(#ident.as_vec_string()),
-                    Some(_) => quote!(#ident.as_mut_vec_string()),
+                Type::RustVec(vec) if vec.inner == RustString => match ty.mutable {
+                    false => quote!(#ident.as_vec_string()),
+                    true => quote!(#ident.as_mut_vec_string()),
                 },
-                Type::RustVec(_) => match ty.mutability {
-                    None => quote!(#ident.as_vec()),
-                    Some(_) => quote!(#ident.as_mut_vec()),
+                Type::RustVec(_) => match ty.mutable {
+                    false => quote!(#ident.as_vec()),
+                    true => quote!(#ident.as_mut_vec()),
                 },
                 _ => quote!(#ident),
             },
             Type::Str(_) => quote!(#ident.as_str()),
-            Type::SliceRefU8(_) => quote!(#ident.as_slice()),
+            Type::SliceRef(slice) => {
+                let inner = &slice.inner;
+                match slice.mutable {
+                    false => quote!(#ident.as_slice::<#inner>()),
+                    true => quote!(#ident.as_mut_slice::<#inner>()),
+                }
+            }
             ty if types.needs_indirect_abi(ty) => quote!(::std::ptr::read(#ident)),
             _ => quote!(#ident),
         }
@@ -585,22 +961,25 @@
         }
         Type::UniquePtr(_) => Some(quote!(::cxx::UniquePtr::into_raw)),
         Type::Ref(ty) => match &ty.inner {
-            Type::Ident(ident) if ident.rust == RustString => match ty.mutability {
-                None => Some(quote!(::cxx::private::RustString::from_ref)),
-                Some(_) => Some(quote!(::cxx::private::RustString::from_mut)),
+            Type::Ident(ident) if ident.rust == RustString => match ty.mutable {
+                false => Some(quote!(::cxx::private::RustString::from_ref)),
+                true => Some(quote!(::cxx::private::RustString::from_mut)),
             },
-            Type::RustVec(vec) if vec.inner == RustString => match ty.mutability {
-                None => Some(quote!(::cxx::private::RustVec::from_ref_vec_string)),
-                Some(_) => Some(quote!(::cxx::private::RustVec::from_mut_vec_string)),
+            Type::RustVec(vec) if vec.inner == RustString => match ty.mutable {
+                false => Some(quote!(::cxx::private::RustVec::from_ref_vec_string)),
+                true => Some(quote!(::cxx::private::RustVec::from_mut_vec_string)),
             },
-            Type::RustVec(_) => match ty.mutability {
-                None => Some(quote!(::cxx::private::RustVec::from_ref)),
-                Some(_) => Some(quote!(::cxx::private::RustVec::from_mut)),
+            Type::RustVec(_) => match ty.mutable {
+                false => Some(quote!(::cxx::private::RustVec::from_ref)),
+                true => Some(quote!(::cxx::private::RustVec::from_mut)),
             },
             _ => None,
         },
         Type::Str(_) => Some(quote!(::cxx::private::RustStr::from)),
-        Type::SliceRefU8(_) => Some(quote!(::cxx::private::RustSliceU8::from)),
+        Type::SliceRef(ty) => match ty.mutable {
+            false => Some(quote!(::cxx::private::RustSlice::from_ref)),
+            true => Some(quote!(::cxx::private::RustSlice::from_mut)),
+        },
         _ => None,
     });
 
@@ -642,7 +1021,7 @@
     quote! {
         #[doc(hidden)]
         #[export_name = #link_name]
-        unsafe extern "C" fn #local_name(#(#all_args,)* #outparam #pointer) #ret {
+        unsafe extern "C" fn #local_name #generics(#(#all_args,)* #outparam #pointer) #ret {
             let __fn = concat!(module_path!(), #catch_unwind_label);
             #wrap_super
             #expr
@@ -658,6 +1037,7 @@
     invoke: &Ident,
 ) -> TokenStream {
     let unsafety = sig.unsafety;
+    let generics = &sig.generics;
 
     let receiver_var = sig
         .receiver
@@ -682,20 +1062,20 @@
         expand_return_type(&sig.ret)
     };
 
-    let arg_vars = sig.args.iter().map(|arg| &arg.ident);
+    let arg_vars = sig.args.iter().map(|arg| &arg.name.rust);
     let vars = receiver_var.iter().chain(arg_vars);
 
     let span = invoke.span();
     let call = match &sig.receiver {
         None => quote_spanned!(span=> super::#invoke),
         Some(receiver) => {
-            let receiver_type = &receiver.ty;
+            let receiver_type = &receiver.ty.rust;
             quote_spanned!(span=> #receiver_type::#invoke)
         }
     };
 
     quote_spanned! {span=>
-        #unsafety fn #local_name(#(#all_args,)*) #ret {
+        #unsafety fn #local_name #generics(#(#all_args,)*) #ret {
             #call(#(#vars,)*)
         }
     }
@@ -703,11 +1083,19 @@
 
 fn expand_type_alias(alias: &TypeAlias) -> TokenStream {
     let doc = &alias.doc;
+    let attrs = &alias.attrs;
+    let visibility = alias.visibility;
+    let type_token = alias.type_token;
     let ident = &alias.name.rust;
+    let generics = &alias.generics;
+    let eq_token = alias.eq_token;
     let ty = &alias.ty;
+    let semi_token = alias.semi_token;
+
     quote! {
         #doc
-        pub type #ident = #ty;
+        #attrs
+        #visibility #type_token #ident #generics #eq_token #ty #semi_token
     }
 }
 
@@ -734,139 +1122,171 @@
 }
 
 fn type_id(name: &Pair) -> TokenStream {
-    let path = name.to_fully_qualified();
-    quote! {
-        ::cxx::type_id!(#path)
-    }
+    let namespace_segments = name.namespace.iter();
+    let mut segments = Vec::with_capacity(namespace_segments.len() + 1);
+    segments.extend(namespace_segments.cloned());
+    segments.push(Ident::new(&name.cxx.to_string(), Span::call_site()));
+    let qualified = QualifiedName { segments };
+    crate::type_id::expand(Crate::Cxx, qualified)
 }
 
-fn expand_rust_box(ident: &ResolvableName, types: &Types) -> TokenStream {
-    let link_prefix = format!("cxxbridge05$box${}$", types.resolve(ident).to_symbol());
-    let link_uninit = format!("{}uninit", link_prefix);
+fn expand_rust_box(key: NamedImplKey, types: &Types, explicit_impl: Option<&Impl>) -> TokenStream {
+    let ident = key.rust;
+    let resolve = types.resolve(ident);
+    let link_prefix = format!("cxxbridge1$box${}$", resolve.name.to_symbol());
+    let link_alloc = format!("{}alloc", link_prefix);
+    let link_dealloc = format!("{}dealloc", link_prefix);
     let link_drop = format!("{}drop", link_prefix);
 
-    let local_prefix = format_ident!("{}__box_", &ident.rust);
-    let local_uninit = format_ident!("{}uninit", local_prefix);
+    let local_prefix = format_ident!("{}__box_", ident);
+    let local_alloc = format_ident!("{}alloc", local_prefix);
+    let local_dealloc = format_ident!("{}dealloc", local_prefix);
     let local_drop = format_ident!("{}drop", local_prefix);
 
-    let span = ident.span();
-    quote_spanned! {span=>
+    let (impl_generics, ty_generics) = generics::split_for_impl(key, explicit_impl, resolve);
+
+    let begin_span = explicit_impl.map_or(key.begin_span, |explicit| explicit.impl_token.span);
+    let end_span = explicit_impl.map_or(key.end_span, |explicit| explicit.brace_token.span);
+    let unsafe_token = format_ident!("unsafe", span = begin_span);
+
+    quote_spanned! {end_span=>
         #[doc(hidden)]
-        #[export_name = #link_uninit]
-        unsafe extern "C" fn #local_uninit(
-            this: *mut ::std::boxed::Box<::std::mem::MaybeUninit<#ident>>,
-        ) {
-            ::std::ptr::write(
-                this,
-                ::std::boxed::Box::new(::std::mem::MaybeUninit::uninit()),
-            );
+        #unsafe_token impl #impl_generics ::cxx::private::ImplBox for #ident #ty_generics {}
+        #[doc(hidden)]
+        #[export_name = #link_alloc]
+        unsafe extern "C" fn #local_alloc #impl_generics() -> *mut ::std::mem::MaybeUninit<#ident #ty_generics> {
+            ::std::boxed::Box::into_raw(::std::boxed::Box::new(::std::mem::MaybeUninit::uninit()))
+        }
+        #[doc(hidden)]
+        #[export_name = #link_dealloc]
+        unsafe extern "C" fn #local_dealloc #impl_generics(ptr: *mut ::std::mem::MaybeUninit<#ident #ty_generics>) {
+            ::std::boxed::Box::from_raw(ptr);
         }
         #[doc(hidden)]
         #[export_name = #link_drop]
-        unsafe extern "C" fn #local_drop(this: *mut ::std::boxed::Box<#ident>) {
+        unsafe extern "C" fn #local_drop #impl_generics(this: *mut ::std::boxed::Box<#ident #ty_generics>) {
             ::std::ptr::drop_in_place(this);
         }
     }
 }
 
-fn expand_rust_vec(elem: &ResolvableName, types: &Types) -> TokenStream {
-    let link_prefix = format!("cxxbridge05$rust_vec${}$", elem.to_symbol(types));
+fn expand_rust_vec(key: NamedImplKey, types: &Types, explicit_impl: Option<&Impl>) -> TokenStream {
+    let elem = key.rust;
+    let resolve = types.resolve(elem);
+    let link_prefix = format!("cxxbridge1$rust_vec${}$", resolve.name.to_symbol());
     let link_new = format!("{}new", link_prefix);
     let link_drop = format!("{}drop", link_prefix);
     let link_len = format!("{}len", link_prefix);
+    let link_capacity = format!("{}capacity", link_prefix);
     let link_data = format!("{}data", link_prefix);
     let link_reserve_total = format!("{}reserve_total", link_prefix);
     let link_set_len = format!("{}set_len", link_prefix);
-    let link_stride = format!("{}stride", link_prefix);
 
-    let local_prefix = format_ident!("{}__vec_", elem.rust);
+    let local_prefix = format_ident!("{}__vec_", elem);
     let local_new = format_ident!("{}new", local_prefix);
     let local_drop = format_ident!("{}drop", local_prefix);
     let local_len = format_ident!("{}len", local_prefix);
+    let local_capacity = format_ident!("{}capacity", local_prefix);
     let local_data = format_ident!("{}data", local_prefix);
     let local_reserve_total = format_ident!("{}reserve_total", local_prefix);
     let local_set_len = format_ident!("{}set_len", local_prefix);
-    let local_stride = format_ident!("{}stride", local_prefix);
 
-    let span = elem.span();
-    quote_spanned! {span=>
+    let (impl_generics, ty_generics) = generics::split_for_impl(key, explicit_impl, resolve);
+
+    let begin_span = explicit_impl.map_or(key.begin_span, |explicit| explicit.impl_token.span);
+    let end_span = explicit_impl.map_or(key.end_span, |explicit| explicit.brace_token.span);
+    let unsafe_token = format_ident!("unsafe", span = begin_span);
+
+    quote_spanned! {end_span=>
+        #[doc(hidden)]
+        #unsafe_token impl #impl_generics ::cxx::private::ImplVec for #elem #ty_generics {}
         #[doc(hidden)]
         #[export_name = #link_new]
-        unsafe extern "C" fn #local_new(this: *mut ::cxx::private::RustVec<#elem>) {
+        unsafe extern "C" fn #local_new #impl_generics(this: *mut ::cxx::private::RustVec<#elem #ty_generics>) {
             ::std::ptr::write(this, ::cxx::private::RustVec::new());
         }
         #[doc(hidden)]
         #[export_name = #link_drop]
-        unsafe extern "C" fn #local_drop(this: *mut ::cxx::private::RustVec<#elem>) {
+        unsafe extern "C" fn #local_drop #impl_generics(this: *mut ::cxx::private::RustVec<#elem #ty_generics>) {
             ::std::ptr::drop_in_place(this);
         }
         #[doc(hidden)]
         #[export_name = #link_len]
-        unsafe extern "C" fn #local_len(this: *const ::cxx::private::RustVec<#elem>) -> usize {
+        unsafe extern "C" fn #local_len #impl_generics(this: *const ::cxx::private::RustVec<#elem #ty_generics>) -> usize {
             (*this).len()
         }
         #[doc(hidden)]
+        #[export_name = #link_capacity]
+        unsafe extern "C" fn #local_capacity #impl_generics(this: *const ::cxx::private::RustVec<#elem #ty_generics>) -> usize {
+            (*this).capacity()
+        }
+        #[doc(hidden)]
         #[export_name = #link_data]
-        unsafe extern "C" fn #local_data(this: *const ::cxx::private::RustVec<#elem>) -> *const #elem {
+        unsafe extern "C" fn #local_data #impl_generics(this: *const ::cxx::private::RustVec<#elem #ty_generics>) -> *const #elem #ty_generics {
             (*this).as_ptr()
         }
         #[doc(hidden)]
         #[export_name = #link_reserve_total]
-        unsafe extern "C" fn #local_reserve_total(this: *mut ::cxx::private::RustVec<#elem>, cap: usize) {
+        unsafe extern "C" fn #local_reserve_total #impl_generics(this: *mut ::cxx::private::RustVec<#elem #ty_generics>, cap: usize) {
             (*this).reserve_total(cap);
         }
         #[doc(hidden)]
         #[export_name = #link_set_len]
-        unsafe extern "C" fn #local_set_len(this: *mut ::cxx::private::RustVec<#elem>, len: usize) {
+        unsafe extern "C" fn #local_set_len #impl_generics(this: *mut ::cxx::private::RustVec<#elem #ty_generics>, len: usize) {
             (*this).set_len(len);
         }
-        #[doc(hidden)]
-        #[export_name = #link_stride]
-        unsafe extern "C" fn #local_stride() -> usize {
-            ::std::mem::size_of::<#elem>()
-        }
     }
 }
 
 fn expand_unique_ptr(
-    ident: &ResolvableName,
+    key: NamedImplKey,
     types: &Types,
     explicit_impl: Option<&Impl>,
 ) -> TokenStream {
-    let name = ident.rust.to_string();
-    let prefix = format!("cxxbridge05$unique_ptr${}$", ident.to_symbol(types));
+    let ident = key.rust;
+    let name = ident.to_string();
+    let resolve = types.resolve(ident);
+    let prefix = format!("cxxbridge1$unique_ptr${}$", resolve.name.to_symbol());
     let link_null = format!("{}null", prefix);
-    let link_new = format!("{}new", prefix);
+    let link_uninit = format!("{}uninit", prefix);
     let link_raw = format!("{}raw", prefix);
     let link_get = format!("{}get", prefix);
     let link_release = format!("{}release", prefix);
     let link_drop = format!("{}drop", prefix);
 
-    let new_method =
-        if types.structs.contains_key(&ident.rust) || types.aliases.contains_key(&ident.rust) {
-            Some(quote! {
-                fn __new(mut value: Self) -> *mut ::std::ffi::c_void {
-                    extern "C" {
-                        #[link_name = #link_new]
-                        fn __new(this: *mut *mut ::std::ffi::c_void, value: *mut #ident);
-                    }
-                    let mut repr = ::std::ptr::null_mut::<::std::ffi::c_void>();
-                    unsafe { __new(&mut repr, &mut value) }
-                    repr
-                }
-            })
-        } else {
-            None
-        };
+    let (impl_generics, ty_generics) = generics::split_for_impl(key, explicit_impl, resolve);
 
-    let begin_span =
-        explicit_impl.map_or_else(Span::call_site, |explicit| explicit.impl_token.span);
-    let end_span = explicit_impl.map_or_else(Span::call_site, |explicit| explicit.brace_token.span);
+    let can_construct_from_value = types.structs.contains_key(ident)
+        || types.enums.contains_key(ident)
+        || types.aliases.contains_key(ident);
+    let new_method = if can_construct_from_value {
+        Some(quote! {
+            #[doc(hidden)]
+            fn __new(value: Self) -> *mut ::std::ffi::c_void {
+                extern "C" {
+                    #[link_name = #link_uninit]
+                    fn __uninit(this: *mut *mut ::std::ffi::c_void) -> *mut ::std::ffi::c_void;
+                }
+                let mut repr = ::std::ptr::null_mut::<::std::ffi::c_void>();
+                unsafe { __uninit(&mut repr).cast::<#ident #ty_generics>().write(value) }
+                repr
+            }
+        })
+    } else {
+        None
+    };
+
+    let begin_span = explicit_impl.map_or(key.begin_span, |explicit| explicit.impl_token.span);
+    let end_span = explicit_impl.map_or(key.end_span, |explicit| explicit.brace_token.span);
     let unsafe_token = format_ident!("unsafe", span = begin_span);
 
     quote_spanned! {end_span=>
-        #unsafe_token impl ::cxx::private::UniquePtrTarget for #ident {
-            const __NAME: &'static dyn ::std::fmt::Display = &#name;
+        #unsafe_token impl #impl_generics ::cxx::private::UniquePtrTarget for #ident #ty_generics {
+            #[doc(hidden)]
+            fn __typename(f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
+                f.write_str(#name)
+            }
+            #[doc(hidden)]
             fn __null() -> *mut ::std::ffi::c_void {
                 extern "C" {
                     #[link_name = #link_null]
@@ -877,29 +1297,33 @@
                 repr
             }
             #new_method
+            #[doc(hidden)]
             unsafe fn __raw(raw: *mut Self) -> *mut ::std::ffi::c_void {
                 extern "C" {
                     #[link_name = #link_raw]
-                    fn __raw(this: *mut *mut ::std::ffi::c_void, raw: *mut #ident);
+                    fn __raw(this: *mut *mut ::std::ffi::c_void, raw: *mut ::std::ffi::c_void);
                 }
                 let mut repr = ::std::ptr::null_mut::<::std::ffi::c_void>();
-                __raw(&mut repr, raw);
+                __raw(&mut repr, raw.cast());
                 repr
             }
+            #[doc(hidden)]
             unsafe fn __get(repr: *mut ::std::ffi::c_void) -> *const Self {
                 extern "C" {
                     #[link_name = #link_get]
-                    fn __get(this: *const *mut ::std::ffi::c_void) -> *const #ident;
+                    fn __get(this: *const *mut ::std::ffi::c_void) -> *const ::std::ffi::c_void;
                 }
-                __get(&repr)
+                __get(&repr).cast()
             }
+            #[doc(hidden)]
             unsafe fn __release(mut repr: *mut ::std::ffi::c_void) -> *mut Self {
                 extern "C" {
                     #[link_name = #link_release]
-                    fn __release(this: *mut *mut ::std::ffi::c_void) -> *mut #ident;
+                    fn __release(this: *mut *mut ::std::ffi::c_void) -> *mut ::std::ffi::c_void;
                 }
-                __release(&mut repr)
+                __release(&mut repr).cast()
             }
+            #[doc(hidden)]
             unsafe fn __drop(mut repr: *mut ::std::ffi::c_void) {
                 extern "C" {
                     #[link_name = #link_drop]
@@ -911,19 +1335,169 @@
     }
 }
 
+fn expand_shared_ptr(
+    key: NamedImplKey,
+    types: &Types,
+    explicit_impl: Option<&Impl>,
+) -> TokenStream {
+    let ident = key.rust;
+    let name = ident.to_string();
+    let resolve = types.resolve(ident);
+    let prefix = format!("cxxbridge1$shared_ptr${}$", resolve.name.to_symbol());
+    let link_null = format!("{}null", prefix);
+    let link_uninit = format!("{}uninit", prefix);
+    let link_clone = format!("{}clone", prefix);
+    let link_get = format!("{}get", prefix);
+    let link_drop = format!("{}drop", prefix);
+
+    let (impl_generics, ty_generics) = generics::split_for_impl(key, explicit_impl, resolve);
+
+    let can_construct_from_value = types.structs.contains_key(ident)
+        || types.enums.contains_key(ident)
+        || types.aliases.contains_key(ident);
+    let new_method = if can_construct_from_value {
+        Some(quote! {
+            #[doc(hidden)]
+            unsafe fn __new(value: Self, new: *mut ::std::ffi::c_void) {
+                extern "C" {
+                    #[link_name = #link_uninit]
+                    fn __uninit(new: *mut ::std::ffi::c_void) -> *mut ::std::ffi::c_void;
+                }
+                __uninit(new).cast::<#ident #ty_generics>().write(value);
+            }
+        })
+    } else {
+        None
+    };
+
+    let begin_span = explicit_impl.map_or(key.begin_span, |explicit| explicit.impl_token.span);
+    let end_span = explicit_impl.map_or(key.end_span, |explicit| explicit.brace_token.span);
+    let unsafe_token = format_ident!("unsafe", span = begin_span);
+
+    quote_spanned! {end_span=>
+        #unsafe_token impl #impl_generics ::cxx::private::SharedPtrTarget for #ident #ty_generics {
+            #[doc(hidden)]
+            fn __typename(f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
+                f.write_str(#name)
+            }
+            #[doc(hidden)]
+            unsafe fn __null(new: *mut ::std::ffi::c_void) {
+                extern "C" {
+                    #[link_name = #link_null]
+                    fn __null(new: *mut ::std::ffi::c_void);
+                }
+                __null(new);
+            }
+            #new_method
+            #[doc(hidden)]
+            unsafe fn __clone(this: *const ::std::ffi::c_void, new: *mut ::std::ffi::c_void) {
+                extern "C" {
+                    #[link_name = #link_clone]
+                    fn __clone(this: *const ::std::ffi::c_void, new: *mut ::std::ffi::c_void);
+                }
+                __clone(this, new);
+            }
+            #[doc(hidden)]
+            unsafe fn __get(this: *const ::std::ffi::c_void) -> *const Self {
+                extern "C" {
+                    #[link_name = #link_get]
+                    fn __get(this: *const ::std::ffi::c_void) -> *const ::std::ffi::c_void;
+                }
+                __get(this).cast()
+            }
+            #[doc(hidden)]
+            unsafe fn __drop(this: *mut ::std::ffi::c_void) {
+                extern "C" {
+                    #[link_name = #link_drop]
+                    fn __drop(this: *mut ::std::ffi::c_void);
+                }
+                __drop(this);
+            }
+        }
+    }
+}
+
+fn expand_weak_ptr(key: NamedImplKey, types: &Types, explicit_impl: Option<&Impl>) -> TokenStream {
+    let ident = key.rust;
+    let name = ident.to_string();
+    let resolve = types.resolve(ident);
+    let prefix = format!("cxxbridge1$weak_ptr${}$", resolve.name.to_symbol());
+    let link_null = format!("{}null", prefix);
+    let link_clone = format!("{}clone", prefix);
+    let link_downgrade = format!("{}downgrade", prefix);
+    let link_upgrade = format!("{}upgrade", prefix);
+    let link_drop = format!("{}drop", prefix);
+
+    let (impl_generics, ty_generics) = generics::split_for_impl(key, explicit_impl, resolve);
+
+    let begin_span = explicit_impl.map_or(key.begin_span, |explicit| explicit.impl_token.span);
+    let end_span = explicit_impl.map_or(key.end_span, |explicit| explicit.brace_token.span);
+    let unsafe_token = format_ident!("unsafe", span = begin_span);
+
+    quote_spanned! {end_span=>
+        #unsafe_token impl #impl_generics ::cxx::private::WeakPtrTarget for #ident #ty_generics {
+            #[doc(hidden)]
+            fn __typename(f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
+                f.write_str(#name)
+            }
+            #[doc(hidden)]
+            unsafe fn __null(new: *mut ::std::ffi::c_void) {
+                extern "C" {
+                    #[link_name = #link_null]
+                    fn __null(new: *mut ::std::ffi::c_void);
+                }
+                __null(new);
+            }
+            #[doc(hidden)]
+            unsafe fn __clone(this: *const ::std::ffi::c_void, new: *mut ::std::ffi::c_void) {
+                extern "C" {
+                    #[link_name = #link_clone]
+                    fn __clone(this: *const ::std::ffi::c_void, new: *mut ::std::ffi::c_void);
+                }
+                __clone(this, new);
+            }
+            #[doc(hidden)]
+            unsafe fn __downgrade(shared: *const ::std::ffi::c_void, weak: *mut ::std::ffi::c_void) {
+                extern "C" {
+                    #[link_name = #link_downgrade]
+                    fn __downgrade(shared: *const ::std::ffi::c_void, weak: *mut ::std::ffi::c_void);
+                }
+                __downgrade(shared, weak);
+            }
+            #[doc(hidden)]
+            unsafe fn __upgrade(weak: *const ::std::ffi::c_void, shared: *mut ::std::ffi::c_void) {
+                extern "C" {
+                    #[link_name = #link_upgrade]
+                    fn __upgrade(weak: *const ::std::ffi::c_void, shared: *mut ::std::ffi::c_void);
+                }
+                __upgrade(weak, shared);
+            }
+            #[doc(hidden)]
+            unsafe fn __drop(this: *mut ::std::ffi::c_void) {
+                extern "C" {
+                    #[link_name = #link_drop]
+                    fn __drop(this: *mut ::std::ffi::c_void);
+                }
+                __drop(this);
+            }
+        }
+    }
+}
+
 fn expand_cxx_vector(
-    elem: &ResolvableName,
+    key: NamedImplKey,
     explicit_impl: Option<&Impl>,
     types: &Types,
 ) -> TokenStream {
-    let _ = explicit_impl;
-    let name = elem.rust.to_string();
-    let prefix = format!("cxxbridge05$std$vector${}$", elem.to_symbol(types));
+    let elem = key.rust;
+    let name = elem.to_string();
+    let resolve = types.resolve(elem);
+    let prefix = format!("cxxbridge1$std$vector${}$", resolve.name.to_symbol());
     let link_size = format!("{}size", prefix);
     let link_get_unchecked = format!("{}get_unchecked", prefix);
     let unique_ptr_prefix = format!(
-        "cxxbridge05$unique_ptr$std$vector${}$",
-        elem.to_symbol(types)
+        "cxxbridge1$unique_ptr$std$vector${}$",
+        resolve.name.to_symbol(),
     );
     let link_unique_ptr_null = format!("{}null", unique_ptr_prefix);
     let link_unique_ptr_raw = format!("{}raw", unique_ptr_prefix);
@@ -931,28 +1505,35 @@
     let link_unique_ptr_release = format!("{}release", unique_ptr_prefix);
     let link_unique_ptr_drop = format!("{}drop", unique_ptr_prefix);
 
-    let begin_span =
-        explicit_impl.map_or_else(Span::call_site, |explicit| explicit.impl_token.span);
-    let end_span = explicit_impl.map_or_else(Span::call_site, |explicit| explicit.brace_token.span);
+    let (impl_generics, ty_generics) = generics::split_for_impl(key, explicit_impl, resolve);
+
+    let begin_span = explicit_impl.map_or(key.begin_span, |explicit| explicit.impl_token.span);
+    let end_span = explicit_impl.map_or(key.end_span, |explicit| explicit.brace_token.span);
     let unsafe_token = format_ident!("unsafe", span = begin_span);
 
     quote_spanned! {end_span=>
-        #unsafe_token impl ::cxx::private::VectorElement for #elem {
-            const __NAME: &'static dyn ::std::fmt::Display = &#name;
+        #unsafe_token impl #impl_generics ::cxx::private::VectorElement for #elem #ty_generics {
+            #[doc(hidden)]
+            fn __typename(f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
+                f.write_str(#name)
+            }
+            #[doc(hidden)]
             fn __vector_size(v: &::cxx::CxxVector<Self>) -> usize {
                 extern "C" {
                     #[link_name = #link_size]
-                    fn __vector_size(_: &::cxx::CxxVector<#elem>) -> usize;
+                    fn __vector_size #impl_generics(_: &::cxx::CxxVector<#elem #ty_generics>) -> usize;
                 }
                 unsafe { __vector_size(v) }
             }
-            unsafe fn __get_unchecked(v: &::cxx::CxxVector<Self>, pos: usize) -> *const Self {
+            #[doc(hidden)]
+            unsafe fn __get_unchecked(v: *mut ::cxx::CxxVector<Self>, pos: usize) -> *mut Self {
                 extern "C" {
                     #[link_name = #link_get_unchecked]
-                    fn __get_unchecked(_: &::cxx::CxxVector<#elem>, _: usize) -> *const #elem;
+                    fn __get_unchecked #impl_generics(_: *mut ::cxx::CxxVector<#elem #ty_generics>, _: usize) -> *mut #elem #ty_generics;
                 }
                 __get_unchecked(v, pos)
             }
+            #[doc(hidden)]
             fn __unique_ptr_null() -> *mut ::std::ffi::c_void {
                 extern "C" {
                     #[link_name = #link_unique_ptr_null]
@@ -962,29 +1543,33 @@
                 unsafe { __unique_ptr_null(&mut repr) }
                 repr
             }
+            #[doc(hidden)]
             unsafe fn __unique_ptr_raw(raw: *mut ::cxx::CxxVector<Self>) -> *mut ::std::ffi::c_void {
                 extern "C" {
                     #[link_name = #link_unique_ptr_raw]
-                    fn __unique_ptr_raw(this: *mut *mut ::std::ffi::c_void, raw: *mut ::cxx::CxxVector<#elem>);
+                    fn __unique_ptr_raw #impl_generics(this: *mut *mut ::std::ffi::c_void, raw: *mut ::cxx::CxxVector<#elem #ty_generics>);
                 }
                 let mut repr = ::std::ptr::null_mut::<::std::ffi::c_void>();
                 __unique_ptr_raw(&mut repr, raw);
                 repr
             }
+            #[doc(hidden)]
             unsafe fn __unique_ptr_get(repr: *mut ::std::ffi::c_void) -> *const ::cxx::CxxVector<Self> {
                 extern "C" {
                     #[link_name = #link_unique_ptr_get]
-                    fn __unique_ptr_get(this: *const *mut ::std::ffi::c_void) -> *const ::cxx::CxxVector<#elem>;
+                    fn __unique_ptr_get #impl_generics(this: *const *mut ::std::ffi::c_void) -> *const ::cxx::CxxVector<#elem #ty_generics>;
                 }
                 __unique_ptr_get(&repr)
             }
+            #[doc(hidden)]
             unsafe fn __unique_ptr_release(mut repr: *mut ::std::ffi::c_void) -> *mut ::cxx::CxxVector<Self> {
                 extern "C" {
                     #[link_name = #link_unique_ptr_release]
-                    fn __unique_ptr_release(this: *mut *mut ::std::ffi::c_void) -> *mut ::cxx::CxxVector<#elem>;
+                    fn __unique_ptr_release #impl_generics(this: *mut *mut ::std::ffi::c_void) -> *mut ::cxx::CxxVector<#elem #ty_generics>;
                 }
                 __unique_ptr_release(&mut repr)
             }
+            #[doc(hidden)]
             unsafe fn __unique_ptr_drop(mut repr: *mut ::std::ffi::c_void) {
                 extern "C" {
                     #[link_name = #link_unique_ptr_drop]
@@ -1030,15 +1615,24 @@
                     let inner = expand_extern_type(&ty.inner, types, proper);
                     quote!(&#mutability ::cxx::private::RustVec<#inner>)
                 }
-                inner if proper && types.is_considered_improper_ctype(inner) => match mutability {
-                    None => quote!(*const ::std::ffi::c_void),
-                    Some(_) => quote!(*#mutability ::std::ffi::c_void),
+                inner if proper && types.is_considered_improper_ctype(inner) => match ty.mutable {
+                    false => quote!(*const ::std::ffi::c_void),
+                    true => quote!(*#mutability ::std::ffi::c_void),
                 },
                 _ => quote!(#ty),
             }
         }
+        Type::Ptr(ty) => {
+            if proper && types.is_considered_improper_ctype(&ty.inner) {
+                let mutability = ty.mutability;
+                let constness = ty.constness;
+                quote!(*#mutability #constness ::std::ffi::c_void)
+            } else {
+                quote!(#ty)
+            }
+        }
         Type::Str(_) => quote!(::cxx::private::RustStr),
-        Type::SliceRefU8(_) => quote!(::cxx::private::RustSliceU8),
+        Type::SliceRef(_) => quote!(::cxx::private::RustSlice),
         _ => quote!(#ty),
     }
 }
diff --git a/macro/src/generics.rs b/macro/src/generics.rs
new file mode 100644
index 0000000..3de0330
--- /dev/null
+++ b/macro/src/generics.rs
@@ -0,0 +1,63 @@
+use crate::syntax::instantiate::NamedImplKey;
+use crate::syntax::resolve::Resolution;
+use crate::syntax::Impl;
+use proc_macro2::TokenStream;
+use quote::ToTokens;
+use syn::Token;
+
+pub struct ImplGenerics<'a> {
+    explicit_impl: Option<&'a Impl>,
+    resolve: Resolution<'a>,
+}
+
+pub struct TyGenerics<'a> {
+    key: NamedImplKey<'a>,
+    explicit_impl: Option<&'a Impl>,
+    resolve: Resolution<'a>,
+}
+
+pub fn split_for_impl<'a>(
+    key: NamedImplKey<'a>,
+    explicit_impl: Option<&'a Impl>,
+    resolve: Resolution<'a>,
+) -> (ImplGenerics<'a>, TyGenerics<'a>) {
+    let impl_generics = ImplGenerics {
+        explicit_impl,
+        resolve,
+    };
+    let ty_generics = TyGenerics {
+        key,
+        explicit_impl,
+        resolve,
+    };
+    (impl_generics, ty_generics)
+}
+
+impl<'a> ToTokens for ImplGenerics<'a> {
+    fn to_tokens(&self, tokens: &mut TokenStream) {
+        if let Some(imp) = self.explicit_impl {
+            imp.impl_generics.to_tokens(tokens);
+        } else {
+            self.resolve.generics.to_tokens(tokens);
+        }
+    }
+}
+
+impl<'a> ToTokens for TyGenerics<'a> {
+    fn to_tokens(&self, tokens: &mut TokenStream) {
+        if let Some(imp) = self.explicit_impl {
+            imp.ty_generics.to_tokens(tokens);
+        } else if !self.resolve.generics.lifetimes.is_empty() {
+            let span = self.key.rust.span();
+            self.key
+                .lt_token
+                .unwrap_or_else(|| Token![<](span))
+                .to_tokens(tokens);
+            self.resolve.generics.lifetimes.to_tokens(tokens);
+            self.key
+                .gt_token
+                .unwrap_or_else(|| Token![>](span))
+                .to_tokens(tokens);
+        }
+    }
+}
diff --git a/macro/src/lib.rs b/macro/src/lib.rs
index e55a08a..f8e7903 100644
--- a/macro/src/lib.rs
+++ b/macro/src/lib.rs
@@ -1,22 +1,45 @@
 #![allow(
+    clippy::cast_sign_loss,
+    clippy::default_trait_access,
+    clippy::doc_markdown,
+    clippy::enum_glob_use,
+    clippy::filter_map,
     clippy::inherent_to_string,
+    clippy::items_after_statements,
     clippy::large_enum_variant,
+    clippy::match_bool,
+    clippy::match_same_arms,
+    clippy::module_name_repetitions,
+    clippy::needless_pass_by_value,
     clippy::new_without_default,
+    clippy::nonminimal_bool,
+    clippy::option_if_let_else,
     clippy::or_fun_call,
+    clippy::redundant_else,
+    clippy::shadow_unrelated,
+    clippy::similar_names,
+    clippy::single_match,
+    clippy::single_match_else,
+    clippy::too_many_arguments,
+    clippy::too_many_lines,
     clippy::toplevel_ref_arg,
-    clippy::useless_let_if_seq
+    clippy::useless_let_if_seq,
+    // clippy bug: https://github.com/rust-lang/rust-clippy/issues/6983
+    clippy::wrong_self_convention
 )]
 
 extern crate proc_macro;
 
 mod derive;
 mod expand;
+mod generics;
 mod syntax;
 mod type_id;
 
 use crate::syntax::file::Module;
 use crate::syntax::namespace::Namespace;
 use crate::syntax::qualified::QualifiedName;
+use crate::type_id::Crate;
 use proc_macro::TokenStream;
 use syn::parse::{Parse, ParseStream, Parser, Result};
 use syn::parse_macro_input;
@@ -27,7 +50,7 @@
 /// is intended to be used.
 ///
 /// The only additional thing to note here is namespace support &mdash; if the
-/// types and functions on the `extern "C"` side of our bridge are in a
+/// types and functions on the `extern "C++"` side of our bridge are in a
 /// namespace, specify that namespace as an argument of the cxx::bridge
 /// attribute macro.
 ///
@@ -54,16 +77,22 @@
         .into()
 }
 
+#[doc(hidden)]
 #[proc_macro]
 pub fn type_id(input: TokenStream) -> TokenStream {
-    struct TypeId(QualifiedName);
+    struct TypeId {
+        krate: Crate,
+        path: QualifiedName,
+    }
 
     impl Parse for TypeId {
         fn parse(input: ParseStream) -> Result<Self> {
-            QualifiedName::parse_quoted_or_unquoted(input).map(TypeId)
+            let krate = input.parse().map(Crate::DollarCrate)?;
+            let path = QualifiedName::parse_quoted_or_unquoted(input)?;
+            Ok(TypeId { krate, path })
         }
     }
 
     let arg = parse_macro_input!(input as TypeId);
-    type_id::expand(arg.0).into()
+    type_id::expand(arg.krate, arg.path).into()
 }
diff --git a/macro/src/type_id.rs b/macro/src/type_id.rs
index 5c5d9cc..74f0c1c 100644
--- a/macro/src/type_id.rs
+++ b/macro/src/type_id.rs
@@ -1,9 +1,23 @@
 use crate::syntax::qualified::QualifiedName;
-use proc_macro2::TokenStream;
-use quote::{format_ident, quote};
+use proc_macro2::{TokenStream, TokenTree};
+use quote::{format_ident, quote, ToTokens};
+
+pub enum Crate {
+    Cxx,
+    DollarCrate(TokenTree),
+}
+
+impl ToTokens for Crate {
+    fn to_tokens(&self, tokens: &mut TokenStream) {
+        match self {
+            Crate::Cxx => tokens.extend(quote!(::cxx)),
+            Crate::DollarCrate(krate) => krate.to_tokens(tokens),
+        }
+    }
+}
 
 // "folly::File" => `(f, o, l, l, y, (), F, i, l, e)`
-pub fn expand(arg: QualifiedName) -> TokenStream {
+pub fn expand(krate: Crate, arg: QualifiedName) -> TokenStream {
     let mut ids = Vec::new();
 
     for word in arg.segments {
@@ -14,11 +28,11 @@
             ids.push(match ch {
                 'A'..='Z' | 'a'..='z' => {
                     let t = format_ident!("{}", ch);
-                    quote!(::cxx::#t)
+                    quote!(#krate::#t)
                 }
                 '0'..='9' | '_' => {
                     let t = format_ident!("_{}", ch);
-                    quote!(::cxx::#t)
+                    quote!(#krate::#t)
                 }
                 _ => quote!([(); #ch as _]),
             });
diff --git a/src/cxx.cc b/src/cxx.cc
index cbaecbf..c0ef738 100644
--- a/src/cxx.cc
+++ b/src/cxx.cc
@@ -1,38 +1,60 @@
 #include "../include/cxx.h"
-#include <cassert>
 #include <cstring>
-#include <exception>
 #include <iostream>
 #include <memory>
-#include <stdexcept>
-#include <type_traits>
-#include <vector>
 
 extern "C" {
-const char *cxxbridge05$cxx_string$data(const std::string &s) noexcept {
+void cxxbridge1$cxx_string$init(std::string *s, const std::uint8_t *ptr,
+                                std::size_t len) noexcept {
+  new (s) std::string(reinterpret_cast<const char *>(ptr), len);
+}
+
+void cxxbridge1$cxx_string$destroy(std::string *s) noexcept {
+  using std::string;
+  s->~string();
+}
+
+const char *cxxbridge1$cxx_string$data(const std::string &s) noexcept {
   return s.data();
 }
 
-size_t cxxbridge05$cxx_string$length(const std::string &s) noexcept {
+std::size_t cxxbridge1$cxx_string$length(const std::string &s) noexcept {
   return s.length();
 }
 
+void cxxbridge1$cxx_string$push(std::string &s, const std::uint8_t *ptr,
+                                std::size_t len) noexcept {
+  s.append(reinterpret_cast<const char *>(ptr), len);
+}
+
 // rust::String
-void cxxbridge05$string$new(rust::String *self) noexcept;
-void cxxbridge05$string$clone(rust::String *self,
-                              const rust::String &other) noexcept;
-bool cxxbridge05$string$from(rust::String *self, const char *ptr,
-                             size_t len) noexcept;
-void cxxbridge05$string$drop(rust::String *self) noexcept;
-const char *cxxbridge05$string$ptr(const rust::String *self) noexcept;
-size_t cxxbridge05$string$len(const rust::String *self) noexcept;
+void cxxbridge1$string$new(rust::String *self) noexcept;
+void cxxbridge1$string$clone(rust::String *self,
+                             const rust::String &other) noexcept;
+bool cxxbridge1$string$from(rust::String *self, const char *ptr,
+                            std::size_t len) noexcept;
+void cxxbridge1$string$drop(rust::String *self) noexcept;
+const char *cxxbridge1$string$ptr(const rust::String *self) noexcept;
+std::size_t cxxbridge1$string$len(const rust::String *self) noexcept;
+void cxxbridge1$string$reserve_total(rust::String *self, size_t cap) noexcept;
 
 // rust::Str
-bool cxxbridge05$str$valid(const char *ptr, size_t len) noexcept;
+void cxxbridge1$str$new(rust::Str *self) noexcept;
+void cxxbridge1$str$ref(rust::Str *self, const rust::String *string) noexcept;
+bool cxxbridge1$str$from(rust::Str *self, const char *ptr,
+                         std::size_t len) noexcept;
+const char *cxxbridge1$str$ptr(const rust::Str *self) noexcept;
+std::size_t cxxbridge1$str$len(const rust::Str *self) noexcept;
+
+// rust::Slice
+void cxxbridge1$slice$new(void *self, const void *ptr,
+                          std::size_t len) noexcept;
+void *cxxbridge1$slice$ptr(const void *self) noexcept;
+std::size_t cxxbridge1$slice$len(const void *self) noexcept;
 } // extern "C"
 
 namespace rust {
-inline namespace cxxbridge05 {
+inline namespace cxxbridge1 {
 
 template <typename Exception>
 void panic [[noreturn]] (const char *msg) {
@@ -44,23 +66,22 @@
 #endif
 }
 
-template void panic<std::out_of_range>[[noreturn]] (const char *msg);
+template void panic<std::out_of_range> [[noreturn]] (const char *msg);
 
-String::String() noexcept { cxxbridge05$string$new(this); }
+String::String() noexcept { cxxbridge1$string$new(this); }
 
 String::String(const String &other) noexcept {
-  cxxbridge05$string$clone(this, other);
+  cxxbridge1$string$clone(this, other);
 }
 
-String::String(String &&other) noexcept {
-  this->repr = other.repr;
-  cxxbridge05$string$new(&other);
+String::String(String &&other) noexcept : repr(other.repr) {
+  cxxbridge1$string$new(&other);
 }
 
-String::~String() noexcept { cxxbridge05$string$drop(this); }
+String::~String() noexcept { cxxbridge1$string$drop(this); }
 
-static void initString(String *self, const char *s, size_t len) {
-  if (!cxxbridge05$string$from(self, s, len)) {
+static void initString(String *self, const char *s, std::size_t len) {
+  if (!cxxbridge1$string$from(self, s, len)) {
     panic<std::invalid_argument>("data for rust::String is not utf-8");
   }
 }
@@ -72,27 +93,25 @@
   initString(this, s, std::strlen(s));
 }
 
-String::String(const char *s, size_t len) {
+String::String(const char *s, std::size_t len) {
   assert(s != nullptr || len == 0);
   initString(this,
              s == nullptr && len == 0 ? reinterpret_cast<const char *>(1) : s,
              len);
 }
 
-String &String::operator=(const String &other) noexcept {
+String &String::operator=(const String &other) &noexcept {
   if (this != &other) {
-    cxxbridge05$string$drop(this);
-    cxxbridge05$string$clone(this, other);
+    cxxbridge1$string$drop(this);
+    cxxbridge1$string$clone(this, other);
   }
   return *this;
 }
 
-String &String::operator=(String &&other) noexcept {
-  if (this != &other) {
-    cxxbridge05$string$drop(this);
-    this->repr = other.repr;
-    cxxbridge05$string$new(&other);
-  }
+String &String::operator=(String &&other) &noexcept {
+  cxxbridge1$string$drop(this);
+  this->repr = other.repr;
+  cxxbridge1$string$new(&other);
   return *this;
 }
 
@@ -101,12 +120,71 @@
 }
 
 const char *String::data() const noexcept {
-  return cxxbridge05$string$ptr(this);
+  return cxxbridge1$string$ptr(this);
 }
 
-size_t String::size() const noexcept { return cxxbridge05$string$len(this); }
+std::size_t String::size() const noexcept {
+  return cxxbridge1$string$len(this);
+}
 
-size_t String::length() const noexcept { return cxxbridge05$string$len(this); }
+std::size_t String::length() const noexcept {
+  return cxxbridge1$string$len(this);
+}
+
+const char *String::c_str() noexcept {
+  auto len = this->length();
+  cxxbridge1$string$reserve_total(this, len + 1);
+  auto ptr = this->data();
+  const_cast<char *>(ptr)[len] = '\0';
+  return ptr;
+}
+
+String::iterator String::begin() noexcept {
+  return const_cast<char *>(this->data());
+}
+
+String::iterator String::end() noexcept {
+  return const_cast<char *>(this->data()) + this->size();
+}
+
+String::const_iterator String::begin() const noexcept { return this->cbegin(); }
+
+String::const_iterator String::end() const noexcept { return this->cend(); }
+
+String::const_iterator String::cbegin() const noexcept { return this->data(); }
+
+String::const_iterator String::cend() const noexcept {
+  return this->data() + this->size();
+}
+
+bool String::operator==(const String &rhs) const noexcept {
+  return rust::Str(*this) == rust::Str(rhs);
+}
+
+bool String::operator!=(const String &rhs) const noexcept {
+  return rust::Str(*this) != rust::Str(rhs);
+}
+
+bool String::operator<(const String &rhs) const noexcept {
+  return rust::Str(*this) < rust::Str(rhs);
+}
+
+bool String::operator<=(const String &rhs) const noexcept {
+  return rust::Str(*this) <= rust::Str(rhs);
+}
+
+bool String::operator>(const String &rhs) const noexcept {
+  return rust::Str(*this) > rust::Str(rhs);
+}
+
+bool String::operator>=(const String &rhs) const noexcept {
+  return rust::Str(*this) >= rust::Str(rhs);
+}
+
+void String::swap(String &rhs) noexcept {
+  using std::swap;
+  swap(this->repr, rhs.repr);
+}
 
 String::String(unsafe_bitcopy_t, const String &bits) noexcept
     : repr(bits.repr) {}
@@ -116,55 +194,178 @@
   return os;
 }
 
-Str::Str() noexcept : ptr(reinterpret_cast<const char *>(1)), len(0) {}
+Str::Str() noexcept { cxxbridge1$str$new(this); }
 
-static void initStr(const char *ptr, size_t len) {
-  if (!cxxbridge05$str$valid(ptr, len)) {
+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) : ptr(s.data()), len(s.length()) {
-  initStr(this->ptr, this->len);
-}
+Str::Str(const std::string &s) { initStr(this, s.data(), s.length()); }
 
-Str::Str(const char *s) : ptr(s), len(std::strlen(s)) {
+Str::Str(const char *s) {
   assert(s != nullptr);
-  initStr(this->ptr, this->len);
+  initStr(this, s, std::strlen(s));
 }
 
-Str::Str(const char *s, size_t len)
-    : ptr(s == nullptr && len == 0 ? reinterpret_cast<const char *>(1) : s),
-      len(len) {
+Str::Str(const char *s, std::size_t len) {
   assert(s != nullptr || len == 0);
-  initStr(this->ptr, this->len);
+  initStr(this,
+          s == nullptr && len == 0 ? reinterpret_cast<const char *>(1) : s,
+          len);
 }
 
 Str::operator std::string() const {
   return std::string(this->data(), this->size());
 }
 
+const char *Str::data() const noexcept { return cxxbridge1$str$ptr(this); }
+
+std::size_t Str::size() const noexcept { return cxxbridge1$str$len(this); }
+
+std::size_t Str::length() const noexcept { return this->size(); }
+
+Str::const_iterator Str::begin() const noexcept { return this->cbegin(); }
+
+Str::const_iterator Str::end() const noexcept { return this->cend(); }
+
+Str::const_iterator Str::cbegin() const noexcept { return this->data(); }
+
+Str::const_iterator Str::cend() const noexcept {
+  return this->data() + this->size();
+}
+
+bool Str::operator==(const Str &rhs) const noexcept {
+  return this->size() == rhs.size() &&
+         std::equal(this->begin(), this->end(), rhs.begin());
+}
+
+bool Str::operator!=(const Str &rhs) const noexcept { return !(*this == rhs); }
+
+bool Str::operator<(const Str &rhs) const noexcept {
+  return std::lexicographical_compare(this->begin(), this->end(), rhs.begin(),
+                                      rhs.end());
+}
+
+bool Str::operator<=(const Str &rhs) const noexcept {
+  // std::mismatch(this->begin(), this->end(), rhs.begin(), rhs.end()), except
+  // without Undefined Behavior on C++11 if rhs is shorter than *this.
+  const_iterator liter = this->begin(), lend = this->end(), riter = rhs.begin(),
+                 rend = rhs.end();
+  while (liter != lend && riter != rend && *liter == *riter) {
+    ++liter, ++riter;
+  }
+  if (liter == lend) {
+    return true; // equal or *this is a prefix of rhs
+  } else if (riter == rend) {
+    return false; // rhs is a prefix of *this
+  } else {
+    return *liter <= *riter;
+  }
+}
+
+bool Str::operator>(const Str &rhs) const noexcept { return rhs < *this; }
+
+bool Str::operator>=(const Str &rhs) const noexcept { return rhs <= *this; }
+
+void Str::swap(Str &rhs) noexcept {
+  using std::swap;
+  swap(this->repr, rhs.repr);
+}
+
 std::ostream &operator<<(std::ostream &os, const Str &s) {
   os.write(s.data(), s.size());
   return os;
 }
 
+void sliceInit(void *self, const void *ptr, std::size_t len) noexcept {
+  cxxbridge1$slice$new(self, ptr, len);
+}
+
+void *slicePtr(const void *self) noexcept { return cxxbridge1$slice$ptr(self); }
+
+std::size_t sliceLen(const void *self) noexcept {
+  return cxxbridge1$slice$len(self);
+}
+
+// Rust specifies that usize is ABI compatible with C's uintptr_t.
+// https://rust-lang.github.io/unsafe-code-guidelines/layout/scalars.html#isize-and-usize
+// However there is no direct Rust equivalent for size_t. C does not guarantee
+// that size_t and uintptr_t are compatible. In practice though, on all
+// platforms supported by Rust, they are identical for ABI purposes. See the
+// libc crate which unconditionally defines libc::size_t = usize. We expect the
+// same here and these assertions are just here to explicitly document that.
+// *Note that no assumption is made about C++ name mangling of signatures
+// containing these types, not here nor anywhere in CXX.*
+static_assert(sizeof(std::size_t) == sizeof(std::uintptr_t),
+              "unsupported size_t size");
+static_assert(alignof(std::size_t) == alignof(std::uintptr_t),
+              "unsupported size_t alignment");
+static_assert(sizeof(rust::isize) == sizeof(std::intptr_t),
+              "unsupported ssize_t size");
+static_assert(alignof(rust::isize) == alignof(std::intptr_t),
+              "unsupported ssize_t alignment");
+
 static_assert(std::is_trivially_copy_constructible<Str>::value,
               "trivial Str(const Str &)");
 static_assert(std::is_trivially_copy_assignable<Str>::value,
               "trivial operator=(const Str &)");
 static_assert(std::is_trivially_destructible<Str>::value, "trivial ~Str()");
 
-extern "C" {
-const char *cxxbridge05$error(const char *ptr, size_t len) {
+static_assert(
+    std::is_trivially_copy_constructible<Slice<const std::uint8_t>>::value,
+    "trivial Slice(const Slice &)");
+static_assert(
+    std::is_trivially_move_constructible<Slice<const std::uint8_t>>::value,
+    "trivial Slice(Slice &&)");
+static_assert(
+    std::is_trivially_copy_assignable<Slice<const std::uint8_t>>::value,
+    "trivial Slice::operator=(const Slice &) for const slices");
+static_assert(
+    std::is_trivially_move_assignable<Slice<const std::uint8_t>>::value,
+    "trivial Slice::operator=(Slice &&)");
+static_assert(std::is_trivially_destructible<Slice<const std::uint8_t>>::value,
+              "trivial ~Slice()");
+
+static_assert(std::is_trivially_copy_constructible<Slice<std::uint8_t>>::value,
+              "trivial Slice(const Slice &)");
+static_assert(std::is_trivially_move_constructible<Slice<std::uint8_t>>::value,
+              "trivial Slice(Slice &&)");
+static_assert(!std::is_copy_assignable<Slice<std::uint8_t>>::value,
+              "delete Slice::operator=(const Slice &) for mut slices");
+static_assert(std::is_trivially_move_assignable<Slice<std::uint8_t>>::value,
+              "trivial Slice::operator=(Slice &&)");
+static_assert(std::is_trivially_destructible<Slice<std::uint8_t>>::value,
+              "trivial ~Slice()");
+
+static_assert(std::is_same<Vec<std::uint8_t>::const_iterator,
+                           Vec<const std::uint8_t>::iterator>::value,
+              "Vec<T>::const_iterator == Vec<const T>::iterator");
+static_assert(std::is_same<Vec<const std::uint8_t>::const_iterator,
+                           Vec<const std::uint8_t>::iterator>::value,
+              "Vec<const T>::const_iterator == Vec<const T>::iterator");
+static_assert(!std::is_same<Vec<std::uint8_t>::const_iterator,
+                            Vec<std::uint8_t>::iterator>::value,
+              "Vec<T>::const_iterator != Vec<T>::iterator");
+
+static const char *errorCopy(const char *ptr, std::size_t len) {
   char *copy = new char[len];
-  std::strncpy(copy, ptr, len);
+  std::memcpy(copy, ptr, len);
   return copy;
 }
+
+extern "C" {
+const char *cxxbridge1$error(const char *ptr, std::size_t len) noexcept {
+  return errorCopy(ptr, len);
+}
 } // extern "C"
 
 Error::Error(const Error &other)
-    : std::exception(other), msg(cxxbridge05$error(other.msg, other.len)),
+    : std::exception(other),
+      msg(other.msg ? errorCopy(other.msg, other.len) : nullptr),
       len(other.len) {}
 
 Error::Error(Error &&other) noexcept
@@ -175,165 +376,263 @@
 
 Error::~Error() noexcept { delete[] this->msg; }
 
-Error &Error::operator=(const Error &other) {
+Error &Error::operator=(const Error &other) & {
   if (this != &other) {
     std::exception::operator=(other);
     delete[] this->msg;
     this->msg = nullptr;
-    this->msg = cxxbridge05$error(other.msg, other.len);
-    this->len = other.len;
+    if (other.msg) {
+      this->msg = errorCopy(other.msg, other.len);
+      this->len = other.len;
+    }
   }
   return *this;
 }
 
-Error &Error::operator=(Error &&other) noexcept {
-  if (this != &other) {
-    std::exception::operator=(std::move(other));
-    this->msg = other.msg;
-    this->len = other.len;
-    other.msg = nullptr;
-    other.len = 0;
-  }
+Error &Error::operator=(Error &&other) &noexcept {
+  std::exception::operator=(std::move(other));
+  this->msg = other.msg;
+  this->len = other.len;
+  other.msg = nullptr;
+  other.len = 0;
   return *this;
 }
 
 const char *Error::what() const noexcept { return this->msg; }
 
-} // namespace cxxbridge05
+namespace {
+template <typename T>
+union MaybeUninit {
+  T value;
+  MaybeUninit() {}
+  ~MaybeUninit() {}
+};
+} // namespace
+
+namespace detail {
+// On some platforms size_t is the same C++ type as one of the sized integer
+// types; on others it is a distinct type. Only in the latter case do we need to
+// define a specialized impl of rust::Vec<size_t>, because in the former case it
+// would collide with one of the other specializations.
+using usize_if_unique =
+    typename std::conditional<std::is_same<size_t, uint64_t>::value ||
+                                  std::is_same<size_t, uint32_t>::value,
+                              struct usize_ignore, size_t>::type;
+using isize_if_unique =
+    typename std::conditional<std::is_same<rust::isize, int64_t>::value ||
+                                  std::is_same<rust::isize, int32_t>::value,
+                              struct isize_ignore, rust::isize>::type;
+} // namespace detail
+
+} // namespace cxxbridge1
 } // namespace rust
 
 extern "C" {
-void cxxbridge05$unique_ptr$std$string$null(
+void cxxbridge1$unique_ptr$std$string$null(
     std::unique_ptr<std::string> *ptr) noexcept {
   new (ptr) std::unique_ptr<std::string>();
 }
-void cxxbridge05$unique_ptr$std$string$raw(std::unique_ptr<std::string> *ptr,
-                                           std::string *raw) noexcept {
+void cxxbridge1$unique_ptr$std$string$raw(std::unique_ptr<std::string> *ptr,
+                                          std::string *raw) noexcept {
   new (ptr) std::unique_ptr<std::string>(raw);
 }
-const std::string *cxxbridge05$unique_ptr$std$string$get(
+const std::string *cxxbridge1$unique_ptr$std$string$get(
     const std::unique_ptr<std::string> &ptr) noexcept {
   return ptr.get();
 }
-std::string *cxxbridge05$unique_ptr$std$string$release(
+std::string *cxxbridge1$unique_ptr$std$string$release(
     std::unique_ptr<std::string> &ptr) noexcept {
   return ptr.release();
 }
-void cxxbridge05$unique_ptr$std$string$drop(
+void cxxbridge1$unique_ptr$std$string$drop(
     std::unique_ptr<std::string> *ptr) noexcept {
   ptr->~unique_ptr();
 }
 } // extern "C"
 
+namespace {
+const std::size_t kMaxExpectedWordsInString = 8;
+static_assert(alignof(std::string) <= alignof(void *),
+              "unexpectedly large std::string alignment");
+static_assert(sizeof(std::string) <= kMaxExpectedWordsInString * sizeof(void *),
+              "unexpectedly large std::string size");
+} // namespace
+
 #define STD_VECTOR_OPS(RUST_TYPE, CXX_TYPE)                                    \
-  size_t cxxbridge05$std$vector$##RUST_TYPE##$size(                            \
+  std::size_t cxxbridge1$std$vector$##RUST_TYPE##$size(                        \
       const std::vector<CXX_TYPE> &s) noexcept {                               \
     return s.size();                                                           \
   }                                                                            \
-  const CXX_TYPE *cxxbridge05$std$vector$##RUST_TYPE##$get_unchecked(          \
-      const std::vector<CXX_TYPE> &s, size_t pos) noexcept {                   \
-    return &s[pos];                                                            \
+  CXX_TYPE *cxxbridge1$std$vector$##RUST_TYPE##$get_unchecked(                 \
+      std::vector<CXX_TYPE> *s, std::size_t pos) noexcept {                    \
+    return &(*s)[pos];                                                         \
   }                                                                            \
-  void cxxbridge05$unique_ptr$std$vector$##RUST_TYPE##$null(                   \
+  void cxxbridge1$unique_ptr$std$vector$##RUST_TYPE##$null(                    \
       std::unique_ptr<std::vector<CXX_TYPE>> *ptr) noexcept {                  \
     new (ptr) std::unique_ptr<std::vector<CXX_TYPE>>();                        \
   }                                                                            \
-  void cxxbridge05$unique_ptr$std$vector$##RUST_TYPE##$raw(                    \
+  void cxxbridge1$unique_ptr$std$vector$##RUST_TYPE##$raw(                     \
       std::unique_ptr<std::vector<CXX_TYPE>> *ptr,                             \
       std::vector<CXX_TYPE> *raw) noexcept {                                   \
     new (ptr) std::unique_ptr<std::vector<CXX_TYPE>>(raw);                     \
   }                                                                            \
   const std::vector<CXX_TYPE>                                                  \
-      *cxxbridge05$unique_ptr$std$vector$##RUST_TYPE##$get(                    \
+      *cxxbridge1$unique_ptr$std$vector$##RUST_TYPE##$get(                     \
           const std::unique_ptr<std::vector<CXX_TYPE>> &ptr) noexcept {        \
     return ptr.get();                                                          \
   }                                                                            \
   std::vector<CXX_TYPE>                                                        \
-      *cxxbridge05$unique_ptr$std$vector$##RUST_TYPE##$release(                \
+      *cxxbridge1$unique_ptr$std$vector$##RUST_TYPE##$release(                 \
           std::unique_ptr<std::vector<CXX_TYPE>> &ptr) noexcept {              \
     return ptr.release();                                                      \
   }                                                                            \
-  void cxxbridge05$unique_ptr$std$vector$##RUST_TYPE##$drop(                   \
+  void cxxbridge1$unique_ptr$std$vector$##RUST_TYPE##$drop(                    \
       std::unique_ptr<std::vector<CXX_TYPE>> *ptr) noexcept {                  \
     ptr->~unique_ptr();                                                        \
   }
 
 #define RUST_VEC_EXTERNS(RUST_TYPE, CXX_TYPE)                                  \
-  void cxxbridge05$rust_vec$##RUST_TYPE##$new(                                 \
+  void cxxbridge1$rust_vec$##RUST_TYPE##$new(                                  \
       rust::Vec<CXX_TYPE> *ptr) noexcept;                                      \
-  void cxxbridge05$rust_vec$##RUST_TYPE##$drop(                                \
+  void cxxbridge1$rust_vec$##RUST_TYPE##$drop(                                 \
       rust::Vec<CXX_TYPE> *ptr) noexcept;                                      \
-  size_t cxxbridge05$rust_vec$##RUST_TYPE##$len(                               \
+  std::size_t cxxbridge1$rust_vec$##RUST_TYPE##$len(                           \
       const rust::Vec<CXX_TYPE> *ptr) noexcept;                                \
-  const CXX_TYPE *cxxbridge05$rust_vec$##RUST_TYPE##$data(                     \
+  std::size_t cxxbridge1$rust_vec$##RUST_TYPE##$capacity(                      \
       const rust::Vec<CXX_TYPE> *ptr) noexcept;                                \
-  void cxxbridge05$rust_vec$##RUST_TYPE##$reserve_total(                       \
-      rust::Vec<CXX_TYPE> *ptr, size_t cap) noexcept;                          \
-  void cxxbridge05$rust_vec$##RUST_TYPE##$set_len(rust::Vec<CXX_TYPE> *ptr,    \
-                                                  size_t len) noexcept;        \
-  size_t cxxbridge05$rust_vec$##RUST_TYPE##$stride() noexcept;
+  const CXX_TYPE *cxxbridge1$rust_vec$##RUST_TYPE##$data(                      \
+      const rust::Vec<CXX_TYPE> *ptr) noexcept;                                \
+  void cxxbridge1$rust_vec$##RUST_TYPE##$reserve_total(                        \
+      rust::Vec<CXX_TYPE> *ptr, std::size_t cap) noexcept;                     \
+  void cxxbridge1$rust_vec$##RUST_TYPE##$set_len(rust::Vec<CXX_TYPE> *ptr,     \
+                                                 std::size_t len) noexcept;
 
 #define RUST_VEC_OPS(RUST_TYPE, CXX_TYPE)                                      \
   template <>                                                                  \
   Vec<CXX_TYPE>::Vec() noexcept {                                              \
-    cxxbridge05$rust_vec$##RUST_TYPE##$new(this);                              \
+    cxxbridge1$rust_vec$##RUST_TYPE##$new(this);                               \
   }                                                                            \
   template <>                                                                  \
   void Vec<CXX_TYPE>::drop() noexcept {                                        \
-    return cxxbridge05$rust_vec$##RUST_TYPE##$drop(this);                      \
+    return cxxbridge1$rust_vec$##RUST_TYPE##$drop(this);                       \
   }                                                                            \
   template <>                                                                  \
-  size_t Vec<CXX_TYPE>::size() const noexcept {                                \
-    return cxxbridge05$rust_vec$##RUST_TYPE##$len(this);                       \
+  std::size_t Vec<CXX_TYPE>::size() const noexcept {                           \
+    return cxxbridge1$rust_vec$##RUST_TYPE##$len(this);                        \
+  }                                                                            \
+  template <>                                                                  \
+  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 cxxbridge05$rust_vec$##RUST_TYPE##$data(this);                      \
+    return cxxbridge1$rust_vec$##RUST_TYPE##$data(this);                       \
   }                                                                            \
   template <>                                                                  \
-  void Vec<CXX_TYPE>::reserve_total(size_t cap) noexcept {                     \
-    cxxbridge05$rust_vec$##RUST_TYPE##$reserve_total(this, cap);               \
+  void Vec<CXX_TYPE>::reserve_total(std::size_t cap) noexcept {                \
+    cxxbridge1$rust_vec$##RUST_TYPE##$reserve_total(this, cap);                \
   }                                                                            \
   template <>                                                                  \
-  void Vec<CXX_TYPE>::set_len(size_t len) noexcept {                           \
-    cxxbridge05$rust_vec$##RUST_TYPE##$set_len(this, len);                     \
+  void Vec<CXX_TYPE>::set_len(std::size_t len) noexcept {                      \
+    cxxbridge1$rust_vec$##RUST_TYPE##$set_len(this, len);                      \
+  }
+
+#define SHARED_PTR_OPS(RUST_TYPE, CXX_TYPE)                                    \
+  static_assert(sizeof(std::shared_ptr<CXX_TYPE>) == 2 * sizeof(void *), "");  \
+  static_assert(alignof(std::shared_ptr<CXX_TYPE>) == alignof(void *), "");    \
+  void cxxbridge1$std$shared_ptr$##RUST_TYPE##$null(                           \
+      std::shared_ptr<CXX_TYPE> *ptr) noexcept {                               \
+    new (ptr) std::shared_ptr<CXX_TYPE>();                                     \
   }                                                                            \
-  template <>                                                                  \
-  size_t Vec<CXX_TYPE>::stride() noexcept {                                    \
-    return cxxbridge05$rust_vec$##RUST_TYPE##$stride();                        \
+  CXX_TYPE *cxxbridge1$std$shared_ptr$##RUST_TYPE##$uninit(                    \
+      std::shared_ptr<CXX_TYPE> *ptr) noexcept {                               \
+    CXX_TYPE *uninit =                                                         \
+        reinterpret_cast<CXX_TYPE *>(new rust::MaybeUninit<CXX_TYPE>);         \
+    new (ptr) std::shared_ptr<CXX_TYPE>(uninit);                               \
+    return uninit;                                                             \
+  }                                                                            \
+  void cxxbridge1$std$shared_ptr$##RUST_TYPE##$clone(                          \
+      const std::shared_ptr<CXX_TYPE> &self,                                   \
+      std::shared_ptr<CXX_TYPE> *ptr) noexcept {                               \
+    new (ptr) std::shared_ptr<CXX_TYPE>(self);                                 \
+  }                                                                            \
+  const CXX_TYPE *cxxbridge1$std$shared_ptr$##RUST_TYPE##$get(                 \
+      const std::shared_ptr<CXX_TYPE> &self) noexcept {                        \
+    return self.get();                                                         \
+  }                                                                            \
+  void cxxbridge1$std$shared_ptr$##RUST_TYPE##$drop(                           \
+      const std::shared_ptr<CXX_TYPE> *self) noexcept {                        \
+    self->~shared_ptr();                                                       \
+  }                                                                            \
+  static_assert(sizeof(std::weak_ptr<CXX_TYPE>) == 2 * sizeof(void *), "");    \
+  static_assert(alignof(std::weak_ptr<CXX_TYPE>) == alignof(void *), "");      \
+  void cxxbridge1$std$weak_ptr$##RUST_TYPE##$null(                             \
+      std::weak_ptr<CXX_TYPE> *ptr) noexcept {                                 \
+    new (ptr) std::weak_ptr<CXX_TYPE>();                                       \
+  }                                                                            \
+  void cxxbridge1$std$weak_ptr$##RUST_TYPE##$clone(                            \
+      const std::weak_ptr<CXX_TYPE> &self,                                     \
+      std::weak_ptr<CXX_TYPE> *ptr) noexcept {                                 \
+    new (ptr) std::weak_ptr<CXX_TYPE>(self);                                   \
+  }                                                                            \
+  void cxxbridge1$std$weak_ptr$##RUST_TYPE##$downgrade(                        \
+      const std::shared_ptr<CXX_TYPE> &shared,                                 \
+      std::weak_ptr<CXX_TYPE> *weak) noexcept {                                \
+    new (weak) std::weak_ptr<CXX_TYPE>(shared);                                \
+  }                                                                            \
+  void cxxbridge1$std$weak_ptr$##RUST_TYPE##$upgrade(                          \
+      const std::weak_ptr<CXX_TYPE> &weak,                                     \
+      std::shared_ptr<CXX_TYPE> *shared) noexcept {                            \
+    new (shared) std::shared_ptr<CXX_TYPE>(weak.lock());                       \
+  }                                                                            \
+  void cxxbridge1$std$weak_ptr$##RUST_TYPE##$drop(                             \
+      const std::weak_ptr<CXX_TYPE> *self) noexcept {                          \
+    self->~weak_ptr();                                                         \
   }
 
 // Usize and isize are the same type as one of the below.
 #define FOR_EACH_NUMERIC(MACRO)                                                \
-  MACRO(u8, uint8_t)                                                           \
-  MACRO(u16, uint16_t)                                                         \
-  MACRO(u32, uint32_t)                                                         \
-  MACRO(u64, uint64_t)                                                         \
-  MACRO(i8, int8_t)                                                            \
-  MACRO(i16, int16_t)                                                          \
-  MACRO(i32, int32_t)                                                          \
-  MACRO(i64, int64_t)                                                          \
+  MACRO(u8, std::uint8_t)                                                      \
+  MACRO(u16, std::uint16_t)                                                    \
+  MACRO(u32, std::uint32_t)                                                    \
+  MACRO(u64, std::uint64_t)                                                    \
+  MACRO(i8, std::int8_t)                                                       \
+  MACRO(i16, std::int16_t)                                                     \
+  MACRO(i32, std::int32_t)                                                     \
+  MACRO(i64, std::int64_t)                                                     \
   MACRO(f32, float)                                                            \
   MACRO(f64, double)
 
 #define FOR_EACH_STD_VECTOR(MACRO)                                             \
   FOR_EACH_NUMERIC(MACRO)                                                      \
-  MACRO(usize, size_t)                                                         \
+  MACRO(usize, std::size_t)                                                    \
   MACRO(isize, rust::isize)                                                    \
   MACRO(string, std::string)
 
 #define FOR_EACH_RUST_VEC(MACRO)                                               \
   FOR_EACH_NUMERIC(MACRO)                                                      \
   MACRO(bool, bool)                                                            \
-  MACRO(string, rust::String)
+  MACRO(char, char)                                                            \
+  MACRO(usize, rust::detail::usize_if_unique)                                  \
+  MACRO(isize, rust::detail::isize_if_unique)                                  \
+  MACRO(string, rust::String)                                                  \
+  MACRO(str, rust::Str)
+
+#define FOR_EACH_SHARED_PTR(MACRO)                                             \
+  FOR_EACH_NUMERIC(MACRO)                                                      \
+  MACRO(bool, bool)                                                            \
+  MACRO(usize, std::size_t)                                                    \
+  MACRO(isize, rust::isize)                                                    \
+  MACRO(string, std::string)
 
 extern "C" {
 FOR_EACH_STD_VECTOR(STD_VECTOR_OPS)
 FOR_EACH_RUST_VEC(RUST_VEC_EXTERNS)
+FOR_EACH_SHARED_PTR(SHARED_PTR_OPS)
 } // extern "C"
 
 namespace rust {
-inline namespace cxxbridge05 {
+inline namespace cxxbridge1 {
 FOR_EACH_RUST_VEC(RUST_VEC_OPS)
-} // namespace cxxbridge05
+} // namespace cxxbridge1
 } // namespace rust
diff --git a/src/cxx_string.rs b/src/cxx_string.rs
index 7b47feb..dce7053 100644
--- a/src/cxx_string.rs
+++ b/src/cxx_string.rs
@@ -1,14 +1,26 @@
+use crate::actually_private::Private;
 use alloc::borrow::Cow;
 use alloc::string::String;
+use core::cmp::Ordering;
 use core::fmt::{self, Debug, Display};
+use core::hash::{Hash, Hasher};
+use core::marker::{PhantomData, PhantomPinned};
+use core::mem::MaybeUninit;
+use core::pin::Pin;
 use core::slice;
 use core::str::{self, Utf8Error};
 
 extern "C" {
-    #[link_name = "cxxbridge05$cxx_string$data"]
-    fn string_data(_: &CxxString) -> *const u8;
-    #[link_name = "cxxbridge05$cxx_string$length"]
-    fn string_length(_: &CxxString) -> usize;
+    #[link_name = "cxxbridge1$cxx_string$init"]
+    fn string_init(this: &mut MaybeUninit<CxxString>, ptr: *const u8, len: usize);
+    #[link_name = "cxxbridge1$cxx_string$destroy"]
+    fn string_destroy(this: &mut MaybeUninit<CxxString>);
+    #[link_name = "cxxbridge1$cxx_string$data"]
+    fn string_data(this: &CxxString) -> *const u8;
+    #[link_name = "cxxbridge1$cxx_string$length"]
+    fn string_length(this: &CxxString) -> usize;
+    #[link_name = "cxxbridge1$cxx_string$push"]
+    fn string_push(this: Pin<&mut CxxString>, ptr: *const u8, len: usize);
 }
 
 /// Binding to C++ `std::string`.
@@ -24,9 +36,58 @@
 #[repr(C)]
 pub struct CxxString {
     _private: [u8; 0],
+    _pinned: PhantomData<PhantomPinned>,
+}
+
+/// Construct a C++ std::string on the Rust stack.
+///
+/// # Syntax
+///
+/// In statement position:
+///
+/// ```
+/// # use cxx::let_cxx_string;
+/// # let expression = "";
+/// let_cxx_string!(var = expression);
+/// ```
+///
+/// The `expression` may have any type that implements `AsRef<[u8]>`. Commonly
+/// it will be a string literal, but for example `&[u8]` and `String` would work
+/// as well.
+///
+/// The macro expands to something resembling `let $var: Pin<&mut CxxString> =
+/// /*???*/;`. The resulting [`Pin`] can be deref'd to `&CxxString` as needed.
+///
+/// # Example
+///
+/// ```
+/// use cxx::{let_cxx_string, CxxString};
+///
+/// fn f(s: &CxxString) {/* ... */}
+///
+/// fn main() {
+///     let_cxx_string!(s = "example");
+///     f(&s);
+/// }
+/// ```
+#[macro_export]
+macro_rules! let_cxx_string {
+    ($var:ident = $value:expr $(,)?) => {
+        let mut cxx_stack_string = $crate::private::StackString::new();
+        #[allow(unused_mut, unused_unsafe)]
+        let mut $var = match $value {
+            let_cxx_string => unsafe { cxx_stack_string.init(let_cxx_string) },
+        };
+    };
 }
 
 impl CxxString {
+    /// `CxxString` is not constructible via `new`. Instead, use the
+    /// [`let_cxx_string!`] macro.
+    pub fn new<T: Private>() -> Self {
+        unreachable!()
+    }
+
     /// Returns the length of the string in bytes.
     ///
     /// Matches the behavior of C++ [std::string::size][size].
@@ -82,6 +143,16 @@
     pub fn to_string_lossy(&self) -> Cow<str> {
         String::from_utf8_lossy(self.as_bytes())
     }
+
+    /// Appends a given string slice onto the end of this C++ string.
+    pub fn push_str(self: Pin<&mut Self>, s: &str) {
+        self.push_bytes(s.as_bytes());
+    }
+
+    /// Appends arbitrary bytes onto the end of this C++ string.
+    pub fn push_bytes(self: Pin<&mut Self>, bytes: &[u8]) {
+        unsafe { string_push(self, bytes.as_ptr(), bytes.len()) }
+    }
 }
 
 impl Display for CxxString {
@@ -97,7 +168,7 @@
 }
 
 impl PartialEq for CxxString {
-    fn eq(&self, other: &CxxString) -> bool {
+    fn eq(&self, other: &Self) -> bool {
         self.as_bytes() == other.as_bytes()
     }
 }
@@ -113,3 +184,55 @@
         self.as_bytes() == other.as_bytes()
     }
 }
+
+impl Eq for CxxString {}
+
+impl PartialOrd for CxxString {
+    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
+        self.as_bytes().partial_cmp(other.as_bytes())
+    }
+}
+
+impl Ord for CxxString {
+    fn cmp(&self, other: &Self) -> Ordering {
+        self.as_bytes().cmp(other.as_bytes())
+    }
+}
+
+impl Hash for CxxString {
+    fn hash<H: Hasher>(&self, state: &mut H) {
+        self.as_bytes().hash(state);
+    }
+}
+
+#[doc(hidden)]
+#[repr(C)]
+pub struct StackString {
+    // Static assertions in cxx.cc validate that this is large enough and
+    // aligned enough.
+    space: MaybeUninit<[usize; 8]>,
+}
+
+impl StackString {
+    pub fn new() -> Self {
+        StackString {
+            space: MaybeUninit::uninit(),
+        }
+    }
+
+    pub unsafe fn init(&mut self, value: impl AsRef<[u8]>) -> Pin<&mut CxxString> {
+        let value = value.as_ref();
+        let this = &mut *self.space.as_mut_ptr().cast::<MaybeUninit<CxxString>>();
+        string_init(this, value.as_ptr(), value.len());
+        Pin::new_unchecked(&mut *this.as_mut_ptr())
+    }
+}
+
+impl Drop for StackString {
+    fn drop(&mut self) {
+        unsafe {
+            let this = &mut *self.space.as_mut_ptr().cast::<MaybeUninit<CxxString>>();
+            string_destroy(this);
+        }
+    }
+}
diff --git a/src/cxx_vector.rs b/src/cxx_vector.rs
index 5fb0807..1643341 100644
--- a/src/cxx_vector.rs
+++ b/src/cxx_vector.rs
@@ -1,8 +1,15 @@
-use crate::cxx_string::CxxString;
+//! Less used details of `CxxVector` are exposed in this module. `CxxVector`
+//! itself is exposed at the crate root.
+
+use crate::extern_type::ExternType;
+use crate::kind::Trivial;
+use crate::string::CxxString;
 use core::ffi::c_void;
-use core::fmt::{self, Display};
-use core::marker::PhantomData;
+use core::fmt::{self, Debug};
+use core::iter::FusedIterator;
+use core::marker::{PhantomData, PhantomPinned};
 use core::mem;
+use core::pin::Pin;
 use core::ptr;
 use core::slice;
 
@@ -17,6 +24,7 @@
 #[repr(C, packed)]
 pub struct CxxVector<T> {
     _private: [T; 0],
+    _pinned: PhantomData<PhantomPinned>,
 }
 
 impl<T> CxxVector<T>
@@ -51,6 +59,16 @@
         }
     }
 
+    /// Returns a pinned mutable reference to an element at the given position,
+    /// or `None` if out of bounds.
+    pub fn index_mut(self: Pin<&mut Self>, pos: usize) -> Option<Pin<&mut T>> {
+        if pos < self.len() {
+            Some(unsafe { self.index_unchecked_mut(pos) })
+        } else {
+            None
+        }
+    }
+
     /// Returns a reference to an element without doing bounds checking.
     ///
     /// This is generally not recommended, use with caution! Calling this method
@@ -58,15 +76,36 @@
     /// 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>;
+        let ptr = T::__get_unchecked(this, pos) as *const T;
+        &*ptr
+    }
+
+    /// Returns a pinned mutable reference to an element without doing bounds
+    /// checking.
+    ///
+    /// This is generally not recommended, use with caution! Calling this method
+    /// with an out-of-bounds index is undefined behavior even if the resulting
+    /// reference is not used.
+    ///
+    /// Matches the behavior of C++
     /// [std::vector\<T\>::operator\[\]][operator_at].
     ///
     /// [operator_at]: https://en.cppreference.com/w/cpp/container/vector/operator_at
-    pub unsafe fn get_unchecked(&self, pos: usize) -> &T {
-        &*T::__get_unchecked(self, pos)
+    pub unsafe fn index_unchecked_mut(self: Pin<&mut Self>, pos: usize) -> Pin<&mut T> {
+        let ptr = T::__get_unchecked(self.get_unchecked_mut(), pos);
+        Pin::new_unchecked(&mut *ptr)
     }
 
     /// Returns a slice to the underlying contiguous array of elements.
-    pub fn as_slice(&self) -> &[T] {
+    pub fn as_slice(&self) -> &[T]
+    where
+        T: ExternType<Kind = Trivial>,
+    {
         let len = self.len();
         if len == 0 {
             // The slice::from_raw_parts in the other branch requires a nonnull
@@ -77,12 +116,41 @@
             // which upholds the invariants.
             &[]
         } else {
-            let ptr = unsafe { T::__get_unchecked(self, 0) };
+            let this = self as *const CxxVector<T> as *mut CxxVector<T>;
+            let ptr = unsafe { T::__get_unchecked(this, 0) };
             unsafe { slice::from_raw_parts(ptr, len) }
         }
     }
+
+    /// Returns a slice to the underlying contiguous array of elements by
+    /// mutable reference.
+    pub fn as_mut_slice(self: Pin<&mut Self>) -> &mut [T]
+    where
+        T: ExternType<Kind = Trivial>,
+    {
+        let len = self.len();
+        if len == 0 {
+            &mut []
+        } else {
+            let ptr = unsafe { T::__get_unchecked(self.get_unchecked_mut(), 0) };
+            unsafe { slice::from_raw_parts_mut(ptr, len) }
+        }
+    }
+
+    /// Returns an iterator over elements of type `&T`.
+    pub fn iter(&self) -> Iter<T> {
+        Iter { v: self, index: 0 }
+    }
+
+    /// Returns an iterator over elements of type `Pin<&mut T>`.
+    pub fn iter_mut(self: Pin<&mut Self>) -> IterMut<T> {
+        IterMut { v: self, index: 0 }
+    }
 }
 
+/// Iterator over elements of a `CxxVector` by shared reference.
+///
+/// The iterator element type is `&'a T`.
 pub struct Iter<'a, T> {
     v: &'a CxxVector<T>,
     index: usize,
@@ -96,7 +164,7 @@
     type IntoIter = Iter<'a, T>;
 
     fn into_iter(self) -> Self::IntoIter {
-        Iter { v: self, index: 0 }
+        self.iter()
     }
 }
 
@@ -107,44 +175,135 @@
     type Item = &'a T;
 
     fn next(&mut self) -> Option<Self::Item> {
-        let next = self.v.get(self.index);
+        let next = self.v.get(self.index)?;
         self.index += 1;
-        next
+        Some(next)
+    }
+
+    fn size_hint(&self) -> (usize, Option<usize>) {
+        let len = self.len();
+        (len, Some(len))
     }
 }
 
-pub struct TypeName<T> {
-    element: PhantomData<T>,
-}
-
-impl<T> TypeName<T> {
-    pub const fn new() -> Self {
-        TypeName {
-            element: PhantomData,
-        }
-    }
-}
-
-impl<T> Display for TypeName<T>
+impl<'a, T> ExactSizeIterator for Iter<'a, T>
 where
     T: VectorElement,
 {
-    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
-        write!(formatter, "CxxVector<{}>", T::__NAME)
+    fn len(&self) -> usize {
+        self.v.len() - self.index
     }
 }
 
-// Methods are private; not intended to be implemented outside of cxxbridge
-// codebase.
-#[doc(hidden)]
+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 is not supported.
+///
+/// # Example
+///
+/// A bound `T: VectorElement` may be necessary when manipulating [`CxxVector`]
+/// in generic code.
+///
+/// ```
+/// use cxx::vector::{CxxVector, VectorElement};
+/// use std::fmt::Display;
+///
+/// pub fn take_generic_vector<T>(vector: &CxxVector<T>)
+/// where
+///     T: VectorElement + Display,
+/// {
+///     println!("the vector elements are:");
+///     for element in vector {
+///         println!("  • {}", element);
+///     }
+/// }
+/// ```
+///
+/// Writing the same generic function without a `VectorElement` trait bound
+/// would not compile.
 pub unsafe trait VectorElement: Sized {
-    const __NAME: &'static dyn Display;
+    #[doc(hidden)]
+    fn __typename(f: &mut fmt::Formatter) -> fmt::Result;
+    #[doc(hidden)]
     fn __vector_size(v: &CxxVector<Self>) -> usize;
-    unsafe fn __get_unchecked(v: &CxxVector<Self>, pos: usize) -> *const Self;
+    #[doc(hidden)]
+    unsafe fn __get_unchecked(v: *mut CxxVector<Self>, pos: usize) -> *mut Self;
+    #[doc(hidden)]
     fn __unique_ptr_null() -> *mut c_void;
+    #[doc(hidden)]
     unsafe fn __unique_ptr_raw(raw: *mut CxxVector<Self>) -> *mut c_void;
+    #[doc(hidden)]
     unsafe fn __unique_ptr_get(repr: *mut c_void) -> *const CxxVector<Self>;
+    #[doc(hidden)]
     unsafe fn __unique_ptr_release(repr: *mut c_void) -> *mut CxxVector<Self>;
+    #[doc(hidden)]
     unsafe fn __unique_ptr_drop(repr: *mut c_void);
 }
 
@@ -153,29 +312,35 @@
         const_assert_eq!(1, mem::align_of::<CxxVector<$ty>>());
 
         unsafe impl VectorElement for $ty {
-            const __NAME: &'static dyn Display = &$name;
+            #[doc(hidden)]
+            fn __typename(f: &mut fmt::Formatter) -> fmt::Result {
+                f.write_str($name)
+            }
+            #[doc(hidden)]
             fn __vector_size(v: &CxxVector<$ty>) -> usize {
                 extern "C" {
                     attr! {
-                        #[link_name = concat!("cxxbridge05$std$vector$", $segment, "$size")]
+                        #[link_name = concat!("cxxbridge1$std$vector$", $segment, "$size")]
                         fn __vector_size(_: &CxxVector<$ty>) -> usize;
                     }
                 }
                 unsafe { __vector_size(v) }
             }
-            unsafe fn __get_unchecked(v: &CxxVector<$ty>, pos: usize) -> *const $ty {
+            #[doc(hidden)]
+            unsafe fn __get_unchecked(v: *mut CxxVector<$ty>, pos: usize) -> *mut $ty {
                 extern "C" {
                     attr! {
-                        #[link_name = concat!("cxxbridge05$std$vector$", $segment, "$get_unchecked")]
-                        fn __get_unchecked(_: &CxxVector<$ty>, _: usize) -> *const $ty;
+                        #[link_name = concat!("cxxbridge1$std$vector$", $segment, "$get_unchecked")]
+                        fn __get_unchecked(_: *mut CxxVector<$ty>, _: usize) -> *mut $ty;
                     }
                 }
                 __get_unchecked(v, pos)
             }
+            #[doc(hidden)]
             fn __unique_ptr_null() -> *mut c_void {
                 extern "C" {
                     attr! {
-                        #[link_name = concat!("cxxbridge05$unique_ptr$std$vector$", $segment, "$null")]
+                        #[link_name = concat!("cxxbridge1$unique_ptr$std$vector$", $segment, "$null")]
                         fn __unique_ptr_null(this: *mut *mut c_void);
                     }
                 }
@@ -183,10 +348,11 @@
                 unsafe { __unique_ptr_null(&mut repr) }
                 repr
             }
+            #[doc(hidden)]
             unsafe fn __unique_ptr_raw(raw: *mut CxxVector<Self>) -> *mut c_void {
                 extern "C" {
                     attr! {
-                        #[link_name = concat!("cxxbridge05$unique_ptr$std$vector$", $segment, "$raw")]
+                        #[link_name = concat!("cxxbridge1$unique_ptr$std$vector$", $segment, "$raw")]
                         fn __unique_ptr_raw(this: *mut *mut c_void, raw: *mut CxxVector<$ty>);
                     }
                 }
@@ -194,28 +360,31 @@
                 __unique_ptr_raw(&mut repr, raw);
                 repr
             }
+            #[doc(hidden)]
             unsafe fn __unique_ptr_get(repr: *mut c_void) -> *const CxxVector<Self> {
                 extern "C" {
                     attr! {
-                        #[link_name = concat!("cxxbridge05$unique_ptr$std$vector$", $segment, "$get")]
+                        #[link_name = concat!("cxxbridge1$unique_ptr$std$vector$", $segment, "$get")]
                         fn __unique_ptr_get(this: *const *mut c_void) -> *const CxxVector<$ty>;
                     }
                 }
                 __unique_ptr_get(&repr)
             }
+            #[doc(hidden)]
             unsafe fn __unique_ptr_release(mut repr: *mut c_void) -> *mut CxxVector<Self> {
                 extern "C" {
                     attr! {
-                        #[link_name = concat!("cxxbridge05$unique_ptr$std$vector$", $segment, "$release")]
+                        #[link_name = concat!("cxxbridge1$unique_ptr$std$vector$", $segment, "$release")]
                         fn __unique_ptr_release(this: *mut *mut c_void) -> *mut CxxVector<$ty>;
                     }
                 }
                 __unique_ptr_release(&mut repr)
             }
+            #[doc(hidden)]
             unsafe fn __unique_ptr_drop(mut repr: *mut c_void) {
                 extern "C" {
                     attr! {
-                        #[link_name = concat!("cxxbridge05$unique_ptr$std$vector$", $segment, "$drop")]
+                        #[link_name = concat!("cxxbridge1$unique_ptr$std$vector$", $segment, "$drop")]
                         fn __unique_ptr_drop(this: *mut *mut c_void);
                     }
                 }
diff --git a/src/exception.rs b/src/exception.rs
index 0ffca66..f61e8fa 100644
--- a/src/exception.rs
+++ b/src/exception.rs
@@ -1,7 +1,7 @@
 use alloc::boxed::Box;
 use core::fmt::{self, Debug, Display};
 
-/// Exception thrown from an `extern "C"` function.
+/// Exception thrown from an `extern "C++"` function.
 #[derive(Debug)]
 pub struct Exception {
     pub(crate) what: Box<str>,
diff --git a/src/extern_type.rs b/src/extern_type.rs
index f92ff40..35057ac 100644
--- a/src/extern_type.rs
+++ b/src/extern_type.rs
@@ -1,4 +1,6 @@
 use self::kind::{Kind, Opaque, Trivial};
+use crate::CxxString;
+use alloc::string::String;
 
 /// A type for which the layout is determined by its C++ definition.
 ///
@@ -30,7 +32,7 @@
 /// # mod file1 {
 /// #[cxx::bridge(namespace = "example")]
 /// pub mod ffi {
-///     extern "C" {
+///     unsafe extern "C++" {
 ///         type Demo;
 ///
 ///         fn create_demo() -> UniquePtr<Demo>;
@@ -41,7 +43,7 @@
 /// // file2.rs
 /// #[cxx::bridge(namespace = "example")]
 /// pub mod ffi {
-///     extern "C" {
+///     unsafe extern "C++" {
 ///         type Demo = crate::file1::ffi::Demo;
 ///
 ///         fn take_ref_demo(demo: &Demo);
@@ -80,7 +82,7 @@
 ///
 /// #[cxx::bridge(namespace = "folly")]
 /// pub mod ffi {
-///     extern "C" {
+///     unsafe extern "C++" {
 ///         include!("rust_cxx_bindings.h");
 ///
 ///         type StringPiece = crate::folly_sys::StringPiece;
@@ -181,3 +183,36 @@
 
 #[doc(hidden)]
 pub fn verify_extern_kind<T: ExternType<Kind = Kind>, Kind: self::Kind>() {}
+
+macro_rules! impl_extern_type {
+    ($([$kind:ident] $($ty:path = $cxxpath:literal)*)*) => {
+        $($(
+            unsafe impl ExternType for $ty {
+                #[doc(hidden)]
+                type Id = crate::type_id!($cxxpath);
+                type Kind = $kind;
+            }
+        )*)*
+    };
+}
+
+impl_extern_type! {
+    [Trivial]
+    bool = "bool"
+    u8 = "std::uint8_t"
+    u16 = "std::uint16_t"
+    u32 = "std::uint32_t"
+    u64 = "std::uint64_t"
+    usize = "size_t"
+    i8 = "std::int8_t"
+    i16 = "std::int16_t"
+    i32 = "std::int32_t"
+    i64 = "std::int64_t"
+    isize = "rust::isize"
+    f32 = "float"
+    f64 = "double"
+    String = "rust::String"
+
+    [Opaque]
+    CxxString = "std::string"
+}
diff --git a/src/fmt.rs b/src/fmt.rs
new file mode 100644
index 0000000..db14a7c
--- /dev/null
+++ b/src/fmt.rs
@@ -0,0 +1,16 @@
+use std::fmt::{self, Display};
+
+pub(crate) fn display(fmt: impl Fn(&mut fmt::Formatter) -> fmt::Result) -> impl Display {
+    DisplayInvoke(fmt)
+}
+
+struct DisplayInvoke<T>(T);
+
+impl<T> Display for DisplayInvoke<T>
+where
+    T: Fn(&mut fmt::Formatter) -> fmt::Result,
+{
+    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+        (self.0)(formatter)
+    }
+}
diff --git a/src/lib.rs b/src/lib.rs
index 17d7548..19a708c 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -18,11 +18,18 @@
 //!
 //! <br>
 //!
-//! *Compiler support: requires rustc 1.43+ and c++11 or newer*<br>
+//! *Compiler support: requires rustc 1.48+ and c++11 or newer*<br>
 //! *[Release notes](https://github.com/dtolnay/cxx/releases)*
 //!
 //! <br>
 //!
+//! # Guide
+//!
+//! Please see **<https://cxx.rs>** for a tutorial, reference material, and
+//! example code.
+//!
+//! <br>
+//!
 //! # Overview
 //!
 //! The idea is that we define the signatures of both sides of our FFI boundary
@@ -86,7 +93,7 @@
 //!         fn next_chunk(buf: &mut MultiBuf) -> &[u8];
 //!     }
 //!
-//!     extern "C++" {
+//!     unsafe extern "C++" {
 //!         // One or more headers with the matching C++ declarations. Our code
 //!         // generators don't read it but it gets #include'd and used in static
 //!         // assertions to ensure our picture of the FFI boundary is accurate.
@@ -156,20 +163,20 @@
 //! - **Functions** &mdash; implemented in either language, callable from the
 //!   other language.
 //!
-//! Within the `extern "C"` part of the CXX bridge we list the types and
-//! functions for which C++ is the source of truth, as well as the header(s)
-//! that declare those APIs. In the future it's possible that this section could
-//! be generated bindgen-style from the headers but for now we need the
-//! signatures written out; static assertions will verify that they are
-//! accurate.
+//! Within the `extern "Rust"` part of the CXX bridge we list the types and
+//! functions for which Rust is the source of truth. These all implicitly refer
+//! to the `super` module, the parent module of the CXX bridge. You can think of
+//! the two items listed in the example above as being like `use
+//! super::MultiBuf` and `use super::next_chunk` except re-exported to C++. The
+//! parent module will either contain the definitions directly for simple
+//! things, or contain the relevant `use` statements to bring them into scope
+//! from elsewhere.
 //!
-//! Within the `extern "Rust"` part, we list types and functions for which Rust
-//! is the source of truth. These all implicitly refer to the `super` module,
-//! the parent module of the CXX bridge. You can think of the two items listed
-//! in the example above as being like `use super::ThingR` and `use
-//! super::print_r` except re-exported to C++. The parent module will either
-//! contain the definitions directly for simple things, or contain the relevant
-//! `use` statements to bring them into scope from elsewhere.
+//! Within the `extern "C++"` part, we list types and functions for which C++ is
+//! the source of truth, as well as the header(s) that declare those APIs. In
+//! the future it's possible that this section could be generated bindgen-style
+//! from the headers but for now we need the signatures written out; static
+//! assertions will verify that they are accurate.
 //!
 //! Your function implementations themselves, whether in C++ or Rust, *do not*
 //! need to be defined as `extern "C"` ABI or no\_mangle. CXX will put in the
@@ -235,7 +242,7 @@
 //! # Cargo.toml
 //!
 //! [build-dependencies]
-//! cxx-build = "0.5"
+//! cxx-build = "1.0"
 //! ```
 //!
 //! ```no_run
@@ -324,12 +331,16 @@
 //! <tr><th>name in Rust</th><th>name in C++</th><th>restrictions</th></tr>
 //! <tr><td>String</td><td>rust::String</td><td></td></tr>
 //! <tr><td>&amp;str</td><td>rust::Str</td><td></td></tr>
-//! <tr><td>&amp;[u8]</td><td>rust::Slice&lt;uint8_t&gt;</td><td><sup><i>arbitrary &amp;[T] not implemented yet</i></sup></td></tr>
+//! <tr><td>&amp;[T]</td><td>rust::Slice&lt;const T&gt;</td><td><sup><i>cannot hold opaque C++ type</i></sup></td></tr>
+//! <tr><td>&amp;mut [T]</td><td>rust::Slice&lt;T&gt;</td><td><sup><i>cannot hold opaque C++ type</i></sup></td></tr>
 //! <tr><td><a href="struct.CxxString.html">CxxString</a></td><td>std::string</td><td><sup><i>cannot be passed by value</i></sup></td></tr>
 //! <tr><td>Box&lt;T&gt;</td><td>rust::Box&lt;T&gt;</td><td><sup><i>cannot hold opaque C++ type</i></sup></td></tr>
 //! <tr><td><a href="struct.UniquePtr.html">UniquePtr&lt;T&gt;</a></td><td>std::unique_ptr&lt;T&gt;</td><td><sup><i>cannot hold opaque Rust type</i></sup></td></tr>
+//! <tr><td><a href="struct.SharedPtr.html">SharedPtr&lt;T&gt;</a></td><td>std::shared_ptr&lt;T&gt;</td><td><sup><i>cannot hold opaque Rust type</i></sup></td></tr>
+//! <tr><td>[T; N]</td><td>std::array&lt;T, N&gt;</td><td><sup><i>cannot hold opaque C++ type</i></sup></td></tr>
 //! <tr><td>Vec&lt;T&gt;</td><td>rust::Vec&lt;T&gt;</td><td><sup><i>cannot hold opaque C++ type</i></sup></td></tr>
 //! <tr><td><a href="struct.CxxVector.html">CxxVector&lt;T&gt;</a></td><td>std::vector&lt;T&gt;</td><td><sup><i>cannot be passed by value, cannot hold opaque Rust type</i></sup></td></tr>
+//! <tr><td>*mut T, *const T</td><td>T*, const T*</td><td><sup><i>fn with a raw pointer argument must be declared unsafe to call</i></sup></td></tr>
 //! <tr><td>fn(T, U) -&gt; V</td><td>rust::Fn&lt;V(T, U)&gt;</td><td><sup><i>only passing from Rust to C++ is implemented so far</i></sup></td></tr>
 //! <tr><td>Result&lt;T&gt;</td><td>throw/catch</td><td><sup><i>allowed as return type only</i></sup></td></tr>
 //! </table>
@@ -350,65 +361,77 @@
 //! <tr><td>Option&lt;T&gt;</td><td><sup><i>tbd</i></sup></td></tr>
 //! <tr><td><sup><i>tbd</i></sup></td><td>std::map&lt;K, V&gt;</td></tr>
 //! <tr><td><sup><i>tbd</i></sup></td><td>std::unordered_map&lt;K, V&gt;</td></tr>
-//! <tr><td><sup><i>tbd</i></sup></td><td>std::shared_ptr&lt;T&gt;</td></tr>
 //! </table>
 
 #![no_std]
-#![doc(html_root_url = "https://docs.rs/cxx/0.5.9")]
+#![doc(html_root_url = "https://docs.rs/cxx/1.0.42")]
 #![deny(improper_ctypes)]
 #![allow(non_camel_case_types)]
 #![allow(
     clippy::cognitive_complexity,
     clippy::declare_interior_mutable_const,
+    clippy::doc_markdown,
+    clippy::empty_enum,
     clippy::inherent_to_string,
+    clippy::items_after_statements,
     clippy::large_enum_variant,
     clippy::len_without_is_empty,
+    clippy::missing_errors_doc,
     clippy::missing_safety_doc,
     clippy::module_inception,
+    clippy::module_name_repetitions,
+    clippy::must_use_candidate,
     clippy::needless_doctest_main,
     clippy::new_without_default,
     clippy::or_fun_call,
     clippy::ptr_arg,
     clippy::toplevel_ref_arg,
-    clippy::useless_let_if_seq
+    clippy::useless_let_if_seq,
+    clippy::wrong_self_convention
 )]
 
 #[cfg(built_with_cargo)]
 extern crate link_cplusplus;
 
 extern crate alloc;
+extern crate self as cxx;
 extern crate std;
 
 #[macro_use]
 mod macros;
 
-mod cxx_string;
 mod cxx_vector;
 mod exception;
 mod extern_type;
+mod fmt;
 mod function;
+pub mod memory;
 mod opaque;
 mod result;
-mod rust_sliceu8;
+mod rust_slice;
 mod rust_str;
 mod rust_string;
+mod rust_type;
 mod rust_vec;
+mod shared_ptr;
+#[path = "cxx_string.rs"]
+mod string;
 mod symbols;
+mod type_id;
 mod unique_ptr;
 mod unwind;
+pub mod vector;
+mod weak_ptr;
 
-pub use crate::cxx_string::CxxString;
 pub use crate::cxx_vector::CxxVector;
 pub use crate::exception::Exception;
 pub use crate::extern_type::{kind, ExternType};
+pub use crate::shared_ptr::SharedPtr;
+pub use crate::string::CxxString;
 pub use crate::unique_ptr::UniquePtr;
+pub use crate::weak_ptr::WeakPtr;
 pub use cxxbridge_macro::bridge;
 
-/// For use in impls of the `ExternType` trait. See [`ExternType`].
-///
-/// [`ExternType`]: trait.ExternType.html
-pub use cxxbridge_macro::type_id;
-
 /// Synonym for `CxxString`.
 ///
 /// To avoid confusion with Rust's standard library string you probably
@@ -431,12 +454,21 @@
     pub use crate::function::FatFunction;
     pub use crate::opaque::Opaque;
     pub use crate::result::{r#try, Result};
-    pub use crate::rust_sliceu8::RustSliceU8;
+    pub use crate::rust_slice::RustSlice;
     pub use crate::rust_str::RustStr;
     pub use crate::rust_string::RustString;
+    pub use crate::rust_type::{ImplBox, ImplVec, RustType};
     pub use crate::rust_vec::RustVec;
+    pub use crate::shared_ptr::SharedPtrTarget;
+    pub use crate::string::StackString;
     pub use crate::unique_ptr::UniquePtrTarget;
     pub use crate::unwind::catch_unwind;
+    pub use crate::weak_ptr::WeakPtrTarget;
+    pub use cxxbridge_macro::type_id;
+}
+
+mod actually_private {
+    pub trait Private {}
 }
 
 macro_rules! chars {
diff --git a/src/memory.rs b/src/memory.rs
new file mode 100644
index 0000000..441d3d8
--- /dev/null
+++ b/src/memory.rs
@@ -0,0 +1,8 @@
+//! Less used details of `UniquePtr` and `SharedPtr`.
+//!
+//! The pointer types themselves are exposed at the crate root.
+
+pub use crate::shared_ptr::SharedPtrTarget;
+pub use crate::unique_ptr::UniquePtrTarget;
+#[doc(no_inline)]
+pub use cxx::{SharedPtr, UniquePtr};
diff --git a/src/opaque.rs b/src/opaque.rs
index bad57e7..3c8f536 100644
--- a/src/opaque.rs
+++ b/src/opaque.rs
@@ -1,3 +1,4 @@
+use core::marker::{PhantomData, PhantomPinned};
 use core::mem;
 
 // . size = 0
@@ -5,9 +6,11 @@
 // . ffi-safe
 // . !Send
 // . !Sync
+// . !Unpin
 #[repr(C, packed)]
 pub struct Opaque {
     _private: [*const u8; 0],
+    _pinned: PhantomData<PhantomPinned>,
 }
 
 const_assert_eq!(0, mem::size_of::<Opaque>());
diff --git a/src/result.rs b/src/result.rs
index fcced76..f41639a 100644
--- a/src/result.rs
+++ b/src/result.rs
@@ -1,16 +1,22 @@
 use crate::exception::Exception;
-use crate::rust_str::RustStr;
 use alloc::boxed::Box;
 use alloc::string::{String, ToString};
 use core::fmt::Display;
-use core::ptr;
+use core::ptr::{self, NonNull};
 use core::result::Result as StdResult;
 use core::slice;
 use core::str;
 
 #[repr(C)]
+#[derive(Copy, Clone)]
+struct PtrLen {
+    ptr: NonNull<u8>,
+    len: usize,
+}
+
+#[repr(C)]
 pub union Result {
-    err: RustStr,
+    err: PtrLen,
     ok: *const u8, // null
 }
 
@@ -34,14 +40,12 @@
     let len = msg.len();
 
     extern "C" {
-        #[link_name = "cxxbridge05$error"]
-        fn error(ptr: *const u8, len: usize) -> *const u8;
+        #[link_name = "cxxbridge1$error"]
+        fn error(ptr: *const u8, len: usize) -> NonNull<u8>;
     }
 
     let copy = error(ptr, len);
-    let slice = slice::from_raw_parts(copy, len);
-    let string = str::from_utf8_unchecked(slice);
-    let err = RustStr::from(string);
+    let err = PtrLen { ptr: copy, len };
     Result { err }
 }
 
diff --git a/src/rust_slice.rs b/src/rust_slice.rs
new file mode 100644
index 0000000..b4b5f2c
--- /dev/null
+++ b/src/rust_slice.rs
@@ -0,0 +1,41 @@
+use core::mem;
+use core::ptr::{self, NonNull};
+use core::slice;
+
+#[repr(C)]
+pub struct RustSlice {
+    pub(crate) repr: NonNull<[()]>,
+}
+
+impl RustSlice {
+    pub fn from_ref<T>(slice: &[T]) -> Self {
+        let ptr = ptr::slice_from_raw_parts::<()>(slice.as_ptr().cast(), slice.len());
+        RustSlice {
+            repr: unsafe { NonNull::new_unchecked(ptr as *mut _) },
+        }
+    }
+
+    pub fn from_mut<T>(slice: &mut [T]) -> Self {
+        let ptr = ptr::slice_from_raw_parts_mut(slice.as_mut_ptr().cast(), slice.len());
+        RustSlice {
+            repr: unsafe { NonNull::new_unchecked(ptr) },
+        }
+    }
+
+    pub unsafe fn as_slice<'a, T>(self) -> &'a [T] {
+        let ptr = self.repr.as_ptr();
+        let len = self.repr.as_ref().len();
+        slice::from_raw_parts(ptr.cast(), len)
+    }
+
+    pub unsafe fn as_mut_slice<'a, T>(self) -> &'a mut [T] {
+        let ptr = self.repr.as_ptr();
+        let len = self.repr.as_ref().len();
+        slice::from_raw_parts_mut(ptr.cast(), len)
+    }
+}
+
+const_assert_eq!(
+    mem::size_of::<Option<RustSlice>>(),
+    mem::size_of::<RustSlice>(),
+);
diff --git a/src/rust_sliceu8.rs b/src/rust_sliceu8.rs
deleted file mode 100644
index 32f8798..0000000
--- a/src/rust_sliceu8.rs
+++ /dev/null
@@ -1,29 +0,0 @@
-use core::mem;
-use core::ptr::NonNull;
-use core::slice;
-
-// Not necessarily ABI compatible with &[u8]. Codegen performs the translation.
-#[repr(C)]
-#[derive(Copy, Clone)]
-pub struct RustSliceU8 {
-    pub(crate) ptr: NonNull<u8>,
-    pub(crate) len: usize,
-}
-
-impl RustSliceU8 {
-    pub fn from(s: &[u8]) -> Self {
-        RustSliceU8 {
-            ptr: NonNull::from(s).cast::<u8>(),
-            len: s.len(),
-        }
-    }
-
-    pub unsafe fn as_slice<'a>(self) -> &'a [u8] {
-        slice::from_raw_parts(self.ptr.as_ptr(), self.len)
-    }
-}
-
-const_assert_eq!(
-    mem::size_of::<Option<RustSliceU8>>(),
-    mem::size_of::<RustSliceU8>(),
-);
diff --git a/src/rust_str.rs b/src/rust_str.rs
index 38e5cab..9bc2614 100644
--- a/src/rust_str.rs
+++ b/src/rust_str.rs
@@ -1,27 +1,20 @@
 use core::mem;
 use core::ptr::NonNull;
-use core::slice;
 use core::str;
 
-// Not necessarily ABI compatible with &str. Codegen performs the translation.
 #[repr(C)]
-#[derive(Copy, Clone)]
 pub struct RustStr {
-    pub(crate) ptr: NonNull<u8>,
-    pub(crate) len: usize,
+    repr: NonNull<str>,
 }
 
 impl RustStr {
-    pub fn from(s: &str) -> Self {
-        RustStr {
-            ptr: NonNull::from(s).cast::<u8>(),
-            len: s.len(),
-        }
+    pub fn from(repr: &str) -> Self {
+        let repr = NonNull::from(repr);
+        RustStr { repr }
     }
 
     pub unsafe fn as_str<'a>(self) -> &'a str {
-        let slice = slice::from_raw_parts(self.ptr.as_ptr(), self.len);
-        str::from_utf8_unchecked(slice)
+        &*self.repr.as_ptr()
     }
 }
 
diff --git a/src/rust_type.rs b/src/rust_type.rs
new file mode 100644
index 0000000..7bcf440
--- /dev/null
+++ b/src/rust_type.rs
@@ -0,0 +1,3 @@
+pub unsafe trait RustType {}
+pub unsafe trait ImplBox {}
+pub unsafe trait ImplVec {}
diff --git a/src/rust_vec.rs b/src/rust_vec.rs
index 8ddc4a7..126fdbf 100644
--- a/src/rust_vec.rs
+++ b/src/rust_vec.rs
@@ -41,6 +41,10 @@
         self.repr.len()
     }
 
+    pub fn capacity(&self) -> usize {
+        self.repr.capacity()
+    }
+
     pub fn as_ptr(&self) -> *const T {
         self.repr.as_ptr()
     }
diff --git a/src/shared_ptr.rs b/src/shared_ptr.rs
new file mode 100644
index 0000000..66b988b
--- /dev/null
+++ b/src/shared_ptr.rs
@@ -0,0 +1,285 @@
+use crate::fmt::display;
+use crate::kind::Trivial;
+use crate::string::CxxString;
+use crate::weak_ptr::{WeakPtr, WeakPtrTarget};
+use crate::ExternType;
+use core::ffi::c_void;
+use core::fmt::{self, Debug, Display};
+use core::marker::PhantomData;
+use core::mem::MaybeUninit;
+use core::ops::Deref;
+
+/// Binding to C++ `std::shared_ptr<T>`.
+#[repr(C)]
+pub struct SharedPtr<T>
+where
+    T: SharedPtrTarget,
+{
+    repr: [*mut c_void; 2],
+    ty: PhantomData<T>,
+}
+
+impl<T> SharedPtr<T>
+where
+    T: SharedPtrTarget,
+{
+    /// Makes a new SharedPtr wrapping a null pointer.
+    ///
+    /// Matches the behavior of default-constructing a std::shared\_ptr.
+    pub fn null() -> Self {
+        let mut shared_ptr = MaybeUninit::<SharedPtr<T>>::uninit();
+        let new = shared_ptr.as_mut_ptr().cast();
+        unsafe {
+            T::__null(new);
+            shared_ptr.assume_init()
+        }
+    }
+
+    /// Allocates memory on the heap and makes a SharedPtr owner for it.
+    pub fn new(value: T) -> Self
+    where
+        T: ExternType<Kind = Trivial>,
+    {
+        let mut shared_ptr = MaybeUninit::<SharedPtr<T>>::uninit();
+        let new = shared_ptr.as_mut_ptr().cast();
+        unsafe {
+            T::__new(value, new);
+            shared_ptr.assume_init()
+        }
+    }
+
+    /// Checks whether the SharedPtr does not own an object.
+    ///
+    /// This is the opposite of [std::shared_ptr\<T\>::operator bool](https://en.cppreference.com/w/cpp/memory/shared_ptr/operator_bool).
+    pub fn is_null(&self) -> bool {
+        let this = self as *const Self as *const c_void;
+        let ptr = unsafe { T::__get(this) };
+        ptr.is_null()
+    }
+
+    /// Returns a reference to the object owned by this SharedPtr if any,
+    /// otherwise None.
+    pub fn as_ref(&self) -> Option<&T> {
+        let this = self as *const Self as *const c_void;
+        unsafe { T::__get(this).as_ref() }
+    }
+
+    /// Constructs new WeakPtr as a non-owning reference to the object managed
+    /// by `self`. If `self` manages no object, the WeakPtr manages no object
+    /// too.
+    ///
+    /// Matches the behavior of [std::weak_ptr\<T\>::weak_ptr(const std::shared_ptr\<T\> \&)](https://en.cppreference.com/w/cpp/memory/weak_ptr/weak_ptr).
+    pub fn downgrade(self: &SharedPtr<T>) -> WeakPtr<T>
+    where
+        T: WeakPtrTarget,
+    {
+        let this = self as *const Self as *const c_void;
+        let mut weak_ptr = MaybeUninit::<WeakPtr<T>>::uninit();
+        let new = weak_ptr.as_mut_ptr().cast();
+        unsafe {
+            T::__downgrade(this, new);
+            weak_ptr.assume_init()
+        }
+    }
+}
+
+unsafe impl<T> Send for SharedPtr<T> where T: Send + Sync + SharedPtrTarget {}
+unsafe impl<T> Sync for SharedPtr<T> where T: Send + Sync + SharedPtrTarget {}
+
+impl<T> Clone for SharedPtr<T>
+where
+    T: SharedPtrTarget,
+{
+    fn clone(&self) -> Self {
+        let mut shared_ptr = MaybeUninit::<SharedPtr<T>>::uninit();
+        let new = shared_ptr.as_mut_ptr().cast();
+        let this = self as *const Self as *mut c_void;
+        unsafe {
+            T::__clone(this, new);
+            shared_ptr.assume_init()
+        }
+    }
+}
+
+impl<T> Drop for SharedPtr<T>
+where
+    T: SharedPtrTarget,
+{
+    fn drop(&mut self) {
+        let this = self as *mut Self as *mut c_void;
+        unsafe { T::__drop(this) }
+    }
+}
+
+impl<T> Deref for SharedPtr<T>
+where
+    T: SharedPtrTarget,
+{
+    type Target = T;
+
+    fn deref(&self) -> &Self::Target {
+        match self.as_ref() {
+            Some(target) => target,
+            None => panic!(
+                "called deref on a null SharedPtr<{}>",
+                display(T::__typename),
+            ),
+        }
+    }
+}
+
+impl<T> Debug for SharedPtr<T>
+where
+    T: Debug + SharedPtrTarget,
+{
+    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+        match self.as_ref() {
+            None => formatter.write_str("nullptr"),
+            Some(value) => Debug::fmt(value, formatter),
+        }
+    }
+}
+
+impl<T> Display for SharedPtr<T>
+where
+    T: Display + SharedPtrTarget,
+{
+    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+        match self.as_ref() {
+            None => formatter.write_str("nullptr"),
+            Some(value) => Display::fmt(value, formatter),
+        }
+    }
+}
+
+/// Trait bound for types which may be used as the `T` inside of a
+/// `SharedPtr<T>` in generic code.
+///
+/// This trait has no publicly callable or implementable methods. Implementing
+/// it outside of the CXX codebase is not supported.
+///
+/// # Example
+///
+/// A bound `T: SharedPtrTarget` may be necessary when manipulating
+/// [`SharedPtr`] in generic code.
+///
+/// ```
+/// use cxx::memory::{SharedPtr, SharedPtrTarget};
+/// use std::fmt::Display;
+///
+/// pub fn take_generic_ptr<T>(ptr: SharedPtr<T>)
+/// where
+///     T: SharedPtrTarget + Display,
+/// {
+///     println!("the shared_ptr points to: {}", *ptr);
+/// }
+/// ```
+///
+/// Writing the same generic function without a `SharedPtrTarget` trait bound
+/// would not compile.
+pub unsafe trait SharedPtrTarget {
+    #[doc(hidden)]
+    fn __typename(f: &mut fmt::Formatter) -> fmt::Result;
+    #[doc(hidden)]
+    unsafe fn __null(new: *mut c_void);
+    #[doc(hidden)]
+    unsafe fn __new(value: Self, new: *mut c_void)
+    where
+        Self: Sized,
+    {
+        // Opoaque C types do not get this method because they can never exist
+        // by value on the Rust side of the bridge.
+        let _ = value;
+        let _ = new;
+        unreachable!()
+    }
+    #[doc(hidden)]
+    unsafe fn __clone(this: *const c_void, new: *mut c_void);
+    #[doc(hidden)]
+    unsafe fn __get(this: *const c_void) -> *const Self;
+    #[doc(hidden)]
+    unsafe fn __drop(this: *mut c_void);
+}
+
+macro_rules! impl_shared_ptr_target {
+    ($segment:expr, $name:expr, $ty:ty) => {
+        unsafe impl SharedPtrTarget for $ty {
+            #[doc(hidden)]
+            fn __typename(f: &mut fmt::Formatter) -> fmt::Result {
+                f.write_str($name)
+            }
+            #[doc(hidden)]
+            unsafe fn __null(new: *mut c_void) {
+                extern "C" {
+                    attr! {
+                        #[link_name = concat!("cxxbridge1$std$shared_ptr$", $segment, "$null")]
+                        fn __null(new: *mut c_void);
+                    }
+                }
+                __null(new);
+            }
+            #[doc(hidden)]
+            unsafe fn __new(value: Self, new: *mut c_void) {
+                extern "C" {
+                    attr! {
+                        #[link_name = concat!("cxxbridge1$std$shared_ptr$", $segment, "$uninit")]
+                        fn __uninit(new: *mut c_void) -> *mut c_void;
+                    }
+                }
+                __uninit(new).cast::<$ty>().write(value);
+            }
+            #[doc(hidden)]
+            unsafe fn __clone(this: *const c_void, new: *mut c_void) {
+                extern "C" {
+                    attr! {
+                        #[link_name = concat!("cxxbridge1$std$shared_ptr$", $segment, "$clone")]
+                        fn __clone(this: *const c_void, new: *mut c_void);
+                    }
+                }
+                __clone(this, new);
+            }
+            #[doc(hidden)]
+            unsafe fn __get(this: *const c_void) -> *const Self {
+                extern "C" {
+                    attr! {
+                        #[link_name = concat!("cxxbridge1$std$shared_ptr$", $segment, "$get")]
+                        fn __get(this: *const c_void) -> *const c_void;
+                    }
+                }
+                __get(this).cast()
+            }
+            #[doc(hidden)]
+            unsafe fn __drop(this: *mut c_void) {
+                extern "C" {
+                    attr! {
+                        #[link_name = concat!("cxxbridge1$std$shared_ptr$", $segment, "$drop")]
+                        fn __drop(this: *mut c_void);
+                    }
+                }
+                __drop(this);
+            }
+        }
+    };
+}
+
+macro_rules! impl_shared_ptr_target_for_primitive {
+    ($ty:ident) => {
+        impl_shared_ptr_target!(stringify!($ty), stringify!($ty), $ty);
+    };
+}
+
+impl_shared_ptr_target_for_primitive!(bool);
+impl_shared_ptr_target_for_primitive!(u8);
+impl_shared_ptr_target_for_primitive!(u16);
+impl_shared_ptr_target_for_primitive!(u32);
+impl_shared_ptr_target_for_primitive!(u64);
+impl_shared_ptr_target_for_primitive!(usize);
+impl_shared_ptr_target_for_primitive!(i8);
+impl_shared_ptr_target_for_primitive!(i16);
+impl_shared_ptr_target_for_primitive!(i32);
+impl_shared_ptr_target_for_primitive!(i64);
+impl_shared_ptr_target_for_primitive!(isize);
+impl_shared_ptr_target_for_primitive!(f32);
+impl_shared_ptr_target_for_primitive!(f64);
+
+impl_shared_ptr_target!("string", "CxxString", CxxString);
diff --git a/src/symbols/exception.rs b/src/symbols/exception.rs
index b408f25..0c1bb87 100644
--- a/src/symbols/exception.rs
+++ b/src/symbols/exception.rs
@@ -2,7 +2,7 @@
 use alloc::string::String;
 use core::slice;
 
-#[export_name = "cxxbridge05$exception"]
+#[export_name = "cxxbridge1$exception"]
 unsafe extern "C" fn exception(ptr: *const u8, len: usize) -> *const u8 {
     let slice = slice::from_raw_parts(ptr, len);
     let boxed = String::from_utf8_lossy(slice).into_owned().into_boxed_str();
diff --git a/src/symbols/mod.rs b/src/symbols/mod.rs
index a9d158d..e00bb55 100644
--- a/src/symbols/mod.rs
+++ b/src/symbols/mod.rs
@@ -1,4 +1,5 @@
 mod exception;
+mod rust_slice;
 mod rust_str;
 mod rust_string;
 mod rust_vec;
diff --git a/src/symbols/rust_slice.rs b/src/symbols/rust_slice.rs
new file mode 100644
index 0000000..055b4de
--- /dev/null
+++ b/src/symbols/rust_slice.rs
@@ -0,0 +1,22 @@
+use crate::rust_slice::RustSlice;
+use core::mem::MaybeUninit;
+use core::ptr::{self, NonNull};
+
+#[export_name = "cxxbridge1$slice$new"]
+unsafe extern "C" fn slice_new(this: &mut MaybeUninit<RustSlice>, ptr: *const (), len: usize) {
+    let ptr = ptr::slice_from_raw_parts(ptr, len);
+    let rust_slice = RustSlice {
+        repr: NonNull::new_unchecked(ptr as *mut _),
+    };
+    ptr::write(this.as_mut_ptr(), rust_slice);
+}
+
+#[export_name = "cxxbridge1$slice$ptr"]
+unsafe extern "C" fn slice_ptr(this: &RustSlice) -> *const () {
+    this.repr.as_ptr().cast()
+}
+
+#[export_name = "cxxbridge1$slice$len"]
+unsafe extern "C" fn slice_len(this: &RustSlice) -> usize {
+    this.repr.as_ref().len()
+}
diff --git a/src/symbols/rust_str.rs b/src/symbols/rust_str.rs
index b655381..a9e84ef 100644
--- a/src/symbols/rust_str.rs
+++ b/src/symbols/rust_str.rs
@@ -1,8 +1,37 @@
+use alloc::string::String;
+use core::mem::MaybeUninit;
+use core::ptr;
 use core::slice;
 use core::str;
 
-#[export_name = "cxxbridge05$str$valid"]
-unsafe extern "C" fn str_valid(ptr: *const u8, len: usize) -> bool {
+#[export_name = "cxxbridge1$str$new"]
+unsafe extern "C" fn str_new(this: &mut MaybeUninit<&str>) {
+    ptr::write(this.as_mut_ptr(), "");
+}
+
+#[export_name = "cxxbridge1$str$ref"]
+unsafe extern "C" fn str_ref<'a>(this: &mut MaybeUninit<&'a str>, string: &'a String) {
+    ptr::write(this.as_mut_ptr(), string.as_str());
+}
+
+#[export_name = "cxxbridge1$str$from"]
+unsafe extern "C" fn str_from(this: &mut MaybeUninit<&str>, ptr: *const u8, len: usize) -> bool {
     let slice = slice::from_raw_parts(ptr, len);
-    str::from_utf8(slice).is_ok()
+    match str::from_utf8(slice) {
+        Ok(s) => {
+            ptr::write(this.as_mut_ptr(), s);
+            true
+        }
+        Err(_) => false,
+    }
+}
+
+#[export_name = "cxxbridge1$str$ptr"]
+unsafe extern "C" fn str_ptr(this: &&str) -> *const u8 {
+    this.as_ptr()
+}
+
+#[export_name = "cxxbridge1$str$len"]
+unsafe extern "C" fn str_len(this: &&str) -> usize {
+    this.len()
 }
diff --git a/src/symbols/rust_string.rs b/src/symbols/rust_string.rs
index e5ab9ea..91fd78a 100644
--- a/src/symbols/rust_string.rs
+++ b/src/symbols/rust_string.rs
@@ -5,17 +5,17 @@
 use core::slice;
 use core::str;
 
-#[export_name = "cxxbridge05$string$new"]
+#[export_name = "cxxbridge1$string$new"]
 unsafe extern "C" fn string_new(this: &mut MaybeUninit<String>) {
     ptr::write(this.as_mut_ptr(), String::new());
 }
 
-#[export_name = "cxxbridge05$string$clone"]
+#[export_name = "cxxbridge1$string$clone"]
 unsafe extern "C" fn string_clone(this: &mut MaybeUninit<String>, other: &String) {
     ptr::write(this.as_mut_ptr(), other.clone());
 }
 
-#[export_name = "cxxbridge05$string$from"]
+#[export_name = "cxxbridge1$string$from"]
 unsafe extern "C" fn string_from(
     this: &mut MaybeUninit<String>,
     ptr: *const u8,
@@ -31,17 +31,22 @@
     }
 }
 
-#[export_name = "cxxbridge05$string$drop"]
+#[export_name = "cxxbridge1$string$drop"]
 unsafe extern "C" fn string_drop(this: &mut ManuallyDrop<String>) {
     ManuallyDrop::drop(this);
 }
 
-#[export_name = "cxxbridge05$string$ptr"]
+#[export_name = "cxxbridge1$string$ptr"]
 unsafe extern "C" fn string_ptr(this: &String) -> *const u8 {
     this.as_ptr()
 }
 
-#[export_name = "cxxbridge05$string$len"]
+#[export_name = "cxxbridge1$string$len"]
 unsafe extern "C" fn string_len(this: &String) -> usize {
     this.len()
 }
+
+#[export_name = "cxxbridge1$string$reserve_total"]
+unsafe extern "C" fn string_reserve_total(this: &mut String, cap: usize) {
+    this.reserve(cap);
+}
diff --git a/src/symbols/rust_vec.rs b/src/symbols/rust_vec.rs
index 90fbc54..a26a156 100644
--- a/src/symbols/rust_vec.rs
+++ b/src/symbols/rust_vec.rs
@@ -3,6 +3,7 @@
 use alloc::vec::Vec;
 use core::mem;
 use core::ptr;
+use std::os::raw::c_char;
 
 macro_rules! rust_vec_shims {
     ($segment:expr, $ty:ty) => {
@@ -11,47 +12,47 @@
 
         const _: () = {
             attr! {
-                #[export_name = concat!("cxxbridge05$rust_vec$", $segment, "$new")]
+                #[export_name = concat!("cxxbridge1$rust_vec$", $segment, "$new")]
                 unsafe extern "C" fn __new(this: *mut RustVec<$ty>) {
                     ptr::write(this, RustVec { repr: Vec::new() });
                 }
             }
             attr! {
-                #[export_name = concat!("cxxbridge05$rust_vec$", $segment, "$drop")]
+                #[export_name = concat!("cxxbridge1$rust_vec$", $segment, "$drop")]
                 unsafe extern "C" fn __drop(this: *mut RustVec<$ty>) {
                     ptr::drop_in_place(this);
                 }
             }
             attr! {
-                #[export_name = concat!("cxxbridge05$rust_vec$", $segment, "$len")]
+                #[export_name = concat!("cxxbridge1$rust_vec$", $segment, "$len")]
                 unsafe extern "C" fn __len(this: *const RustVec<$ty>) -> usize {
                     (*this).repr.len()
                 }
             }
             attr! {
-                #[export_name = concat!("cxxbridge05$rust_vec$", $segment, "$data")]
+                #[export_name = concat!("cxxbridge1$rust_vec$", $segment, "$capacity")]
+                unsafe extern "C" fn __capacity(this: *const RustVec<$ty>) -> usize {
+                    (*this).repr.capacity()
+                }
+            }
+            attr! {
+                #[export_name = concat!("cxxbridge1$rust_vec$", $segment, "$data")]
                 unsafe extern "C" fn __data(this: *const RustVec<$ty>) -> *const $ty {
                     (*this).repr.as_ptr()
                 }
             }
             attr! {
-                #[export_name = concat!("cxxbridge05$rust_vec$", $segment, "$reserve_total")]
+                #[export_name = concat!("cxxbridge1$rust_vec$", $segment, "$reserve_total")]
                 unsafe extern "C" fn __reserve_total(this: *mut RustVec<$ty>, cap: usize) {
                     (*this).reserve_total(cap);
                 }
             }
             attr! {
-                #[export_name = concat!("cxxbridge05$rust_vec$", $segment, "$set_len")]
+                #[export_name = concat!("cxxbridge1$rust_vec$", $segment, "$set_len")]
                 unsafe extern "C" fn __set_len(this: *mut RustVec<$ty>, len: usize) {
                     (*this).repr.set_len(len);
                 }
             }
-            attr! {
-                #[export_name = concat!("cxxbridge05$rust_vec$", $segment, "$stride")]
-                unsafe extern "C" fn __stride() -> usize {
-                    mem::size_of::<$ty>()
-                }
-            }
         };
     };
 }
@@ -67,11 +68,15 @@
 rust_vec_shims_for_primitive!(u16);
 rust_vec_shims_for_primitive!(u32);
 rust_vec_shims_for_primitive!(u64);
+rust_vec_shims_for_primitive!(usize);
 rust_vec_shims_for_primitive!(i8);
 rust_vec_shims_for_primitive!(i16);
 rust_vec_shims_for_primitive!(i32);
 rust_vec_shims_for_primitive!(i64);
+rust_vec_shims_for_primitive!(isize);
 rust_vec_shims_for_primitive!(f32);
 rust_vec_shims_for_primitive!(f64);
 
+rust_vec_shims!("char", c_char);
 rust_vec_shims!("string", RustString);
+rust_vec_shims!("str", &str);
diff --git a/src/type_id.rs b/src/type_id.rs
new file mode 100644
index 0000000..bd2b4ea
--- /dev/null
+++ b/src/type_id.rs
@@ -0,0 +1,9 @@
+/// For use in impls of the `ExternType` trait. See [`ExternType`].
+///
+/// [`ExternType`]: trait.ExternType.html
+#[macro_export]
+macro_rules! type_id {
+    ($($path:tt)*) => {
+        $crate::private::type_id! { $crate $($path)* }
+    };
+}
diff --git a/src/unique_ptr.rs b/src/unique_ptr.rs
index 3a5fc66..836f467 100644
--- a/src/unique_ptr.rs
+++ b/src/unique_ptr.rs
@@ -1,12 +1,14 @@
-use crate::cxx_string::CxxString;
-use crate::cxx_vector::{self, CxxVector, VectorElement};
+use crate::cxx_vector::{CxxVector, VectorElement};
+use crate::fmt::display;
 use crate::kind::Trivial;
+use crate::string::CxxString;
 use crate::ExternType;
 use core::ffi::c_void;
 use core::fmt::{self, Debug, Display};
 use core::marker::PhantomData;
 use core::mem;
 use core::ops::{Deref, DerefMut};
+use core::pin::Pin;
 use core::ptr;
 
 /// Binding to C++ `std::unique_ptr<T, std::default_delete<T>>`.
@@ -58,10 +60,29 @@
         unsafe { T::__get(self.repr).as_ref() }
     }
 
-    /// Returns a mutable reference to the object owned by this UniquePtr if
-    /// any, otherwise None.
-    pub fn as_mut(&mut self) -> Option<&mut T> {
-        unsafe { (T::__get(self.repr) as *mut T).as_mut() }
+    /// Returns a mutable pinned reference to the object owned by this UniquePtr
+    /// if any, otherwise None.
+    pub fn as_mut(&mut self) -> Option<Pin<&mut T>> {
+        unsafe {
+            let mut_reference = (T::__get(self.repr) as *mut T).as_mut()?;
+            Some(Pin::new_unchecked(mut_reference))
+        }
+    }
+
+    /// Returns a mutable pinned reference to the object owned by this
+    /// UniquePtr.
+    ///
+    /// # Panics
+    ///
+    /// Panics if the UniquePtr holds a null pointer.
+    pub fn pin_mut(&mut self) -> Pin<&mut T> {
+        match self.as_mut() {
+            Some(target) => target,
+            None => panic!(
+                "called pin_mut on a null UniquePtr<{}>",
+                display(T::__typename),
+            ),
+        }
     }
 
     /// Consumes the UniquePtr, releasing its ownership of the heap-allocated T.
@@ -110,19 +131,25 @@
     fn deref(&self) -> &Self::Target {
         match self.as_ref() {
             Some(target) => target,
-            None => panic!("called deref on a null UniquePtr<{}>", T::__NAME),
+            None => panic!(
+                "called deref on a null UniquePtr<{}>",
+                display(T::__typename),
+            ),
         }
     }
 }
 
 impl<T> DerefMut for UniquePtr<T>
 where
-    T: UniquePtrTarget,
+    T: UniquePtrTarget + Unpin,
 {
     fn deref_mut(&mut self) -> &mut Self::Target {
         match self.as_mut() {
-            Some(target) => target,
-            None => panic!("called deref_mut on a null UniquePtr<{}>", T::__NAME),
+            Some(target) => Pin::into_inner(target),
+            None => panic!(
+                "called deref_mut on a null UniquePtr<{}>",
+                display(T::__typename),
+            ),
         }
     }
 }
@@ -151,11 +178,34 @@
     }
 }
 
-// Methods are private; not intended to be implemented outside of cxxbridge
-// codebase.
+/// Trait bound for types which may be used as the `T` inside of a
+/// `UniquePtr<T>` in generic code.
+///
+/// This trait has no publicly callable or implementable methods. Implementing
+/// it outside of the CXX codebase is not supported.
+///
+/// # Example
+///
+/// A bound `T: UniquePtrTarget` may be necessary when manipulating
+/// [`UniquePtr`] in generic code.
+///
+/// ```
+/// use cxx::memory::{UniquePtr, UniquePtrTarget};
+/// use std::fmt::Display;
+///
+/// pub fn take_generic_ptr<T>(ptr: UniquePtr<T>)
+/// where
+///     T: UniquePtrTarget + Display,
+/// {
+///     println!("the unique_ptr points to: {}", *ptr);
+/// }
+/// ```
+///
+/// Writing the same generic function without a `UniquePtrTarget` trait bound
+/// would not compile.
 pub unsafe trait UniquePtrTarget {
     #[doc(hidden)]
-    const __NAME: &'static dyn Display;
+    fn __typename(f: &mut fmt::Formatter) -> fmt::Result;
     #[doc(hidden)]
     fn __null() -> *mut c_void;
     #[doc(hidden)]
@@ -179,20 +229,24 @@
 }
 
 extern "C" {
-    #[link_name = "cxxbridge05$unique_ptr$std$string$null"]
+    #[link_name = "cxxbridge1$unique_ptr$std$string$null"]
     fn unique_ptr_std_string_null(this: *mut *mut c_void);
-    #[link_name = "cxxbridge05$unique_ptr$std$string$raw"]
+    #[link_name = "cxxbridge1$unique_ptr$std$string$raw"]
     fn unique_ptr_std_string_raw(this: *mut *mut c_void, raw: *mut CxxString);
-    #[link_name = "cxxbridge05$unique_ptr$std$string$get"]
+    #[link_name = "cxxbridge1$unique_ptr$std$string$get"]
     fn unique_ptr_std_string_get(this: *const *mut c_void) -> *const CxxString;
-    #[link_name = "cxxbridge05$unique_ptr$std$string$release"]
+    #[link_name = "cxxbridge1$unique_ptr$std$string$release"]
     fn unique_ptr_std_string_release(this: *mut *mut c_void) -> *mut CxxString;
-    #[link_name = "cxxbridge05$unique_ptr$std$string$drop"]
+    #[link_name = "cxxbridge1$unique_ptr$std$string$drop"]
     fn unique_ptr_std_string_drop(this: *mut *mut c_void);
 }
 
 unsafe impl UniquePtrTarget for CxxString {
-    const __NAME: &'static dyn Display = &"CxxString";
+    #[doc(hidden)]
+    fn __typename(f: &mut fmt::Formatter) -> fmt::Result {
+        f.write_str("CxxString")
+    }
+    #[doc(hidden)]
     fn __null() -> *mut c_void {
         let mut repr = ptr::null_mut::<c_void>();
         unsafe {
@@ -200,17 +254,21 @@
         }
         repr
     }
+    #[doc(hidden)]
     unsafe fn __raw(raw: *mut Self) -> *mut c_void {
         let mut repr = ptr::null_mut::<c_void>();
         unique_ptr_std_string_raw(&mut repr, raw);
         repr
     }
+    #[doc(hidden)]
     unsafe fn __get(repr: *mut c_void) -> *const Self {
         unique_ptr_std_string_get(&repr)
     }
+    #[doc(hidden)]
     unsafe fn __release(mut repr: *mut c_void) -> *mut Self {
         unique_ptr_std_string_release(&mut repr)
     }
+    #[doc(hidden)]
     unsafe fn __drop(mut repr: *mut c_void) {
         unique_ptr_std_string_drop(&mut repr);
     }
@@ -218,21 +276,29 @@
 
 unsafe impl<T> UniquePtrTarget for CxxVector<T>
 where
-    T: VectorElement + 'static,
+    T: VectorElement,
 {
-    const __NAME: &'static dyn Display = &cxx_vector::TypeName::<T>::new();
+    #[doc(hidden)]
+    fn __typename(f: &mut fmt::Formatter) -> fmt::Result {
+        write!(f, "CxxVector<{}>", display(T::__typename))
+    }
+    #[doc(hidden)]
     fn __null() -> *mut c_void {
         T::__unique_ptr_null()
     }
+    #[doc(hidden)]
     unsafe fn __raw(raw: *mut Self) -> *mut c_void {
         T::__unique_ptr_raw(raw)
     }
+    #[doc(hidden)]
     unsafe fn __get(repr: *mut c_void) -> *const Self {
         T::__unique_ptr_get(repr)
     }
+    #[doc(hidden)]
     unsafe fn __release(repr: *mut c_void) -> *mut Self {
         T::__unique_ptr_release(repr)
     }
+    #[doc(hidden)]
     unsafe fn __drop(repr: *mut c_void) {
         T::__unique_ptr_drop(repr);
     }
diff --git a/src/vector.rs b/src/vector.rs
new file mode 100644
index 0000000..4afd487
--- /dev/null
+++ b/src/vector.rs
@@ -0,0 +1,9 @@
+//! Less used details of `CxxVector`.
+//!
+//! `CxxVector` itself is exposed at the crate root.
+
+pub use crate::cxx_vector::{Iter, IterMut, VectorElement};
+#[doc(inline)]
+pub use crate::Vector;
+#[doc(no_inline)]
+pub use cxx::CxxVector;
diff --git a/src/weak_ptr.rs b/src/weak_ptr.rs
new file mode 100644
index 0000000..8291d59
--- /dev/null
+++ b/src/weak_ptr.rs
@@ -0,0 +1,192 @@
+use crate::shared_ptr::{SharedPtr, SharedPtrTarget};
+use crate::string::CxxString;
+use core::ffi::c_void;
+use core::fmt::{self, Debug};
+use core::marker::PhantomData;
+use core::mem::MaybeUninit;
+
+/// Binding to C++ `std::weak_ptr<T>`.
+///
+/// The typical way to construct a WeakPtr from Rust is by [downgrading] from a
+/// SharedPtr.
+///
+/// [downgrading]: crate::SharedPtr::downgrade
+#[repr(C)]
+pub struct WeakPtr<T>
+where
+    T: WeakPtrTarget,
+{
+    repr: [*mut c_void; 2],
+    ty: PhantomData<T>,
+}
+
+impl<T> WeakPtr<T>
+where
+    T: WeakPtrTarget,
+{
+    /// Makes a new WeakPtr wrapping a null pointer.
+    ///
+    /// Matches the behavior of default-constructing a std::weak\_ptr.
+    pub fn null() -> Self {
+        let mut weak_ptr = MaybeUninit::<WeakPtr<T>>::uninit();
+        let new = weak_ptr.as_mut_ptr().cast();
+        unsafe {
+            T::__null(new);
+            weak_ptr.assume_init()
+        }
+    }
+
+    /// Upgrades a non-owning reference into an owning reference if possible,
+    /// otherwise to a null reference.
+    ///
+    /// Matches the behavior of [std::weak_ptr\<T\>::lock](https://en.cppreference.com/w/cpp/memory/weak_ptr/lock).
+    pub fn upgrade(&self) -> SharedPtr<T>
+    where
+        T: SharedPtrTarget,
+    {
+        let this = self as *const Self as *const c_void;
+        let mut shared_ptr = MaybeUninit::<SharedPtr<T>>::uninit();
+        let new = shared_ptr.as_mut_ptr().cast();
+        unsafe {
+            T::__upgrade(this, new);
+            shared_ptr.assume_init()
+        }
+    }
+}
+
+unsafe impl<T> Send for WeakPtr<T> where T: Send + Sync + WeakPtrTarget {}
+unsafe impl<T> Sync for WeakPtr<T> where T: Send + Sync + WeakPtrTarget {}
+
+impl<T> Clone for WeakPtr<T>
+where
+    T: WeakPtrTarget,
+{
+    fn clone(&self) -> Self {
+        let mut weak_ptr = MaybeUninit::<WeakPtr<T>>::uninit();
+        let new = weak_ptr.as_mut_ptr().cast();
+        let this = self as *const Self as *mut c_void;
+        unsafe {
+            T::__clone(this, new);
+            weak_ptr.assume_init()
+        }
+    }
+}
+
+impl<T> Drop for WeakPtr<T>
+where
+    T: WeakPtrTarget,
+{
+    fn drop(&mut self) {
+        let this = self as *mut Self as *mut c_void;
+        unsafe { T::__drop(this) }
+    }
+}
+
+impl<T> Debug for WeakPtr<T>
+where
+    T: Debug + WeakPtrTarget + SharedPtrTarget,
+{
+    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+        Debug::fmt(&self.upgrade(), formatter)
+    }
+}
+
+// Methods are private; not intended to be implemented outside of cxxbridge
+// codebase.
+pub unsafe trait WeakPtrTarget {
+    #[doc(hidden)]
+    fn __typename(f: &mut fmt::Formatter) -> fmt::Result;
+    #[doc(hidden)]
+    unsafe fn __null(new: *mut c_void);
+    #[doc(hidden)]
+    unsafe fn __clone(this: *const c_void, new: *mut c_void);
+    #[doc(hidden)]
+    unsafe fn __downgrade(shared: *const c_void, new: *mut c_void);
+    #[doc(hidden)]
+    unsafe fn __upgrade(weak: *const c_void, shared: *mut c_void);
+    #[doc(hidden)]
+    unsafe fn __drop(this: *mut c_void);
+}
+
+macro_rules! impl_weak_ptr_target {
+    ($segment:expr, $name:expr, $ty:ty) => {
+        unsafe impl WeakPtrTarget for $ty {
+            #[doc(hidden)]
+            fn __typename(f: &mut fmt::Formatter) -> fmt::Result {
+                f.write_str($name)
+            }
+            #[doc(hidden)]
+            unsafe fn __null(new: *mut c_void) {
+                extern "C" {
+                    attr! {
+                        #[link_name = concat!("cxxbridge1$std$weak_ptr$", $segment, "$null")]
+                        fn __null(new: *mut c_void);
+                    }
+                }
+                __null(new);
+            }
+            #[doc(hidden)]
+            unsafe fn __clone(this: *const c_void, new: *mut c_void) {
+                extern "C" {
+                    attr! {
+                        #[link_name = concat!("cxxbridge1$std$weak_ptr$", $segment, "$clone")]
+                        fn __clone(this: *const c_void, new: *mut c_void);
+                    }
+                }
+                __clone(this, new);
+            }
+            #[doc(hidden)]
+            unsafe fn __downgrade(shared: *const c_void, weak: *mut c_void) {
+                extern "C" {
+                    attr! {
+                        #[link_name = concat!("cxxbridge1$std$weak_ptr$", $segment, "$downgrade")]
+                        fn __downgrade(shared: *const c_void, weak: *mut c_void);
+                    }
+                }
+                __downgrade(shared, weak);
+            }
+            #[doc(hidden)]
+            unsafe fn __upgrade(weak: *const c_void, shared: *mut c_void) {
+                extern "C" {
+                    attr! {
+                        #[link_name = concat!("cxxbridge1$std$weak_ptr$", $segment, "$upgrade")]
+                        fn __upgrade(weak: *const c_void, shared: *mut c_void);
+                    }
+                }
+                __upgrade(weak, shared);
+            }
+            #[doc(hidden)]
+            unsafe fn __drop(this: *mut c_void) {
+                extern "C" {
+                    attr! {
+                        #[link_name = concat!("cxxbridge1$std$weak_ptr$", $segment, "$drop")]
+                        fn __drop(this: *mut c_void);
+                    }
+                }
+                __drop(this);
+            }
+        }
+    };
+}
+
+macro_rules! impl_weak_ptr_target_for_primitive {
+    ($ty:ident) => {
+        impl_weak_ptr_target!(stringify!($ty), stringify!($ty), $ty);
+    };
+}
+
+impl_weak_ptr_target_for_primitive!(bool);
+impl_weak_ptr_target_for_primitive!(u8);
+impl_weak_ptr_target_for_primitive!(u16);
+impl_weak_ptr_target_for_primitive!(u32);
+impl_weak_ptr_target_for_primitive!(u64);
+impl_weak_ptr_target_for_primitive!(usize);
+impl_weak_ptr_target_for_primitive!(i8);
+impl_weak_ptr_target_for_primitive!(i16);
+impl_weak_ptr_target_for_primitive!(i32);
+impl_weak_ptr_target_for_primitive!(i64);
+impl_weak_ptr_target_for_primitive!(isize);
+impl_weak_ptr_target_for_primitive!(f32);
+impl_weak_ptr_target_for_primitive!(f64);
+
+impl_weak_ptr_target!("string", "CxxString", CxxString);
diff --git a/syntax/atom.rs b/syntax/atom.rs
index 7d0ef6b..d4ad78f 100644
--- a/syntax/atom.rs
+++ b/syntax/atom.rs
@@ -5,6 +5,7 @@
 #[derive(Copy, Clone, PartialEq)]
 pub enum Atom {
     Bool,
+    Char, // C char, not Rust char
     U8,
     U16,
     U32,
@@ -30,6 +31,7 @@
         use self::Atom::*;
         match s {
             "bool" => Some(Bool),
+            "c_char" => Some(Char),
             "u8" => Some(U8),
             "u16" => Some(U16),
             "u32" => Some(U32),
@@ -60,6 +62,7 @@
         use self::Atom::*;
         match self {
             Bool => "bool",
+            Char => "c_char",
             U8 => "u8",
             U16 => "u16",
             U32 => "u32",
diff --git a/syntax/attrs.rs b/syntax/attrs.rs
index af73b7a..4808f2e 100644
--- a/syntax/attrs.rs
+++ b/syntax/attrs.rs
@@ -1,22 +1,47 @@
 use crate::syntax::namespace::Namespace;
 use crate::syntax::report::Errors;
 use crate::syntax::Atom::{self, *};
-use crate::syntax::{Derive, Doc};
-use proc_macro2::Ident;
+use crate::syntax::{Derive, Doc, ForeignName};
+use proc_macro2::{Ident, TokenStream};
+use quote::ToTokens;
 use syn::parse::{ParseStream, Parser as _};
 use syn::{Attribute, Error, LitStr, Path, Result, Token};
 
+// Intended usage:
+//
+//     let mut doc = Doc::new();
+//     let mut cxx_name = None;
+//     let mut rust_name = None;
+//     /* ... */
+//     let attrs = attrs::parse(
+//         cx,
+//         item.attrs,
+//         attrs::Parser {
+//             doc: Some(&mut doc),
+//             cxx_name: Some(&mut cxx_name),
+//             rust_name: Some(&mut rust_name),
+//             /* ... */
+//             ..Default::default()
+//         },
+//     );
+//
 #[derive(Default)]
 pub struct Parser<'a> {
     pub doc: Option<&'a mut Doc>,
     pub derives: Option<&'a mut Vec<Derive>>,
     pub repr: Option<&'a mut Option<Atom>>,
-    pub cxx_name: Option<&'a mut Option<Ident>>,
-    pub rust_name: Option<&'a mut Option<Ident>>,
     pub namespace: Option<&'a mut Namespace>,
+    pub cxx_name: Option<&'a mut Option<ForeignName>>,
+    pub rust_name: Option<&'a mut Option<Ident>>,
+
+    // Suppress clippy needless_update lint ("struct update has no effect, all
+    // the fields in the struct have already been specified") when preemptively
+    // writing `..Default::default()`.
+    pub(crate) _more: (),
 }
 
-pub(super) fn parse(cx: &mut Errors, attrs: &[Attribute], mut parser: Parser) {
+pub fn parse(cx: &mut Errors, attrs: Vec<Attribute>, mut parser: Parser) -> OtherAttrs {
+    let mut passthrough_attrs = Vec::new();
     for attr in attrs {
         if attr.path.is_ident("doc") {
             match parse_doc_attribute.parse2(attr.tokens.clone()) {
@@ -26,17 +51,23 @@
                         continue;
                     }
                 }
-                Err(err) => return cx.push(err),
+                Err(err) => {
+                    cx.push(err);
+                    break;
+                }
             }
         } else if attr.path.is_ident("derive") {
-            match attr.parse_args_with(parse_derive_attribute) {
+            match attr.parse_args_with(|attr: ParseStream| parse_derive_attribute(cx, attr)) {
                 Ok(attr) => {
                     if let Some(derives) = &mut parser.derives {
                         derives.extend(attr);
                         continue;
                     }
                 }
-                Err(err) => return cx.push(err),
+                Err(err) => {
+                    cx.push(err);
+                    break;
+                }
             }
         } else if attr.path.is_ident("repr") {
             match attr.parse_args_with(parse_repr_attribute) {
@@ -46,27 +77,10 @@
                         continue;
                     }
                 }
-                Err(err) => return cx.push(err),
-            }
-        } else if attr.path.is_ident("cxx_name") {
-            match parse_function_alias_attribute.parse2(attr.tokens.clone()) {
-                Ok(attr) => {
-                    if let Some(cxx_name) = &mut parser.cxx_name {
-                        **cxx_name = Some(attr);
-                        continue;
-                    }
+                Err(err) => {
+                    cx.push(err);
+                    break;
                 }
-                Err(err) => return cx.push(err),
-            }
-        } else if attr.path.is_ident("rust_name") {
-            match parse_function_alias_attribute.parse2(attr.tokens.clone()) {
-                Ok(attr) => {
-                    if let Some(rust_name) = &mut parser.rust_name {
-                        **rust_name = Some(attr);
-                        continue;
-                    }
-                }
-                Err(err) => return cx.push(err),
             }
         } else if attr.path.is_ident("namespace") {
             match parse_namespace_attribute.parse2(attr.tokens.clone()) {
@@ -76,11 +90,61 @@
                         continue;
                     }
                 }
-                Err(err) => return cx.push(err),
+                Err(err) => {
+                    cx.push(err);
+                    break;
+                }
+            }
+        } else if attr.path.is_ident("cxx_name") {
+            match parse_cxx_name_attribute.parse2(attr.tokens.clone()) {
+                Ok(attr) => {
+                    if let Some(cxx_name) = &mut parser.cxx_name {
+                        **cxx_name = Some(attr);
+                        continue;
+                    }
+                }
+                Err(err) => {
+                    cx.push(err);
+                    break;
+                }
+            }
+        } else if attr.path.is_ident("rust_name") {
+            match parse_rust_name_attribute.parse2(attr.tokens.clone()) {
+                Ok(attr) => {
+                    if let Some(rust_name) = &mut parser.rust_name {
+                        **rust_name = Some(attr);
+                        continue;
+                    }
+                }
+                Err(err) => {
+                    cx.push(err);
+                    break;
+                }
+            }
+        } else if attr.path.is_ident("allow")
+            || attr.path.is_ident("warn")
+            || attr.path.is_ident("deny")
+            || attr.path.is_ident("forbid")
+            || attr.path.is_ident("deprecated")
+            || attr.path.is_ident("must_use")
+        {
+            // https://doc.rust-lang.org/reference/attributes/diagnostics.html
+            passthrough_attrs.push(attr);
+            continue;
+        } else if attr.path.segments.len() > 1 {
+            let tool = &attr.path.segments.first().unwrap().ident;
+            if tool == "rustfmt" {
+                // Skip, rustfmt only needs to find it in the pre-expansion source file.
+                continue;
+            } else if tool == "clippy" {
+                passthrough_attrs.push(attr);
+                continue;
             }
         }
-        return cx.error(attr, "unsupported attribute");
+        cx.error(attr, "unsupported attribute");
+        break;
     }
+    OtherAttrs(passthrough_attrs)
 }
 
 fn parse_doc_attribute(input: ParseStream) -> Result<LitStr> {
@@ -89,19 +153,20 @@
     Ok(lit)
 }
 
-fn parse_derive_attribute(input: ParseStream) -> Result<Vec<Derive>> {
-    input
-        .parse_terminated::<Path, Token![,]>(Path::parse_mod_style)?
-        .into_iter()
-        .map(|path| {
-            if let Some(ident) = path.get_ident() {
-                if let Some(derive) = Derive::from(ident) {
-                    return Ok(derive);
-                }
+fn parse_derive_attribute(cx: &mut Errors, input: ParseStream) -> Result<Vec<Derive>> {
+    let paths = input.parse_terminated::<Path, Token![,]>(Path::parse_mod_style)?;
+
+    let mut derives = Vec::new();
+    for path in paths {
+        if let Some(ident) = path.get_ident() {
+            if let Some(derive) = Derive::from(ident) {
+                derives.push(derive);
+                continue;
             }
-            Err(Error::new_spanned(path, "unsupported derive"))
-        })
-        .collect()
+        }
+        cx.error(path, "unsupported derive");
+    }
+    Ok(derives)
 }
 
 fn parse_repr_attribute(input: ParseStream) -> Result<Atom> {
@@ -121,7 +186,24 @@
     ))
 }
 
-fn parse_function_alias_attribute(input: ParseStream) -> Result<Ident> {
+fn parse_namespace_attribute(input: ParseStream) -> Result<Namespace> {
+    input.parse::<Token![=]>()?;
+    let namespace = input.parse::<Namespace>()?;
+    Ok(namespace)
+}
+
+fn parse_cxx_name_attribute(input: ParseStream) -> Result<ForeignName> {
+    input.parse::<Token![=]>()?;
+    if input.peek(LitStr) {
+        let lit: LitStr = input.parse()?;
+        ForeignName::parse(&lit.value(), lit.span())
+    } else {
+        let ident: Ident = input.parse()?;
+        ForeignName::parse(&ident.to_string(), ident.span())
+    }
+}
+
+fn parse_rust_name_attribute(input: ParseStream) -> Result<Ident> {
     input.parse::<Token![=]>()?;
     if input.peek(LitStr) {
         let lit: LitStr = input.parse()?;
@@ -131,8 +213,18 @@
     }
 }
 
-fn parse_namespace_attribute(input: ParseStream) -> Result<Namespace> {
-    input.parse::<Token![=]>()?;
-    let namespace = input.parse::<Namespace>()?;
-    Ok(namespace)
+pub struct OtherAttrs(Vec<Attribute>);
+
+impl OtherAttrs {
+    pub fn none() -> Self {
+        OtherAttrs(Vec::new())
+    }
+}
+
+impl ToTokens for OtherAttrs {
+    fn to_tokens(&self, tokens: &mut TokenStream) {
+        for attr in &self.0 {
+            attr.to_tokens(tokens);
+        }
+    }
 }
diff --git a/syntax/check.rs b/syntax/check.rs
index e8a6448..bab2dec 100644
--- a/syntax/check.rs
+++ b/syntax/check.rs
@@ -1,9 +1,9 @@
 use crate::syntax::atom::Atom::{self, *};
 use crate::syntax::report::Errors;
-use crate::syntax::types::TrivialReason;
+use crate::syntax::visit::{self, Visit};
 use crate::syntax::{
-    error, ident, Api, Enum, ExternFn, ExternType, Impl, Lang, Receiver, Ref, Slice, Struct, Ty1,
-    Type, Types,
+    error, ident, trivial, Api, Array, Enum, ExternFn, ExternType, Impl, Lang, NamedType, Ptr,
+    Receiver, Ref, Signature, SliceRef, Struct, Trait, Ty1, Type, TypeAlias, Types,
 };
 use proc_macro2::{Delimiter, Group, Ident, TokenStream};
 use quote::{quote, ToTokens};
@@ -28,25 +28,31 @@
 
     for ty in cx.types {
         match ty {
-            Type::Ident(ident) => check_type_ident(cx, &ident.rust),
+            Type::Ident(ident) => check_type_ident(cx, ident),
             Type::RustBox(ptr) => check_type_box(cx, ptr),
             Type::RustVec(ty) => check_type_rust_vec(cx, ty),
             Type::UniquePtr(ptr) => check_type_unique_ptr(cx, ptr),
+            Type::SharedPtr(ptr) => check_type_shared_ptr(cx, ptr),
+            Type::WeakPtr(ptr) => check_type_weak_ptr(cx, ptr),
             Type::CxxVector(ptr) => check_type_cxx_vector(cx, ptr),
             Type::Ref(ty) => check_type_ref(cx, ty),
-            Type::Slice(ty) => check_type_slice(cx, ty),
-            _ => {}
+            Type::Ptr(ty) => check_type_ptr(cx, ty),
+            Type::Array(array) => check_type_array(cx, array),
+            Type::Fn(ty) => check_type_fn(cx, ty),
+            Type::SliceRef(ty) => check_type_slice_ref(cx, ty),
+            Type::Str(_) | Type::Void(_) => {}
         }
     }
 
     for api in cx.apis {
         match api {
+            Api::Include(_) => {}
             Api::Struct(strct) => check_api_struct(cx, strct),
             Api::Enum(enm) => check_api_enum(cx, enm),
             Api::CxxType(ety) | Api::RustType(ety) => check_api_type(cx, ety),
             Api::CxxFunction(efn) | Api::RustFunction(efn) => check_api_fn(cx, efn),
+            Api::TypeAlias(alias) => check_api_type_alias(cx, alias),
             Api::Impl(imp) => check_api_impl(cx, imp),
-            _ => {}
         }
     }
 }
@@ -57,7 +63,8 @@
     }
 }
 
-fn check_type_ident(cx: &mut Check, ident: &Ident) {
+fn check_type_ident(cx: &mut Check, name: &NamedType) {
+    let ident = &name.rust;
     if Atom::from(ident).is_none()
         && !cx.types.structs.contains_key(ident)
         && !cx.types.enums.contains_key(ident)
@@ -72,6 +79,7 @@
 fn check_type_box(cx: &mut Check, ptr: &Ty1) {
     if let Type::Ident(ident) = &ptr.inner {
         if cx.types.cxx.contains(&ident.rust)
+            && !cx.types.aliases.contains_key(&ident.rust)
             && !cx.types.structs.contains_key(&ident.rust)
             && !cx.types.enums.contains_key(&ident.rust)
         {
@@ -87,22 +95,27 @@
 }
 
 fn check_type_rust_vec(cx: &mut Check, ty: &Ty1) {
-    if let Type::Ident(ident) = &ty.inner {
-        if cx.types.cxx.contains(&ident.rust)
-            && !cx.types.structs.contains_key(&ident.rust)
-            && !cx.types.enums.contains_key(&ident.rust)
-        {
-            cx.error(ty, "Rust Vec containing C++ type is not supported yet");
-            return;
-        }
+    match &ty.inner {
+        Type::Ident(ident) => {
+            if cx.types.cxx.contains(&ident.rust)
+                && !cx.types.aliases.contains_key(&ident.rust)
+                && !cx.types.structs.contains_key(&ident.rust)
+                && !cx.types.enums.contains_key(&ident.rust)
+            {
+                cx.error(ty, "Rust Vec containing C++ type is not supported yet");
+                return;
+            }
 
-        match Atom::from(&ident.rust) {
-            None | Some(U8) | Some(U16) | Some(U32) | Some(U64) | Some(Usize) | Some(I8)
-            | Some(I16) | Some(I32) | Some(I64) | Some(Isize) | Some(F32) | Some(F64)
-            | Some(RustString) => return,
-            Some(Bool) => { /* todo */ }
-            Some(CxxString) => {}
+            match Atom::from(&ident.rust) {
+                None | Some(Char) | Some(U8) | Some(U16) | Some(U32) | Some(U64) | Some(Usize)
+                | Some(I8) | Some(I16) | Some(I32) | Some(I64) | Some(Isize) | Some(F32)
+                | Some(F64) | Some(RustString) => return,
+                Some(Bool) => { /* todo */ }
+                Some(CxxString) => {}
+            }
         }
+        Type::Str(_) => return,
+        _ => {}
     }
 
     cx.error(ty, "unsupported element type of Vec");
@@ -112,6 +125,7 @@
     if let Type::Ident(ident) = &ptr.inner {
         if cx.types.rust.contains(&ident.rust) {
             cx.error(ptr, "unique_ptr of a Rust type is not supported yet");
+            return;
         }
 
         match Atom::from(&ident.rust) {
@@ -125,6 +139,48 @@
     cx.error(ptr, "unsupported unique_ptr target type");
 }
 
+fn check_type_shared_ptr(cx: &mut Check, ptr: &Ty1) {
+    if let Type::Ident(ident) = &ptr.inner {
+        if cx.types.rust.contains(&ident.rust) {
+            cx.error(ptr, "shared_ptr of a Rust type is not supported yet");
+            return;
+        }
+
+        match Atom::from(&ident.rust) {
+            None | Some(Bool) | Some(U8) | Some(U16) | Some(U32) | Some(U64) | Some(Usize)
+            | Some(I8) | Some(I16) | Some(I32) | Some(I64) | Some(Isize) | Some(F32)
+            | Some(F64) | Some(CxxString) => return,
+            Some(Char) | Some(RustString) => {}
+        }
+    } else if let Type::CxxVector(_) = &ptr.inner {
+        cx.error(ptr, "std::shared_ptr<std::vector> is not supported yet");
+        return;
+    }
+
+    cx.error(ptr, "unsupported shared_ptr target type");
+}
+
+fn check_type_weak_ptr(cx: &mut Check, ptr: &Ty1) {
+    if let Type::Ident(ident) = &ptr.inner {
+        if cx.types.rust.contains(&ident.rust) {
+            cx.error(ptr, "weak_ptr of a Rust type is not supported yet");
+            return;
+        }
+
+        match Atom::from(&ident.rust) {
+            None | Some(Bool) | Some(U8) | Some(U16) | Some(U32) | Some(U64) | Some(Usize)
+            | Some(I8) | Some(I16) | Some(I32) | Some(I64) | Some(Isize) | Some(F32)
+            | Some(F64) | Some(CxxString) => return,
+            Some(Char) | Some(RustString) => {}
+        }
+    } else if let Type::CxxVector(_) = &ptr.inner {
+        cx.error(ptr, "std::weak_ptr<std::vector> is not supported yet");
+        return;
+    }
+
+    cx.error(ptr, "unsupported weak_ptr target type");
+}
+
 fn check_type_cxx_vector(cx: &mut Check, ptr: &Ty1) {
     if let Type::Ident(ident) = &ptr.inner {
         if cx.types.rust.contains(&ident.rust) {
@@ -132,22 +188,38 @@
                 ptr,
                 "C++ vector containing a Rust type is not supported yet",
             );
+            return;
         }
 
         match Atom::from(&ident.rust) {
             None | Some(U8) | Some(U16) | Some(U32) | Some(U64) | Some(Usize) | Some(I8)
             | Some(I16) | Some(I32) | Some(I64) | Some(Isize) | Some(F32) | Some(F64)
             | Some(CxxString) => return,
+            Some(Char) => { /* todo */ }
             Some(Bool) | Some(RustString) => {}
         }
     }
 
-    cx.error(ptr, "unsupported vector target type");
+    cx.error(ptr, "unsupported vector element type");
 }
 
 fn check_type_ref(cx: &mut Check, ty: &Ref) {
-    if ty.lifetime.is_some() {
-        cx.error(ty, "references with explicit lifetimes are not supported");
+    if ty.mutable && !ty.pinned {
+        if let Some(requires_pin) = match &ty.inner {
+            Type::Ident(ident) if ident.rust == CxxString || is_opaque_cxx(cx, &ident.rust) => {
+                Some(ident.rust.to_string())
+            }
+            Type::CxxVector(_) => Some("CxxVector<...>".to_owned()),
+            _ => None,
+        } {
+            cx.error(
+                ty,
+                format!(
+                    "mutable reference to C++ type requires a pin -- use Pin<&mut {}>",
+                    requires_pin,
+                ),
+            );
+        }
     }
 
     match ty.inner {
@@ -162,8 +234,61 @@
     cx.error(ty, "unsupported reference type");
 }
 
-fn check_type_slice(cx: &mut Check, ty: &Slice) {
-    cx.error(ty, "only &[u8] is supported so far, not other slice types");
+fn check_type_ptr(cx: &mut Check, ty: &Ptr) {
+    match ty.inner {
+        Type::Fn(_) | Type::Void(_) => {}
+        Type::Ref(_) => {
+            cx.error(ty, "C++ does not allow pointer to reference as a type");
+            return;
+        }
+        _ => return,
+    }
+
+    cx.error(ty, "unsupported pointer type");
+}
+
+fn check_type_slice_ref(cx: &mut Check, ty: &SliceRef) {
+    let supported = !is_unsized(cx, &ty.inner)
+        || match &ty.inner {
+            Type::Ident(ident) => cx.types.rust.contains(&ident.rust),
+            _ => false,
+        };
+
+    if !supported {
+        let mutable = if ty.mutable { "mut " } else { "" };
+        let mut msg = format!("unsupported &{}[T] element type", mutable);
+        if let Type::Ident(ident) = &ty.inner {
+            if is_opaque_cxx(cx, &ident.rust) {
+                msg += ": opaque C++ type is not supported yet";
+            }
+        }
+        cx.error(ty, msg);
+    }
+}
+
+fn check_type_array(cx: &mut Check, ty: &Array) {
+    let supported = !is_unsized(cx, &ty.inner);
+
+    if !supported {
+        cx.error(ty, "unsupported array element type");
+    }
+}
+
+fn check_type_fn(cx: &mut Check, ty: &Signature) {
+    if ty.throws {
+        cx.error(ty, "function pointer returning Result is not supported yet");
+    }
+
+    for arg in &ty.args {
+        if let Type::Ptr(_) = arg.ty {
+            if ty.unsafety.is_none() {
+                cx.error(
+                    arg,
+                    "pointer argument requires that the function pointer be marked unsafe",
+                );
+            }
+        }
+    }
 }
 
 fn check_api_struct(cx: &mut Check, strct: &Struct) {
@@ -182,17 +307,23 @@
         }
     }
 
-    for field in &strct.fields {
-        if is_unsized(cx, &field.ty) {
-            let desc = describe(cx, &field.ty);
-            let msg = format!("using {} by value is not supported", desc);
-            cx.error(field, msg);
+    for derive in &strct.derives {
+        if derive.what == Trait::ExternType {
+            let msg = format!("derive({}) on shared struct is not supported", derive);
+            cx.error(derive, msg);
         }
+    }
+
+    for field in &strct.fields {
         if let Type::Fn(_) = field.ty {
             cx.error(
                 field,
                 "function pointers in a struct field are not implemented yet",
             );
+        } else if is_unsized(cx, &field.ty) {
+            let desc = describe(cx, &field.ty);
+            let msg = format!("using {} by value is not supported", desc);
+            cx.error(field, msg);
         }
     }
 }
@@ -200,63 +331,110 @@
 fn check_api_enum(cx: &mut Check, enm: &Enum) {
     check_reserved_name(cx, &enm.name.rust);
 
-    if enm.variants.is_empty() {
+    if enm.variants.is_empty() && !enm.explicit_repr {
         let span = span_for_enum_error(enm);
-        cx.error(span, "enums without any variants are not supported");
+        cx.error(
+            span,
+            "explicit #[repr(...)] is required for enum without any variants",
+        );
+    }
+
+    for derive in &enm.derives {
+        if derive.what == Trait::Default || derive.what == Trait::ExternType {
+            let msg = format!("derive({}) on shared enum is not supported", derive);
+            cx.error(derive, msg);
+        }
     }
 }
 
 fn check_api_type(cx: &mut Check, ety: &ExternType) {
     check_reserved_name(cx, &ety.name.rust);
 
-    if let Some(reason) = cx.types.required_trivial.get(&ety.name.rust) {
-        let what = match reason {
-            TrivialReason::StructField(strct) => format!("a field of `{}`", strct.name.rust),
-            TrivialReason::FunctionArgument(efn) => format!("an argument of `{}`", efn.name.rust),
-            TrivialReason::FunctionReturn(efn) => format!("a return value of `{}`", efn.name.rust),
+    for derive in &ety.derives {
+        if derive.what == Trait::ExternType && ety.lang == Lang::Rust {
+            continue;
+        }
+        let lang = match ety.lang {
+            Lang::Rust => "Rust",
+            Lang::Cxx => "C++",
         };
         let msg = format!(
+            "derive({}) on opaque {} type is not supported yet",
+            derive, lang,
+        );
+        cx.error(derive, msg);
+    }
+
+    if !ety.bounds.is_empty() {
+        let bounds = &ety.bounds;
+        let span = quote!(#(#bounds)*);
+        cx.error(span, "extern type bounds are not implemented yet");
+    }
+
+    if let Some(reasons) = cx.types.required_trivial.get(&ety.name.rust) {
+        let msg = format!(
             "needs a cxx::ExternType impl in order to be used as {}",
-            what,
+            trivial::as_what(&ety.name, reasons),
         );
         cx.error(ety, msg);
     }
 }
 
 fn check_api_fn(cx: &mut Check, efn: &ExternFn) {
+    match efn.lang {
+        Lang::Cxx => {
+            if !efn.generics.params.is_empty() && !efn.trusted {
+                let ref span = span_for_generics_error(efn);
+                cx.error(span, "extern C++ function with lifetimes must be declared in `unsafe extern \"C++\"` block");
+            }
+        }
+        Lang::Rust => {
+            if !efn.generics.params.is_empty() && efn.unsafety.is_none() {
+                let ref span = span_for_generics_error(efn);
+                let message = format!(
+                    "must be `unsafe fn {}` in order to expose explicit lifetimes to C++",
+                    efn.name.rust,
+                );
+                cx.error(span, message);
+            }
+        }
+    }
+
     if let Some(receiver) = &efn.receiver {
         let ref span = span_for_receiver_error(receiver);
 
-        if receiver.ty.is_self() {
-            let mutability = match receiver.mutability {
-                Some(_) => "mut ",
-                None => "",
+        if receiver.ty.rust == "Self" {
+            let mutability = match receiver.mutable {
+                true => "mut ",
+                false => "",
             };
             let msg = format!(
-                "unnamed receiver type is only allowed if the surrounding \
-                 extern block contains exactly one extern type; \
-                 use `self: &{mutability}TheType`",
+                "unnamed receiver type is only allowed if the surrounding extern block contains exactly one extern type; use `self: &{mutability}TheType`",
                 mutability = mutability,
             );
             cx.error(span, msg);
+        } else if cx.types.enums.contains_key(&receiver.ty.rust) {
+            cx.error(
+                span,
+                "unsupported receiver type; C++ does not allow member functions on enums",
+            );
         } else if !cx.types.structs.contains_key(&receiver.ty.rust)
             && !cx.types.cxx.contains(&receiver.ty.rust)
             && !cx.types.rust.contains(&receiver.ty.rust)
         {
             cx.error(span, "unrecognized receiver type");
-        }
-
-        if receiver.lifetime.is_some() {
-            cx.error(span, "references with explicit lifetimes are not supported");
+        } else if receiver.mutable && !receiver.pinned && is_opaque_cxx(cx, &receiver.ty.rust) {
+            cx.error(
+                span,
+                format!(
+                    "mutable reference to opaque C++ type requires a pin -- use `self: Pin<&mut {}>`",
+                    receiver.ty.rust,
+                ),
+            );
         }
     }
 
     for arg in &efn.args {
-        if is_unsized(cx, &arg.ty) {
-            let desc = describe(cx, &arg.ty);
-            let msg = format!("passing {} by value is not supported", desc);
-            cx.error(arg, msg);
-        }
         if let Type::Fn(_) = arg.ty {
             if efn.lang == Lang::Rust {
                 cx.error(
@@ -264,51 +442,125 @@
                     "passing a function pointer from C++ to Rust is not implemented yet",
                 );
             }
+        } else if let Type::Ptr(_) = arg.ty {
+            if efn.sig.unsafety.is_none() {
+                cx.error(
+                    arg,
+                    "pointer argument requires that the function be marked unsafe",
+                );
+            }
+        } else if is_unsized(cx, &arg.ty) {
+            let desc = describe(cx, &arg.ty);
+            let msg = format!("passing {} by value is not supported", desc);
+            cx.error(arg, msg);
         }
     }
 
     if let Some(ty) = &efn.ret {
-        if is_unsized(cx, ty) {
+        if let Type::Fn(_) = ty {
+            cx.error(ty, "returning a function pointer is not implemented yet");
+        } else if is_unsized(cx, ty) {
             let desc = describe(cx, ty);
             let msg = format!("returning {} by value is not supported", desc);
             cx.error(ty, msg);
         }
-        if let Type::Fn(_) = ty {
-            cx.error(ty, "returning a function pointer is not implemented yet");
-        }
     }
 
     if efn.lang == Lang::Cxx {
         check_mut_return_restriction(cx, efn);
     }
+}
 
-    check_multiple_arg_lifetimes(cx, efn);
+fn check_api_type_alias(cx: &mut Check, alias: &TypeAlias) {
+    for derive in &alias.derives {
+        let msg = format!("derive({}) on extern type alias is not supported", derive);
+        cx.error(derive, msg);
+    }
 }
 
 fn check_api_impl(cx: &mut Check, imp: &Impl) {
-    if let Type::UniquePtr(ty) | Type::CxxVector(ty) = &imp.ty {
-        if let Type::Ident(inner) = &ty.inner {
-            if Atom::from(&inner.rust).is_none() {
-                return;
+    let ty = &imp.ty;
+
+    if let Some(negative) = imp.negative_token {
+        let span = quote!(#negative #ty);
+        cx.error(span, "negative impl is not supported yet");
+        return;
+    }
+
+    match ty {
+        Type::RustBox(ty)
+        | Type::RustVec(ty)
+        | Type::UniquePtr(ty)
+        | Type::SharedPtr(ty)
+        | Type::WeakPtr(ty)
+        | Type::CxxVector(ty) => {
+            if let Type::Ident(inner) = &ty.inner {
+                if Atom::from(&inner.rust).is_none() {
+                    return;
+                }
             }
         }
+        _ => {}
     }
 
     cx.error(imp, "unsupported Self type of explicit impl");
 }
 
 fn check_mut_return_restriction(cx: &mut Check, efn: &ExternFn) {
+    if efn.sig.unsafety.is_some() {
+        // Unrestricted as long as the function is made unsafe-to-call.
+        return;
+    }
+
     match &efn.ret {
-        Some(Type::Ref(ty)) if ty.mutability.is_some() => {}
+        Some(Type::Ref(ty)) if ty.mutable => {}
+        Some(Type::SliceRef(slice)) if slice.mutable => {}
         _ => return,
     }
 
-    for arg in &efn.args {
-        if let Type::Ref(ty) = &arg.ty {
-            if ty.mutability.is_some() {
-                return;
-            }
+    if let Some(receiver) = &efn.receiver {
+        if receiver.mutable {
+            return;
         }
+        let resolve = match cx.types.try_resolve(&receiver.ty) {
+            Some(resolve) => resolve,
+            None => return,
+        };
+        if !resolve.generics.lifetimes.is_empty() {
+            return;
+        }
+    }
+
+    struct FindLifetimeMut<'a> {
+        cx: &'a Check<'a>,
+        found: bool,
+    }
+
+    impl<'t, 'a> Visit<'t> for FindLifetimeMut<'a> {
+        fn visit_type(&mut self, ty: &'t Type) {
+            self.found |= match ty {
+                Type::Ref(ty) => ty.mutable,
+                Type::SliceRef(slice) => slice.mutable,
+                Type::Ident(ident) if Atom::from(&ident.rust).is_none() => {
+                    match self.cx.types.try_resolve(ident) {
+                        Some(resolve) => !resolve.generics.lifetimes.is_empty(),
+                        None => true,
+                    }
+                }
+                _ => false,
+            };
+            visit::visit_type(self, ty);
+        }
+    }
+
+    let mut visitor = FindLifetimeMut { cx, found: false };
+
+    for arg in &efn.args {
+        visitor.visit_type(&arg.ty);
+    }
+
+    if visitor.found {
+        return;
     }
 
     cx.error(
@@ -317,36 +569,14 @@
     );
 }
 
-fn check_multiple_arg_lifetimes(cx: &mut Check, efn: &ExternFn) {
-    match &efn.ret {
-        Some(Type::Ref(_)) => {}
-        _ => return,
-    }
-
-    let mut reference_args = 0;
-    for arg in &efn.args {
-        if let Type::Ref(_) = &arg.ty {
-            reference_args += 1;
-        }
-    }
-
-    if efn.receiver.is_some() {
-        reference_args += 1;
-    }
-
-    if reference_args != 1 {
-        cx.error(
-            efn,
-            "functions that return a reference must take exactly one input reference",
-        );
-    }
-}
-
 fn check_reserved_name(cx: &mut Check, ident: &Ident) {
     if ident == "Box"
         || ident == "UniquePtr"
+        || ident == "SharedPtr"
+        || ident == "WeakPtr"
         || ident == "Vec"
         || ident == "CxxVector"
+        || ident == "str"
         || Atom::from(ident).is_some()
     {
         cx.error(ident, "reserved name");
@@ -354,18 +584,30 @@
 }
 
 fn is_unsized(cx: &mut Check, ty: &Type) -> bool {
-    let ident = match ty {
-        Type::Ident(ident) => &ident.rust,
-        Type::CxxVector(_) | Type::Slice(_) | Type::Void(_) => return true,
-        _ => return false,
-    };
-    ident == CxxString
-        || cx.types.cxx.contains(ident)
-            && !cx.types.structs.contains_key(ident)
-            && !cx.types.enums.contains_key(ident)
-            && !(cx.types.aliases.contains_key(ident)
-                && cx.types.required_trivial.contains_key(ident))
-        || cx.types.rust.contains(ident)
+    match ty {
+        Type::Ident(ident) => {
+            let ident = &ident.rust;
+            ident == CxxString || is_opaque_cxx(cx, ident) || cx.types.rust.contains(ident)
+        }
+        Type::Array(array) => is_unsized(cx, &array.inner),
+        Type::CxxVector(_) | Type::Fn(_) | Type::Void(_) => true,
+        Type::RustBox(_)
+        | Type::RustVec(_)
+        | Type::UniquePtr(_)
+        | Type::SharedPtr(_)
+        | Type::WeakPtr(_)
+        | Type::Ref(_)
+        | Type::Ptr(_)
+        | Type::Str(_)
+        | Type::SliceRef(_) => false,
+    }
+}
+
+fn is_opaque_cxx(cx: &mut Check, ty: &Ident) -> bool {
+    cx.types.cxx.contains(ty)
+        && !cx.types.structs.contains_key(ty)
+        && !cx.types.enums.contains_key(ty)
+        && !(cx.types.aliases.contains_key(ty) && cx.types.required_trivial.contains_key(ty))
 }
 
 fn span_for_struct_error(strct: &Struct) -> TokenStream {
@@ -395,6 +637,13 @@
     }
 }
 
+fn span_for_generics_error(efn: &ExternFn) -> TokenStream {
+    let unsafety = efn.unsafety;
+    let fn_token = efn.fn_token;
+    let generics = &efn.generics;
+    quote!(#unsafety #fn_token #generics)
+}
+
 fn describe(cx: &mut Check, ty: &Type) -> String {
     match ty {
         Type::Ident(ident) => {
@@ -410,6 +659,8 @@
                 "opaque Rust type".to_owned()
             } else if Atom::from(&ident.rust) == Some(CxxString) {
                 "C++ string".to_owned()
+            } else if Atom::from(&ident.rust) == Some(Char) {
+                "C char".to_owned()
             } else {
                 ident.rust.to_string()
             }
@@ -417,12 +668,15 @@
         Type::RustBox(_) => "Box".to_owned(),
         Type::RustVec(_) => "Vec".to_owned(),
         Type::UniquePtr(_) => "unique_ptr".to_owned(),
+        Type::SharedPtr(_) => "shared_ptr".to_owned(),
+        Type::WeakPtr(_) => "weak_ptr".to_owned(),
         Type::Ref(_) => "reference".to_owned(),
+        Type::Ptr(_) => "raw pointer".to_owned(),
         Type::Str(_) => "&str".to_owned(),
         Type::CxxVector(_) => "C++ vector".to_owned(),
-        Type::Slice(_) => "slice".to_owned(),
-        Type::SliceRefU8(_) => "&[u8]".to_owned(),
+        Type::SliceRef(_) => "slice".to_owned(),
         Type::Fn(_) => "function pointer".to_owned(),
         Type::Void(_) => "()".to_owned(),
+        Type::Array(_) => "array".to_owned(),
     }
 }
diff --git a/syntax/derive.rs b/syntax/derive.rs
index 435aa20..96b3eea 100644
--- a/syntax/derive.rs
+++ b/syntax/derive.rs
@@ -1,26 +1,75 @@
-use proc_macro2::Ident;
+use proc_macro2::{Ident, Span};
+use std::fmt::{self, Display};
+
+#[derive(Copy, Clone)]
+pub struct Derive {
+    pub what: Trait,
+    pub span: Span,
+}
 
 #[derive(Copy, Clone, PartialEq)]
-pub enum Derive {
+pub enum Trait {
     Clone,
     Copy,
+    Debug,
+    Default,
+    Eq,
+    ExternType,
+    Hash,
+    Ord,
+    PartialEq,
+    PartialOrd,
 }
 
 impl Derive {
     pub fn from(ident: &Ident) -> Option<Self> {
-        match ident.to_string().as_str() {
-            "Clone" => Some(Derive::Clone),
-            "Copy" => Some(Derive::Copy),
-            _ => None,
+        let what = match ident.to_string().as_str() {
+            "Clone" => Trait::Clone,
+            "Copy" => Trait::Copy,
+            "Debug" => Trait::Debug,
+            "Default" => Trait::Default,
+            "Eq" => Trait::Eq,
+            "ExternType" => Trait::ExternType,
+            "Hash" => Trait::Hash,
+            "Ord" => Trait::Ord,
+            "PartialEq" => Trait::PartialEq,
+            "PartialOrd" => Trait::PartialOrd,
+            _ => return None,
+        };
+        let span = ident.span();
+        Some(Derive { what, span })
+    }
+}
+
+impl PartialEq<Trait> for Derive {
+    fn eq(&self, other: &Trait) -> bool {
+        self.what == *other
+    }
+}
+
+impl AsRef<str> for Trait {
+    fn as_ref(&self) -> &str {
+        match self {
+            Trait::Clone => "Clone",
+            Trait::Copy => "Copy",
+            Trait::Debug => "Debug",
+            Trait::Default => "Default",
+            Trait::Eq => "Eq",
+            Trait::ExternType => "ExternType",
+            Trait::Hash => "Hash",
+            Trait::Ord => "Ord",
+            Trait::PartialEq => "PartialEq",
+            Trait::PartialOrd => "PartialOrd",
         }
     }
 }
 
-impl AsRef<str> for Derive {
-    fn as_ref(&self) -> &str {
-        match self {
-            Derive::Clone => "Clone",
-            Derive::Copy => "Copy",
-        }
+impl Display for Derive {
+    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+        formatter.write_str(self.what.as_ref())
     }
 }
+
+pub fn contains(derives: &[Derive], query: Trait) -> bool {
+    derives.iter().any(|derive| derive.what == query)
+}
diff --git a/syntax/discriminant.rs b/syntax/discriminant.rs
index 6a04d88..80f7c0b 100644
--- a/syntax/discriminant.rs
+++ b/syntax/discriminant.rs
@@ -16,10 +16,16 @@
 
 #[derive(Copy, Clone, Eq, PartialEq)]
 pub struct Discriminant {
-    negative: bool,
+    sign: Sign,
     magnitude: u64,
 }
 
+#[derive(Copy, Clone, Eq, PartialEq)]
+enum Sign {
+    Negative,
+    Positive,
+}
+
 impl DiscriminantSet {
     pub fn new(repr: Option<Atom>) -> Self {
         DiscriminantSet {
@@ -59,21 +65,23 @@
     pub fn insert_next(&mut self) -> Result<Discriminant> {
         let discriminant = match self.previous {
             None => Discriminant::zero(),
-            Some(mut discriminant) if discriminant.negative => {
-                discriminant.magnitude -= 1;
-                if discriminant.magnitude == 0 {
-                    discriminant.negative = false;
+            Some(mut discriminant) => match discriminant.sign {
+                Sign::Negative => {
+                    discriminant.magnitude -= 1;
+                    if discriminant.magnitude == 0 {
+                        discriminant.sign = Sign::Positive;
+                    }
+                    discriminant
                 }
-                discriminant
-            }
-            Some(mut discriminant) => {
-                if discriminant.magnitude == u64::MAX {
-                    let msg = format!("discriminant overflow on value after {}", u64::MAX);
-                    return Err(Error::new(Span::call_site(), msg));
+                Sign::Positive => {
+                    if discriminant.magnitude == u64::MAX {
+                        let msg = format!("discriminant overflow on value after {}", u64::MAX);
+                        return Err(Error::new(Span::call_site(), msg));
+                    }
+                    discriminant.magnitude += 1;
+                    discriminant
                 }
-                discriminant.magnitude += 1;
-                discriminant
-            }
+            },
         };
         insert(self, discriminant)
     }
@@ -109,7 +117,10 @@
         Expr::Unary(unary) => {
             if let UnOp::Neg(_) = unary.op {
                 let (mut discriminant, repr) = expr_to_discriminant(&unary.expr)?;
-                discriminant.negative ^= true;
+                discriminant.sign = match discriminant.sign {
+                    Sign::Positive => Sign::Negative,
+                    Sign::Negative => Sign::Positive,
+                };
                 return Ok((discriminant, repr));
             }
         }
@@ -133,33 +144,33 @@
             }
         }
     }
-    if set.values.insert(discriminant) {
-        set.previous = Some(discriminant);
-        Ok(discriminant)
-    } else {
-        let msg = format!("discriminant value `{}` already exists", discriminant);
-        Err(Error::new(Span::call_site(), msg))
-    }
+    set.values.insert(discriminant);
+    set.previous = Some(discriminant);
+    Ok(discriminant)
 }
 
 impl Discriminant {
     const fn zero() -> Self {
         Discriminant {
-            negative: false,
+            sign: Sign::Positive,
             magnitude: 0,
         }
     }
 
     const fn pos(u: u64) -> Self {
         Discriminant {
-            negative: false,
+            sign: Sign::Positive,
             magnitude: u,
         }
     }
 
     const fn neg(i: i64) -> Self {
         Discriminant {
-            negative: i < 0,
+            sign: if i < 0 {
+                Sign::Negative
+            } else {
+                Sign::Positive
+            },
             // This is `i.abs() as u64` but without overflow on MIN. Uses the
             // fact that MIN.wrapping_abs() wraps back to MIN whose binary
             // representation is 1<<63, and thus the `as u64` conversion
@@ -172,7 +183,7 @@
 
 impl Display for Discriminant {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        if self.negative {
+        if self.sign == Sign::Negative {
             f.write_str("-")?;
         }
         Display::fmt(&self.magnitude, f)
@@ -181,7 +192,7 @@
 
 impl ToTokens for Discriminant {
     fn to_tokens(&self, tokens: &mut TokenStream) {
-        if self.negative {
+        if self.sign == Sign::Negative {
             Token![-](Span::call_site()).to_tokens(tokens);
         }
         Literal::u64_unsuffixed(self.magnitude).to_tokens(tokens);
@@ -192,15 +203,14 @@
     type Err = Error;
 
     fn from_str(mut s: &str) -> Result<Self> {
-        let negative = s.starts_with('-');
-        if negative {
+        let sign = if s.starts_with('-') {
             s = &s[1..];
-        }
+            Sign::Negative
+        } else {
+            Sign::Positive
+        };
         match s.parse::<u64>() {
-            Ok(magnitude) => Ok(Discriminant {
-                negative,
-                magnitude,
-            }),
+            Ok(magnitude) => Ok(Discriminant { sign, magnitude }),
             Err(_) => Err(Error::new(
                 Span::call_site(),
                 "discriminant value outside of supported range",
@@ -211,11 +221,12 @@
 
 impl Ord for Discriminant {
     fn cmp(&self, other: &Self) -> Ordering {
-        match (self.negative, other.negative) {
-            (true, true) => self.magnitude.cmp(&other.magnitude).reverse(),
-            (true, false) => Ordering::Less, // negative < positive
-            (false, true) => Ordering::Greater, // positive > negative
-            (false, false) => self.magnitude.cmp(&other.magnitude),
+        use self::Sign::{Negative, Positive};
+        match (self.sign, other.sign) {
+            (Negative, Negative) => self.magnitude.cmp(&other.magnitude).reverse(),
+            (Negative, Positive) => Ordering::Less, // negative < positive
+            (Positive, Negative) => Ordering::Greater, // positive > negative
+            (Positive, Positive) => self.magnitude.cmp(&other.magnitude),
         }
     }
 }
diff --git a/syntax/error.rs b/syntax/error.rs
index d0ae021..a672329 100644
--- a/syntax/error.rs
+++ b/syntax/error.rs
@@ -29,7 +29,7 @@
 pub static BOX_CXX_TYPE: Error = Error {
     msg: "Box of a C++ type is not supported yet",
     label: None,
-    note: Some("hint: use UniquePtr<>"),
+    note: Some("hint: use UniquePtr<> or SharedPtr<>"),
 };
 
 pub static CXXBRIDGE_RESERVED: Error = Error {
@@ -47,7 +47,7 @@
 pub static CXX_TYPE_BY_VALUE: Error = Error {
     msg: "C++ type by value is not supported",
     label: None,
-    note: Some("hint: wrap it in a UniquePtr<>"),
+    note: Some("hint: wrap it in a UniquePtr<> or SharedPtr<>"),
 };
 
 pub static DISCRIMINANT_OVERFLOW: Error = Error {
diff --git a/syntax/ident.rs b/syntax/ident.rs
index aaf7832..bb2281e 100644
--- a/syntax/ident.rs
+++ b/syntax/ident.rs
@@ -1,22 +1,27 @@
 use crate::syntax::check::Check;
 use crate::syntax::{error, Api, Pair};
-use proc_macro2::Ident;
 
-fn check(cx: &mut Check, ident: &Ident) {
-    let s = ident.to_string();
-    if s.starts_with("cxxbridge") {
-        cx.error(ident, error::CXXBRIDGE_RESERVED.msg);
-    }
-    if s.contains("__") {
-        cx.error(ident, error::DOUBLE_UNDERSCORE.msg);
-    }
-}
-
-fn check_ident(cx: &mut Check, name: &Pair) {
+fn check(cx: &mut Check, name: &Pair) {
     for segment in &name.namespace {
-        check(cx, segment);
+        check_cxx_ident(cx, &segment.to_string());
     }
-    check(cx, &name.cxx);
+    check_cxx_ident(cx, &name.cxx.to_string());
+    check_rust_ident(cx, &name.rust.to_string());
+
+    fn check_cxx_ident(cx: &mut Check, ident: &str) {
+        if ident.starts_with("cxxbridge") {
+            cx.error(ident, error::CXXBRIDGE_RESERVED.msg);
+        }
+        if ident.contains("__") {
+            cx.error(ident, error::DOUBLE_UNDERSCORE.msg);
+        }
+    }
+
+    fn check_rust_ident(cx: &mut Check, ident: &str) {
+        if ident.starts_with("cxxbridge") {
+            cx.error(ident, error::CXXBRIDGE_RESERVED.msg);
+        }
+    }
 }
 
 pub(crate) fn check_all(cx: &mut Check, apis: &[Api]) {
@@ -24,28 +29,28 @@
         match api {
             Api::Include(_) | Api::Impl(_) => {}
             Api::Struct(strct) => {
-                check_ident(cx, &strct.name);
+                check(cx, &strct.name);
                 for field in &strct.fields {
-                    check(cx, &field.ident);
+                    check(cx, &field.name);
                 }
             }
             Api::Enum(enm) => {
-                check_ident(cx, &enm.name);
+                check(cx, &enm.name);
                 for variant in &enm.variants {
-                    check(cx, &variant.ident);
+                    check(cx, &variant.name);
                 }
             }
             Api::CxxType(ety) | Api::RustType(ety) => {
-                check_ident(cx, &ety.name);
+                check(cx, &ety.name);
             }
             Api::CxxFunction(efn) | Api::RustFunction(efn) => {
-                check(cx, &efn.name.rust);
+                check(cx, &efn.name);
                 for arg in &efn.args {
-                    check(cx, &arg.ident);
+                    check(cx, &arg.name);
                 }
             }
             Api::TypeAlias(alias) => {
-                check_ident(cx, &alias.name);
+                check(cx, &alias.name);
             }
         }
     }
diff --git a/syntax/impls.rs b/syntax/impls.rs
index a4b393a..8b0743b 100644
--- a/syntax/impls.rs
+++ b/syntax/impls.rs
@@ -1,11 +1,12 @@
-use crate::syntax::{ExternFn, Impl, Include, Receiver, Ref, Signature, Slice, Ty1, Type};
-use std::borrow::Borrow;
+use crate::syntax::{
+    Array, ExternFn, Include, Lifetimes, Ptr, Receiver, Ref, Signature, SliceRef, Ty1, Type, Var,
+};
 use std::hash::{Hash, Hasher};
 use std::mem;
 use std::ops::{Deref, DerefMut};
 
 impl PartialEq for Include {
-    fn eq(&self, other: &Include) -> bool {
+    fn eq(&self, other: &Self) -> bool {
         let Include {
             path,
             kind,
@@ -43,13 +44,16 @@
             Type::Ident(t) => t.hash(state),
             Type::RustBox(t) => t.hash(state),
             Type::UniquePtr(t) => t.hash(state),
+            Type::SharedPtr(t) => t.hash(state),
+            Type::WeakPtr(t) => t.hash(state),
             Type::Ref(t) => t.hash(state),
+            Type::Ptr(t) => t.hash(state),
             Type::Str(t) => t.hash(state),
             Type::RustVec(t) => t.hash(state),
             Type::CxxVector(t) => t.hash(state),
             Type::Fn(t) => t.hash(state),
-            Type::Slice(t) => t.hash(state),
-            Type::SliceRefU8(t) => t.hash(state),
+            Type::SliceRef(t) => t.hash(state),
+            Type::Array(t) => t.hash(state),
             Type::Void(_) => {}
         }
     }
@@ -58,28 +62,61 @@
 impl Eq for Type {}
 
 impl PartialEq for Type {
-    fn eq(&self, other: &Type) -> bool {
+    fn eq(&self, other: &Self) -> bool {
         match (self, other) {
             (Type::Ident(lhs), Type::Ident(rhs)) => lhs == rhs,
             (Type::RustBox(lhs), Type::RustBox(rhs)) => lhs == rhs,
             (Type::UniquePtr(lhs), Type::UniquePtr(rhs)) => lhs == rhs,
+            (Type::SharedPtr(lhs), Type::SharedPtr(rhs)) => lhs == rhs,
+            (Type::WeakPtr(lhs), Type::WeakPtr(rhs)) => lhs == rhs,
             (Type::Ref(lhs), Type::Ref(rhs)) => lhs == rhs,
             (Type::Str(lhs), Type::Str(rhs)) => lhs == rhs,
             (Type::RustVec(lhs), Type::RustVec(rhs)) => lhs == rhs,
             (Type::CxxVector(lhs), Type::CxxVector(rhs)) => lhs == rhs,
             (Type::Fn(lhs), Type::Fn(rhs)) => lhs == rhs,
-            (Type::Slice(lhs), Type::Slice(rhs)) => lhs == rhs,
-            (Type::SliceRefU8(lhs), Type::SliceRefU8(rhs)) => lhs == rhs,
+            (Type::SliceRef(lhs), Type::SliceRef(rhs)) => lhs == rhs,
             (Type::Void(_), Type::Void(_)) => true,
             (_, _) => false,
         }
     }
 }
 
+impl Eq for Lifetimes {}
+
+impl PartialEq for Lifetimes {
+    fn eq(&self, other: &Self) -> bool {
+        let Lifetimes {
+            lt_token: _,
+            lifetimes,
+            gt_token: _,
+        } = self;
+        let Lifetimes {
+            lt_token: _,
+            lifetimes: lifetimes2,
+            gt_token: _,
+        } = other;
+        lifetimes.iter().eq(lifetimes2)
+    }
+}
+
+impl Hash for Lifetimes {
+    fn hash<H: Hasher>(&self, state: &mut H) {
+        let Lifetimes {
+            lt_token: _,
+            lifetimes,
+            gt_token: _,
+        } = self;
+        lifetimes.len().hash(state);
+        for lifetime in lifetimes {
+            lifetime.hash(state);
+        }
+    }
+}
+
 impl Eq for Ty1 {}
 
 impl PartialEq for Ty1 {
-    fn eq(&self, other: &Ty1) -> bool {
+    fn eq(&self, other: &Self) -> bool {
         let Ty1 {
             name,
             langle: _,
@@ -112,64 +149,167 @@
 impl Eq for Ref {}
 
 impl PartialEq for Ref {
-    fn eq(&self, other: &Ref) -> bool {
+    fn eq(&self, other: &Self) -> bool {
         let Ref {
+            pinned,
             ampersand: _,
             lifetime,
-            mutability,
+            mutable,
             inner,
+            pin_tokens: _,
+            mutability: _,
         } = self;
         let Ref {
+            pinned: pinned2,
             ampersand: _,
             lifetime: lifetime2,
-            mutability: mutability2,
+            mutable: mutable2,
             inner: inner2,
+            pin_tokens: _,
+            mutability: _,
         } = other;
-        lifetime == lifetime2 && mutability.is_some() == mutability2.is_some() && inner == inner2
+        pinned == pinned2 && lifetime == lifetime2 && mutable == mutable2 && inner == inner2
     }
 }
 
 impl Hash for Ref {
     fn hash<H: Hasher>(&self, state: &mut H) {
         let Ref {
+            pinned,
             ampersand: _,
             lifetime,
-            mutability,
+            mutable,
             inner,
+            pin_tokens: _,
+            mutability: _,
         } = self;
+        pinned.hash(state);
         lifetime.hash(state);
-        mutability.is_some().hash(state);
+        mutable.hash(state);
         inner.hash(state);
     }
 }
 
-impl Eq for Slice {}
+impl Eq for Ptr {}
 
-impl PartialEq for Slice {
-    fn eq(&self, other: &Slice) -> bool {
-        let Slice { bracket: _, inner } = self;
-        let Slice {
+impl PartialEq for Ptr {
+    fn eq(&self, other: &Ptr) -> bool {
+        let Ptr {
+            star: _,
+            mutable,
+            inner,
+            mutability: _,
+            constness: _,
+        } = self;
+        let Ptr {
+            star: _,
+            mutable: mutable2,
+            inner: inner2,
+            mutability: _,
+            constness: _,
+        } = other;
+        mutable == mutable2 && inner == inner2
+    }
+}
+
+impl Hash for Ptr {
+    fn hash<H: Hasher>(&self, state: &mut H) {
+        let Ptr {
+            star: _,
+            mutable,
+            inner,
+            mutability: _,
+            constness: _,
+        } = self;
+        mutable.hash(state);
+        inner.hash(state);
+    }
+}
+
+impl Eq for SliceRef {}
+
+impl PartialEq for SliceRef {
+    fn eq(&self, other: &Self) -> bool {
+        let SliceRef {
+            ampersand: _,
+            lifetime,
+            mutable,
+            bracket: _,
+            inner,
+            mutability: _,
+        } = self;
+        let SliceRef {
+            ampersand: _,
+            lifetime: lifetime2,
+            mutable: mutable2,
             bracket: _,
             inner: inner2,
+            mutability: _,
         } = other;
-        inner == inner2
+        lifetime == lifetime2 && mutable == mutable2 && inner == inner2
     }
 }
 
-impl Hash for Slice {
+impl Hash for SliceRef {
     fn hash<H: Hasher>(&self, state: &mut H) {
-        let Slice { bracket: _, inner } = self;
+        let SliceRef {
+            ampersand: _,
+            lifetime,
+            mutable,
+            bracket: _,
+            inner,
+            mutability: _,
+        } = self;
+        lifetime.hash(state);
+        mutable.hash(state);
         inner.hash(state);
     }
 }
 
+impl Eq for Array {}
+
+impl PartialEq for Array {
+    fn eq(&self, other: &Self) -> bool {
+        let Array {
+            bracket: _,
+            inner,
+            semi_token: _,
+            len,
+            len_token: _,
+        } = self;
+        let Array {
+            bracket: _,
+            inner: inner2,
+            semi_token: _,
+            len: len2,
+            len_token: _,
+        } = other;
+        inner == inner2 && len == len2
+    }
+}
+
+impl Hash for Array {
+    fn hash<H: Hasher>(&self, state: &mut H) {
+        let Array {
+            bracket: _,
+            inner,
+            semi_token: _,
+            len,
+            len_token: _,
+        } = self;
+        inner.hash(state);
+        len.hash(state);
+    }
+}
+
 impl Eq for Signature {}
 
 impl PartialEq for Signature {
-    fn eq(&self, other: &Signature) -> bool {
+    fn eq(&self, other: &Self) -> bool {
         let Signature {
             unsafety,
             fn_token: _,
+            generics: _,
             receiver,
             args,
             ret,
@@ -180,6 +320,7 @@
         let Signature {
             unsafety: unsafety2,
             fn_token: _,
+            generics: _,
             receiver: receiver2,
             args: args2,
             ret: ret2,
@@ -192,7 +333,23 @@
             && ret == ret2
             && throws == throws2
             && args.len() == args2.len()
-            && args.iter().zip(args2).all(|(arg, arg2)| arg == arg2)
+            && args.iter().zip(args2).all(|(arg, arg2)| {
+                let Var {
+                    doc: _,
+                    attrs: _,
+                    visibility: _,
+                    name: _,
+                    ty,
+                } = arg;
+                let Var {
+                    doc: _,
+                    attrs: _,
+                    visibility: _,
+                    name: _,
+                    ty: ty2,
+                } = arg2;
+                ty == ty2
+            })
     }
 }
 
@@ -201,6 +358,7 @@
         let Signature {
             unsafety,
             fn_token: _,
+            generics: _,
             receiver,
             args,
             ret,
@@ -211,7 +369,14 @@
         unsafety.is_some().hash(state);
         receiver.hash(state);
         for arg in args {
-            arg.hash(state);
+            let Var {
+                doc: _,
+                attrs: _,
+                visibility: _,
+                name: _,
+                ty,
+            } = arg;
+            ty.hash(state);
         }
         ret.hash(state);
         throws.hash(state);
@@ -221,74 +386,49 @@
 impl Eq for Receiver {}
 
 impl PartialEq for Receiver {
-    fn eq(&self, other: &Receiver) -> bool {
+    fn eq(&self, other: &Self) -> bool {
         let Receiver {
+            pinned,
             ampersand: _,
             lifetime,
-            mutability,
+            mutable,
             var: _,
             ty,
             shorthand: _,
+            pin_tokens: _,
+            mutability: _,
         } = self;
         let Receiver {
+            pinned: pinned2,
             ampersand: _,
             lifetime: lifetime2,
-            mutability: mutability2,
+            mutable: mutable2,
             var: _,
             ty: ty2,
             shorthand: _,
+            pin_tokens: _,
+            mutability: _,
         } = other;
-        lifetime == lifetime2 && mutability.is_some() == mutability2.is_some() && ty == ty2
+        pinned == pinned2 && lifetime == lifetime2 && mutable == mutable2 && ty == ty2
     }
 }
 
 impl Hash for Receiver {
     fn hash<H: Hasher>(&self, state: &mut H) {
         let Receiver {
+            pinned,
             ampersand: _,
             lifetime,
-            mutability,
+            mutable,
             var: _,
             ty,
             shorthand: _,
+            pin_tokens: _,
+            mutability: _,
         } = self;
+        pinned.hash(state);
         lifetime.hash(state);
-        mutability.is_some().hash(state);
+        mutable.hash(state);
         ty.hash(state);
     }
 }
-
-impl Hash for Impl {
-    fn hash<H: Hasher>(&self, state: &mut H) {
-        let Impl {
-            impl_token: _,
-            ty,
-            brace_token: _,
-        } = self;
-        ty.hash(state);
-    }
-}
-
-impl Eq for Impl {}
-
-impl PartialEq for Impl {
-    fn eq(&self, other: &Impl) -> bool {
-        let Impl {
-            impl_token: _,
-            ty,
-            brace_token: _,
-        } = self;
-        let Impl {
-            impl_token: _,
-            ty: ty2,
-            brace_token: _,
-        } = other;
-        ty == ty2
-    }
-}
-
-impl Borrow<Type> for &Impl {
-    fn borrow(&self) -> &Type {
-        &self.ty
-    }
-}
diff --git a/syntax/improper.rs b/syntax/improper.rs
index 6fd3162..f19eb86 100644
--- a/syntax/improper.rs
+++ b/syntax/improper.rs
@@ -19,7 +19,7 @@
                 } else if let Some(strct) = self.structs.get(ident) {
                     Depends(&strct.name.rust) // iterate to fixed-point
                 } else {
-                    Definite(self.rust.contains(ident))
+                    Definite(self.rust.contains(ident) || self.aliases.contains_key(ident))
                 }
             }
             Type::RustBox(_)
@@ -27,10 +27,13 @@
             | Type::Str(_)
             | Type::Fn(_)
             | Type::Void(_)
-            | Type::Slice(_)
-            | Type::SliceRefU8(_) => Definite(true),
-            Type::UniquePtr(_) | Type::CxxVector(_) => Definite(false),
+            | Type::SliceRef(_) => Definite(true),
+            Type::UniquePtr(_) | Type::SharedPtr(_) | Type::WeakPtr(_) | Type::CxxVector(_) => {
+                Definite(false)
+            }
             Type::Ref(ty) => self.determine_improper_ctype(&ty.inner),
+            Type::Ptr(ty) => self.determine_improper_ctype(&ty.inner),
+            Type::Array(ty) => self.determine_improper_ctype(&ty.inner),
         }
     }
 }
diff --git a/syntax/instantiate.rs b/syntax/instantiate.rs
new file mode 100644
index 0000000..b6cbf24
--- /dev/null
+++ b/syntax/instantiate.rs
@@ -0,0 +1,80 @@
+use crate::syntax::{NamedType, Ty1, Type};
+use proc_macro2::{Ident, Span};
+use std::hash::{Hash, Hasher};
+use syn::Token;
+
+#[derive(Copy, Clone, PartialEq, Eq, Hash)]
+pub enum ImplKey<'a> {
+    RustBox(NamedImplKey<'a>),
+    RustVec(NamedImplKey<'a>),
+    UniquePtr(NamedImplKey<'a>),
+    SharedPtr(NamedImplKey<'a>),
+    WeakPtr(NamedImplKey<'a>),
+    CxxVector(NamedImplKey<'a>),
+}
+
+#[derive(Copy, Clone)]
+pub struct NamedImplKey<'a> {
+    pub begin_span: Span,
+    pub rust: &'a Ident,
+    pub lt_token: Option<Token![<]>,
+    pub gt_token: Option<Token![>]>,
+    pub end_span: Span,
+}
+
+impl Type {
+    pub(crate) fn impl_key(&self) -> Option<ImplKey> {
+        if let Type::RustBox(ty) = self {
+            if let Type::Ident(ident) = &ty.inner {
+                return Some(ImplKey::RustBox(NamedImplKey::new(ty, ident)));
+            }
+        } else if let Type::RustVec(ty) = self {
+            if let Type::Ident(ident) = &ty.inner {
+                return Some(ImplKey::RustVec(NamedImplKey::new(ty, ident)));
+            }
+        } else if let Type::UniquePtr(ty) = self {
+            if let Type::Ident(ident) = &ty.inner {
+                return Some(ImplKey::UniquePtr(NamedImplKey::new(ty, ident)));
+            }
+        } else if let Type::SharedPtr(ty) = self {
+            if let Type::Ident(ident) = &ty.inner {
+                return Some(ImplKey::SharedPtr(NamedImplKey::new(ty, ident)));
+            }
+        } else if let Type::WeakPtr(ty) = self {
+            if let Type::Ident(ident) = &ty.inner {
+                return Some(ImplKey::WeakPtr(NamedImplKey::new(ty, ident)));
+            }
+        } else if let Type::CxxVector(ty) = self {
+            if let Type::Ident(ident) = &ty.inner {
+                return Some(ImplKey::CxxVector(NamedImplKey::new(ty, ident)));
+            }
+        }
+        None
+    }
+}
+
+impl<'a> PartialEq for NamedImplKey<'a> {
+    fn eq(&self, other: &Self) -> bool {
+        PartialEq::eq(self.rust, other.rust)
+    }
+}
+
+impl<'a> Eq for NamedImplKey<'a> {}
+
+impl<'a> Hash for NamedImplKey<'a> {
+    fn hash<H: Hasher>(&self, hasher: &mut H) {
+        self.rust.hash(hasher);
+    }
+}
+
+impl<'a> NamedImplKey<'a> {
+    fn new(outer: &Ty1, inner: &'a NamedType) -> Self {
+        NamedImplKey {
+            begin_span: outer.name.span(),
+            rust: &inner.rust,
+            lt_token: inner.generics.lt_token,
+            gt_token: inner.generics.gt_token,
+            end_span: outer.rangle.span,
+        }
+    }
+}
diff --git a/syntax/mangle.rs b/syntax/mangle.rs
index cc5115a..287b443 100644
--- a/syntax/mangle.rs
+++ b/syntax/mangle.rs
@@ -1,12 +1,86 @@
-use crate::syntax::symbol::{self, Symbol};
-use crate::syntax::{ExternFn, Types};
-use proc_macro2::Ident;
+// Mangled symbol arrangements:
+//
+//   (a) One-off internal symbol.
+//          pattern:  {CXXBRIDGE} $ {NAME}
+//          examples:
+//             - cxxbridge1$exception
+//          defining characteristics:
+//             - 2 segments
+//             - starts with cxxbridge
+//
+//   (b) Behavior on a builtin binding without generic parameter.
+//          pattern:  {CXXBRIDGE} $ {TYPE} $ {NAME}
+//          examples:
+//             - cxxbridge1$string$len
+//          defining characteristics:
+//             - 3 segments
+//             - starts with cxxbridge
+//
+//   (c) Behavior on a builtin binding with generic parameter.
+//          pattern:  {CXXBRIDGE} $ {TYPE} $ {PARAM...} $ {NAME}
+//          examples:
+//             - cxxbridge1$box$org$rust$Struct$alloc
+//             - cxxbridge1$unique_ptr$std$vector$u8$drop
+//          defining characteristics:
+//             - 4+ segments
+//             - starts with cxxbridge
+//
+//   (d) User-defined extern function.
+//          pattern:  {NAMESPACE...} $ {CXXBRIDGE} $ {NAME}
+//          examples:
+//             - cxxbridge1$new_client
+//             - org$rust$cxxbridge1$new_client
+//          defining characteristics:
+//             - cxxbridge is second from end
+//          FIXME: conflict with (a) if they collide with one of our one-off symbol names in the global namespace
+//
+//   (e) User-defined extern member function.
+//          pattern:  {NAMESPACE...} $ {CXXBRIDGE} $ {TYPE} $ {NAME}
+//          examples:
+//             - org$cxxbridge1$Struct$get
+//          defining characteristics:
+//             - cxxbridge is third from end
+//          FIXME: conflict with (b) if e.g. user binds a type in global namespace that collides with our builtin type names
+//
+//   (f) Operator overload.
+//          pattern:  {NAMESPACE...} $ {CXXBRIDGE} $ {TYPE} $ operator $ {NAME}
+//          examples:
+//             - org$rust$cxxbridge1$Struct$operator$eq
+//          defining characteristics:
+//             - second segment from end is `operator` (not possible in type or namespace names)
+//
+//   (g) Closure trampoline.
+//          pattern:  {NAMESPACE...} $ {CXXBRIDGE} $ {TYPE?} $ {NAME} $ {ARGUMENT} $ {DIRECTION}
+//          examples:
+//             - org$rust$cxxbridge1$Struct$invoke$f$0
+//          defining characteristics:
+//             - last symbol is `0` (C half) or `1` (Rust half) which are not legal identifiers on their own
+//
+//
+// Mangled preprocessor variable arrangements:
+//
+//   (A) One-off internal variable.
+//          pattern:  {CXXBRIDGE} _ {NAME}
+//          examples:
+//             - CXXBRIDGE1_PANIC
+//             - CXXBRIDGE1_RUST_STRING
+//          defining characteristics:
+//             - NAME does not begin with STRUCT or ENUM
+//
+//   (B) Guard around user-defined type.
+//          pattern:  {CXXBRIDGE} _ {STRUCT or ENUM} _ {NAMESPACE...} $ {TYPE}
+//          examples:
+//             - CXXBRIDGE1_STRUCT_org$rust$Struct
+//             - CXXBRIDGE1_ENUM_Enabled
 
-const CXXBRIDGE: &str = "cxxbridge05";
+use crate::syntax::symbol::{self, Symbol};
+use crate::syntax::{ExternFn, Pair, Types};
+
+const CXXBRIDGE: &str = "cxxbridge1";
 
 macro_rules! join {
-    ($($segment:expr),*) => {
-        symbol::join(&[$(&$segment),*])
+    ($($segment:expr),+ $(,)?) => {
+        symbol::join(&[$(&$segment),+])
     };
 }
 
@@ -17,20 +91,30 @@
             join!(
                 efn.name.namespace,
                 CXXBRIDGE,
-                receiver_ident.cxx,
-                efn.name.rust
+                receiver_ident.name.cxx,
+                efn.name.rust,
             )
         }
         None => join!(efn.name.namespace, CXXBRIDGE, efn.name.rust),
     }
 }
 
+pub fn operator(receiver: &Pair, operator: &'static str) -> Symbol {
+    join!(
+        receiver.namespace,
+        CXXBRIDGE,
+        receiver.cxx,
+        "operator",
+        operator,
+    )
+}
+
 // The C half of a function pointer trampoline.
-pub fn c_trampoline(efn: &ExternFn, var: &Ident, types: &Types) -> Symbol {
-    join!(extern_fn(efn, types), var, 0)
+pub fn c_trampoline(efn: &ExternFn, var: &Pair, types: &Types) -> Symbol {
+    join!(extern_fn(efn, types), var.rust, 0)
 }
 
 // The Rust half of a function pointer trampoline.
-pub fn r_trampoline(efn: &ExternFn, var: &Ident, types: &Types) -> Symbol {
-    join!(extern_fn(efn, types), var, 1)
+pub fn r_trampoline(efn: &ExternFn, var: &Pair, types: &Types) -> Symbol {
+    join!(extern_fn(efn, types), var.rust, 1)
 }
diff --git a/syntax/map.rs b/syntax/map.rs
new file mode 100644
index 0000000..4873409
--- /dev/null
+++ b/syntax/map.rs
@@ -0,0 +1,179 @@
+use std::borrow::Borrow;
+use std::hash::Hash;
+use std::ops::Index;
+use std::slice;
+
+pub use self::ordered::OrderedMap;
+pub use self::unordered::UnorderedMap;
+pub use std::collections::hash_map::Entry;
+
+mod ordered {
+    use super::{Entry, Iter, UnorderedMap};
+    use std::borrow::Borrow;
+    use std::hash::Hash;
+    use std::mem;
+
+    pub struct OrderedMap<K, V> {
+        map: UnorderedMap<K, usize>,
+        vec: Vec<(K, V)>,
+    }
+
+    impl<K, V> OrderedMap<K, V> {
+        pub fn new() -> Self {
+            OrderedMap {
+                map: UnorderedMap::new(),
+                vec: Vec::new(),
+            }
+        }
+
+        pub fn iter(&self) -> Iter<K, V> {
+            Iter(self.vec.iter())
+        }
+
+        pub fn keys(&self) -> impl Iterator<Item = &K> {
+            self.vec.iter().map(|(k, _v)| k)
+        }
+    }
+
+    impl<K, V> OrderedMap<K, V>
+    where
+        K: Copy + Hash + Eq,
+    {
+        pub fn insert(&mut self, key: K, value: V) -> Option<V> {
+            match self.map.entry(key) {
+                Entry::Occupied(entry) => {
+                    let i = &mut self.vec[*entry.get()];
+                    Some(mem::replace(&mut i.1, value))
+                }
+                Entry::Vacant(entry) => {
+                    entry.insert(self.vec.len());
+                    self.vec.push((key, value));
+                    None
+                }
+            }
+        }
+
+        pub fn contains_key<Q>(&self, key: &Q) -> bool
+        where
+            K: Borrow<Q>,
+            Q: ?Sized + Hash + Eq,
+        {
+            self.map.contains_key(key)
+        }
+
+        pub fn get<Q>(&self, key: &Q) -> Option<&V>
+        where
+            K: Borrow<Q>,
+            Q: ?Sized + Hash + Eq,
+        {
+            let i = *self.map.get(key)?;
+            Some(&self.vec[i].1)
+        }
+    }
+
+    impl<'a, K, V> IntoIterator for &'a OrderedMap<K, V> {
+        type Item = (&'a K, &'a V);
+        type IntoIter = Iter<'a, K, V>;
+        fn into_iter(self) -> Self::IntoIter {
+            self.iter()
+        }
+    }
+}
+
+mod unordered {
+    use crate::syntax::set::UnorderedSet;
+    use std::borrow::Borrow;
+    use std::collections::hash_map::{Entry, HashMap};
+    use std::hash::Hash;
+
+    // Wrapper prohibits accidentally introducing iteration over the map, which
+    // could lead to nondeterministic generated code.
+    pub struct UnorderedMap<K, V>(HashMap<K, V>);
+
+    impl<K, V> UnorderedMap<K, V> {
+        pub fn new() -> Self {
+            UnorderedMap(HashMap::new())
+        }
+    }
+
+    impl<K, V> UnorderedMap<K, V>
+    where
+        K: Hash + Eq,
+    {
+        pub fn insert(&mut self, key: K, value: V) -> Option<V> {
+            self.0.insert(key, value)
+        }
+
+        pub fn contains_key<Q>(&self, key: &Q) -> bool
+        where
+            K: Borrow<Q>,
+            Q: ?Sized + Hash + Eq,
+        {
+            self.0.contains_key(key)
+        }
+
+        pub fn get<Q>(&self, key: &Q) -> Option<&V>
+        where
+            K: Borrow<Q>,
+            Q: ?Sized + Hash + Eq,
+        {
+            self.0.get(key)
+        }
+
+        pub fn entry(&mut self, key: K) -> Entry<K, V> {
+            self.0.entry(key)
+        }
+
+        pub fn remove<Q>(&mut self, key: &Q) -> Option<V>
+        where
+            K: Borrow<Q>,
+            Q: ?Sized + Hash + Eq,
+        {
+            self.0.remove(key)
+        }
+
+        pub fn keys(&self) -> UnorderedSet<K>
+        where
+            K: Copy,
+        {
+            let mut set = UnorderedSet::new();
+            for key in self.0.keys() {
+                set.insert(*key);
+            }
+            set
+        }
+    }
+}
+
+pub struct Iter<'a, K, V>(slice::Iter<'a, (K, V)>);
+
+impl<'a, K, V> Iterator for Iter<'a, K, V> {
+    type Item = (&'a K, &'a V);
+
+    fn next(&mut self) -> Option<Self::Item> {
+        let (k, v) = self.0.next()?;
+        Some((k, v))
+    }
+
+    fn size_hint(&self) -> (usize, Option<usize>) {
+        self.0.size_hint()
+    }
+}
+
+impl<K, V> Default for UnorderedMap<K, V> {
+    fn default() -> Self {
+        UnorderedMap::new()
+    }
+}
+
+impl<Q, K, V> Index<&Q> for UnorderedMap<K, V>
+where
+    K: Borrow<Q> + Hash + Eq,
+    Q: ?Sized + Hash + Eq,
+{
+    type Output = V;
+
+    fn index(&self, key: &Q) -> &V {
+        self.get(key).unwrap()
+    }
+}
diff --git a/syntax/mod.rs b/syntax/mod.rs
index 33ed31c..3d56293 100644
--- a/syntax/mod.rs
+++ b/syntax/mod.rs
@@ -1,9 +1,9 @@
 // Functionality that is shared between the cxxbridge macro and the cmd.
 
 pub mod atom;
-mod attrs;
+pub mod attrs;
 pub mod check;
-mod derive;
+pub mod derive;
 mod discriminant;
 mod doc;
 pub mod error;
@@ -11,18 +11,25 @@
 pub mod ident;
 mod impls;
 mod improper;
+pub mod instantiate;
 pub mod mangle;
+pub mod map;
 mod names;
 pub mod namespace;
 mod parse;
+mod pod;
 pub mod qualified;
 pub mod report;
+pub mod resolve;
 pub mod set;
 pub mod symbol;
 mod tokens;
 mod toposort;
+pub mod trivial;
 pub mod types;
+mod visit;
 
+use self::attrs::OtherAttrs;
 use self::discriminant::Discriminant;
 use self::namespace::Namespace;
 use self::parse::kw;
@@ -30,11 +37,12 @@
 use proc_macro2::{Ident, Span};
 use syn::punctuated::Punctuated;
 use syn::token::{Brace, Bracket, Paren};
-use syn::{Expr, Lifetime, Token, Type as RustType};
+use syn::{Expr, Generics, Lifetime, LitInt, Token, Type as RustType};
 
 pub use self::atom::Atom;
-pub use self::derive::Derive;
+pub use self::derive::{Derive, Trait};
 pub use self::doc::Doc;
+pub use self::names::ForeignName;
 pub use self::parse::parse_items;
 pub use self::types::Types;
 
@@ -67,9 +75,16 @@
 }
 
 pub struct ExternType {
+    pub lang: Lang,
     pub doc: Doc,
+    pub derives: Vec<Derive>,
+    pub attrs: OtherAttrs,
+    pub visibility: Token![pub],
     pub type_token: Token![type],
     pub name: Pair,
+    pub generics: Lifetimes,
+    pub colon_token: Option<Token![:]>,
+    pub bounds: Vec<Derive>,
     pub semi_token: Token![;],
     pub trusted: bool,
 }
@@ -77,33 +92,49 @@
 pub struct Struct {
     pub doc: Doc,
     pub derives: Vec<Derive>,
+    pub attrs: OtherAttrs,
+    pub visibility: Token![pub],
     pub struct_token: Token![struct],
     pub name: Pair,
+    pub generics: Lifetimes,
     pub brace_token: Brace,
     pub fields: Vec<Var>,
 }
 
 pub struct Enum {
     pub doc: Doc,
+    pub derives: Vec<Derive>,
+    pub attrs: OtherAttrs,
+    pub visibility: Token![pub],
     pub enum_token: Token![enum],
     pub name: Pair,
+    pub generics: Lifetimes,
     pub brace_token: Brace,
     pub variants: Vec<Variant>,
     pub repr: Atom,
+    pub repr_type: Type,
+    pub explicit_repr: bool,
 }
 
 pub struct ExternFn {
     pub lang: Lang,
     pub doc: Doc,
+    pub attrs: OtherAttrs,
+    pub visibility: Token![pub],
     pub name: Pair,
     pub sig: Signature,
     pub semi_token: Token![;],
+    pub trusted: bool,
 }
 
 pub struct TypeAlias {
     pub doc: Doc,
+    pub derives: Vec<Derive>,
+    pub attrs: OtherAttrs,
+    pub visibility: Token![pub],
     pub type_token: Token![type],
     pub name: Pair,
+    pub generics: Lifetimes,
     pub eq_token: Token![=],
     pub ty: RustType,
     pub semi_token: Token![;],
@@ -111,13 +142,25 @@
 
 pub struct Impl {
     pub impl_token: Token![impl],
+    pub impl_generics: Lifetimes,
+    pub negative: bool,
     pub ty: Type,
+    pub ty_generics: Lifetimes,
     pub brace_token: Brace,
+    pub negative_token: Option<Token![!]>,
+}
+
+#[derive(Clone, Default)]
+pub struct Lifetimes {
+    pub lt_token: Option<Token![<]>,
+    pub lifetimes: Punctuated<Lifetime, Token![,]>,
+    pub gt_token: Option<Token![>]>,
 }
 
 pub struct Signature {
     pub unsafety: Option<Token![unsafe]>,
     pub fn_token: Token![fn],
+    pub generics: Generics,
     pub receiver: Option<Receiver>,
     pub args: Punctuated<Var, Token![,]>,
     pub ret: Option<Type>,
@@ -126,39 +169,49 @@
     pub throws_tokens: Option<(kw::Result, Token![<], Token![>])>,
 }
 
-#[derive(Eq, PartialEq, Hash)]
 pub struct Var {
-    pub ident: Ident,
+    pub doc: Doc,
+    pub attrs: OtherAttrs,
+    pub visibility: Token![pub],
+    pub name: Pair,
     pub ty: Type,
 }
 
 pub struct Receiver {
+    pub pinned: bool,
     pub ampersand: Token![&],
     pub lifetime: Option<Lifetime>,
-    pub mutability: Option<Token![mut]>,
+    pub mutable: bool,
     pub var: Token![self],
-    pub ty: ResolvableName,
+    pub ty: NamedType,
     pub shorthand: bool,
+    pub pin_tokens: Option<(kw::Pin, Token![<], Token![>])>,
+    pub mutability: Option<Token![mut]>,
 }
 
 pub struct Variant {
-    pub ident: Ident,
+    pub doc: Doc,
+    pub attrs: OtherAttrs,
+    pub name: Pair,
     pub discriminant: Discriminant,
     pub expr: Option<Expr>,
 }
 
 pub enum Type {
-    Ident(ResolvableName),
+    Ident(NamedType),
     RustBox(Box<Ty1>),
     RustVec(Box<Ty1>),
     UniquePtr(Box<Ty1>),
+    SharedPtr(Box<Ty1>),
+    WeakPtr(Box<Ty1>),
     Ref(Box<Ref>),
+    Ptr(Box<Ptr>),
     Str(Box<Ref>),
     CxxVector(Box<Ty1>),
     Fn(Box<Signature>),
     Void(Span),
-    Slice(Box<Slice>),
-    SliceRefU8(Box<Ref>),
+    SliceRef(Box<SliceRef>),
+    Array(Box<Array>),
 }
 
 pub struct Ty1 {
@@ -169,15 +222,38 @@
 }
 
 pub struct Ref {
+    pub pinned: bool,
     pub ampersand: Token![&],
     pub lifetime: Option<Lifetime>,
-    pub mutability: Option<Token![mut]>,
+    pub mutable: bool,
     pub inner: Type,
+    pub pin_tokens: Option<(kw::Pin, Token![<], Token![>])>,
+    pub mutability: Option<Token![mut]>,
 }
 
-pub struct Slice {
+pub struct Ptr {
+    pub star: Token![*],
+    pub mutable: bool,
+    pub inner: Type,
+    pub mutability: Option<Token![mut]>,
+    pub constness: Option<Token![const]>,
+}
+
+pub struct SliceRef {
+    pub ampersand: Token![&],
+    pub lifetime: Option<Lifetime>,
+    pub mutable: bool,
     pub bracket: Bracket,
     pub inner: Type,
+    pub mutability: Option<Token![mut]>,
+}
+
+pub struct Array {
+    pub bracket: Bracket,
+    pub inner: Type,
+    pub semi_token: Token![;],
+    pub len: usize,
+    pub len_token: LitInt,
 }
 
 #[derive(Copy, Clone, PartialEq)]
@@ -191,13 +267,14 @@
 #[derive(Clone)]
 pub struct Pair {
     pub namespace: Namespace,
-    pub cxx: Ident,
+    pub cxx: ForeignName,
     pub rust: Ident,
 }
 
 // Wrapper for a type which needs to be resolved before it can be printed in
 // C++.
-#[derive(Clone, PartialEq, Hash)]
-pub struct ResolvableName {
+#[derive(PartialEq, Eq, Hash)]
+pub struct NamedType {
     pub rust: Ident,
+    pub generics: Lifetimes,
 }
diff --git a/syntax/names.rs b/syntax/names.rs
index 25ae9f4..cb6ace5 100644
--- a/syntax/names.rs
+++ b/syntax/names.rs
@@ -1,73 +1,70 @@
-use crate::syntax::{Namespace, Pair, ResolvableName, Symbol, Types};
+use crate::syntax::symbol::Segment;
+use crate::syntax::{Lifetimes, NamedType, Pair, Symbol};
 use proc_macro2::{Ident, Span};
+use std::fmt::{self, Display};
 use std::iter;
-use syn::Token;
+use syn::parse::{Error, Result};
+use syn::punctuated::Punctuated;
+
+#[derive(Clone)]
+pub struct ForeignName {
+    text: String,
+    span: Span,
+}
 
 impl Pair {
-    // Use this constructor when the item can't have a different name in Rust
-    // and C++.
-    pub fn new(namespace: Namespace, ident: Ident) -> Self {
-        Self {
-            namespace,
-            cxx: ident.clone(),
-            rust: ident,
-        }
-    }
-
-    // Use this constructor when attributes such as #[rust_name] can be used to
-    // potentially give a different name in Rust vs C++.
-    pub fn new_from_differing_names(
-        namespace: Namespace,
-        cxx_ident: Ident,
-        rust_ident: Ident,
-    ) -> Self {
-        Self {
-            namespace,
-            cxx: cxx_ident,
-            rust: rust_ident,
-        }
-    }
-
     pub fn to_symbol(&self) -> Symbol {
-        Symbol::from_idents(self.iter_all_segments())
+        let segments = self
+            .namespace
+            .iter()
+            .map(|ident| ident as &dyn Segment)
+            .chain(iter::once(&self.cxx as &dyn Segment));
+        Symbol::from_idents(segments)
     }
 
     pub fn to_fully_qualified(&self) -> String {
-        format!("::{}", self.join("::"))
-    }
-
-    fn iter_all_segments(&self) -> impl Iterator<Item = &Ident> {
-        self.namespace.iter().chain(iter::once(&self.cxx))
-    }
-
-    fn join(&self, sep: &str) -> String {
-        self.iter_all_segments()
-            .map(|s| s.to_string())
-            .collect::<Vec<_>>()
-            .join(sep)
+        let mut fully_qualified = String::new();
+        for segment in &self.namespace {
+            fully_qualified += "::";
+            fully_qualified += &segment.to_string();
+        }
+        fully_qualified += "::";
+        fully_qualified += &self.cxx.to_string();
+        fully_qualified
     }
 }
 
-impl ResolvableName {
-    pub fn new(ident: Ident) -> Self {
-        Self { rust: ident }
-    }
-
-    pub fn make_self(span: Span) -> Self {
-        Self {
-            rust: Token![Self](span).into(),
-        }
-    }
-
-    pub fn is_self(&self) -> bool {
-        self.rust == "Self"
+impl NamedType {
+    pub fn new(rust: Ident) -> Self {
+        let generics = Lifetimes {
+            lt_token: None,
+            lifetimes: Punctuated::new(),
+            gt_token: None,
+        };
+        NamedType { rust, generics }
     }
 
     pub fn span(&self) -> Span {
         self.rust.span()
     }
+}
 
-    pub fn to_symbol(&self, types: &Types) -> Symbol {
-        types.resolve(self).to_symbol()
+impl ForeignName {
+    pub fn parse(text: &str, span: Span) -> Result<Self> {
+        // TODO: support C++ names containing whitespace (`unsigned int`) or
+        // non-alphanumeric characters (`operator++`).
+        match syn::parse_str::<Ident>(text) {
+            Ok(ident) => {
+                let text = ident.to_string();
+                Ok(ForeignName { text, span })
+            }
+            Err(err) => Err(Error::new(span, err)),
+        }
+    }
+}
+
+impl Display for ForeignName {
+    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+        formatter.write_str(&self.text)
     }
 }
diff --git a/syntax/parse.rs b/syntax/parse.rs
index a5f8bd1..d792076 100644
--- a/syntax/parse.rs
+++ b/syntax/parse.rs
@@ -1,23 +1,28 @@
+use crate::syntax::attrs::OtherAttrs;
 use crate::syntax::discriminant::DiscriminantSet;
 use crate::syntax::file::{Item, ItemForeignMod};
 use crate::syntax::report::Errors;
 use crate::syntax::Atom::*;
 use crate::syntax::{
-    attrs, error, Api, Doc, Enum, ExternFn, ExternType, Impl, Include, IncludeKind, Lang,
-    Namespace, Pair, Receiver, Ref, ResolvableName, Signature, Slice, Struct, Ty1, Type, TypeAlias,
-    Var, Variant,
+    attrs, error, Api, Array, Derive, Doc, Enum, ExternFn, ExternType, ForeignName, Impl, Include,
+    IncludeKind, Lang, Lifetimes, NamedType, Namespace, Pair, Ptr, Receiver, Ref, Signature,
+    SliceRef, Struct, Ty1, Type, TypeAlias, Var, Variant,
 };
-use proc_macro2::{Delimiter, Group, TokenStream, TokenTree};
+use proc_macro2::{Delimiter, Group, Span, TokenStream, TokenTree};
 use quote::{format_ident, quote, quote_spanned};
+use std::mem;
 use syn::parse::{ParseStream, Parser};
 use syn::punctuated::Punctuated;
 use syn::{
-    Abi, Attribute, Error, Fields, FnArg, ForeignItem, ForeignItemFn, ForeignItemType,
-    GenericArgument, Ident, ItemEnum, ItemImpl, ItemStruct, LitStr, Pat, PathArguments, Result,
-    ReturnType, Token, Type as RustType, TypeBareFn, TypePath, TypeReference, TypeSlice,
+    Abi, Attribute, Error, Expr, Fields, FnArg, ForeignItem, ForeignItemFn, ForeignItemType,
+    GenericArgument, GenericParam, Generics, Ident, ItemEnum, ItemImpl, ItemStruct, Lit, LitStr,
+    Pat, PathArguments, Result, ReturnType, Signature as RustSignature, Token, TraitBound,
+    TraitBoundModifier, Type as RustType, TypeArray, TypeBareFn, TypeParamBound, TypePath, TypePtr,
+    TypeReference, Variant as RustVariant, Visibility,
 };
 
 pub mod kw {
+    syn::custom_keyword!(Pin);
     syn::custom_keyword!(Result);
 }
 
@@ -34,14 +39,11 @@
                 Ok(strct) => apis.push(strct),
                 Err(err) => cx.push(err),
             },
-            Item::Enum(item) => match parse_enum(cx, item, namespace) {
-                Ok(enm) => apis.push(enm),
-                Err(err) => cx.push(err),
-            },
+            Item::Enum(item) => apis.push(parse_enum(cx, item, namespace)),
             Item::ForeignMod(foreign_mod) => {
                 parse_foreign_mod(cx, foreign_mod, &mut apis, trusted, namespace)
             }
-            Item::Impl(item) => match parse_impl(item, namespace) {
+            Item::Impl(item) => match parse_impl(item) {
                 Ok(imp) => apis.push(imp),
                 Err(err) => cx.push(err),
             },
@@ -52,29 +54,21 @@
     apis
 }
 
-fn parse_struct(cx: &mut Errors, item: ItemStruct, namespace: &Namespace) -> Result<Api> {
-    let generics = &item.generics;
-    if !generics.params.is_empty() || generics.where_clause.is_some() {
-        let struct_token = item.struct_token;
-        let ident = &item.ident;
-        let where_clause = &generics.where_clause;
-        let span = quote!(#struct_token #ident #generics #where_clause);
-        return Err(Error::new_spanned(
-            span,
-            "struct with generic parameters is not supported yet",
-        ));
-    }
-
+fn parse_struct(cx: &mut Errors, mut item: ItemStruct, namespace: &Namespace) -> Result<Api> {
     let mut doc = Doc::new();
     let mut derives = Vec::new();
     let mut namespace = namespace.clone();
-    attrs::parse(
+    let mut cxx_name = None;
+    let mut rust_name = None;
+    let attrs = attrs::parse(
         cx,
-        &item.attrs,
+        mem::take(&mut item.attrs),
         attrs::Parser {
             doc: Some(&mut doc),
             derives: Some(&mut derives),
             namespace: Some(&mut namespace),
+            cxx_name: Some(&mut cxx_name),
+            rust_name: Some(&mut rust_name),
             ..Default::default()
         },
     );
@@ -87,87 +81,149 @@
         }
     };
 
-    let fields = named_fields
-        .named
-        .into_iter()
-        .map(|field| {
-            Ok(Var {
-                ident: field.ident.unwrap(),
-                ty: parse_type(&field.ty, &namespace)?,
-            })
-        })
-        .collect::<Result<_>>()?;
+    let mut lifetimes = Punctuated::new();
+    let mut has_unsupported_generic_param = false;
+    for pair in item.generics.params.into_pairs() {
+        let (param, punct) = pair.into_tuple();
+        match param {
+            GenericParam::Lifetime(param) => {
+                if !param.bounds.is_empty() && !has_unsupported_generic_param {
+                    let msg = "lifetime parameter with bounds is not supported yet";
+                    cx.error(&param, msg);
+                    has_unsupported_generic_param = true;
+                }
+                lifetimes.push_value(param.lifetime);
+                if let Some(punct) = punct {
+                    lifetimes.push_punct(punct);
+                }
+            }
+            GenericParam::Type(param) => {
+                if !has_unsupported_generic_param {
+                    let msg = "struct with generic type parameter is not supported yet";
+                    cx.error(&param, msg);
+                    has_unsupported_generic_param = true;
+                }
+            }
+            GenericParam::Const(param) => {
+                if !has_unsupported_generic_param {
+                    let msg = "struct with const generic parameter is not supported yet";
+                    cx.error(&param, msg);
+                    has_unsupported_generic_param = true;
+                }
+            }
+        }
+    }
+
+    if let Some(where_clause) = &item.generics.where_clause {
+        cx.error(
+            where_clause,
+            "struct with where-clause is not supported yet",
+        );
+    }
+
+    let mut fields = Vec::new();
+    for field in named_fields.named {
+        let ident = field.ident.unwrap();
+        let mut doc = Doc::new();
+        let mut cxx_name = None;
+        let mut rust_name = None;
+        let attrs = attrs::parse(
+            cx,
+            field.attrs,
+            attrs::Parser {
+                doc: Some(&mut doc),
+                cxx_name: Some(&mut cxx_name),
+                rust_name: Some(&mut rust_name),
+                ..Default::default()
+            },
+        );
+        let ty = match parse_type(&field.ty) {
+            Ok(ty) => ty,
+            Err(err) => {
+                cx.push(err);
+                continue;
+            }
+        };
+        let visibility = visibility_pub(&field.vis, ident.span());
+        let name = pair(Namespace::default(), &ident, cxx_name, rust_name);
+        fields.push(Var {
+            doc,
+            attrs,
+            visibility,
+            name,
+            ty,
+        });
+    }
+
+    let struct_token = item.struct_token;
+    let visibility = visibility_pub(&item.vis, struct_token.span);
+    let name = pair(namespace, &item.ident, cxx_name, rust_name);
+    let generics = Lifetimes {
+        lt_token: item.generics.lt_token,
+        lifetimes,
+        gt_token: item.generics.gt_token,
+    };
+    let brace_token = named_fields.brace_token;
 
     Ok(Api::Struct(Struct {
         doc,
         derives,
-        struct_token: item.struct_token,
-        name: Pair::new(namespace, item.ident),
-        brace_token: named_fields.brace_token,
+        attrs,
+        visibility,
+        struct_token,
+        name,
+        generics,
+        brace_token,
         fields,
     }))
 }
 
-fn parse_enum(cx: &mut Errors, item: ItemEnum, namespace: &Namespace) -> Result<Api> {
-    let generics = &item.generics;
-    if !generics.params.is_empty() || generics.where_clause.is_some() {
-        let enum_token = item.enum_token;
-        let ident = &item.ident;
-        let where_clause = &generics.where_clause;
-        let span = quote!(#enum_token #ident #generics #where_clause);
-        return Err(Error::new_spanned(
-            span,
-            "enums with generic parameters are not allowed",
-        ));
-    }
-
+fn parse_enum(cx: &mut Errors, item: ItemEnum, namespace: &Namespace) -> Api {
     let mut doc = Doc::new();
+    let mut derives = Vec::new();
     let mut repr = None;
     let mut namespace = namespace.clone();
-    attrs::parse(
+    let mut cxx_name = None;
+    let mut rust_name = None;
+    let attrs = attrs::parse(
         cx,
-        &item.attrs,
+        item.attrs,
         attrs::Parser {
             doc: Some(&mut doc),
+            derives: Some(&mut derives),
             repr: Some(&mut repr),
             namespace: Some(&mut namespace),
+            cxx_name: Some(&mut cxx_name),
+            rust_name: Some(&mut rust_name),
             ..Default::default()
         },
     );
 
+    if !item.generics.params.is_empty() {
+        let vis = &item.vis;
+        let enum_token = item.enum_token;
+        let ident = &item.ident;
+        let generics = &item.generics;
+        let span = quote!(#vis #enum_token #ident #generics);
+        cx.error(span, "enum with generic parameters is not supported");
+    } else if let Some(where_clause) = &item.generics.where_clause {
+        cx.error(where_clause, "enum with where-clause is not supported");
+    }
+
     let mut variants = Vec::new();
     let mut discriminants = DiscriminantSet::new(repr);
     for variant in item.variants {
-        match variant.fields {
-            Fields::Unit => {}
-            _ => {
-                cx.error(variant, "enums with data are not supported yet");
-                break;
-            }
+        match parse_variant(cx, variant, &mut discriminants) {
+            Ok(variant) => variants.push(variant),
+            Err(err) => cx.push(err),
         }
-        let expr = variant.discriminant.as_ref().map(|(_, expr)| expr);
-        let try_discriminant = match &expr {
-            Some(lit) => discriminants.insert(lit),
-            None => discriminants.insert_next(),
-        };
-        let discriminant = match try_discriminant {
-            Ok(discriminant) => discriminant,
-            Err(err) => {
-                cx.error(variant, err);
-                break;
-            }
-        };
-        let expr = variant.discriminant.map(|(_, expr)| expr);
-        variants.push(Variant {
-            ident: variant.ident,
-            discriminant,
-            expr,
-        });
     }
 
     let enum_token = item.enum_token;
+    let visibility = visibility_pub(&item.vis, enum_token.span);
     let brace_token = item.brace_token;
 
+    let explicit_repr = repr.is_some();
     let mut repr = U8;
     match discriminants.inferred_repr() {
         Ok(inferred) => repr = inferred,
@@ -178,14 +234,78 @@
         }
     }
 
-    Ok(Api::Enum(Enum {
+    let name = pair(namespace, &item.ident, cxx_name, rust_name);
+    let repr_ident = Ident::new(repr.as_ref(), Span::call_site());
+    let repr_type = Type::Ident(NamedType::new(repr_ident));
+    let generics = Lifetimes {
+        lt_token: None,
+        lifetimes: Punctuated::new(),
+        gt_token: None,
+    };
+
+    Api::Enum(Enum {
         doc,
+        derives,
+        attrs,
+        visibility,
         enum_token,
-        name: Pair::new(namespace, item.ident),
+        name,
+        generics,
         brace_token,
         variants,
         repr,
-    }))
+        repr_type,
+        explicit_repr,
+    })
+}
+
+fn parse_variant(
+    cx: &mut Errors,
+    mut variant: RustVariant,
+    discriminants: &mut DiscriminantSet,
+) -> Result<Variant> {
+    let mut doc = Doc::new();
+    let mut cxx_name = None;
+    let mut rust_name = None;
+    let attrs = attrs::parse(
+        cx,
+        mem::take(&mut variant.attrs),
+        attrs::Parser {
+            doc: Some(&mut doc),
+            cxx_name: Some(&mut cxx_name),
+            rust_name: Some(&mut rust_name),
+            ..Default::default()
+        },
+    );
+
+    match variant.fields {
+        Fields::Unit => {}
+        _ => {
+            let msg = "enums with data are not supported yet";
+            return Err(Error::new_spanned(variant, msg));
+        }
+    }
+
+    let expr = variant.discriminant.as_ref().map(|(_, expr)| expr);
+    let try_discriminant = match &expr {
+        Some(lit) => discriminants.insert(lit),
+        None => discriminants.insert_next(),
+    };
+    let discriminant = match try_discriminant {
+        Ok(discriminant) => discriminant,
+        Err(err) => return Err(Error::new_spanned(variant, err)),
+    };
+
+    let name = pair(Namespace::ROOT, &variant.ident, cxx_name, rust_name);
+    let expr = variant.discriminant.map(|(_, expr)| expr);
+
+    Ok(Variant {
+        doc,
+        attrs,
+        name,
+        discriminant,
+        expr,
+    })
 }
 
 fn parse_foreign_mod(
@@ -204,7 +324,7 @@
         Lang::Rust => {
             if foreign_mod.unsafety.is_some() {
                 let unsafety = foreign_mod.unsafety;
-                let abi = foreign_mod.abi;
+                let abi = &foreign_mod.abi;
                 let span = quote!(#unsafety #abi);
                 cx.error(span, "extern \"Rust\" block does not need to be unsafe");
             }
@@ -217,7 +337,7 @@
     let mut namespace = namespace.clone();
     attrs::parse(
         cx,
-        &foreign_mod.attrs,
+        foreign_mod.attrs,
         attrs::Parser {
             namespace: Some(&mut namespace),
             ..Default::default()
@@ -225,18 +345,18 @@
     );
 
     let mut items = Vec::new();
-    for foreign in &foreign_mod.items {
+    for foreign in foreign_mod.items {
         match foreign {
             ForeignItem::Type(foreign) => {
-                match parse_extern_type(cx, foreign, lang, trusted, &namespace) {
-                    Ok(ety) => items.push(ety),
+                let ety = parse_extern_type(cx, foreign, lang, trusted, &namespace);
+                items.push(ety);
+            }
+            ForeignItem::Fn(foreign) => {
+                match parse_extern_fn(cx, foreign, lang, trusted, &namespace) {
+                    Ok(efn) => items.push(efn),
                     Err(err) => cx.push(err),
                 }
             }
-            ForeignItem::Fn(foreign) => match parse_extern_fn(cx, foreign, lang, &namespace) {
-                Ok(efn) => items.push(efn),
-                Err(err) => cx.push(err),
-            },
             ForeignItem::Macro(foreign) if foreign.mac.path.is_ident("include") => {
                 match foreign.mac.parse_body_with(parse_include) {
                     Ok(include) => items.push(Api::Include(include)),
@@ -244,7 +364,7 @@
                 }
             }
             ForeignItem::Verbatim(tokens) => {
-                match parse_extern_verbatim(cx, tokens, lang, &namespace) {
+                match parse_extern_verbatim(cx, tokens, lang, trusted, &namespace) {
                     Ok(api) => items.push(api),
                     Err(err) => cx.push(err),
                 }
@@ -253,6 +373,18 @@
         }
     }
 
+    if !trusted
+        && items.iter().any(|api| match api {
+            Api::CxxFunction(efn) => efn.unsafety.is_none(),
+            _ => false,
+        })
+    {
+        cx.error(
+            foreign_mod.abi,
+            "block must be declared `unsafe extern \"C++\"` if it contains any safe-to-call C++ functions",
+        );
+    }
+
     let mut types = items.iter().filter_map(|item| match item {
         Api::CxxType(ety) | Api::RustType(ety) => Some(&ety.name),
         Api::TypeAlias(alias) => Some(&alias.name),
@@ -263,8 +395,8 @@
         for item in &mut items {
             if let Api::CxxFunction(efn) | Api::RustFunction(efn) = item {
                 if let Some(receiver) = &mut efn.receiver {
-                    if receiver.ty.is_self() {
-                        receiver.ty = ResolvableName::new(single_type.rust.clone());
+                    if receiver.ty.rust == "Self" {
+                        receiver.ty.rust = single_type.rust.clone();
                     }
                 }
             }
@@ -280,64 +412,113 @@
         None => {
             return Err(Error::new_spanned(
                 abi,
-                "ABI name is required, extern \"C\" or extern \"Rust\"",
+                "ABI name is required, extern \"C++\" or extern \"Rust\"",
             ));
         }
     };
+
     match name.value().as_str() {
-        "C" | "C++" => Ok(Lang::Cxx),
+        "C++" => Ok(Lang::Cxx),
         "Rust" => Ok(Lang::Rust),
-        _ => Err(Error::new_spanned(abi, "unrecognized ABI")),
+        _ => Err(Error::new_spanned(
+            abi,
+            "unrecognized ABI, requires either \"C++\" or \"Rust\"",
+        )),
     }
 }
 
 fn parse_extern_type(
     cx: &mut Errors,
-    foreign_type: &ForeignItemType,
+    foreign_type: ForeignItemType,
+    lang: Lang,
+    trusted: bool,
+    namespace: &Namespace,
+) -> Api {
+    let mut doc = Doc::new();
+    let mut derives = Vec::new();
+    let mut namespace = namespace.clone();
+    let mut cxx_name = None;
+    let mut rust_name = None;
+    let attrs = attrs::parse(
+        cx,
+        foreign_type.attrs,
+        attrs::Parser {
+            doc: Some(&mut doc),
+            derives: Some(&mut derives),
+            namespace: Some(&mut namespace),
+            cxx_name: Some(&mut cxx_name),
+            rust_name: Some(&mut rust_name),
+            ..Default::default()
+        },
+    );
+
+    let type_token = foreign_type.type_token;
+    let visibility = visibility_pub(&foreign_type.vis, type_token.span);
+    let name = pair(namespace, &foreign_type.ident, cxx_name, rust_name);
+    let generics = Lifetimes {
+        lt_token: None,
+        lifetimes: Punctuated::new(),
+        gt_token: None,
+    };
+    let colon_token = None;
+    let bounds = Vec::new();
+    let semi_token = foreign_type.semi_token;
+
+    (match lang {
+        Lang::Cxx => Api::CxxType,
+        Lang::Rust => Api::RustType,
+    })(ExternType {
+        lang,
+        doc,
+        derives,
+        attrs,
+        visibility,
+        type_token,
+        name,
+        generics,
+        colon_token,
+        bounds,
+        semi_token,
+        trusted,
+    })
+}
+
+fn parse_extern_fn(
+    cx: &mut Errors,
+    mut foreign_fn: ForeignItemFn,
     lang: Lang,
     trusted: bool,
     namespace: &Namespace,
 ) -> Result<Api> {
     let mut doc = Doc::new();
     let mut namespace = namespace.clone();
-    attrs::parse(
+    let mut cxx_name = None;
+    let mut rust_name = None;
+    let attrs = attrs::parse(
         cx,
-        &foreign_type.attrs,
+        mem::take(&mut foreign_fn.attrs),
         attrs::Parser {
             doc: Some(&mut doc),
             namespace: Some(&mut namespace),
+            cxx_name: Some(&mut cxx_name),
+            rust_name: Some(&mut rust_name),
             ..Default::default()
         },
     );
-    let type_token = foreign_type.type_token;
-    let ident = foreign_type.ident.clone();
-    let semi_token = foreign_type.semi_token;
-    let api_type = match lang {
-        Lang::Cxx => Api::CxxType,
-        Lang::Rust => Api::RustType,
-    };
-    Ok(api_type(ExternType {
-        doc,
-        type_token,
-        name: Pair::new(namespace, ident),
-        semi_token,
-        trusted,
-    }))
-}
 
-fn parse_extern_fn(
-    cx: &mut Errors,
-    foreign_fn: &ForeignItemFn,
-    lang: Lang,
-    namespace: &Namespace,
-) -> Result<Api> {
     let generics = &foreign_fn.sig.generics;
-    if !generics.params.is_empty() || generics.where_clause.is_some() {
+    if generics.where_clause.is_some()
+        || generics.params.iter().any(|param| match param {
+            GenericParam::Lifetime(lifetime) => !lifetime.bounds.is_empty(),
+            GenericParam::Type(_) | GenericParam::Const(_) => true,
+        })
+    {
         return Err(Error::new_spanned(
             foreign_fn,
             "extern function with generic parameters is not supported yet",
         ));
     }
+
     if let Some(variadic) = &foreign_fn.sig.variadic {
         return Err(Error::new_spanned(
             variadic,
@@ -345,21 +526,26 @@
         ));
     }
 
-    let mut doc = Doc::new();
-    let mut cxx_name = None;
-    let mut rust_name = None;
-    let mut namespace = namespace.clone();
-    attrs::parse(
-        cx,
-        &foreign_fn.attrs,
-        attrs::Parser {
-            doc: Some(&mut doc),
-            cxx_name: Some(&mut cxx_name),
-            rust_name: Some(&mut rust_name),
-            namespace: Some(&mut namespace),
-            ..Default::default()
-        },
-    );
+    if foreign_fn.sig.asyncness.is_some() {
+        return Err(Error::new_spanned(
+            foreign_fn,
+            "async function is not directly supported yet, but see https://cxx.rs/async.html for a working approach",
+        ));
+    }
+
+    if foreign_fn.sig.constness.is_some() {
+        return Err(Error::new_spanned(
+            foreign_fn,
+            "const extern function is not supported",
+        ));
+    }
+
+    if let Some(abi) = &foreign_fn.sig.abi {
+        return Err(Error::new_spanned(
+            abi,
+            "explicit ABI on extern function is not supported",
+        ));
+    }
 
     let mut receiver = None;
     let mut args = Punctuated::new();
@@ -369,12 +555,15 @@
             FnArg::Receiver(arg) => {
                 if let Some((ampersand, lifetime)) = &arg.reference {
                     receiver = Some(Receiver {
+                        pinned: false,
                         ampersand: *ampersand,
                         lifetime: lifetime.clone(),
-                        mutability: arg.mutability,
+                        mutable: arg.mutability.is_some(),
                         var: arg.self_token,
-                        ty: ResolvableName::make_self(arg.self_token.span),
+                        ty: NamedType::new(Ident::new("Self", arg.self_token.span)),
                         shorthand: true,
+                        pin_tokens: None,
+                        mutability: arg.mutability,
                     });
                     continue;
                 }
@@ -384,13 +573,23 @@
                 let ident = match arg.pat.as_ref() {
                     Pat::Ident(pat) => pat.ident.clone(),
                     Pat::Wild(pat) => {
-                        Ident::new(&format!("_{}", args.len()), pat.underscore_token.span)
+                        Ident::new(&format!("arg{}", args.len()), pat.underscore_token.span)
                     }
                     _ => return Err(Error::new_spanned(arg, "unsupported signature")),
                 };
-                let ty = parse_type(&arg.ty, &namespace)?;
+                let ty = parse_type(&arg.ty)?;
                 if ident != "self" {
-                    args.push_value(Var { ident, ty });
+                    let doc = Doc::new();
+                    let attrs = OtherAttrs::none();
+                    let visibility = Token![pub](ident.span());
+                    let name = pair(Namespace::default(), &ident, None, None);
+                    args.push_value(Var {
+                        doc,
+                        attrs,
+                        visibility,
+                        name,
+                        ty,
+                    });
                     if let Some(comma) = comma {
                         args.push_punct(*comma);
                     }
@@ -399,12 +598,15 @@
                 if let Type::Ref(reference) = ty {
                     if let Type::Ident(ident) = reference.inner {
                         receiver = Some(Receiver {
+                            pinned: reference.pinned,
                             ampersand: reference.ampersand,
                             lifetime: reference.lifetime,
-                            mutability: reference.mutability,
+                            mutable: reference.mutable,
                             var: Token![self](ident.rust.span()),
                             ty: ident,
                             shorthand: false,
+                            pin_tokens: reference.pin_tokens,
+                            mutability: reference.mutability,
                         });
                         continue;
                     }
@@ -415,29 +617,30 @@
     }
 
     let mut throws_tokens = None;
-    let ret = parse_return_type(&foreign_fn.sig.output, &mut throws_tokens, &namespace)?;
+    let ret = parse_return_type(&foreign_fn.sig.output, &mut throws_tokens)?;
     let throws = throws_tokens.is_some();
     let unsafety = foreign_fn.sig.unsafety;
     let fn_token = foreign_fn.sig.fn_token;
-    let name = Pair::new_from_differing_names(
-        namespace,
-        cxx_name.unwrap_or(foreign_fn.sig.ident.clone()),
-        rust_name.unwrap_or(foreign_fn.sig.ident.clone()),
-    );
+    let inherited_span = unsafety.map_or(fn_token.span, |unsafety| unsafety.span);
+    let visibility = visibility_pub(&foreign_fn.vis, inherited_span);
+    let name = pair(namespace, &foreign_fn.sig.ident, cxx_name, rust_name);
+    let generics = generics.clone();
     let paren_token = foreign_fn.sig.paren_token;
     let semi_token = foreign_fn.semi_token;
-    let api_function = match lang {
+
+    Ok(match lang {
         Lang::Cxx => Api::CxxFunction,
         Lang::Rust => Api::RustFunction,
-    };
-
-    Ok(api_function(ExternFn {
+    }(ExternFn {
         lang,
         doc,
+        attrs,
+        visibility,
         name,
         sig: Signature {
             unsafety,
             fn_token,
+            generics,
             receiver,
             args,
             ret,
@@ -446,72 +649,258 @@
             throws_tokens,
         },
         semi_token,
+        trusted,
     }))
 }
 
 fn parse_extern_verbatim(
     cx: &mut Errors,
-    tokens: &TokenStream,
+    tokens: TokenStream,
     lang: Lang,
+    trusted: bool,
     namespace: &Namespace,
 ) -> Result<Api> {
-    // type Alias = crate::path::to::Type;
-    let parse = |input: ParseStream| -> Result<TypeAlias> {
+    |input: ParseStream| -> Result<Api> {
         let attrs = input.call(Attribute::parse_outer)?;
-        let type_token: Token![type] = match input.parse()? {
-            Some(type_token) => type_token,
-            None => {
-                let span = input.cursor().token_stream();
-                return Err(Error::new_spanned(span, "unsupported foreign item"));
-            }
-        };
-        let ident: Ident = input.parse()?;
-        let eq_token: Token![=] = input.parse()?;
-        let ty: RustType = input.parse()?;
-        let semi_token: Token![;] = input.parse()?;
-        let mut doc = Doc::new();
-        let mut namespace = namespace.clone();
-        attrs::parse(
-            cx,
-            &attrs,
-            attrs::Parser {
-                doc: Some(&mut doc),
-                namespace: Some(&mut namespace),
-                ..Default::default()
-            },
-        );
-
-        Ok(TypeAlias {
-            doc,
-            type_token,
-            name: Pair::new(namespace, ident),
-            eq_token,
-            ty,
-            semi_token,
-        })
-    };
-
-    let type_alias = parse.parse2(tokens.clone())?;
-    match lang {
-        Lang::Cxx => Ok(Api::TypeAlias(type_alias)),
-        Lang::Rust => {
-            let (type_token, semi_token) = (type_alias.type_token, type_alias.semi_token);
-            let span = quote!(#type_token #semi_token);
-            let msg = "type alias in extern \"Rust\" block is not supported";
-            Err(Error::new_spanned(span, msg))
+        let visibility: Visibility = input.parse()?;
+        if input.peek(Token![type]) {
+            parse_extern_verbatim_type(cx, attrs, visibility, input, lang, trusted, namespace)
+        } else if input.peek(Token![fn]) {
+            parse_extern_verbatim_fn(input)
+        } else {
+            let span = input.cursor().token_stream();
+            Err(Error::new_spanned(
+                span,
+                "unsupported foreign item, expected `type` or `fn`",
+            ))
         }
     }
+    .parse2(tokens)
+}
+
+fn parse_extern_verbatim_type(
+    cx: &mut Errors,
+    attrs: Vec<Attribute>,
+    visibility: Visibility,
+    input: ParseStream,
+    lang: Lang,
+    trusted: bool,
+    namespace: &Namespace,
+) -> Result<Api> {
+    let type_token: Token![type] = input.parse()?;
+    let ident: Ident = input.parse()?;
+    let generics: Generics = input.parse()?;
+    let mut lifetimes = Punctuated::new();
+    let mut has_unsupported_generic_param = false;
+    for pair in generics.params.into_pairs() {
+        let (param, punct) = pair.into_tuple();
+        match param {
+            GenericParam::Lifetime(param) => {
+                if !param.bounds.is_empty() && !has_unsupported_generic_param {
+                    let msg = "lifetime parameter with bounds is not supported yet";
+                    cx.error(&param, msg);
+                    has_unsupported_generic_param = true;
+                }
+                lifetimes.push_value(param.lifetime);
+                if let Some(punct) = punct {
+                    lifetimes.push_punct(punct);
+                }
+            }
+            GenericParam::Type(param) => {
+                if !has_unsupported_generic_param {
+                    let msg = "extern type with generic type parameter is not supported yet";
+                    cx.error(&param, msg);
+                    has_unsupported_generic_param = true;
+                }
+            }
+            GenericParam::Const(param) => {
+                if !has_unsupported_generic_param {
+                    let msg = "extern type with const generic parameter is not supported yet";
+                    cx.error(&param, msg);
+                    has_unsupported_generic_param = true;
+                }
+            }
+        }
+    }
+    let lifetimes = Lifetimes {
+        lt_token: generics.lt_token,
+        lifetimes,
+        gt_token: generics.gt_token,
+    };
+    let lookahead = input.lookahead1();
+    if lookahead.peek(Token![=]) {
+        // type Alias = crate::path::to::Type;
+        parse_type_alias(
+            cx, attrs, visibility, type_token, ident, lifetimes, input, lang, namespace,
+        )
+    } else if lookahead.peek(Token![:]) || lookahead.peek(Token![;]) {
+        // type Opaque: Bound2 + Bound2;
+        parse_extern_type_bounded(
+            cx, attrs, visibility, type_token, ident, lifetimes, input, lang, trusted, namespace,
+        )
+    } else {
+        Err(lookahead.error())
+    }
 }
 
-fn parse_impl(imp: ItemImpl, namespace: &Namespace) -> Result<Api> {
+fn parse_extern_verbatim_fn(input: ParseStream) -> Result<Api> {
+    input.parse::<RustSignature>()?;
+    input.parse::<Token![;]>()?;
+    unreachable!()
+}
+
+fn parse_type_alias(
+    cx: &mut Errors,
+    attrs: Vec<Attribute>,
+    visibility: Visibility,
+    type_token: Token![type],
+    ident: Ident,
+    generics: Lifetimes,
+    input: ParseStream,
+    lang: Lang,
+    namespace: &Namespace,
+) -> Result<Api> {
+    let eq_token: Token![=] = input.parse()?;
+    let ty: RustType = input.parse()?;
+    let semi_token: Token![;] = input.parse()?;
+
+    let mut doc = Doc::new();
+    let mut derives = Vec::new();
+    let mut namespace = namespace.clone();
+    let mut cxx_name = None;
+    let mut rust_name = None;
+    let attrs = attrs::parse(
+        cx,
+        attrs,
+        attrs::Parser {
+            doc: Some(&mut doc),
+            derives: Some(&mut derives),
+            namespace: Some(&mut namespace),
+            cxx_name: Some(&mut cxx_name),
+            rust_name: Some(&mut rust_name),
+            ..Default::default()
+        },
+    );
+
+    if lang == Lang::Rust {
+        let span = quote!(#type_token #semi_token);
+        let msg = "type alias in extern \"Rust\" block is not supported";
+        return Err(Error::new_spanned(span, msg));
+    }
+
+    let visibility = visibility_pub(&visibility, type_token.span);
+    let name = pair(namespace, &ident, cxx_name, rust_name);
+
+    Ok(Api::TypeAlias(TypeAlias {
+        doc,
+        derives,
+        attrs,
+        visibility,
+        type_token,
+        name,
+        generics,
+        eq_token,
+        ty,
+        semi_token,
+    }))
+}
+
+fn parse_extern_type_bounded(
+    cx: &mut Errors,
+    attrs: Vec<Attribute>,
+    visibility: Visibility,
+    type_token: Token![type],
+    ident: Ident,
+    generics: Lifetimes,
+    input: ParseStream,
+    lang: Lang,
+    trusted: bool,
+    namespace: &Namespace,
+) -> Result<Api> {
+    let mut bounds = Vec::new();
+    let colon_token: Option<Token![:]> = input.parse()?;
+    if colon_token.is_some() {
+        loop {
+            match input.parse()? {
+                TypeParamBound::Trait(TraitBound {
+                    paren_token: None,
+                    modifier: TraitBoundModifier::None,
+                    lifetimes: None,
+                    path,
+                }) if if let Some(derive) = path.get_ident().and_then(Derive::from) {
+                    bounds.push(derive);
+                    true
+                } else {
+                    false
+                } => {}
+                bound @ TypeParamBound::Trait(_) | bound @ TypeParamBound::Lifetime(_) => {
+                    cx.error(bound, "unsupported trait");
+                }
+            }
+
+            let lookahead = input.lookahead1();
+            if lookahead.peek(Token![+]) {
+                input.parse::<Token![+]>()?;
+            } else if lookahead.peek(Token![;]) {
+                break;
+            } else {
+                return Err(lookahead.error());
+            }
+        }
+    }
+    let semi_token: Token![;] = input.parse()?;
+
+    let mut doc = Doc::new();
+    let mut derives = Vec::new();
+    let mut namespace = namespace.clone();
+    let mut cxx_name = None;
+    let mut rust_name = None;
+    let attrs = attrs::parse(
+        cx,
+        attrs,
+        attrs::Parser {
+            doc: Some(&mut doc),
+            derives: Some(&mut derives),
+            namespace: Some(&mut namespace),
+            cxx_name: Some(&mut cxx_name),
+            rust_name: Some(&mut rust_name),
+            ..Default::default()
+        },
+    );
+
+    let visibility = visibility_pub(&visibility, type_token.span);
+    let name = pair(namespace, &ident, cxx_name, rust_name);
+
+    Ok(match lang {
+        Lang::Cxx => Api::CxxType,
+        Lang::Rust => Api::RustType,
+    }(ExternType {
+        lang,
+        doc,
+        derives,
+        attrs,
+        visibility,
+        type_token,
+        name,
+        generics,
+        colon_token,
+        bounds,
+        semi_token,
+        trusted,
+    }))
+}
+
+fn parse_impl(imp: ItemImpl) -> Result<Api> {
+    let impl_token = imp.impl_token;
+
     if !imp.items.is_empty() {
         let mut span = Group::new(Delimiter::Brace, TokenStream::new());
         span.set_span(imp.brace_token.span);
         return Err(Error::new_spanned(span, "expected an empty impl block"));
     }
 
-    let self_ty = &imp.self_ty;
     if let Some((bang, path, for_token)) = &imp.trait_ {
+        let self_ty = &imp.self_ty;
         let span = quote!(#bang #path #for_token #self_ty);
         return Err(Error::new_spanned(
             span,
@@ -519,18 +908,83 @@
         ));
     }
 
-    let generics = &imp.generics;
-    if !generics.params.is_empty() || generics.where_clause.is_some() {
+    if let Some(where_clause) = imp.generics.where_clause {
         return Err(Error::new_spanned(
-            imp,
-            "generic parameters on an impl is not supported",
+            where_clause,
+            "where-clause on an impl is not supported yet",
         ));
     }
+    let mut impl_generics = Lifetimes {
+        lt_token: imp.generics.lt_token,
+        lifetimes: Punctuated::new(),
+        gt_token: imp.generics.gt_token,
+    };
+    for pair in imp.generics.params.into_pairs() {
+        let (param, punct) = pair.into_tuple();
+        match param {
+            GenericParam::Lifetime(def) if def.bounds.is_empty() => {
+                impl_generics.lifetimes.push_value(def.lifetime);
+                if let Some(punct) = punct {
+                    impl_generics.lifetimes.push_punct(punct);
+                }
+            }
+            _ => {
+                let span = quote!(#impl_token #impl_generics);
+                return Err(Error::new_spanned(
+                    span,
+                    "generic parameter on an impl is not supported yet",
+                ));
+            }
+        }
+    }
+
+    let mut negative_token = None;
+    let mut self_ty = *imp.self_ty;
+    if let RustType::Verbatim(ty) = &self_ty {
+        let mut iter = ty.clone().into_iter();
+        if let Some(TokenTree::Punct(punct)) = iter.next() {
+            if punct.as_char() == '!' {
+                let ty = iter.collect::<TokenStream>();
+                if !ty.is_empty() {
+                    negative_token = Some(Token![!](punct.span()));
+                    self_ty = syn::parse2(ty)?;
+                }
+            }
+        }
+    }
+
+    let ty = parse_type(&self_ty)?;
+    let ty_generics = match &ty {
+        Type::RustBox(ty)
+        | Type::RustVec(ty)
+        | Type::UniquePtr(ty)
+        | Type::SharedPtr(ty)
+        | Type::WeakPtr(ty)
+        | Type::CxxVector(ty) => match &ty.inner {
+            Type::Ident(ident) => ident.generics.clone(),
+            _ => Lifetimes::default(),
+        },
+        Type::Ident(_)
+        | Type::Ref(_)
+        | Type::Ptr(_)
+        | Type::Str(_)
+        | Type::Fn(_)
+        | Type::Void(_)
+        | Type::SliceRef(_)
+        | Type::Array(_) => Lifetimes::default(),
+    };
+
+    let negative = negative_token.is_some();
+    let brace_token = imp.brace_token;
 
     Ok(Api::Impl(Impl {
-        impl_token: imp.impl_token,
-        ty: parse_type(&self_ty, namespace)?,
-        brace_token: imp.brace_token,
+        impl_token,
+        impl_generics,
+        negative,
+        ty,
+        ty_generics,
+        brace_token,
+        negative_token,
     }))
 }
 
@@ -578,20 +1032,42 @@
     Err(input.error("expected \"quoted/path/to\" or <bracketed/path/to>"))
 }
 
-fn parse_type(ty: &RustType, namespace: &Namespace) -> Result<Type> {
+fn parse_type(ty: &RustType) -> Result<Type> {
     match ty {
-        RustType::Reference(ty) => parse_type_reference(ty, namespace),
-        RustType::Path(ty) => parse_type_path(ty, namespace),
-        RustType::Slice(ty) => parse_type_slice(ty, namespace),
-        RustType::BareFn(ty) => parse_type_fn(ty, namespace),
+        RustType::Reference(ty) => parse_type_reference(ty),
+        RustType::Ptr(ty) => parse_type_ptr(ty),
+        RustType::Path(ty) => parse_type_path(ty),
+        RustType::Array(ty) => parse_type_array(ty),
+        RustType::BareFn(ty) => parse_type_fn(ty),
         RustType::Tuple(ty) if ty.elems.is_empty() => Ok(Type::Void(ty.paren_token.span)),
         _ => Err(Error::new_spanned(ty, "unsupported type")),
     }
 }
 
-fn parse_type_reference(ty: &TypeReference, namespace: &Namespace) -> Result<Type> {
-    let inner = parse_type(&ty.elem, namespace)?;
-    let which = match &inner {
+fn parse_type_reference(ty: &TypeReference) -> Result<Type> {
+    let ampersand = ty.and_token;
+    let lifetime = ty.lifetime.clone();
+    let mutable = ty.mutability.is_some();
+    let mutability = ty.mutability;
+
+    if let RustType::Slice(slice) = ty.elem.as_ref() {
+        let inner = parse_type(&slice.elem)?;
+        let bracket = slice.bracket_token;
+        return Ok(Type::SliceRef(Box::new(SliceRef {
+            ampersand,
+            lifetime,
+            mutable,
+            bracket,
+            inner,
+            mutability,
+        })));
+    }
+
+    let inner = parse_type(&ty.elem)?;
+    let pinned = false;
+    let pin_tokens = None;
+
+    Ok(match &inner {
         Type::Ident(ident) if ident.rust == "str" => {
             if ty.mutability.is_some() {
                 return Err(Error::new_spanned(ty, "unsupported type"));
@@ -599,31 +1075,46 @@
                 Type::Str
             }
         }
-        Type::Slice(slice) => match &slice.inner {
-            Type::Ident(ident) if ident.rust == U8 && ty.mutability.is_none() => Type::SliceRefU8,
-            _ => Type::Ref,
-        },
         _ => Type::Ref,
-    };
-    Ok(which(Box::new(Ref {
-        ampersand: ty.and_token,
-        lifetime: ty.lifetime.clone(),
-        mutability: ty.mutability,
+    }(Box::new(Ref {
+        pinned,
+        ampersand,
+        lifetime,
+        mutable,
         inner,
+        pin_tokens,
+        mutability,
     })))
 }
 
-fn parse_type_path(ty: &TypePath, namespace: &Namespace) -> Result<Type> {
+fn parse_type_ptr(ty: &TypePtr) -> Result<Type> {
+    let star = ty.star_token;
+    let mutable = ty.mutability.is_some();
+    let constness = ty.const_token;
+    let mutability = ty.mutability;
+
+    let inner = parse_type(&ty.elem)?;
+
+    Ok(Type::Ptr(Box::new(Ptr {
+        star,
+        mutable,
+        inner,
+        mutability,
+        constness,
+    })))
+}
+
+fn parse_type_path(ty: &TypePath) -> Result<Type> {
     let path = &ty.path;
     if ty.qself.is_none() && path.leading_colon.is_none() && path.segments.len() == 1 {
         let segment = &path.segments[0];
         let ident = segment.ident.clone();
         match &segment.arguments {
-            PathArguments::None => return Ok(Type::Ident(ResolvableName::new(ident))),
+            PathArguments::None => return Ok(Type::Ident(NamedType::new(ident))),
             PathArguments::AngleBracketed(generic) => {
                 if ident == "UniquePtr" && generic.args.len() == 1 {
                     if let GenericArgument::Type(arg) = &generic.args[0] {
-                        let inner = parse_type(arg, namespace)?;
+                        let inner = parse_type(arg)?;
                         return Ok(Type::UniquePtr(Box::new(Ty1 {
                             name: ident,
                             langle: generic.lt_token,
@@ -631,9 +1122,29 @@
                             rangle: generic.gt_token,
                         })));
                     }
+                } else if ident == "SharedPtr" && generic.args.len() == 1 {
+                    if let GenericArgument::Type(arg) = &generic.args[0] {
+                        let inner = parse_type(arg)?;
+                        return Ok(Type::SharedPtr(Box::new(Ty1 {
+                            name: ident,
+                            langle: generic.lt_token,
+                            inner,
+                            rangle: generic.gt_token,
+                        })));
+                    }
+                } else if ident == "WeakPtr" && generic.args.len() == 1 {
+                    if let GenericArgument::Type(arg) = &generic.args[0] {
+                        let inner = parse_type(arg)?;
+                        return Ok(Type::WeakPtr(Box::new(Ty1 {
+                            name: ident,
+                            langle: generic.lt_token,
+                            inner,
+                            rangle: generic.gt_token,
+                        })));
+                    }
                 } else if ident == "CxxVector" && generic.args.len() == 1 {
                     if let GenericArgument::Type(arg) = &generic.args[0] {
-                        let inner = parse_type(arg, namespace)?;
+                        let inner = parse_type(arg)?;
                         return Ok(Type::CxxVector(Box::new(Ty1 {
                             name: ident,
                             langle: generic.lt_token,
@@ -643,7 +1154,7 @@
                     }
                 } else if ident == "Box" && generic.args.len() == 1 {
                     if let GenericArgument::Type(arg) = &generic.args[0] {
-                        let inner = parse_type(arg, namespace)?;
+                        let inner = parse_type(arg)?;
                         return Ok(Type::RustBox(Box::new(Ty1 {
                             name: ident,
                             langle: generic.lt_token,
@@ -653,7 +1164,7 @@
                     }
                 } else if ident == "Vec" && generic.args.len() == 1 {
                     if let GenericArgument::Type(arg) = &generic.args[0] {
-                        let inner = parse_type(arg, namespace)?;
+                        let inner = parse_type(arg)?;
                         return Ok(Type::RustVec(Box::new(Ty1 {
                             name: ident,
                             langle: generic.lt_token,
@@ -661,59 +1172,144 @@
                             rangle: generic.gt_token,
                         })));
                     }
+                } else if ident == "Pin" && generic.args.len() == 1 {
+                    if let GenericArgument::Type(arg) = &generic.args[0] {
+                        let inner = parse_type(arg)?;
+                        let pin_token = kw::Pin(ident.span());
+                        if let Type::Ref(mut inner) = inner {
+                            inner.pinned = true;
+                            inner.pin_tokens =
+                                Some((pin_token, generic.lt_token, generic.gt_token));
+                            return Ok(Type::Ref(inner));
+                        }
+                    }
+                } else {
+                    let mut lifetimes = Punctuated::new();
+                    let mut only_lifetimes = true;
+                    for pair in generic.args.pairs() {
+                        let (param, punct) = pair.into_tuple();
+                        if let GenericArgument::Lifetime(param) = param {
+                            lifetimes.push_value(param.clone());
+                            if let Some(punct) = punct {
+                                lifetimes.push_punct(*punct);
+                            }
+                        } else {
+                            only_lifetimes = false;
+                            break;
+                        }
+                    }
+                    if only_lifetimes {
+                        return Ok(Type::Ident(NamedType {
+                            rust: ident,
+                            generics: Lifetimes {
+                                lt_token: Some(generic.lt_token),
+                                lifetimes,
+                                gt_token: Some(generic.gt_token),
+                            },
+                        }));
+                    }
                 }
             }
             PathArguments::Parenthesized(_) => {}
         }
     }
+
     Err(Error::new_spanned(ty, "unsupported type"))
 }
 
-fn parse_type_slice(ty: &TypeSlice, namespace: &Namespace) -> Result<Type> {
-    let inner = parse_type(&ty.elem, namespace)?;
-    Ok(Type::Slice(Box::new(Slice {
-        bracket: ty.bracket_token,
+fn parse_type_array(ty: &TypeArray) -> Result<Type> {
+    let inner = parse_type(&ty.elem)?;
+
+    let len_expr = if let Expr::Lit(lit) = &ty.len {
+        lit
+    } else {
+        let msg = "unsupported expression, array length must be an integer literal";
+        return Err(Error::new_spanned(&ty.len, msg));
+    };
+
+    let len_token = if let Lit::Int(int) = &len_expr.lit {
+        int.clone()
+    } else {
+        let msg = "array length must be an integer literal";
+        return Err(Error::new_spanned(len_expr, msg));
+    };
+
+    let len = len_token.base10_parse::<usize>()?;
+    if len == 0 {
+        let msg = "array with zero size is not supported";
+        return Err(Error::new_spanned(ty, msg));
+    }
+
+    let bracket = ty.bracket_token;
+    let semi_token = ty.semi_token;
+
+    Ok(Type::Array(Box::new(Array {
+        bracket,
         inner,
+        semi_token,
+        len,
+        len_token,
     })))
 }
 
-fn parse_type_fn(ty: &TypeBareFn, namespace: &Namespace) -> Result<Type> {
+fn parse_type_fn(ty: &TypeBareFn) -> Result<Type> {
     if ty.lifetimes.is_some() {
         return Err(Error::new_spanned(
             ty,
             "function pointer with lifetime parameters is not supported yet",
         ));
     }
+
     if ty.variadic.is_some() {
         return Err(Error::new_spanned(
             ty,
             "variadic function pointer is not supported yet",
         ));
     }
+
     let args = ty
         .inputs
         .iter()
         .enumerate()
         .map(|(i, arg)| {
-            let ty = parse_type(&arg.ty, namespace)?;
+            let ty = parse_type(&arg.ty)?;
             let ident = match &arg.name {
                 Some(ident) => ident.0.clone(),
-                None => format_ident!("_{}", i),
+                None => format_ident!("arg{}", i),
             };
-            Ok(Var { ident, ty })
+            let doc = Doc::new();
+            let attrs = OtherAttrs::none();
+            let visibility = Token![pub](ident.span());
+            let name = pair(Namespace::default(), &ident, None, None);
+            Ok(Var {
+                doc,
+                attrs,
+                visibility,
+                name,
+                ty,
+            })
         })
         .collect::<Result<_>>()?;
+
     let mut throws_tokens = None;
-    let ret = parse_return_type(&ty.output, &mut throws_tokens, namespace)?;
+    let ret = parse_return_type(&ty.output, &mut throws_tokens)?;
     let throws = throws_tokens.is_some();
+
+    let unsafety = ty.unsafety;
+    let fn_token = ty.fn_token;
+    let generics = Generics::default();
+    let receiver = None;
+    let paren_token = ty.paren_token;
+
     Ok(Type::Fn(Box::new(Signature {
-        unsafety: ty.unsafety,
-        fn_token: ty.fn_token,
-        receiver: None,
+        unsafety,
+        fn_token,
+        generics,
+        receiver,
         args,
         ret,
         throws,
-        paren_token: ty.paren_token,
+        paren_token,
         throws_tokens,
     })))
 }
@@ -721,12 +1317,12 @@
 fn parse_return_type(
     ty: &ReturnType,
     throws_tokens: &mut Option<(kw::Result, Token![<], Token![>])>,
-    namespace: &Namespace,
 ) -> Result<Option<Type>> {
     let mut ret = match ty {
         ReturnType::Default => return Ok(None),
         ReturnType::Type(_, ret) => ret.as_ref(),
     };
+
     if let RustType::Path(ty) = ret {
         let path = &ty.path;
         if ty.qself.is_none() && path.leading_colon.is_none() && path.segments.len() == 1 {
@@ -743,8 +1339,32 @@
             }
         }
     }
-    match parse_type(ret, namespace)? {
+
+    match parse_type(ret)? {
         Type::Void(_) => Ok(None),
         ty => Ok(Some(ty)),
     }
 }
+
+fn visibility_pub(vis: &Visibility, inherited: Span) -> Token![pub] {
+    Token![pub](match vis {
+        Visibility::Public(vis) => vis.pub_token.span,
+        Visibility::Crate(vis) => vis.crate_token.span,
+        Visibility::Restricted(vis) => vis.pub_token.span,
+        Visibility::Inherited => inherited,
+    })
+}
+
+fn pair(
+    namespace: Namespace,
+    default: &Ident,
+    cxx: Option<ForeignName>,
+    rust: Option<Ident>,
+) -> Pair {
+    Pair {
+        namespace,
+        cxx: cxx
+            .unwrap_or_else(|| ForeignName::parse(&default.to_string(), default.span()).unwrap()),
+        rust: rust.unwrap_or_else(|| default.clone()),
+    }
+}
diff --git a/syntax/pod.rs b/syntax/pod.rs
new file mode 100644
index 0000000..0bf152e
--- /dev/null
+++ b/syntax/pod.rs
@@ -0,0 +1,36 @@
+use crate::syntax::atom::Atom::{self, *};
+use crate::syntax::{derive, Trait, Type, Types};
+
+impl<'a> Types<'a> {
+    pub fn is_guaranteed_pod(&self, ty: &Type) -> bool {
+        match ty {
+            Type::Ident(ident) => {
+                let ident = &ident.rust;
+                if let Some(atom) = Atom::from(ident) {
+                    match atom {
+                        Bool | Char | U8 | U16 | U32 | U64 | Usize | I8 | I16 | I32 | I64
+                        | Isize | F32 | F64 => true,
+                        CxxString | RustString => false,
+                    }
+                } else if let Some(strct) = self.structs.get(ident) {
+                    derive::contains(&strct.derives, Trait::Copy)
+                        || strct
+                            .fields
+                            .iter()
+                            .all(|field| self.is_guaranteed_pod(&field.ty))
+                } else {
+                    self.enums.contains_key(ident)
+                }
+            }
+            Type::RustBox(_)
+            | Type::RustVec(_)
+            | Type::UniquePtr(_)
+            | Type::SharedPtr(_)
+            | Type::WeakPtr(_)
+            | Type::CxxVector(_)
+            | Type::Void(_) => false,
+            Type::Ref(_) | Type::Str(_) | Type::Fn(_) | Type::SliceRef(_) | Type::Ptr(_) => true,
+            Type::Array(array) => self.is_guaranteed_pod(&array.inner),
+        }
+    }
+}
diff --git a/syntax/qualified.rs b/syntax/qualified.rs
index 5eefb8d..96f07c1 100644
--- a/syntax/qualified.rs
+++ b/syntax/qualified.rs
@@ -10,14 +10,14 @@
     pub fn parse_unquoted(input: ParseStream) -> Result<Self> {
         let mut segments = Vec::new();
         let mut trailing_punct = true;
-        input.parse::<Option<Token![::]>>()?;
+        let leading_colons: Option<Token![::]> = input.parse()?;
         while trailing_punct && input.peek(Ident::peek_any) {
             let ident = Ident::parse_any(input)?;
             segments.push(ident);
             let colons: Option<Token![::]> = input.parse()?;
             trailing_punct = colons.is_some();
         }
-        if segments.is_empty() {
+        if segments.is_empty() && leading_colons.is_none() {
             return Err(input.error("expected path"));
         } else if trailing_punct {
             return Err(input.error("expected path segment"));
@@ -28,7 +28,12 @@
     pub fn parse_quoted_or_unquoted(input: ParseStream) -> Result<Self> {
         if input.peek(LitStr) {
             let lit: LitStr = input.parse()?;
-            lit.parse_with(Self::parse_unquoted)
+            if lit.value().is_empty() {
+                let segments = Vec::new();
+                Ok(QualifiedName { segments })
+            } else {
+                lit.parse_with(Self::parse_unquoted)
+            }
         } else {
             Self::parse_unquoted(input)
         }
diff --git a/syntax/resolve.rs b/syntax/resolve.rs
new file mode 100644
index 0000000..3a2635b
--- /dev/null
+++ b/syntax/resolve.rs
@@ -0,0 +1,46 @@
+use crate::syntax::instantiate::NamedImplKey;
+use crate::syntax::{Lifetimes, NamedType, Pair, Types};
+use proc_macro2::Ident;
+
+#[derive(Copy, Clone)]
+pub struct Resolution<'a> {
+    pub name: &'a Pair,
+    pub generics: &'a Lifetimes,
+}
+
+impl<'a> Types<'a> {
+    pub fn resolve(&self, ident: &impl UnresolvedName) -> Resolution<'a> {
+        let ident = ident.ident();
+        match self.try_resolve(ident) {
+            Some(resolution) => resolution,
+            None => panic!("Unable to resolve type `{}`", ident),
+        }
+    }
+
+    pub fn try_resolve(&self, ident: &impl UnresolvedName) -> Option<Resolution<'a>> {
+        let ident = ident.ident();
+        self.resolutions.get(ident).copied()
+    }
+}
+
+pub trait UnresolvedName {
+    fn ident(&self) -> &Ident;
+}
+
+impl UnresolvedName for Ident {
+    fn ident(&self) -> &Ident {
+        self
+    }
+}
+
+impl UnresolvedName for NamedType {
+    fn ident(&self) -> &Ident {
+        &self.rust
+    }
+}
+
+impl<'a> UnresolvedName for NamedImplKey<'a> {
+    fn ident(&self) -> &Ident {
+        self.rust
+    }
+}
diff --git a/syntax/set.rs b/syntax/set.rs
index 6508f55..ca0c43e 100644
--- a/syntax/set.rs
+++ b/syntax/set.rs
@@ -50,11 +50,21 @@
         }
     }
 
+    impl<'a, T> OrderedSet<&'a T> {
+        pub fn is_empty(&self) -> bool {
+            self.vec.is_empty()
+        }
+
+        pub fn iter(&self) -> Iter<'_, 'a, T> {
+            Iter(self.vec.iter())
+        }
+    }
+
     impl<'s, 'a, T> IntoIterator for &'s OrderedSet<&'a T> {
         type Item = &'a T;
         type IntoIter = Iter<'s, 'a, T>;
         fn into_iter(self) -> Self::IntoIter {
-            Iter(self.vec.iter())
+            self.iter()
         }
     }
 }
@@ -95,6 +105,10 @@
         {
             self.0.get(value)
         }
+
+        pub fn retain(&mut self, f: impl FnMut(&T) -> bool) {
+            self.0.retain(f);
+        }
     }
 }
 
@@ -102,9 +116,14 @@
 
 impl<'s, 'a, T> Iterator for Iter<'s, 'a, T> {
     type Item = &'a T;
+
     fn next(&mut self) -> Option<Self::Item> {
         self.0.next().copied()
     }
+
+    fn size_hint(&self) -> (usize, Option<usize>) {
+        self.0.size_hint()
+    }
 }
 
 impl<'a, T> Debug for OrderedSet<&'a T>
diff --git a/syntax/symbol.rs b/syntax/symbol.rs
index 253f57d..a13a4f3 100644
--- a/syntax/symbol.rs
+++ b/syntax/symbol.rs
@@ -1,11 +1,11 @@
 use crate::syntax::namespace::Namespace;
-use crate::syntax::Pair;
+use crate::syntax::{ForeignName, Pair};
 use proc_macro2::{Ident, TokenStream};
 use quote::ToTokens;
 use std::fmt::{self, Display, Write};
 
 // A mangled symbol consisting of segments separated by '$'.
-// For example: cxxbridge05$string$new
+// For example: cxxbridge1$string$new
 pub struct Symbol(String);
 
 impl Display for Symbol {
@@ -30,7 +30,7 @@
         assert!(self.0.len() > len_before);
     }
 
-    pub fn from_idents<'a, T: Iterator<Item = &'a Ident>>(it: T) -> Self {
+    pub fn from_idents<'a>(it: impl Iterator<Item = &'a dyn Segment>) -> Self {
         let mut symbol = Symbol(String::new());
         for segment in it {
             segment.write(&mut symbol);
@@ -55,16 +55,19 @@
         symbol.push(&self);
     }
 }
+
 impl Segment for usize {
     fn write(&self, symbol: &mut Symbol) {
         symbol.push(&self);
     }
 }
+
 impl Segment for Ident {
     fn write(&self, symbol: &mut Symbol) {
         symbol.push(&self);
     }
 }
+
 impl Segment for Symbol {
     fn write(&self, symbol: &mut Symbol) {
         symbol.push(&self);
@@ -86,6 +89,14 @@
     }
 }
 
+impl Segment for ForeignName {
+    fn write(&self, symbol: &mut Symbol) {
+        // TODO: support C++ names containing whitespace (`unsigned int`) or
+        // non-alphanumeric characters (`operator++`).
+        self.to_string().write(symbol);
+    }
+}
+
 impl<T> Segment for &'_ T
 where
     T: ?Sized + Segment + Display,
diff --git a/syntax/tokens.rs b/syntax/tokens.rs
index 500ea0b..c1a06a2 100644
--- a/syntax/tokens.rs
+++ b/syntax/tokens.rs
@@ -1,77 +1,155 @@
 use crate::syntax::atom::Atom::*;
 use crate::syntax::{
-    Atom, Derive, Enum, ExternFn, ExternType, Impl, Receiver, Ref, ResolvableName, Signature,
-    Slice, Struct, Ty1, Type, TypeAlias, Var,
+    Array, Atom, Derive, Enum, ExternFn, ExternType, Impl, Lifetimes, NamedType, Ptr, Receiver,
+    Ref, Signature, SliceRef, Struct, Ty1, Type, TypeAlias, Var,
 };
 use proc_macro2::{Ident, Span, TokenStream};
 use quote::{quote_spanned, ToTokens};
-use syn::Token;
+use syn::{token, Token};
 
 impl ToTokens for Type {
     fn to_tokens(&self, tokens: &mut TokenStream) {
         match self {
             Type::Ident(ident) => {
-                if ident.rust == CxxString {
+                if ident.rust == Char {
+                    let span = ident.rust.span();
+                    tokens.extend(quote_spanned!(span=> ::std::os::raw::));
+                } else if ident.rust == CxxString {
                     let span = ident.rust.span();
                     tokens.extend(quote_spanned!(span=> ::cxx::));
                 }
-                ident.rust.to_tokens(tokens);
+                ident.to_tokens(tokens);
             }
-            Type::RustBox(ty) | Type::UniquePtr(ty) | Type::CxxVector(ty) | Type::RustVec(ty) => {
-                ty.to_tokens(tokens)
-            }
-            Type::Ref(r) | Type::Str(r) | Type::SliceRefU8(r) => r.to_tokens(tokens),
-            Type::Slice(s) => s.to_tokens(tokens),
+            Type::RustBox(ty)
+            | Type::UniquePtr(ty)
+            | Type::SharedPtr(ty)
+            | Type::WeakPtr(ty)
+            | Type::CxxVector(ty)
+            | Type::RustVec(ty) => ty.to_tokens(tokens),
+            Type::Ref(r) | Type::Str(r) => r.to_tokens(tokens),
+            Type::Ptr(p) => p.to_tokens(tokens),
+            Type::Array(a) => a.to_tokens(tokens),
             Type::Fn(f) => f.to_tokens(tokens),
             Type::Void(span) => tokens.extend(quote_spanned!(*span=> ())),
+            Type::SliceRef(r) => r.to_tokens(tokens),
         }
     }
 }
 
 impl ToTokens for Var {
     fn to_tokens(&self, tokens: &mut TokenStream) {
-        self.ident.to_tokens(tokens);
-        Token![:](self.ident.span()).to_tokens(tokens);
-        self.ty.to_tokens(tokens);
+        let Var {
+            doc: _,
+            attrs: _,
+            visibility: _,
+            name,
+            ty,
+        } = self;
+        name.rust.to_tokens(tokens);
+        Token![:](name.rust.span()).to_tokens(tokens);
+        ty.to_tokens(tokens);
     }
 }
 
 impl ToTokens for Ty1 {
     fn to_tokens(&self, tokens: &mut TokenStream) {
-        let span = self.name.span();
-        let name = self.name.to_string();
-        if let "UniquePtr" | "CxxVector" = name.as_str() {
-            tokens.extend(quote_spanned!(span=> ::cxx::));
-        } else if name == "Vec" {
-            tokens.extend(quote_spanned!(span=> ::std::vec::));
+        let Ty1 {
+            name,
+            langle,
+            inner,
+            rangle,
+        } = self;
+        let span = name.span();
+        match name.to_string().as_str() {
+            "UniquePtr" | "SharedPtr" | "WeakPtr" | "CxxVector" => {
+                tokens.extend(quote_spanned!(span=> ::cxx::));
+            }
+            "Vec" => {
+                tokens.extend(quote_spanned!(span=> ::std::vec::));
+            }
+            _ => {}
         }
-        self.name.to_tokens(tokens);
-        self.langle.to_tokens(tokens);
-        self.inner.to_tokens(tokens);
-        self.rangle.to_tokens(tokens);
+        name.to_tokens(tokens);
+        langle.to_tokens(tokens);
+        inner.to_tokens(tokens);
+        rangle.to_tokens(tokens);
     }
 }
 
 impl ToTokens for Ref {
     fn to_tokens(&self, tokens: &mut TokenStream) {
-        self.ampersand.to_tokens(tokens);
-        self.lifetime.to_tokens(tokens);
-        self.mutability.to_tokens(tokens);
-        self.inner.to_tokens(tokens);
+        let Ref {
+            pinned: _,
+            ampersand,
+            lifetime,
+            mutable: _,
+            inner,
+            pin_tokens,
+            mutability,
+        } = self;
+        if let Some((pin, langle, _rangle)) = pin_tokens {
+            tokens.extend(quote_spanned!(pin.span=> ::std::pin::Pin));
+            langle.to_tokens(tokens);
+        }
+        ampersand.to_tokens(tokens);
+        lifetime.to_tokens(tokens);
+        mutability.to_tokens(tokens);
+        inner.to_tokens(tokens);
+        if let Some((_pin, _langle, rangle)) = pin_tokens {
+            rangle.to_tokens(tokens);
+        }
     }
 }
 
-impl ToTokens for Slice {
+impl ToTokens for Ptr {
     fn to_tokens(&self, tokens: &mut TokenStream) {
-        self.bracket.surround(tokens, |tokens| {
-            self.inner.to_tokens(tokens);
+        let Ptr {
+            star,
+            mutable: _,
+            inner,
+            mutability,
+            constness,
+        } = self;
+        star.to_tokens(tokens);
+        mutability.to_tokens(tokens);
+        constness.to_tokens(tokens);
+        inner.to_tokens(tokens);
+    }
+}
+
+impl ToTokens for SliceRef {
+    fn to_tokens(&self, tokens: &mut TokenStream) {
+        let SliceRef {
+            ampersand,
+            lifetime,
+            mutable: _,
+            bracket,
+            inner,
+            mutability,
+        } = self;
+        ampersand.to_tokens(tokens);
+        lifetime.to_tokens(tokens);
+        mutability.to_tokens(tokens);
+        bracket.surround(tokens, |tokens| {
+            inner.to_tokens(tokens);
         });
     }
 }
 
-impl ToTokens for Derive {
+impl ToTokens for Array {
     fn to_tokens(&self, tokens: &mut TokenStream) {
-        Ident::new(self.as_ref(), Span::call_site()).to_tokens(tokens);
+        let Array {
+            bracket,
+            inner,
+            semi_token,
+            len: _,
+            len_token,
+        } = self;
+        bracket.surround(tokens, |tokens| {
+            inner.to_tokens(tokens);
+            semi_token.to_tokens(tokens);
+            len_token.to_tokens(tokens);
+        });
     }
 }
 
@@ -81,11 +159,18 @@
     }
 }
 
+impl ToTokens for Derive {
+    fn to_tokens(&self, tokens: &mut TokenStream) {
+        Ident::new(self.what.as_ref(), self.span).to_tokens(tokens);
+    }
+}
+
 impl ToTokens for ExternType {
     fn to_tokens(&self, tokens: &mut TokenStream) {
         // Notional token range for error reporting purposes.
         self.type_token.to_tokens(tokens);
         self.name.rust.to_tokens(tokens);
+        self.generics.to_tokens(tokens);
     }
 }
 
@@ -94,6 +179,7 @@
         // Notional token range for error reporting purposes.
         self.type_token.to_tokens(tokens);
         self.name.rust.to_tokens(tokens);
+        self.generics.to_tokens(tokens);
     }
 }
 
@@ -102,6 +188,7 @@
         // Notional token range for error reporting purposes.
         self.struct_token.to_tokens(tokens);
         self.name.rust.to_tokens(tokens);
+        self.generics.to_tokens(tokens);
     }
 }
 
@@ -110,12 +197,14 @@
         // Notional token range for error reporting purposes.
         self.enum_token.to_tokens(tokens);
         self.name.rust.to_tokens(tokens);
+        self.generics.to_tokens(tokens);
     }
 }
 
 impl ToTokens for ExternFn {
     fn to_tokens(&self, tokens: &mut TokenStream) {
         // Notional token range for error reporting purposes.
+        self.unsafety.to_tokens(tokens);
         self.sig.fn_token.to_tokens(tokens);
         self.semi_token.to_tokens(tokens);
     }
@@ -123,21 +212,56 @@
 
 impl ToTokens for Impl {
     fn to_tokens(&self, tokens: &mut TokenStream) {
-        self.impl_token.to_tokens(tokens);
-        self.ty.to_tokens(tokens);
-        self.brace_token.surround(tokens, |_tokens| {});
+        let Impl {
+            impl_token,
+            impl_generics,
+            negative: _,
+            ty,
+            ty_generics: _,
+            brace_token,
+            negative_token,
+        } = self;
+        impl_token.to_tokens(tokens);
+        impl_generics.to_tokens(tokens);
+        negative_token.to_tokens(tokens);
+        ty.to_tokens(tokens);
+        brace_token.surround(tokens, |_tokens| {});
+    }
+}
+
+impl ToTokens for Lifetimes {
+    fn to_tokens(&self, tokens: &mut TokenStream) {
+        let Lifetimes {
+            lt_token,
+            lifetimes,
+            gt_token,
+        } = self;
+        lt_token.to_tokens(tokens);
+        lifetimes.to_tokens(tokens);
+        gt_token.to_tokens(tokens);
     }
 }
 
 impl ToTokens for Signature {
     fn to_tokens(&self, tokens: &mut TokenStream) {
-        self.fn_token.to_tokens(tokens);
-        self.paren_token.surround(tokens, |tokens| {
-            self.args.to_tokens(tokens);
+        let Signature {
+            unsafety: _,
+            fn_token,
+            generics: _,
+            receiver: _,
+            args,
+            ret,
+            throws: _,
+            paren_token,
+            throws_tokens,
+        } = self;
+        fn_token.to_tokens(tokens);
+        paren_token.surround(tokens, |tokens| {
+            args.to_tokens(tokens);
         });
-        if let Some(ret) = &self.ret {
-            Token![->](self.paren_token.span).to_tokens(tokens);
-            if let Some((result, langle, rangle)) = self.throws_tokens {
+        if let Some(ret) = ret {
+            Token![->](paren_token.span).to_tokens(tokens);
+            if let Some((result, langle, rangle)) = throws_tokens {
                 result.to_tokens(tokens);
                 langle.to_tokens(tokens);
                 ret.to_tokens(tokens);
@@ -145,30 +269,89 @@
             } else {
                 ret.to_tokens(tokens);
             }
+        } else if let Some((result, langle, rangle)) = throws_tokens {
+            Token![->](paren_token.span).to_tokens(tokens);
+            result.to_tokens(tokens);
+            langle.to_tokens(tokens);
+            token::Paren(langle.span).surround(tokens, |_| ());
+            rangle.to_tokens(tokens);
         }
     }
 }
 
-impl ToTokens for ResolvableName {
+impl ToTokens for NamedType {
     fn to_tokens(&self, tokens: &mut TokenStream) {
-        self.rust.to_tokens(tokens);
+        let NamedType { rust, generics } = self;
+        rust.to_tokens(tokens);
+        generics.to_tokens(tokens);
     }
 }
 
 pub struct ReceiverType<'a>(&'a Receiver);
+pub struct ReceiverTypeSelf<'a>(&'a Receiver);
 
 impl Receiver {
     // &TheType
     pub fn ty(&self) -> ReceiverType {
         ReceiverType(self)
     }
+
+    // &Self
+    pub fn ty_self(&self) -> ReceiverTypeSelf {
+        ReceiverTypeSelf(self)
+    }
 }
 
 impl ToTokens for ReceiverType<'_> {
     fn to_tokens(&self, tokens: &mut TokenStream) {
-        self.0.ampersand.to_tokens(tokens);
-        self.0.lifetime.to_tokens(tokens);
-        self.0.mutability.to_tokens(tokens);
-        self.0.ty.to_tokens(tokens);
+        let Receiver {
+            pinned: _,
+            ampersand,
+            lifetime,
+            mutable: _,
+            var: _,
+            ty,
+            shorthand: _,
+            pin_tokens,
+            mutability,
+        } = &self.0;
+        if let Some((pin, langle, _rangle)) = pin_tokens {
+            tokens.extend(quote_spanned!(pin.span=> ::std::pin::Pin));
+            langle.to_tokens(tokens);
+        }
+        ampersand.to_tokens(tokens);
+        lifetime.to_tokens(tokens);
+        mutability.to_tokens(tokens);
+        ty.to_tokens(tokens);
+        if let Some((_pin, _langle, rangle)) = pin_tokens {
+            rangle.to_tokens(tokens);
+        }
+    }
+}
+
+impl ToTokens for ReceiverTypeSelf<'_> {
+    fn to_tokens(&self, tokens: &mut TokenStream) {
+        let Receiver {
+            pinned: _,
+            ampersand,
+            lifetime,
+            mutable: _,
+            var: _,
+            ty,
+            shorthand: _,
+            pin_tokens,
+            mutability,
+        } = &self.0;
+        if let Some((pin, langle, _rangle)) = pin_tokens {
+            tokens.extend(quote_spanned!(pin.span=> ::std::pin::Pin));
+            langle.to_tokens(tokens);
+        }
+        ampersand.to_tokens(tokens);
+        lifetime.to_tokens(tokens);
+        mutability.to_tokens(tokens);
+        Token![Self](ty.rust.span()).to_tokens(tokens);
+        if let Some((_pin, _langle, rangle)) = pin_tokens {
+            rangle.to_tokens(tokens);
+        }
     }
 }
diff --git a/syntax/toposort.rs b/syntax/toposort.rs
index 876f9ad..8fe55b8 100644
--- a/syntax/toposort.rs
+++ b/syntax/toposort.rs
@@ -1,6 +1,6 @@
+use crate::syntax::map::{Entry, UnorderedMap as Map};
 use crate::syntax::report::Errors;
 use crate::syntax::{Api, Struct, Type, Types};
-use std::collections::btree_map::{BTreeMap as Map, Entry};
 
 enum Mark {
     Visiting,
diff --git a/syntax/trivial.rs b/syntax/trivial.rs
new file mode 100644
index 0000000..fe95e2b
--- /dev/null
+++ b/syntax/trivial.rs
@@ -0,0 +1,257 @@
+use crate::syntax::map::UnorderedMap;
+use crate::syntax::set::{OrderedSet as Set, UnorderedSet};
+use crate::syntax::{Api, Enum, ExternFn, NamedType, Pair, Struct, Type};
+use proc_macro2::Ident;
+use std::fmt::{self, Display};
+
+#[derive(Copy, Clone)]
+pub enum TrivialReason<'a> {
+    StructField(&'a Struct),
+    FunctionArgument(&'a ExternFn),
+    FunctionReturn(&'a ExternFn),
+    BoxTarget,
+    VecElement,
+    UnpinnedMut(&'a ExternFn),
+}
+
+pub fn required_trivial_reasons<'a>(
+    apis: &'a [Api],
+    all: &Set<&'a Type>,
+    structs: &UnorderedMap<&'a Ident, &'a Struct>,
+    enums: &UnorderedMap<&'a Ident, &'a Enum>,
+    cxx: &UnorderedSet<&'a Ident>,
+) -> UnorderedMap<&'a Ident, Vec<TrivialReason<'a>>> {
+    let mut required_trivial = UnorderedMap::new();
+
+    let mut insist_extern_types_are_trivial = |ident: &'a NamedType, reason| {
+        if cxx.contains(&ident.rust)
+            && !structs.contains_key(&ident.rust)
+            && !enums.contains_key(&ident.rust)
+        {
+            required_trivial
+                .entry(&ident.rust)
+                .or_insert_with(Vec::new)
+                .push(reason);
+        }
+    };
+
+    for api in apis {
+        match api {
+            Api::Struct(strct) => {
+                for field in &strct.fields {
+                    if let Type::Ident(ident) = &field.ty {
+                        let reason = TrivialReason::StructField(strct);
+                        insist_extern_types_are_trivial(ident, reason);
+                    }
+                }
+            }
+            Api::CxxFunction(efn) | Api::RustFunction(efn) => {
+                if let Some(receiver) = &efn.receiver {
+                    if receiver.mutable && !receiver.pinned {
+                        let reason = TrivialReason::UnpinnedMut(efn);
+                        insist_extern_types_are_trivial(&receiver.ty, reason);
+                    }
+                }
+                for arg in &efn.args {
+                    match &arg.ty {
+                        Type::Ident(ident) => {
+                            let reason = TrivialReason::FunctionArgument(efn);
+                            insist_extern_types_are_trivial(ident, reason);
+                        }
+                        Type::Ref(ty) => {
+                            if ty.mutable && !ty.pinned {
+                                if let Type::Ident(ident) = &ty.inner {
+                                    let reason = TrivialReason::UnpinnedMut(efn);
+                                    insist_extern_types_are_trivial(ident, reason);
+                                }
+                            }
+                        }
+                        _ => {}
+                    }
+                }
+                if let Some(ret) = &efn.ret {
+                    match ret {
+                        Type::Ident(ident) => {
+                            let reason = TrivialReason::FunctionReturn(efn);
+                            insist_extern_types_are_trivial(ident, reason);
+                        }
+                        Type::Ref(ty) => {
+                            if ty.mutable && !ty.pinned {
+                                if let Type::Ident(ident) = &ty.inner {
+                                    let reason = TrivialReason::UnpinnedMut(efn);
+                                    insist_extern_types_are_trivial(ident, reason);
+                                }
+                            }
+                        }
+                        _ => {}
+                    }
+                }
+            }
+            _ => {}
+        }
+    }
+
+    for ty in all {
+        match ty {
+            Type::RustBox(ty) => {
+                if let Type::Ident(ident) = &ty.inner {
+                    let reason = TrivialReason::BoxTarget;
+                    insist_extern_types_are_trivial(ident, reason);
+                }
+            }
+            Type::RustVec(ty) => {
+                if let Type::Ident(ident) = &ty.inner {
+                    let reason = TrivialReason::VecElement;
+                    insist_extern_types_are_trivial(ident, reason);
+                }
+            }
+            _ => {}
+        }
+    }
+
+    required_trivial
+}
+
+// Context:
+// "type {type} should be trivially move constructible and trivially destructible in C++ to be used as {what} in Rust"
+// "needs a cxx::ExternType impl in order to be used as {what}"
+pub fn as_what<'a>(name: &'a Pair, reasons: &'a [TrivialReason]) -> impl Display + 'a {
+    struct Description<'a> {
+        name: &'a Pair,
+        reasons: &'a [TrivialReason<'a>],
+    }
+
+    impl<'a> Display for Description<'a> {
+        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+            let mut field_of = Set::new();
+            let mut argument_of = Set::new();
+            let mut return_of = Set::new();
+            let mut box_target = false;
+            let mut vec_element = false;
+            let mut unpinned_mut = Set::new();
+
+            for reason in self.reasons {
+                match reason {
+                    TrivialReason::StructField(strct) => {
+                        field_of.insert(&strct.name.rust);
+                    }
+                    TrivialReason::FunctionArgument(efn) => {
+                        argument_of.insert(&efn.name.rust);
+                    }
+                    TrivialReason::FunctionReturn(efn) => {
+                        return_of.insert(&efn.name.rust);
+                    }
+                    TrivialReason::BoxTarget => box_target = true,
+                    TrivialReason::VecElement => vec_element = true,
+                    TrivialReason::UnpinnedMut(efn) => {
+                        unpinned_mut.insert(&efn.name.rust);
+                    }
+                }
+            }
+
+            let mut clauses = Vec::new();
+            if !field_of.is_empty() {
+                clauses.push(Clause::Set {
+                    article: "a",
+                    desc: "field of",
+                    set: &field_of,
+                });
+            }
+            if !argument_of.is_empty() {
+                clauses.push(Clause::Set {
+                    article: "an",
+                    desc: "argument of",
+                    set: &argument_of,
+                });
+            }
+            if !return_of.is_empty() {
+                clauses.push(Clause::Set {
+                    article: "a",
+                    desc: "return value of",
+                    set: &return_of,
+                });
+            }
+            if box_target {
+                clauses.push(Clause::Ty1 {
+                    article: "type",
+                    desc: "Box",
+                    param: self.name,
+                });
+            }
+            if vec_element {
+                clauses.push(Clause::Ty1 {
+                    article: "a",
+                    desc: "vector element in Vec",
+                    param: self.name,
+                });
+            }
+            if !unpinned_mut.is_empty() {
+                clauses.push(Clause::Set {
+                    article: "a",
+                    desc: "non-pinned mutable reference in signature of",
+                    set: &unpinned_mut,
+                });
+            }
+
+            for (i, clause) in clauses.iter().enumerate() {
+                if i == 0 {
+                    write!(f, "{} ", clause.article())?;
+                } else if i + 1 < clauses.len() {
+                    write!(f, ", ")?;
+                } else {
+                    write!(f, " or ")?;
+                }
+                clause.fmt(f)?;
+            }
+
+            Ok(())
+        }
+    }
+
+    enum Clause<'a> {
+        Set {
+            article: &'a str,
+            desc: &'a str,
+            set: &'a Set<&'a Ident>,
+        },
+        Ty1 {
+            article: &'a str,
+            desc: &'a str,
+            param: &'a Pair,
+        },
+    }
+
+    impl<'a> Clause<'a> {
+        fn article(&self) -> &'a str {
+            match self {
+                Clause::Set { article, .. } | Clause::Ty1 { article, .. } => article,
+            }
+        }
+
+        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+            match self {
+                Clause::Set {
+                    article: _,
+                    desc,
+                    set,
+                } => {
+                    write!(f, "{} ", desc)?;
+                    for (i, ident) in set.iter().take(3).enumerate() {
+                        if i > 0 {
+                            write!(f, ", ")?;
+                        }
+                        write!(f, "`{}`", ident)?;
+                    }
+                    Ok(())
+                }
+                Clause::Ty1 {
+                    article: _,
+                    desc,
+                    param,
+                } => write!(f, "{}<{}>", desc, param.rust),
+            }
+        }
+    }
+
+    Description { name, reasons }
+}
diff --git a/syntax/types.rs b/syntax/types.rs
index 90a8221..af7916d 100644
--- a/syntax/types.rs
+++ b/syntax/types.rs
@@ -1,67 +1,61 @@
-use crate::syntax::atom::Atom::{self, *};
 use crate::syntax::improper::ImproperCtype;
+use crate::syntax::instantiate::ImplKey;
+use crate::syntax::map::{OrderedMap, UnorderedMap};
 use crate::syntax::report::Errors;
-use crate::syntax::set::{OrderedSet as Set, UnorderedSet};
+use crate::syntax::resolve::Resolution;
+use crate::syntax::set::{OrderedSet, UnorderedSet};
+use crate::syntax::trivial::{self, TrivialReason};
+use crate::syntax::visit::{self, Visit};
 use crate::syntax::{
-    toposort, Api, Derive, Enum, ExternFn, ExternType, Impl, Pair, ResolvableName, Struct, Type,
-    TypeAlias,
+    toposort, Api, Atom, Enum, ExternType, Impl, Lifetimes, Pair, Struct, Type, TypeAlias,
 };
 use proc_macro2::Ident;
 use quote::ToTokens;
-use std::collections::BTreeMap as Map;
 
 pub struct Types<'a> {
-    pub all: Set<&'a Type>,
-    pub structs: Map<&'a Ident, &'a Struct>,
-    pub enums: Map<&'a Ident, &'a Enum>,
-    pub cxx: Set<&'a Ident>,
-    pub rust: Set<&'a Ident>,
-    pub aliases: Map<&'a Ident, &'a TypeAlias>,
-    pub untrusted: Map<&'a Ident, &'a ExternType>,
-    pub required_trivial: Map<&'a Ident, TrivialReason<'a>>,
-    pub explicit_impls: Set<&'a Impl>,
-    pub resolutions: Map<&'a Ident, &'a Pair>,
+    pub all: OrderedSet<&'a Type>,
+    pub structs: UnorderedMap<&'a Ident, &'a Struct>,
+    pub enums: UnorderedMap<&'a Ident, &'a Enum>,
+    pub cxx: UnorderedSet<&'a Ident>,
+    pub rust: UnorderedSet<&'a Ident>,
+    pub aliases: UnorderedMap<&'a Ident, &'a TypeAlias>,
+    pub untrusted: UnorderedMap<&'a Ident, &'a ExternType>,
+    pub required_trivial: UnorderedMap<&'a Ident, Vec<TrivialReason<'a>>>,
+    pub impls: OrderedMap<ImplKey<'a>, Option<&'a Impl>>,
+    pub resolutions: UnorderedMap<&'a Ident, Resolution<'a>>,
     pub struct_improper_ctypes: UnorderedSet<&'a Ident>,
     pub toposorted_structs: Vec<&'a Struct>,
 }
 
 impl<'a> Types<'a> {
     pub fn collect(cx: &mut Errors, apis: &'a [Api]) -> Self {
-        let mut all = Set::new();
-        let mut structs = Map::new();
-        let mut enums = Map::new();
-        let mut cxx = Set::new();
-        let mut rust = Set::new();
-        let mut aliases = Map::new();
-        let mut untrusted = Map::new();
-        let mut explicit_impls = Set::new();
-        let mut resolutions = Map::new();
+        let mut all = OrderedSet::new();
+        let mut structs = UnorderedMap::new();
+        let mut enums = UnorderedMap::new();
+        let mut cxx = UnorderedSet::new();
+        let mut rust = UnorderedSet::new();
+        let mut aliases = UnorderedMap::new();
+        let mut untrusted = UnorderedMap::new();
+        let mut impls = OrderedMap::new();
+        let mut resolutions = UnorderedMap::new();
         let struct_improper_ctypes = UnorderedSet::new();
         let toposorted_structs = Vec::new();
 
-        fn visit<'a>(all: &mut Set<&'a Type>, ty: &'a Type) {
-            all.insert(ty);
-            match ty {
-                Type::Ident(_) | Type::Str(_) | Type::Void(_) | Type::SliceRefU8(_) => {}
-                Type::RustBox(ty)
-                | Type::UniquePtr(ty)
-                | Type::CxxVector(ty)
-                | Type::RustVec(ty) => visit(all, &ty.inner),
-                Type::Ref(r) => visit(all, &r.inner),
-                Type::Slice(s) => visit(all, &s.inner),
-                Type::Fn(f) => {
-                    if let Some(ret) = &f.ret {
-                        visit(all, ret);
-                    }
-                    for arg in &f.args {
-                        visit(all, &arg.ty);
-                    }
+        fn visit<'a>(all: &mut OrderedSet<&'a Type>, ty: &'a Type) {
+            struct CollectTypes<'s, 'a>(&'s mut OrderedSet<&'a Type>);
+
+            impl<'s, 'a> Visit<'a> for CollectTypes<'s, 'a> {
+                fn visit_type(&mut self, ty: &'a Type) {
+                    self.0.insert(ty);
+                    visit::visit_type(self, ty);
                 }
             }
+
+            CollectTypes(all).visit_type(ty);
         }
 
-        let mut add_resolution = |pair: &'a Pair| {
-            resolutions.insert(&pair.rust, pair);
+        let mut add_resolution = |name: &'a Pair, generics: &'a Lifetimes| {
+            resolutions.insert(&name.rust, Resolution { name, generics });
         };
 
         let mut type_names = UnorderedSet::new();
@@ -91,9 +85,10 @@
                     for field in &strct.fields {
                         visit(&mut all, &field.ty);
                     }
-                    add_resolution(&strct.name);
+                    add_resolution(&strct.name, &strct.generics);
                 }
                 Api::Enum(enm) => {
+                    all.insert(&enm.repr_type);
                     let ident = &enm.name.rust;
                     if !type_names.insert(ident)
                         && (!cxx.contains(ident)
@@ -106,7 +101,7 @@
                         duplicate_name(cx, enm, ident);
                     }
                     enums.insert(ident, enm);
-                    add_resolution(&enm.name);
+                    add_resolution(&enm.name, &enm.generics);
                 }
                 Api::CxxType(ety) => {
                     let ident = &ety.name.rust;
@@ -123,7 +118,7 @@
                     if !ety.trusted {
                         untrusted.insert(ident, ety);
                     }
-                    add_resolution(&ety.name);
+                    add_resolution(&ety.name, &ety.generics);
                 }
                 Api::RustType(ety) => {
                     let ident = &ety.name.rust;
@@ -131,7 +126,7 @@
                         duplicate_name(cx, ety, ident);
                     }
                     rust.insert(ident);
-                    add_resolution(&ety.name);
+                    add_resolution(&ety.name, &ety.generics);
                 }
                 Api::CxxFunction(efn) | Api::RustFunction(efn) => {
                     // Note: duplication of the C++ name is fine because C++ has
@@ -153,48 +148,43 @@
                     }
                     cxx.insert(ident);
                     aliases.insert(ident, alias);
-                    add_resolution(&alias.name);
+                    add_resolution(&alias.name, &alias.generics);
                 }
                 Api::Impl(imp) => {
                     visit(&mut all, &imp.ty);
-                    explicit_impls.insert(imp);
+                    if let Some(key) = imp.ty.impl_key() {
+                        impls.insert(key, Some(imp));
+                    }
                 }
             }
         }
 
+        for ty in &all {
+            let impl_key = match ty.impl_key() {
+                Some(impl_key) => impl_key,
+                None => continue,
+            };
+            let implicit_impl = match impl_key {
+                ImplKey::RustBox(ident)
+                | ImplKey::RustVec(ident)
+                | ImplKey::UniquePtr(ident)
+                | ImplKey::SharedPtr(ident)
+                | ImplKey::WeakPtr(ident)
+                | ImplKey::CxxVector(ident) => {
+                    Atom::from(ident.rust).is_none() && !aliases.contains_key(ident.rust)
+                }
+            };
+            if implicit_impl && !impls.contains_key(&impl_key) {
+                impls.insert(impl_key, None);
+            }
+        }
+
         // All these APIs may contain types passed by value. We need to ensure
         // we check that this is permissible. We do this _after_ scanning all
         // the APIs above, in case some function or struct references a type
         // which is declared subsequently.
-        let mut required_trivial = Map::new();
-        let mut insist_alias_types_are_trivial = |ty: &'a Type, reason| {
-            if let Type::Ident(ident) = ty {
-                if cxx.contains(&ident.rust) {
-                    required_trivial.entry(&ident.rust).or_insert(reason);
-                }
-            }
-        };
-        for api in apis {
-            match api {
-                Api::Struct(strct) => {
-                    let reason = TrivialReason::StructField(strct);
-                    for field in &strct.fields {
-                        insist_alias_types_are_trivial(&field.ty, reason);
-                    }
-                }
-                Api::CxxFunction(efn) | Api::RustFunction(efn) => {
-                    let reason = TrivialReason::FunctionArgument(efn);
-                    for arg in &efn.args {
-                        insist_alias_types_are_trivial(&arg.ty, reason);
-                    }
-                    if let Some(ret) = &efn.ret {
-                        let reason = TrivialReason::FunctionReturn(efn);
-                        insist_alias_types_are_trivial(&ret, reason);
-                    }
-                }
-                _ => {}
-            }
-        }
+        let required_trivial =
+            trivial::required_trivial_reasons(apis, &all, &structs, &enums, &cxx);
 
         let mut types = Types {
             all,
@@ -205,7 +195,7 @@
             aliases,
             untrusted,
             required_trivial,
-            explicit_impls,
+            impls,
             resolutions,
             struct_improper_ctypes,
             toposorted_structs,
@@ -213,7 +203,7 @@
 
         types.toposorted_structs = toposort::sort(cx, apis, &types);
 
-        let mut unresolved_structs: Vec<&Ident> = types.structs.keys().copied().collect();
+        let mut unresolved_structs = types.structs.keys();
         let mut new_information = true;
         while new_information {
             new_information = false;
@@ -242,27 +232,12 @@
 
     pub fn needs_indirect_abi(&self, ty: &Type) -> bool {
         match ty {
-            Type::Ident(ident) => {
-                if let Some(strct) = self.structs.get(&ident.rust) {
-                    !self.is_pod(strct)
-                } else {
-                    Atom::from(&ident.rust) == Some(RustString)
-                }
-            }
-            Type::RustVec(_) => true,
-            _ => false,
+            Type::RustBox(_) | Type::UniquePtr(_) => false,
+            Type::Array(_) => true,
+            _ => !self.is_guaranteed_pod(ty),
         }
     }
 
-    pub fn is_pod(&self, strct: &Struct) -> bool {
-        for derive in &strct.derives {
-            if *derive == Derive::Copy {
-                return true;
-            }
-        }
-        false
-    }
-
     // Types that trigger rustc's default #[warn(improper_ctypes)] lint, even if
     // they may be otherwise unproblematic to mention in an extern signature.
     // For example in a signature like `extern "C" fn(*const String)`, rustc
@@ -275,12 +250,6 @@
             ImproperCtype::Depends(ident) => self.struct_improper_ctypes.contains(ident),
         }
     }
-
-    pub fn resolve(&self, ident: &ResolvableName) -> &Pair {
-        self.resolutions
-            .get(&ident.rust)
-            .expect("Unable to resolve type")
-    }
 }
 
 impl<'t, 'a> IntoIterator for &'t Types<'a> {
@@ -291,13 +260,6 @@
     }
 }
 
-#[derive(Copy, Clone)]
-pub enum TrivialReason<'a> {
-    StructField(&'a Struct),
-    FunctionArgument(&'a ExternFn),
-    FunctionReturn(&'a ExternFn),
-}
-
 fn duplicate_name(cx: &mut Errors, sp: impl ToTokens, ident: &Ident) {
     let msg = format!("the name `{}` is defined multiple times", ident);
     cx.error(sp, msg);
diff --git a/syntax/visit.rs b/syntax/visit.rs
new file mode 100644
index 0000000..2f31378
--- /dev/null
+++ b/syntax/visit.rs
@@ -0,0 +1,34 @@
+use crate::syntax::Type;
+
+pub trait Visit<'a> {
+    fn visit_type(&mut self, ty: &'a Type) {
+        visit_type(self, ty);
+    }
+}
+
+pub fn visit_type<'a, V>(visitor: &mut V, ty: &'a Type)
+where
+    V: Visit<'a> + ?Sized,
+{
+    match ty {
+        Type::Ident(_) | Type::Str(_) | Type::Void(_) => {}
+        Type::RustBox(ty)
+        | Type::UniquePtr(ty)
+        | Type::SharedPtr(ty)
+        | Type::WeakPtr(ty)
+        | Type::CxxVector(ty)
+        | Type::RustVec(ty) => visitor.visit_type(&ty.inner),
+        Type::Ref(r) => visitor.visit_type(&r.inner),
+        Type::Ptr(p) => visitor.visit_type(&p.inner),
+        Type::Array(a) => visitor.visit_type(&a.inner),
+        Type::SliceRef(s) => visitor.visit_type(&s.inner),
+        Type::Fn(fun) => {
+            if let Some(ret) = &fun.ret {
+                visitor.visit_type(ret);
+            }
+            for arg in &fun.args {
+                visitor.visit_type(&arg.ty);
+            }
+        }
+    }
+}
diff --git a/tests/BUCK b/tests/BUCK
index 5bbe500..d131ddb 100644
--- a/tests/BUCK
+++ b/tests/BUCK
@@ -12,7 +12,7 @@
 rust_library(
     name = "ffi",
     srcs = [
-        "ffi/extra.rs",
+        "ffi/cast.rs",
         "ffi/lib.rs",
         "ffi/module.rs",
     ],
@@ -28,11 +28,11 @@
     srcs = [
         "ffi/tests.cc",
         ":bridge/source",
-        ":extra/source",
         ":module/source",
     ],
     headers = {
         "ffi/lib.rs.h": ":bridge/header",
+        "ffi/module.rs.h": ":module/header",
         "ffi/tests.h": "ffi/tests.h",
     },
     deps = ["//:core"],
@@ -44,11 +44,6 @@
 )
 
 rust_cxx_bridge(
-    name = "extra",
-    src = "ffi/extra.rs",
-)
-
-rust_cxx_bridge(
     name = "module",
     src = "ffi/module.rs",
 )
diff --git a/tests/BUILD b/tests/BUILD
index 57ffab9..7886e4f 100644
--- a/tests/BUILD
+++ b/tests/BUILD
@@ -15,7 +15,7 @@
 rust_library(
     name = "cxx_test_suite",
     srcs = [
-        "ffi/extra.rs",
+        "ffi/cast.rs",
         "ffi/lib.rs",
         "ffi/module.rs",
     ],
@@ -30,12 +30,12 @@
     srcs = [
         "ffi/tests.cc",
         ":bridge/source",
-        ":extra/source",
         ":module/source",
     ],
     hdrs = ["ffi/tests.h"],
     deps = [
         ":bridge/include",
+        ":module/include",
         "//:core",
     ],
 )
@@ -47,12 +47,6 @@
 )
 
 rust_cxx_bridge(
-    name = "extra",
-    src = "ffi/extra.rs",
-    deps = [":impl"],
-)
-
-rust_cxx_bridge(
     name = "module",
     src = "ffi/module.rs",
     deps = [":impl"],
diff --git a/tests/cxx_gen.rs b/tests/cxx_gen.rs
index 06d7bc7..e91675d 100644
--- a/tests/cxx_gen.rs
+++ b/tests/cxx_gen.rs
@@ -6,7 +6,7 @@
 const BRIDGE0: &str = r#"
     #[cxx::bridge]
     mod ffi {
-        extern "C" {
+        unsafe extern "C++" {
             pub fn do_cpp_thing(foo: &str);
         }
     }
@@ -20,7 +20,7 @@
     let output = str::from_utf8(&generated.implementation).unwrap();
     // To avoid continual breakage we won't test every byte.
     // Let's look for the major features.
-    assert!(output.contains("void cxxbridge05$do_cpp_thing(::rust::repr::PtrLen foo)"));
+    assert!(output.contains("void cxxbridge1$do_cpp_thing(::rust::Str foo)"));
 }
 
 #[test]
@@ -30,5 +30,5 @@
     let source = BRIDGE0.parse().unwrap();
     let generated = generate_header_and_cc(source, &opt).unwrap();
     let output = str::from_utf8(&generated.implementation).unwrap();
-    assert!(output.contains("ANNOTATION void cxxbridge05$do_cpp_thing(::rust::repr::PtrLen foo)"));
+    assert!(output.contains("ANNOTATION void cxxbridge1$do_cpp_thing(::rust::Str foo)"));
 }
diff --git a/tests/cxx_string.rs b/tests/cxx_string.rs
new file mode 100644
index 0000000..be693cd
--- /dev/null
+++ b/tests/cxx_string.rs
@@ -0,0 +1,15 @@
+use cxx::{let_cxx_string, CxxString};
+
+#[test]
+fn test_async_cxx_string() {
+    async fn f() {
+        let_cxx_string!(s = "...");
+
+        async fn g(_: &CxxString) {}
+        g(&s).await;
+    }
+
+    // https://github.com/dtolnay/cxx/issues/693
+    fn assert_send(_: impl Send) {}
+    assert_send(f());
+}
diff --git a/tests/ffi/build.rs b/tests/ffi/build.rs
index 4b2cbdf..86f8cd3 100644
--- a/tests/ffi/build.rs
+++ b/tests/ffi/build.rs
@@ -6,9 +6,13 @@
     }
 
     CFG.include_prefix = "tests/ffi";
-    let sources = vec!["lib.rs", "extra.rs", "module.rs"];
-    cxx_build::bridges(sources)
-        .file("tests.cc")
-        .flag_if_supported(cxxbridge_flags::STD)
-        .compile("cxx-test-suite");
+    let sources = vec!["lib.rs", "module.rs"];
+    let mut build = cxx_build::bridges(sources);
+    build.file("tests.cc");
+    build.flag_if_supported(cxxbridge_flags::STD);
+    build.warnings_into_errors(cfg!(deny_warnings));
+    if cfg!(not(target_env = "msvc")) {
+        build.define("CXX_TEST_INSTANTIATIONS", None);
+    }
+    build.compile("cxx-test-suite");
 }
diff --git a/tests/ffi/cast.rs b/tests/ffi/cast.rs
new file mode 100644
index 0000000..30ecb6a
--- /dev/null
+++ b/tests/ffi/cast.rs
@@ -0,0 +1,14 @@
+use std::os::raw::c_char;
+use std::slice;
+
+pub fn c_char_to_unsigned(slice: &[c_char]) -> &[u8] {
+    let ptr = slice.as_ptr().cast::<u8>();
+    let len = slice.len();
+    unsafe { slice::from_raw_parts(ptr, len) }
+}
+
+pub fn unsigned_to_c_char(slice: &[u8]) -> &[c_char] {
+    let ptr = slice.as_ptr().cast::<c_char>();
+    let len = slice.len();
+    unsafe { slice::from_raw_parts(ptr, len) }
+}
diff --git a/tests/ffi/extra.rs b/tests/ffi/extra.rs
deleted file mode 100644
index cd76a7d..0000000
--- a/tests/ffi/extra.rs
+++ /dev/null
@@ -1,63 +0,0 @@
-// Separate mod so that &self in the lib.rs mod has an unambiguous receiver. At
-// the moment, the cxx C++ codegen can't convert more than one cxx::bridge mod
-// per file, so that's why we need to put this outside of lib.rs. All of this
-// could go into module.rs instead, but for now its purpose is narrowly scoped
-// for testing aliasing between cxx::bridge mods, so we'll keep it that way and
-// start a new mod here.
-
-// Rustfmt mangles the extern type alias.
-// https://github.com/rust-lang/rustfmt/issues/4159
-#[rustfmt::skip]
-#[cxx::bridge(namespace = "tests")]
-pub mod ffi2 {
-    impl UniquePtr<D> {}
-    impl UniquePtr<E> {}
-    impl UniquePtr<F> {}
-    impl UniquePtr<G> {}
-
-    extern "C" {
-        include!("tests/ffi/tests.h");
-
-        type D = crate::other::D;
-        type E = crate::other::E;
-        #[namespace = "F"]
-        type F = crate::other::f::F;
-        #[namespace = "G"]
-        type G = crate::other::G;
-
-        #[namespace = "H"]
-        type H;
-
-        fn c_take_trivial_ptr(d: UniquePtr<D>);
-        fn c_take_trivial_ref(d: &D);
-        fn c_take_trivial(d: D);
-        fn c_take_trivial_ns_ptr(g: UniquePtr<G>);
-        fn c_take_trivial_ns_ref(g: &G);
-        fn c_take_trivial_ns(g: G);
-        fn c_take_opaque_ptr(e: UniquePtr<E>);
-        fn c_take_opaque_ref(e: &E);
-        fn c_take_opaque_ns_ptr(e: UniquePtr<F>);
-        fn c_take_opaque_ns_ref(e: &F);
-        fn c_return_trivial_ptr() -> UniquePtr<D>;
-        fn c_return_trivial() -> D;
-        fn c_return_trivial_ns_ptr() -> UniquePtr<G>;
-        fn c_return_trivial_ns() -> G;
-        fn c_return_opaque_ptr() -> UniquePtr<E>;
-        fn c_return_ns_opaque_ptr() -> UniquePtr<F>;  
-        fn c_return_ns_unique_ptr() -> UniquePtr<H>;
-        fn c_take_ref_ns_c(h: &H);
-
-        #[namespace = "other"]
-        fn ns_c_take_trivial(d: D);
-        #[namespace = "other"]
-        fn ns_c_return_trivial() -> D;
-
-        #[namespace = "I"]
-        type I;
-
-        fn get(self: &I) -> u32;
-
-        #[namespace = "I"]
-        fn ns_c_return_unique_ptr_ns() -> UniquePtr<I>;
-    }
-}
diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs
index 4c7cbe4..fcbe153 100644
--- a/tests/ffi/lib.rs
+++ b/tests/ffi/lib.rs
@@ -1,15 +1,328 @@
 #![allow(
     clippy::boxed_local,
     clippy::just_underscores_and_digits,
+    clippy::let_underscore_drop,
+    clippy::missing_safety_doc,
+    clippy::must_use_candidate,
+    clippy::needless_lifetimes,
+    clippy::needless_pass_by_value,
     clippy::ptr_arg,
-    clippy::trivially_copy_pass_by_ref
+    clippy::trivially_copy_pass_by_ref,
+    clippy::unnecessary_wraps,
+    clippy::unused_self
 )]
 
-pub mod extra;
+pub mod cast;
 pub mod module;
 
-use cxx::{CxxString, CxxVector, UniquePtr};
+use cxx::{CxxString, CxxVector, SharedPtr, UniquePtr};
 use std::fmt::{self, Display};
+use std::mem::MaybeUninit;
+use std::os::raw::c_char;
+
+#[cxx::bridge(namespace = "tests")]
+pub mod ffi {
+    #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
+    struct Shared {
+        z: usize,
+    }
+
+    #[derive(PartialEq, PartialOrd)]
+    struct SharedString {
+        msg: String,
+    }
+
+    #[derive(Debug, Hash, PartialOrd, Ord)]
+    enum Enum {
+        AVal,
+        BVal = 2020,
+        #[cxx_name = "CVal"]
+        LastVal,
+    }
+
+    #[namespace = "A"]
+    #[derive(Copy, Clone, Default)]
+    struct AShared {
+        z: usize,
+    }
+
+    #[namespace = "A"]
+    enum AEnum {
+        AAVal,
+        ABVal = 2020,
+        ACVal,
+    }
+
+    #[namespace = "A::B"]
+    enum ABEnum {
+        ABAVal,
+        ABBVal = 2020,
+        ABCVal,
+    }
+
+    #[namespace = "A::B"]
+    #[derive(Clone)]
+    struct ABShared {
+        z: usize,
+    }
+
+    #[namespace = "first"]
+    struct First {
+        second: Box<Second>,
+    }
+
+    #[namespace = "second"]
+    #[derive(Hash)]
+    struct Second {
+        i: i32,
+        e: COwnedEnum,
+    }
+
+    pub struct Array {
+        a: [i32; 4],
+    }
+
+    #[derive(Copy, Clone, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
+    pub struct StructWithLifetime<'a> {
+        s: &'a str,
+    }
+
+    unsafe extern "C++" {
+        include!("tests/ffi/tests.h");
+
+        type C;
+
+        fn c_return_primitive() -> usize;
+        fn c_return_shared() -> Shared;
+        fn c_return_box() -> Box<R>;
+        fn c_return_unique_ptr() -> UniquePtr<C>;
+        fn c_return_shared_ptr() -> SharedPtr<C>;
+        fn c_return_ref(shared: &Shared) -> &usize;
+        fn c_return_mut(shared: &mut Shared) -> &mut usize;
+        fn c_return_str(shared: &Shared) -> &str;
+        fn c_return_slice_char(shared: &Shared) -> &[c_char];
+        fn c_return_mutsliceu8(slice: &mut [u8]) -> &mut [u8];
+        fn c_return_rust_string() -> String;
+        fn c_return_unique_ptr_string() -> UniquePtr<CxxString>;
+        fn c_return_unique_ptr_vector_u8() -> UniquePtr<CxxVector<u8>>;
+        fn c_return_unique_ptr_vector_f64() -> UniquePtr<CxxVector<f64>>;
+        fn c_return_unique_ptr_vector_string() -> UniquePtr<CxxVector<CxxString>>;
+        fn c_return_unique_ptr_vector_shared() -> UniquePtr<CxxVector<Shared>>;
+        fn c_return_unique_ptr_vector_opaque() -> UniquePtr<CxxVector<C>>;
+        fn c_return_ref_vector(c: &C) -> &CxxVector<u8>;
+        fn c_return_mut_vector(c: Pin<&mut C>) -> Pin<&mut CxxVector<u8>>;
+        fn c_return_rust_vec() -> Vec<u8>;
+        fn c_return_ref_rust_vec(c: &C) -> &Vec<u8>;
+        fn c_return_mut_rust_vec(c: Pin<&mut C>) -> &mut Vec<u8>;
+        fn c_return_rust_vec_string() -> Vec<String>;
+        fn c_return_identity(_: usize) -> usize;
+        fn c_return_sum(_: usize, _: usize) -> usize;
+        fn c_return_enum(n: u16) -> Enum;
+        fn c_return_ns_ref(shared: &AShared) -> &usize;
+        fn c_return_nested_ns_ref(shared: &ABShared) -> &usize;
+        fn c_return_ns_enum(n: u16) -> AEnum;
+        fn c_return_nested_ns_enum(n: u16) -> ABEnum;
+        fn c_return_const_ptr(n: usize) -> *const C;
+        fn c_return_mut_ptr(n: usize) -> *mut C;
+
+        fn c_take_primitive(n: usize);
+        fn c_take_shared(shared: Shared);
+        fn c_take_box(r: Box<R>);
+        fn c_take_ref_r(r: &R);
+        fn c_take_ref_c(c: &C);
+        fn c_take_str(s: &str);
+        fn c_take_slice_char(s: &[c_char]);
+        fn c_take_slice_shared(s: &[Shared]);
+        fn c_take_slice_shared_sort(s: &mut [Shared]);
+        fn c_take_slice_r(s: &[R]);
+        fn c_take_slice_r_sort(s: &mut [R]);
+        fn c_take_rust_string(s: String);
+        fn c_take_unique_ptr_string(s: UniquePtr<CxxString>);
+        fn c_take_unique_ptr_vector_u8(v: UniquePtr<CxxVector<u8>>);
+        fn c_take_unique_ptr_vector_f64(v: UniquePtr<CxxVector<f64>>);
+        fn c_take_unique_ptr_vector_string(v: UniquePtr<CxxVector<CxxString>>);
+        fn c_take_unique_ptr_vector_shared(v: UniquePtr<CxxVector<Shared>>);
+        fn c_take_ref_vector(v: &CxxVector<u8>);
+        fn c_take_rust_vec(v: Vec<u8>);
+        fn c_take_rust_vec_shared(v: Vec<Shared>);
+        fn c_take_rust_vec_string(v: Vec<String>);
+        fn c_take_rust_vec_index(v: Vec<u8>);
+        fn c_take_rust_vec_shared_index(v: Vec<Shared>);
+        fn c_take_rust_vec_shared_push(v: Vec<Shared>);
+        fn c_take_rust_vec_shared_forward_iterator(v: Vec<Shared>);
+        fn c_take_rust_vec_shared_sort(v: Vec<Shared>);
+        fn c_take_ref_rust_vec(v: &Vec<u8>);
+        fn c_take_ref_rust_vec_string(v: &Vec<String>);
+        fn c_take_ref_rust_vec_index(v: &Vec<u8>);
+        fn c_take_ref_rust_vec_copy(v: &Vec<u8>);
+        fn c_take_ref_shared_string(s: &SharedString) -> &SharedString;
+        fn c_take_callback(callback: fn(String) -> usize);
+        fn c_take_enum(e: Enum);
+        fn c_take_ns_enum(e: AEnum);
+        fn c_take_nested_ns_enum(e: ABEnum);
+        fn c_take_ns_shared(shared: AShared);
+        fn c_take_nested_ns_shared(shared: ABShared);
+        fn c_take_rust_vec_ns_shared(v: Vec<AShared>);
+        fn c_take_rust_vec_nested_ns_shared(v: Vec<ABShared>);
+        unsafe fn c_take_const_ptr(c: *const C) -> usize;
+        unsafe fn c_take_mut_ptr(c: *mut C) -> usize;
+
+        fn c_try_return_void() -> Result<()>;
+        fn c_try_return_primitive() -> Result<usize>;
+        fn c_fail_return_primitive() -> Result<usize>;
+        fn c_try_return_box() -> Result<Box<R>>;
+        fn c_try_return_ref(s: &String) -> Result<&String>;
+        fn c_try_return_str(s: &str) -> Result<&str>;
+        fn c_try_return_sliceu8(s: &[u8]) -> Result<&[u8]>;
+        fn c_try_return_mutsliceu8(s: &mut [u8]) -> Result<&mut [u8]>;
+        fn c_try_return_rust_string() -> Result<String>;
+        fn c_try_return_unique_ptr_string() -> Result<UniquePtr<CxxString>>;
+        fn c_try_return_rust_vec() -> Result<Vec<u8>>;
+        fn c_try_return_rust_vec_string() -> Result<Vec<String>>;
+        fn c_try_return_ref_rust_vec(c: &C) -> Result<&Vec<u8>>;
+
+        fn get(self: &C) -> usize;
+        fn set(self: Pin<&mut C>, n: usize) -> usize;
+        fn get2(&self) -> usize;
+        fn getRef(self: &C) -> &usize;
+        fn getMut(self: Pin<&mut C>) -> &mut usize;
+        fn set_succeed(self: Pin<&mut C>, n: usize) -> Result<usize>;
+        fn get_fail(self: Pin<&mut C>) -> Result<usize>;
+        fn c_method_on_shared(self: &Shared) -> usize;
+        fn c_method_ref_on_shared(self: &Shared) -> &usize;
+        fn c_method_mut_on_shared(self: &mut Shared) -> &mut usize;
+        fn c_set_array(self: &mut Array, value: i32);
+
+        fn c_get_use_count(weak: &WeakPtr<C>) -> usize;
+
+        #[rust_name = "i32_overloaded_method"]
+        fn cOverloadedMethod(&self, x: i32) -> String;
+        #[rust_name = "str_overloaded_method"]
+        fn cOverloadedMethod(&self, x: &str) -> String;
+        #[rust_name = "i32_overloaded_function"]
+        fn cOverloadedFunction(x: i32) -> String;
+        #[rust_name = "str_overloaded_function"]
+        fn cOverloadedFunction(x: &str) -> String;
+
+        #[namespace = "other"]
+        fn ns_c_take_ns_shared(shared: AShared);
+    }
+
+    extern "C++" {
+        include!("tests/ffi/module.rs.h");
+
+        type COwnedEnum;
+        type Job = crate::module::ffi::Job;
+    }
+
+    extern "Rust" {
+        #[derive(ExternType)]
+        type Reference<'a>;
+    }
+
+    unsafe extern "C++" {
+        type Borrow<'a>;
+
+        fn c_return_borrow<'a>(s: &'a CxxString) -> UniquePtr<Borrow<'a>>;
+
+        #[rust_name = "c_return_borrow_elided"]
+        fn c_return_borrow(s: &CxxString) -> UniquePtr<Borrow>;
+
+        fn const_member(self: &Borrow);
+        fn nonconst_member(self: Pin<&mut Borrow>);
+    }
+
+    #[repr(u32)]
+    #[derive(Hash)]
+    enum COwnedEnum {
+        #[cxx_name = "CVAL1"]
+        CVal1,
+        #[cxx_name = "CVAL2"]
+        CVal2,
+    }
+
+    extern "Rust" {
+        type R;
+
+        fn r_return_primitive() -> usize;
+        fn r_return_shared() -> Shared;
+        fn r_return_box() -> Box<R>;
+        fn r_return_unique_ptr() -> UniquePtr<C>;
+        fn r_return_shared_ptr() -> SharedPtr<C>;
+        fn r_return_ref(shared: &Shared) -> &usize;
+        fn r_return_mut(shared: &mut Shared) -> &mut usize;
+        fn r_return_str(shared: &Shared) -> &str;
+        fn r_return_sliceu8(shared: &Shared) -> &[u8];
+        fn r_return_mutsliceu8(slice: &mut [u8]) -> &mut [u8];
+        fn r_return_rust_string() -> String;
+        fn r_return_unique_ptr_string() -> UniquePtr<CxxString>;
+        fn r_return_rust_vec() -> Vec<u8>;
+        fn r_return_rust_vec_string() -> Vec<String>;
+        fn r_return_rust_vec_extern_struct() -> Vec<Job>;
+        fn r_return_ref_rust_vec(shared: &Shared) -> &Vec<u8>;
+        fn r_return_mut_rust_vec(shared: &mut Shared) -> &mut Vec<u8>;
+        fn r_return_identity(_: usize) -> usize;
+        fn r_return_sum(_: usize, _: usize) -> usize;
+        fn r_return_enum(n: u32) -> Enum;
+
+        fn r_take_primitive(n: usize);
+        fn r_take_shared(shared: Shared);
+        fn r_take_box(r: Box<R>);
+        fn r_take_unique_ptr(c: UniquePtr<C>);
+        fn r_take_shared_ptr(c: SharedPtr<C>);
+        fn r_take_ref_r(r: &R);
+        fn r_take_ref_c(c: &C);
+        fn r_take_str(s: &str);
+        fn r_take_slice_char(s: &[c_char]);
+        fn r_take_rust_string(s: String);
+        fn r_take_unique_ptr_string(s: UniquePtr<CxxString>);
+        fn r_take_ref_vector(v: &CxxVector<u8>);
+        fn r_take_ref_empty_vector(v: &CxxVector<u64>);
+        fn r_take_rust_vec(v: Vec<u8>);
+        fn r_take_rust_vec_string(v: Vec<String>);
+        fn r_take_ref_rust_vec(v: &Vec<u8>);
+        fn r_take_ref_rust_vec_string(v: &Vec<String>);
+        fn r_take_enum(e: Enum);
+
+        fn r_try_return_void() -> Result<()>;
+        fn r_try_return_primitive() -> Result<usize>;
+        fn r_try_return_box() -> Result<Box<R>>;
+        fn r_fail_return_primitive() -> Result<usize>;
+        fn r_try_return_sliceu8(s: &[u8]) -> Result<&[u8]>;
+        fn r_try_return_mutsliceu8(s: &mut [u8]) -> Result<&mut [u8]>;
+
+        fn get(self: &R) -> usize;
+        fn set(self: &mut R, n: usize) -> usize;
+        fn r_method_on_shared(self: &Shared) -> String;
+        fn r_get_array_sum(self: &Array) -> i32;
+
+        #[cxx_name = "rAliasedFunction"]
+        fn r_aliased_function(x: i32) -> String;
+    }
+
+    struct Dag0 {
+        i: i32,
+    }
+
+    struct Dag1 {
+        dag2: Dag2,
+        vec: Vec<Dag3>,
+    }
+
+    struct Dag2 {
+        dag4: Dag4,
+    }
+
+    struct Dag3 {
+        dag1: Dag1,
+    }
+
+    struct Dag4 {
+        dag0: Dag0,
+    }
+
+    impl Box<Shared> {}
+}
 
 mod other {
     use cxx::kind::{Opaque, Trivial};
@@ -63,252 +376,10 @@
     }
 }
 
-#[cxx::bridge(namespace = "tests")]
-pub mod ffi {
-    #[derive(Clone)]
-    struct Shared {
-        z: usize,
-    }
+#[derive(PartialEq, Debug)]
+pub struct R(pub usize);
 
-    struct SharedString {
-        msg: String,
-    }
-
-    enum Enum {
-        AVal,
-        BVal = 2020,
-        CVal,
-    }
-
-    #[namespace = "A"]
-    #[derive(Clone)]
-    struct AShared {
-        z: usize,
-    }
-
-    #[namespace = "A"]
-    enum AEnum {
-        AAVal,
-        ABVal = 2020,
-        ACVal,
-    }
-
-    #[namespace = "A::B"]
-    enum ABEnum {
-        ABAVal,
-        ABBVal = 2020,
-        ABCVal,
-    }
-
-    #[namespace = "A::B"]
-    #[derive(Clone)]
-    struct ABShared {
-        z: usize,
-    }
-
-    #[namespace = "first"]
-    struct First {
-        second: Box<Second>,
-    }
-
-    #[namespace = "second"]
-    struct Second {
-        i: i32,
-    }
-
-    extern "C" {
-        include!("tests/ffi/tests.h");
-
-        type C;
-
-        fn c_return_primitive() -> usize;
-        fn c_return_shared() -> Shared;
-        fn c_return_box() -> Box<R>;
-        fn c_return_unique_ptr() -> UniquePtr<C>;
-        fn c_return_ref(shared: &Shared) -> &usize;
-        fn c_return_mut(shared: &mut Shared) -> &mut usize;
-        fn c_return_str(shared: &Shared) -> &str;
-        fn c_return_sliceu8(shared: &Shared) -> &[u8];
-        fn c_return_rust_string() -> String;
-        fn c_return_unique_ptr_string() -> UniquePtr<CxxString>;
-        fn c_return_unique_ptr_vector_u8() -> UniquePtr<CxxVector<u8>>;
-        fn c_return_unique_ptr_vector_f64() -> UniquePtr<CxxVector<f64>>;
-        fn c_return_unique_ptr_vector_string() -> UniquePtr<CxxVector<CxxString>>;
-        fn c_return_unique_ptr_vector_shared() -> UniquePtr<CxxVector<Shared>>;
-        fn c_return_unique_ptr_vector_opaque() -> UniquePtr<CxxVector<C>>;
-        fn c_return_ref_vector(c: &C) -> &CxxVector<u8>;
-        fn c_return_mut_vector(c: &mut C) -> &mut CxxVector<u8>;
-        fn c_return_rust_vec() -> Vec<u8>;
-        fn c_return_ref_rust_vec(c: &C) -> &Vec<u8>;
-        fn c_return_mut_rust_vec(c: &mut C) -> &mut Vec<u8>;
-        fn c_return_rust_vec_string() -> Vec<String>;
-        fn c_return_identity(_: usize) -> usize;
-        fn c_return_sum(_: usize, _: usize) -> usize;
-        fn c_return_enum(n: u16) -> Enum;
-        fn c_return_ns_ref(shared: &AShared) -> &usize;
-        fn c_return_nested_ns_ref(shared: &ABShared) -> &usize;
-        fn c_return_ns_enum(n: u16) -> AEnum;
-        fn c_return_nested_ns_enum(n: u16) -> ABEnum;
-
-        fn c_take_primitive(n: usize);
-        fn c_take_shared(shared: Shared);
-        fn c_take_box(r: Box<R>);
-        fn c_take_ref_r(r: &R);
-        fn c_take_ref_c(c: &C);
-        fn c_take_str(s: &str);
-        fn c_take_sliceu8(s: &[u8]);
-        fn c_take_rust_string(s: String);
-        fn c_take_unique_ptr_string(s: UniquePtr<CxxString>);
-        fn c_take_unique_ptr_vector_u8(v: UniquePtr<CxxVector<u8>>);
-        fn c_take_unique_ptr_vector_f64(v: UniquePtr<CxxVector<f64>>);
-        fn c_take_unique_ptr_vector_string(v: UniquePtr<CxxVector<CxxString>>);
-        fn c_take_unique_ptr_vector_shared(v: UniquePtr<CxxVector<Shared>>);
-        fn c_take_ref_vector(v: &CxxVector<u8>);
-        fn c_take_rust_vec(v: Vec<u8>);
-        fn c_take_rust_vec_shared(v: Vec<Shared>);
-        fn c_take_rust_vec_string(v: Vec<String>);
-        fn c_take_rust_vec_index(v: Vec<u8>);
-        fn c_take_rust_vec_shared_index(v: Vec<Shared>);
-        fn c_take_rust_vec_shared_push(v: Vec<Shared>);
-        fn c_take_rust_vec_shared_forward_iterator(v: Vec<Shared>);
-        fn c_take_ref_rust_vec(v: &Vec<u8>);
-        fn c_take_ref_rust_vec_string(v: &Vec<String>);
-        fn c_take_ref_rust_vec_index(v: &Vec<u8>);
-        fn c_take_ref_rust_vec_copy(v: &Vec<u8>);
-        fn c_take_ref_shared_string(s: &SharedString) -> &SharedString;
-        fn c_take_callback(callback: fn(String) -> usize);
-        fn c_take_enum(e: Enum);
-        fn c_take_ns_enum(e: AEnum);
-        fn c_take_nested_ns_enum(e: ABEnum);
-        fn c_take_ns_shared(shared: AShared);
-        fn c_take_nested_ns_shared(shared: ABShared);
-        fn c_take_rust_vec_ns_shared(v: Vec<AShared>);
-        fn c_take_rust_vec_nested_ns_shared(v: Vec<ABShared>);
-
-        fn c_try_return_void() -> Result<()>;
-        fn c_try_return_primitive() -> Result<usize>;
-        fn c_fail_return_primitive() -> Result<usize>;
-        fn c_try_return_box() -> Result<Box<R>>;
-        fn c_try_return_ref(s: &String) -> Result<&String>;
-        fn c_try_return_str(s: &str) -> Result<&str>;
-        fn c_try_return_sliceu8(s: &[u8]) -> Result<&[u8]>;
-        fn c_try_return_rust_string() -> Result<String>;
-        fn c_try_return_unique_ptr_string() -> Result<UniquePtr<CxxString>>;
-        fn c_try_return_rust_vec() -> Result<Vec<u8>>;
-        fn c_try_return_rust_vec_string() -> Result<Vec<String>>;
-        fn c_try_return_ref_rust_vec(c: &C) -> Result<&Vec<u8>>;
-
-        fn get(self: &C) -> usize;
-        fn set(self: &mut C, n: usize) -> usize;
-        fn get2(&self) -> usize;
-        fn set2(&mut self, n: usize) -> usize;
-        fn set_succeed(&mut self, n: usize) -> Result<usize>;
-        fn get_fail(&mut self) -> Result<usize>;
-        fn c_method_on_shared(self: &Shared) -> usize;
-
-        #[rust_name = "i32_overloaded_method"]
-        fn cOverloadedMethod(&self, x: i32) -> String;
-        #[rust_name = "str_overloaded_method"]
-        fn cOverloadedMethod(&self, x: &str) -> String;
-        #[rust_name = "i32_overloaded_function"]
-        fn cOverloadedFunction(x: i32) -> String;
-        #[rust_name = "str_overloaded_function"]
-        fn cOverloadedFunction(x: &str) -> String;
-
-        #[namespace = "other"]
-        fn ns_c_take_ns_shared(shared: AShared);
-    }
-
-    extern "C" {
-        type COwnedEnum;
-    }
-
-    #[repr(u32)]
-    enum COwnedEnum {
-        CVal1,
-        CVal2,
-    }
-
-    extern "Rust" {
-        type R;
-        type R2;
-
-        fn r_return_primitive() -> usize;
-        fn r_return_shared() -> Shared;
-        fn r_return_box() -> Box<R>;
-        fn r_return_unique_ptr() -> UniquePtr<C>;
-        fn r_return_ref(shared: &Shared) -> &usize;
-        fn r_return_mut(shared: &mut Shared) -> &mut usize;
-        fn r_return_str(shared: &Shared) -> &str;
-        fn r_return_rust_string() -> String;
-        fn r_return_unique_ptr_string() -> UniquePtr<CxxString>;
-        fn r_return_rust_vec() -> Vec<u8>;
-        fn r_return_rust_vec_string() -> Vec<String>;
-        fn r_return_ref_rust_vec(shared: &Shared) -> &Vec<u8>;
-        fn r_return_mut_rust_vec(shared: &mut Shared) -> &mut Vec<u8>;
-        fn r_return_identity(_: usize) -> usize;
-        fn r_return_sum(_: usize, _: usize) -> usize;
-        fn r_return_enum(n: u32) -> Enum;
-
-        fn r_take_primitive(n: usize);
-        fn r_take_shared(shared: Shared);
-        fn r_take_box(r: Box<R>);
-        fn r_take_unique_ptr(c: UniquePtr<C>);
-        fn r_take_ref_r(r: &R);
-        fn r_take_ref_c(c: &C);
-        fn r_take_str(s: &str);
-        fn r_take_sliceu8(s: &[u8]);
-        fn r_take_rust_string(s: String);
-        fn r_take_unique_ptr_string(s: UniquePtr<CxxString>);
-        fn r_take_ref_vector(v: &CxxVector<u8>);
-        fn r_take_ref_empty_vector(v: &CxxVector<u64>);
-        fn r_take_rust_vec(v: Vec<u8>);
-        fn r_take_rust_vec_string(v: Vec<String>);
-        fn r_take_ref_rust_vec(v: &Vec<u8>);
-        fn r_take_ref_rust_vec_string(v: &Vec<String>);
-        fn r_take_enum(e: Enum);
-
-        fn r_try_return_void() -> Result<()>;
-        fn r_try_return_primitive() -> Result<usize>;
-        fn r_try_return_box() -> Result<Box<R>>;
-        fn r_fail_return_primitive() -> Result<usize>;
-
-        fn r_return_r2(n: usize) -> Box<R2>;
-        fn get(self: &R2) -> usize;
-        fn set(self: &mut R2, n: usize) -> usize;
-        fn r_method_on_shared(self: &Shared) -> String;
-
-        #[cxx_name = "rAliasedFunction"]
-        fn r_aliased_function(x: i32) -> String;
-    }
-
-    struct Dag0 {
-        i: i32,
-    }
-
-    struct Dag1 {
-        dag2: Dag2,
-        vec: Vec<Dag3>,
-    }
-
-    struct Dag2 {
-        dag4: Dag4,
-    }
-
-    struct Dag3 {
-        dag1: Dag1,
-    }
-
-    struct Dag4 {
-        dag0: Dag0,
-    }
-}
-
-pub type R = usize;
-
-pub struct R2(usize);
-
-impl R2 {
+impl R {
     fn get(&self) -> usize {
         self.0
     }
@@ -319,12 +390,20 @@
     }
 }
 
+pub struct Reference<'a>(&'a String);
+
 impl ffi::Shared {
     fn r_method_on_shared(&self) -> String {
         "2020".to_owned()
     }
 }
 
+impl ffi::Array {
+    pub fn r_get_array_sum(&self) -> i32 {
+        self.a.iter().sum()
+    }
+}
+
 #[derive(Debug)]
 struct Error;
 
@@ -345,7 +424,7 @@
 }
 
 fn r_return_box() -> Box<R> {
-    Box::new(2020)
+    Box::new(R(2020))
 }
 
 fn r_return_unique_ptr() -> UniquePtr<ffi::C> {
@@ -355,6 +434,18 @@
     unsafe { UniquePtr::from_raw(cxx_test_suite_get_unique_ptr()) }
 }
 
+fn r_return_shared_ptr() -> SharedPtr<ffi::C> {
+    extern "C" {
+        fn cxx_test_suite_get_shared_ptr(repr: *mut SharedPtr<ffi::C>);
+    }
+    let mut shared_ptr = MaybeUninit::<SharedPtr<ffi::C>>::uninit();
+    let repr = shared_ptr.as_mut_ptr();
+    unsafe {
+        cxx_test_suite_get_shared_ptr(repr);
+        shared_ptr.assume_init()
+    }
+}
+
 fn r_return_ref(shared: &ffi::Shared) -> &usize {
     &shared.z
 }
@@ -368,6 +459,15 @@
     "2020"
 }
 
+fn r_return_sliceu8(shared: &ffi::Shared) -> &[u8] {
+    let _ = shared;
+    b"2020"
+}
+
+fn r_return_mutsliceu8(slice: &mut [u8]) -> &mut [u8] {
+    slice
+}
+
 fn r_return_rust_string() -> String {
     "2020".to_owned()
 }
@@ -387,6 +487,10 @@
     Vec::new()
 }
 
+fn r_return_rust_vec_extern_struct() -> Vec<ffi::Job> {
+    Vec::new()
+}
+
 fn r_return_ref_rust_vec(shared: &ffi::Shared) -> &Vec<u8> {
     let _ = shared;
     unimplemented!()
@@ -411,7 +515,7 @@
     } else if n <= 2020 {
         ffi::Enum::BVal
     } else {
-        ffi::Enum::CVal
+        ffi::Enum::LastVal
     }
 }
 
@@ -431,6 +535,10 @@
     let _ = c;
 }
 
+fn r_take_shared_ptr(c: SharedPtr<ffi::C>) {
+    let _ = c;
+}
+
 fn r_take_ref_r(r: &R) {
     let _ = r;
 }
@@ -447,8 +555,9 @@
     assert_eq!(s, "2020");
 }
 
-fn r_take_sliceu8(s: &[u8]) {
+fn r_take_slice_char(s: &[c_char]) {
     assert_eq!(s.len(), 5);
+    let s = cast::c_char_to_unsigned(s);
     assert_eq!(std::str::from_utf8(s).unwrap(), "2020\0");
 }
 
@@ -495,15 +604,19 @@
 }
 
 fn r_try_return_box() -> Result<Box<R>, Error> {
-    Ok(Box::new(2020))
+    Ok(Box::new(R(2020)))
 }
 
 fn r_fail_return_primitive() -> Result<usize, Error> {
     Err(Error)
 }
 
-fn r_return_r2(n: usize) -> Box<R2> {
-    Box::new(R2(n))
+fn r_try_return_sliceu8(slice: &[u8]) -> Result<&[u8], Error> {
+    Ok(slice)
+}
+
+fn r_try_return_mutsliceu8(slice: &mut [u8]) -> Result<&mut [u8], Error> {
+    Ok(slice)
 }
 
 fn r_aliased_function(x: i32) -> String {
diff --git a/tests/ffi/module.rs b/tests/ffi/module.rs
index 899d45b..21a8620 100644
--- a/tests/ffi/module.rs
+++ b/tests/ffi/module.rs
@@ -1,13 +1,78 @@
-// Rustfmt mangles the extern type alias.
-// https://github.com/rust-lang/rustfmt/issues/4159
-#[rustfmt::skip]
 #[cxx::bridge(namespace = "tests")]
 pub mod ffi {
-    extern "C" {
+    struct Job {
+        raw: u32,
+    }
+
+    unsafe extern "C++" {
         include!("tests/ffi/tests.h");
 
         type C = crate::ffi::C;
 
         fn c_take_unique_ptr(c: UniquePtr<C>);
     }
+
+    impl Vec<Job> {}
+}
+
+#[cxx::bridge(namespace = "tests")]
+pub mod ffi2 {
+    unsafe extern "C++" {
+        include!("tests/ffi/tests.h");
+
+        type D = crate::other::D;
+        type E = crate::other::E;
+        #[namespace = "F"]
+        type F = crate::other::f::F;
+        #[namespace = "G"]
+        type G = crate::other::G;
+
+        #[namespace = "H"]
+        type H;
+
+        fn c_take_trivial_ptr(d: UniquePtr<D>);
+        fn c_take_trivial_ref(d: &D);
+        fn c_take_trivial_mut_ref(d: &mut D);
+        fn c_take_trivial_pin_ref(d: Pin<&D>);
+        fn c_take_trivial_pin_mut_ref(d: Pin<&mut D>);
+        fn c_take_trivial_ref_method(self: &D);
+        fn c_take_trivial_mut_ref_method(self: &mut D);
+        fn c_take_trivial(d: D);
+        fn c_take_trivial_ns_ptr(g: UniquePtr<G>);
+        fn c_take_trivial_ns_ref(g: &G);
+        fn c_take_trivial_ns(g: G);
+        fn c_take_opaque_ptr(e: UniquePtr<E>);
+        fn c_take_opaque_ref(e: &E);
+        fn c_take_opaque_ref_method(self: &E);
+        fn c_take_opaque_mut_ref_method(self: Pin<&mut E>);
+        fn c_take_opaque_ns_ptr(e: UniquePtr<F>);
+        fn c_take_opaque_ns_ref(e: &F);
+        fn c_return_trivial_ptr() -> UniquePtr<D>;
+        fn c_return_trivial() -> D;
+        fn c_return_trivial_ns_ptr() -> UniquePtr<G>;
+        fn c_return_trivial_ns() -> G;
+        fn c_return_opaque_ptr() -> UniquePtr<E>;
+        fn c_return_opaque_mut_pin(e: Pin<&mut E>) -> Pin<&mut E>;
+        fn c_return_ns_opaque_ptr() -> UniquePtr<F>;
+        fn c_return_ns_unique_ptr() -> UniquePtr<H>;
+        fn c_take_ref_ns_c(h: &H);
+
+        #[namespace = "other"]
+        fn ns_c_take_trivial(d: D);
+        #[namespace = "other"]
+        fn ns_c_return_trivial() -> D;
+
+        #[namespace = "I"]
+        type I;
+
+        fn get(self: &I) -> u32;
+
+        #[namespace = "I"]
+        fn ns_c_return_unique_ptr_ns() -> UniquePtr<I>;
+    }
+
+    impl UniquePtr<D> {}
+    impl UniquePtr<E> {}
+    impl UniquePtr<F> {}
+    impl UniquePtr<G> {}
 }
diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc
index 2305766..ba12ed4 100644
--- a/tests/ffi/tests.cc
+++ b/tests/ffi/tests.cc
@@ -1,11 +1,13 @@
 #include "tests/ffi/tests.h"
 #include "tests/ffi/lib.rs.h"
+#include <cstdlib>
 #include <cstring>
 #include <iterator>
 #include <memory>
 #include <numeric>
 #include <stdexcept>
 #include <string>
+#include <tuple>
 
 extern "C" void cxx_test_suite_set_correct() noexcept;
 extern "C" tests::R *cxx_test_suite_get_box() noexcept;
@@ -21,22 +23,31 @@
 
 size_t C::get2() const { return this->n; }
 
+const size_t &C::getRef() const { return this->n; }
+
+size_t &C::getMut() { return this->n; }
+
 size_t C::set(size_t n) {
   this->n = n;
   return this->n;
 }
 
-size_t C::set2(size_t n) {
-  this->n = n;
-  return this->n;
-}
-
-size_t C::set_succeed(size_t n) { return this->set2(n); }
+size_t C::set_succeed(size_t n) { return this->set(n); }
 
 size_t C::get_fail() { throw std::runtime_error("unimplemented"); }
 
 size_t Shared::c_method_on_shared() const noexcept { return 2021; }
 
+const size_t &Shared::c_method_ref_on_shared() const noexcept {
+  return this->z;
+}
+
+size_t &Shared::c_method_mut_on_shared() noexcept { return this->z; }
+
+void Array::c_set_array(int32_t val) noexcept {
+  this->a = {val, val, val, val};
+}
+
 const std::vector<uint8_t> &C::get_v() const { return this->v; }
 
 std::vector<uint8_t> &C::get_v() { return this->v; }
@@ -50,6 +61,14 @@
 ::A::B::ABShared c_return_nested_ns_shared() { return ::A::B::ABShared{2020}; }
 
 rust::Box<R> c_return_box() {
+  Shared shared{0};
+  rust::Box<Shared> box{shared}; // explicit constructor from const T&
+  rust::Box<Shared> other{std::move(shared)}; // explicit constructor from T&&
+  box = std::move(other);                     // move assignment
+  rust::Box<Shared> box2(*box);               // copy from another Box
+  rust::Box<Shared> other2(std::move(other)); // move constructor
+  rust::Box<Shared>::in_place(shared.z);      // placement static factory
+  rust::Box<Shared>::in_place<size_t>(0);
   return rust::Box<R>::from_raw(cxx_test_suite_get_box());
 }
 
@@ -57,6 +76,10 @@
   return std::unique_ptr<C>(new C{2020});
 }
 
+std::shared_ptr<C> c_return_shared_ptr() {
+  return std::shared_ptr<C>(new C{2020});
+}
+
 std::unique_ptr<::H::H> c_return_ns_unique_ptr() {
   return std::unique_ptr<::H::H>(new ::H::H{"hello"});
 }
@@ -76,10 +99,13 @@
   return "2020";
 }
 
-rust::Slice<uint8_t> c_return_sliceu8(const Shared &shared) {
+rust::Slice<const char> c_return_slice_char(const Shared &shared) {
   (void)shared;
-  return rust::Slice<uint8_t>(reinterpret_cast<const uint8_t *>(SLICE_DATA),
-                              sizeof(SLICE_DATA));
+  return rust::Slice<const char>(SLICE_DATA, sizeof(SLICE_DATA));
+}
+
+rust::Slice<uint8_t> c_return_mutsliceu8(rust::Slice<uint8_t> slice) {
+  return slice;
 }
 
 rust::String c_return_rust_string() { return "2020"; }
@@ -129,7 +155,8 @@
 std::vector<uint8_t> &c_return_mut_vector(C &c) { return c.get_v(); }
 
 rust::Vec<uint8_t> c_return_rust_vec() {
-  throw std::runtime_error("unimplemented");
+  rust::Vec<uint8_t> vec{2, 0, 2, 0};
+  return vec;
 }
 
 const rust::Vec<uint8_t> &c_return_ref_rust_vec(const C &c) {
@@ -143,7 +170,7 @@
 }
 
 rust::Vec<rust::String> c_return_rust_vec_string() {
-  throw std::runtime_error("unimplemented");
+  return {"2", "0", "2", "0"};
 }
 
 size_t c_return_identity(size_t n) { return n; }
@@ -180,6 +207,20 @@
   }
 }
 
+const C *c_return_const_ptr(size_t c) { return new C(c); }
+
+C *c_return_mut_ptr(size_t c) { return new C(c); }
+
+Borrow::Borrow(const std::string &s) : s(s) {}
+
+void Borrow::const_member() const {}
+
+void Borrow::nonconst_member() {}
+
+std::unique_ptr<Borrow> c_return_borrow(const std::string &s) {
+  return std::unique_ptr<Borrow>(new Borrow(s));
+}
+
 void c_take_primitive(size_t n) {
   if (n == 2020) {
     cxx_test_suite_set_correct();
@@ -240,9 +281,44 @@
   }
 }
 
-void c_take_sliceu8(rust::Slice<uint8_t> s) {
-  if (std::string(reinterpret_cast<const char *>(s.data()), s.size()) ==
-      "2020") {
+void c_take_slice_char(rust::Slice<const char> s) {
+  if (std::string(s.data(), s.size()) == "2020") {
+    cxx_test_suite_set_correct();
+  }
+}
+
+void c_take_slice_shared(rust::Slice<const Shared> s) {
+  if (s.size() == 2 && s.data()->z == 2020 && s[1].z == 2021 &&
+      s.at(1).z == 2021 && s.front().z == 2020 && s.back().z == 2021) {
+    cxx_test_suite_set_correct();
+  }
+}
+
+void c_take_slice_shared_sort(rust::Slice<Shared> s) {
+  // Exercise requirements of RandomAccessIterator.
+  // https://en.cppreference.com/w/cpp/named_req/RandomAccessIterator
+  std::sort(s.begin(), s.end());
+  if (s[0].z == 0 && s[1].z == 2 && s[2].z == 4 && s[3].z == 7) {
+    cxx_test_suite_set_correct();
+  }
+}
+
+void c_take_slice_r(rust::Slice<const R> s) {
+  if (s.size() == 3 && s[0].get() == 2020 && s[1].get() == 2050) {
+    cxx_test_suite_set_correct();
+  }
+}
+
+bool operator<(const R &a, const R &b) noexcept { return a.get() < b.get(); }
+
+void c_take_slice_r_sort(rust::Slice<R> s) {
+  std::qsort(s.data(), s.size(), rust::size_of<decltype(s)::value_type>(),
+             [](const void *fst, const void *snd) {
+               auto &a = *static_cast<const R *>(fst);
+               auto &b = *static_cast<const R *>(snd);
+               return a < b ? -1 : b < a ? 1 : 0;
+             });
+  if (s[0].get() == 2020 && s[1].get() == 2021 && s[2].get() == 2050) {
     cxx_test_suite_set_correct();
   }
 }
@@ -340,11 +416,23 @@
 void c_take_rust_vec_shared_forward_iterator(rust::Vec<Shared> v) {
   // Exercise requirements of ForwardIterator
   // https://en.cppreference.com/w/cpp/named_req/ForwardIterator
-  uint32_t sum = 0;
+  uint32_t sum = 0, csum = 0;
   for (auto it = v.begin(), it_end = v.end(); it != it_end; it++) {
     sum += it->z;
   }
-  if (sum == 2021) {
+  for (auto it = v.cbegin(), it_end = v.cend(); it != it_end; it++) {
+    csum += it->z;
+  }
+  if (sum == 2021 && csum == 2021) {
+    cxx_test_suite_set_correct();
+  }
+}
+
+void c_take_rust_vec_shared_sort(rust::Vec<Shared> v) {
+  // Exercise requirements of RandomAccessIterator.
+  // https://en.cppreference.com/w/cpp/named_req/RandomAccessIterator
+  std::sort(v.begin(), v.end());
+  if (v[0].z == 0 && v[1].z == 2 && v[2].z == 4 && v[3].z == 7) {
     cxx_test_suite_set_correct();
   }
 }
@@ -424,6 +512,14 @@
   }
 }
 
+size_t c_take_const_ptr(const C *c) { return c->get(); }
+
+size_t c_take_mut_ptr(C *c) {
+  size_t result = c->get();
+  delete c;
+  return result;
+}
+
 void c_try_return_void() {}
 
 size_t c_try_return_primitive() { return 2020; }
@@ -436,7 +532,13 @@
 
 rust::Str c_try_return_str(rust::Str s) { return s; }
 
-rust::Slice<uint8_t> c_try_return_sliceu8(rust::Slice<uint8_t> s) { return s; }
+rust::Slice<const uint8_t> c_try_return_sliceu8(rust::Slice<const uint8_t> s) {
+  return s;
+}
+
+rust::Slice<uint8_t> c_try_return_mutsliceu8(rust::Slice<uint8_t> s) {
+  return s;
+}
 
 rust::String c_try_return_rust_string() { return c_return_rust_string(); }
 
@@ -457,10 +559,19 @@
   throw std::runtime_error("unimplemented");
 }
 
+size_t c_get_use_count(const std::weak_ptr<C> &weak) noexcept {
+  return weak.use_count();
+}
+
 extern "C" C *cxx_test_suite_get_unique_ptr() noexcept {
   return std::unique_ptr<C>(new C{2020}).release();
 }
 
+extern "C" void
+cxx_test_suite_get_shared_ptr(std::shared_ptr<C> *repr) noexcept {
+  new (repr) std::shared_ptr<C>(new C{2020});
+}
+
 extern "C" std::string *cxx_test_suite_get_unique_ptr_string() noexcept {
   return std::unique_ptr<std::string>(new std::string("2020")).release();
 }
@@ -493,6 +604,24 @@
   }
 }
 
+void c_take_trivial_mut_ref(D &d) { (void)d; }
+
+void c_take_trivial_pin_ref(const D &d) { (void)d; }
+
+void c_take_trivial_pin_mut_ref(D &d) { (void)d; }
+
+void D::c_take_trivial_ref_method() const {
+  if (d == 30) {
+    cxx_test_suite_set_correct();
+  }
+}
+
+void D::c_take_trivial_mut_ref_method() {
+  if (d == 30) {
+    cxx_test_suite_set_correct();
+  }
+}
+
 void c_take_trivial(D d) {
   if (d.d == 30) {
     cxx_test_suite_set_correct();
@@ -535,6 +664,18 @@
   }
 }
 
+void E::c_take_opaque_ref_method() const {
+  if (e == 40 && e_str == "hello") {
+    cxx_test_suite_set_correct();
+  }
+}
+
+void E::c_take_opaque_mut_ref_method() {
+  if (e == 40 && e_str == "hello") {
+    cxx_test_suite_set_correct();
+  }
+}
+
 void c_take_opaque_ns_ref(const ::F::F &f) {
   if (f.f == 40 && f.f_str == "hello") {
     cxx_test_suite_set_correct();
@@ -572,6 +713,8 @@
   return e;
 }
 
+E &c_return_opaque_mut_pin(E &e) { return e; }
+
 std::unique_ptr<::F::F> c_return_ns_opaque_ptr() {
   auto f = std::unique_ptr<::F::F>(new ::F::F());
   f->f = 40;
@@ -589,10 +732,16 @@
     }                                                                          \
   } while (false)
 
+  ASSERT(rust::size_of<R>() == sizeof(size_t));
+  ASSERT(rust::align_of<R>() == alignof(size_t));
+  ASSERT(rust::size_of<size_t>() == sizeof(size_t));
+  ASSERT(rust::align_of<size_t>() == alignof(size_t));
+
   ASSERT(r_return_primitive() == 2020);
   ASSERT(r_return_shared().z == 2020);
   ASSERT(cxx_test_suite_r_is_correct(&*r_return_box()));
   ASSERT(r_return_unique_ptr()->get() == 2020);
+  ASSERT(r_return_shared_ptr()->get() == 2020);
   ASSERT(r_return_ref(Shared{2020}) == 2020);
   ASSERT(std::string(r_return_str(Shared{2020})) == "2020");
   ASSERT(std::string(r_return_rust_string()) == "2020");
@@ -606,10 +755,10 @@
   r_take_primitive(2020);
   r_take_shared(Shared{2020});
   r_take_unique_ptr(std::unique_ptr<C>(new C{2020}));
+  r_take_shared_ptr(std::shared_ptr<C>(new C{2020}));
   r_take_ref_c(C{2020});
   r_take_str(rust::Str("2020"));
-  r_take_sliceu8(rust::Slice<uint8_t>(
-      reinterpret_cast<const uint8_t *>(SLICE_DATA), sizeof(SLICE_DATA)));
+  r_take_slice_char(rust::Slice<const char>(SLICE_DATA, sizeof(SLICE_DATA)));
   r_take_rust_string(rust::String("2020"));
   r_take_unique_ptr_string(
       std::unique_ptr<std::string>(new std::string("2020")));
@@ -628,16 +777,74 @@
     ASSERT(std::strcmp(e.what(), "rust error") == 0);
   }
 
-  auto r2 = r_return_r2(2020);
-  ASSERT(r2->get() == 2020);
-  ASSERT(r2->set(2021) == 2021);
+  auto r = r_return_box();
+  ASSERT(r->get() == 2020);
+  ASSERT(r->set(2021) == 2021);
+  ASSERT(r->get() == 2021);
+
+  using std::swap;
+  auto r2 = r_return_box();
+  swap(r, r2);
+  ASSERT(r->get() == 2020);
   ASSERT(r2->get() == 2021);
-  ASSERT(r2->set(2020) == 2020);
-  ASSERT(r2->get() == 2020);
+
   ASSERT(std::string(Shared{0}.r_method_on_shared()) == "2020");
 
   ASSERT(std::string(rAliasedFunction(2020)) == "2020");
 
+  ASSERT(Shared{1} == Shared{1});
+  ASSERT(Shared{1} != Shared{2});
+
+  rust::String first = "first", second = "second", sec = "sec";
+  bool (rust::String::*cmp)(const rust::String &) const;
+  bool first_first, first_second, sec_second, second_sec;
+  for (auto test : {
+           std::tuple<decltype(cmp), bool, bool, bool, bool>{
+               &rust::String::operator==, true, false, false, false},
+           {&rust::String::operator!=, false, true, true, true},
+           {&rust::String::operator<, false, true, true, false},
+           {&rust::String::operator<=, true, true, true, false},
+           {&rust::String::operator>, false, false, false, true},
+           {&rust::String::operator>=, true, false, false, true},
+       }) {
+    std::tie(cmp, first_first, first_second, sec_second, second_sec) = test;
+    ASSERT((first.*cmp)(first) == first_first);
+    ASSERT((first.*cmp)(second) == first_second);
+    ASSERT((sec.*cmp)(second) == sec_second);
+    ASSERT((second.*cmp)(sec) == second_sec);
+  }
+
+  rust::String cstring = "test";
+  ASSERT(cstring.length() == 4);
+  ASSERT(strncmp(cstring.data(), "test", 4) == 0);
+  ASSERT(strncmp(cstring.c_str(), "test", 5) == 0);
+  ASSERT(cstring.length() == 4);
+
+  rust::String other_cstring = "foo";
+  swap(cstring, other_cstring);
+  ASSERT(cstring == "foo");
+  ASSERT(other_cstring == "test");
+
+  rust::Str cstr = "test";
+  rust::Str other_cstr = "foo";
+  swap(cstr, other_cstr);
+  ASSERT(cstr == "foo");
+  ASSERT(other_cstr == "test");
+
+  rust::Vec<int> vec1{1, 2};
+  rust::Vec<int> vec2{3, 4};
+  swap(vec1, vec2);
+  ASSERT(vec1[0] == 3 && vec1[1] == 4);
+  ASSERT(vec2[0] == 1 && vec2[1] == 2);
+
+  // Test Vec<usize> and Vec<isize>. These are weird because on Linux and
+  // Windows size_t is exactly the same C++ type as one of the sized integer
+  // types (typically uint64_t, both of which are defined as unsigned long),
+  // while on macOS it is a distinct type.
+  // https://github.com/dtolnay/cxx/issues/705
+  (void)rust::Vec<size_t>();
+  (void)rust::Vec<rust::isize>();
+
   cxx_test_suite_set_correct();
   return nullptr;
 }
@@ -671,3 +878,25 @@
   return std::unique_ptr<I>(new I());
 }
 } // namespace I
+
+// Instantiate any remaining class member functions not already covered above.
+// This is an easy way to at least typecheck anything missed by unit tests.
+// https://en.cppreference.com/w/cpp/language/class_template#Explicit_instantiation
+// > When an explicit instantiation names a class template specialization, it
+// > serves as an explicit instantiation of the same kind (declaration or
+// > definition) of each of its non-inherited non-template members that has not
+// > been previously explicitly specialized in the translation unit.
+#if defined(CXX_TEST_INSTANTIATIONS)
+template class rust::Box<tests::Shared>;
+template class rust::Slice<const char>;
+template class rust::Slice<const uint8_t>;
+template class rust::Slice<uint8_t>;
+template class rust::Slice<const tests::Shared>;
+template class rust::Slice<tests::Shared>;
+template class rust::Slice<const tests::R>;
+template class rust::Slice<tests::R>;
+template class rust::Vec<uint8_t>;
+template class rust::Vec<rust::String>;
+template class rust::Vec<tests::Shared>;
+template class rust::Fn<size_t(rust::String)>;
+#endif
diff --git a/tests/ffi/tests.h b/tests/ffi/tests.h
index 2217c36..74acbf4 100644
--- a/tests/ffi/tests.h
+++ b/tests/ffi/tests.h
@@ -45,7 +45,8 @@
   size_t get() const;
   size_t set(size_t n);
   size_t get2() const;
-  size_t set2(size_t n);
+  const size_t &getRef() const;
+  size_t &getMut();
   size_t set_succeed(size_t n);
   size_t get_fail();
   const std::vector<uint8_t> &get_v() const;
@@ -60,16 +61,27 @@
 
 struct D {
   uint64_t d;
+  void c_take_trivial_ref_method() const;
+  void c_take_trivial_mut_ref_method();
 };
 
 struct E {
   uint64_t e;
   std::string e_str;
+  void c_take_opaque_ref_method() const;
+  void c_take_opaque_mut_ref_method();
 };
 
 enum COwnedEnum {
-  CVal1,
-  CVal2,
+  CVAL1,
+  CVAL2,
+};
+
+struct Borrow {
+  Borrow(const std::string &s);
+  void const_member() const;
+  void nonconst_member();
+  const std::string &s;
 };
 
 size_t c_return_primitive();
@@ -78,13 +90,15 @@
 ::A::B::ABShared c_return_nested_ns_shared();
 rust::Box<R> c_return_box();
 std::unique_ptr<C> c_return_unique_ptr();
+std::shared_ptr<C> c_return_shared_ptr();
 std::unique_ptr<::H::H> c_return_ns_unique_ptr();
 const size_t &c_return_ref(const Shared &shared);
 const size_t &c_return_ns_ref(const ::A::AShared &shared);
 const size_t &c_return_nested_ns_ref(const ::A::B::ABShared &shared);
 size_t &c_return_mut(Shared &shared);
 rust::Str c_return_str(const Shared &shared);
-rust::Slice<uint8_t> c_return_sliceu8(const Shared &shared);
+rust::Slice<const char> c_return_slice_char(const Shared &shared);
+rust::Slice<uint8_t> c_return_mutsliceu8(rust::Slice<uint8_t> slice);
 rust::String c_return_rust_string();
 std::unique_ptr<std::string> c_return_unique_ptr_string();
 std::unique_ptr<std::vector<uint8_t>> c_return_unique_ptr_vector_u8();
@@ -103,6 +117,9 @@
 Enum c_return_enum(uint16_t n);
 ::A::AEnum c_return_ns_enum(uint16_t n);
 ::A::B::ABEnum c_return_nested_ns_enum(uint16_t n);
+std::unique_ptr<Borrow> c_return_borrow(const std::string &s);
+const C *c_return_const_ptr(size_t n);
+C *c_return_mut_ptr(size_t n);
 
 void c_take_primitive(size_t n);
 void c_take_shared(Shared shared);
@@ -114,7 +131,11 @@
 void c_take_ref_c(const C &c);
 void c_take_ref_ns_c(const ::H::H &h);
 void c_take_str(rust::Str s);
-void c_take_sliceu8(rust::Slice<uint8_t> s);
+void c_take_slice_char(rust::Slice<const char> s);
+void c_take_slice_shared(rust::Slice<const Shared> s);
+void c_take_slice_shared_sort(rust::Slice<Shared> s);
+void c_take_slice_r(rust::Slice<const R> s);
+void c_take_slice_r_sort(rust::Slice<R> s);
 void c_take_rust_string(rust::String s);
 void c_take_unique_ptr_string(std::unique_ptr<std::string> s);
 void c_take_unique_ptr_vector_u8(std::unique_ptr<std::vector<uint8_t>> v);
@@ -132,6 +153,7 @@
 void c_take_rust_vec_shared_index(rust::Vec<Shared> v);
 void c_take_rust_vec_shared_push(rust::Vec<Shared> v);
 void c_take_rust_vec_shared_forward_iterator(rust::Vec<Shared> v);
+void c_take_rust_vec_shared_sort(rust::Vec<Shared> v);
 void c_take_ref_rust_vec(const rust::Vec<uint8_t> &v);
 void c_take_ref_rust_vec_string(const rust::Vec<rust::String> &v);
 void c_take_ref_rust_vec_index(const rust::Vec<uint8_t> &v);
@@ -141,6 +163,8 @@
 void c_take_enum(Enum e);
 void c_take_ns_enum(::A::AEnum e);
 void c_take_nested_ns_enum(::A::B::ABEnum e);
+size_t c_take_const_ptr(const C *c);
+size_t c_take_mut_ptr(C *c);
 
 void c_try_return_void();
 size_t c_try_return_primitive();
@@ -148,15 +172,21 @@
 rust::Box<R> c_try_return_box();
 const rust::String &c_try_return_ref(const rust::String &);
 rust::Str c_try_return_str(rust::Str);
-rust::Slice<uint8_t> c_try_return_sliceu8(rust::Slice<uint8_t>);
+rust::Slice<const uint8_t> c_try_return_sliceu8(rust::Slice<const uint8_t>);
+rust::Slice<uint8_t> c_try_return_mutsliceu8(rust::Slice<uint8_t>);
 rust::String c_try_return_rust_string();
 std::unique_ptr<std::string> c_try_return_unique_ptr_string();
 rust::Vec<uint8_t> c_try_return_rust_vec();
 rust::Vec<rust::String> c_try_return_rust_vec_string();
 const rust::Vec<uint8_t> &c_try_return_ref_rust_vec(const C &c);
 
+size_t c_get_use_count(const std::weak_ptr<C> &weak) noexcept;
+
 void c_take_trivial_ptr(std::unique_ptr<D> d);
 void c_take_trivial_ref(const D &d);
+void c_take_trivial_mut_ref(D &d);
+void c_take_trivial_pin_ref(const D &d);
+void c_take_trivial_pin_mut_ref(D &d);
 void c_take_trivial(D d);
 
 void c_take_trivial_ns_ptr(std::unique_ptr<::G::G> g);
@@ -171,6 +201,7 @@
 std::unique_ptr<::G::G> c_return_trivial_ns_ptr();
 ::G::G c_return_trivial_ns();
 std::unique_ptr<E> c_return_opaque_ptr();
+E &c_return_opaque_mut_pin(E &e);
 std::unique_ptr<::F::F> c_return_ns_opaque_ptr();
 
 rust::String cOverloadedFunction(int32_t x);
diff --git a/tests/test.rs b/tests/test.rs
index 2bae4d8..32b8e83 100644
--- a/tests/test.rs
+++ b/tests/test.rs
@@ -1,7 +1,16 @@
-#![allow(clippy::assertions_on_constants, clippy::float_cmp, clippy::unit_cmp)]
+#![allow(
+    clippy::assertions_on_constants,
+    clippy::cast_possible_truncation,
+    clippy::cast_possible_wrap,
+    clippy::float_cmp,
+    clippy::needless_pass_by_value,
+    clippy::unit_cmp,
+    clippy::unseparated_literal_suffix
+)]
 
-use cxx_test_suite::extra::ffi2;
-use cxx_test_suite::ffi;
+use cxx::SharedPtr;
+use cxx_test_suite::module::ffi2;
+use cxx_test_suite::{cast, ffi, R};
 use std::cell::Cell;
 use std::ffi::CStr;
 
@@ -18,7 +27,7 @@
     ($run:expr) => {{
         CORRECT.with(|correct| correct.set(false));
         $run;
-        assert!(CORRECT.with(|correct| correct.get()), stringify!($run));
+        assert!(CORRECT.with(Cell::get), "{}", stringify!($run));
     }};
 }
 
@@ -30,14 +39,17 @@
 
     assert_eq!(2020, ffi::c_return_primitive());
     assert_eq!(2020, ffi::c_return_shared().z);
-    assert_eq!(2020, *ffi::c_return_box());
+    assert_eq!(2020, ffi::c_return_box().0);
     ffi::c_return_unique_ptr();
     ffi2::c_return_ns_unique_ptr();
     assert_eq!(2020, *ffi::c_return_ref(&shared));
     assert_eq!(2020, *ffi::c_return_ns_ref(&ns_shared));
     assert_eq!(2020, *ffi::c_return_nested_ns_ref(&nested_ns_shared));
     assert_eq!("2020", ffi::c_return_str(&shared));
-    assert_eq!(b"2020\0", ffi::c_return_sliceu8(&shared));
+    assert_eq!(
+        b"2020\0",
+        cast::c_char_to_unsigned(ffi::c_return_slice_char(&shared)),
+    );
     assert_eq!("2020", ffi::c_return_rust_string());
     assert_eq!("2020", ffi::c_return_unique_ptr_string().to_str().unwrap());
     assert_eq!(4, ffi::c_return_unique_ptr_vector_u8().len());
@@ -68,7 +80,7 @@
         _ => assert!(false),
     }
     match ffi::c_return_enum(2021) {
-        enm @ ffi::Enum::CVal => assert_eq!(2021, enm.repr),
+        enm @ ffi::Enum::LastVal => assert_eq!(2021, enm.repr),
         _ => assert!(false),
     }
     match ffi::c_return_ns_enum(0) {
@@ -89,7 +101,7 @@
         "logic error",
         ffi::c_fail_return_primitive().unwrap_err().what(),
     );
-    assert_eq!(2020, *ffi::c_try_return_box().unwrap());
+    assert_eq!(2020, ffi::c_try_return_box().unwrap().0);
     assert_eq!("2020", *ffi::c_try_return_ref(&"2020".to_owned()).unwrap());
     assert_eq!("2020", ffi::c_try_return_str("2020").unwrap());
     assert_eq!(b"2020", ffi::c_try_return_sliceu8(b"2020").unwrap());
@@ -107,12 +119,33 @@
     check!(ffi::c_take_ns_shared(ffi::AShared { z: 2020 }));
     check!(ffi::ns_c_take_ns_shared(ffi::AShared { z: 2020 }));
     check!(ffi::c_take_nested_ns_shared(ffi::ABShared { z: 2020 }));
-    check!(ffi::c_take_box(Box::new(2020)));
+    check!(ffi::c_take_box(Box::new(R(2020))));
     check!(ffi::c_take_ref_c(&unique_ptr));
     check!(ffi2::c_take_ref_ns_c(&unique_ptr_ns));
     check!(cxx_test_suite::module::ffi::c_take_unique_ptr(unique_ptr));
     check!(ffi::c_take_str("2020"));
-    check!(ffi::c_take_sliceu8(b"2020"));
+    check!(ffi::c_take_slice_char(cast::unsigned_to_c_char(b"2020")));
+    check!(ffi::c_take_slice_shared(&[
+        ffi::Shared { z: 2020 },
+        ffi::Shared { z: 2021 },
+    ]));
+    let shared_sort_slice = &mut [
+        ffi::Shared { z: 2 },
+        ffi::Shared { z: 0 },
+        ffi::Shared { z: 7 },
+        ffi::Shared { z: 4 },
+    ];
+    check!(ffi::c_take_slice_shared_sort(shared_sort_slice));
+    assert_eq!(shared_sort_slice[0].z, 0);
+    assert_eq!(shared_sort_slice[1].z, 2);
+    assert_eq!(shared_sort_slice[2].z, 4);
+    assert_eq!(shared_sort_slice[3].z, 7);
+    let r_sort_slice = &mut [R(2020), R(2050), R(2021)];
+    check!(ffi::c_take_slice_r(r_sort_slice));
+    check!(ffi::c_take_slice_r_sort(r_sort_slice));
+    assert_eq!(r_sort_slice[0].0, 2020);
+    assert_eq!(r_sort_slice[1].0, 2021);
+    assert_eq!(r_sort_slice[2].0, 2050);
     check!(ffi::c_take_rust_string("2020".to_owned()));
     check!(ffi::c_take_unique_ptr_string(
         ffi::c_return_unique_ptr_string()
@@ -137,6 +170,13 @@
     check!(ffi::c_take_rust_vec_shared_forward_iterator(
         shared_test_vec,
     ));
+    let shared_sort_vec = vec![
+        ffi::Shared { z: 2 },
+        ffi::Shared { z: 0 },
+        ffi::Shared { z: 7 },
+        ffi::Shared { z: 4 },
+    ];
+    check!(ffi::c_take_rust_vec_shared_sort(shared_sort_vec));
     check!(ffi::c_take_ref_rust_vec(&test_vec));
     check!(ffi::c_take_ref_rust_vec_index(&test_vec));
     check!(ffi::c_take_ref_rust_vec_copy(&test_vec));
@@ -188,13 +228,35 @@
 
     let old_value = unique_ptr.get();
     assert_eq!(2020, old_value);
-    assert_eq!(2021, unique_ptr.set(2021));
+    assert_eq!(2021, unique_ptr.pin_mut().set(2021));
     assert_eq!(2021, unique_ptr.get());
-    assert_eq!(old_value, unique_ptr.set2(old_value));
-    assert_eq!(old_value, unique_ptr.get2());
-    assert_eq!(2022, unique_ptr.set_succeed(2022).unwrap());
-    assert!(unique_ptr.get_fail().is_err());
+    assert_eq!(2021, unique_ptr.get2());
+    assert_eq!(2021, *unique_ptr.getRef());
+    assert_eq!(2021, *unique_ptr.pin_mut().getMut());
+    assert_eq!(2022, unique_ptr.pin_mut().set_succeed(2022).unwrap());
+    assert!(unique_ptr.pin_mut().get_fail().is_err());
     assert_eq!(2021, ffi::Shared { z: 0 }.c_method_on_shared());
+    assert_eq!(2022, *ffi::Shared { z: 2022 }.c_method_ref_on_shared());
+    assert_eq!(2023, *ffi::Shared { z: 2023 }.c_method_mut_on_shared());
+
+    let val = 42;
+    let mut array = ffi::Array { a: [0, 0, 0, 0] };
+    array.c_set_array(val);
+    assert_eq!(array.a.len() as i32 * val, array.r_get_array_sum());
+}
+
+#[test]
+fn test_shared_ptr_weak_ptr() {
+    let shared_ptr = ffi::c_return_shared_ptr();
+    let weak_ptr = SharedPtr::downgrade(&shared_ptr);
+    assert_eq!(1, ffi::c_get_use_count(&weak_ptr));
+
+    assert!(!weak_ptr.upgrade().is_null());
+    assert_eq!(1, ffi::c_get_use_count(&weak_ptr));
+
+    drop(shared_ptr);
+    assert_eq!(0, ffi::c_get_use_count(&weak_ptr));
+    assert!(weak_ptr.upgrade().is_null());
 }
 
 #[test]
@@ -209,17 +271,24 @@
 fn test_enum_representations() {
     assert_eq!(0, ffi::Enum::AVal.repr);
     assert_eq!(2020, ffi::Enum::BVal.repr);
-    assert_eq!(2021, ffi::Enum::CVal.repr);
+    assert_eq!(2021, ffi::Enum::LastVal.repr);
+}
+
+#[test]
+fn test_debug() {
+    assert_eq!("Shared { z: 1 }", format!("{:?}", ffi::Shared { z: 1 }));
+    assert_eq!("BVal", format!("{:?}", ffi::Enum::BVal));
+    assert_eq!("Enum(9)", format!("{:?}", ffi::Enum { repr: 9 }));
 }
 
 #[no_mangle]
-extern "C" fn cxx_test_suite_get_box() -> *mut cxx_test_suite::R {
-    Box::into_raw(Box::new(2020usize))
+extern "C" fn cxx_test_suite_get_box() -> *mut R {
+    Box::into_raw(Box::new(R(2020usize)))
 }
 
 #[no_mangle]
-unsafe extern "C" fn cxx_test_suite_r_is_correct(r: *const cxx_test_suite::R) -> bool {
-    *r == 2020
+unsafe extern "C" fn cxx_test_suite_r_is_correct(r: *const R) -> bool {
+    (*r).0 == 2020
 }
 
 #[test]
@@ -233,10 +302,14 @@
 
 #[test]
 fn test_extern_trivial() {
-    let d = ffi2::c_return_trivial();
+    let mut d = ffi2::c_return_trivial();
     check!(ffi2::c_take_trivial_ref(&d));
+    check!(d.c_take_trivial_ref_method());
+    check!(d.c_take_trivial_mut_ref_method());
     check!(ffi2::c_take_trivial(d));
-    let d = ffi2::c_return_trivial_ptr();
+    let mut d = ffi2::c_return_trivial_ptr();
+    check!(d.c_take_trivial_ref_method());
+    check!(d.c_take_trivial_mut_ref_method());
     check!(ffi2::c_take_trivial_ptr(d));
     cxx::UniquePtr::new(ffi2::D { d: 42 });
     let d = ffi2::ns_c_return_trivial();
@@ -252,11 +325,29 @@
 
 #[test]
 fn test_extern_opaque() {
-    let e = ffi2::c_return_opaque_ptr();
+    let mut e = ffi2::c_return_opaque_ptr();
     check!(ffi2::c_take_opaque_ref(e.as_ref().unwrap()));
+    check!(e.c_take_opaque_ref_method());
+    check!(e.pin_mut().c_take_opaque_mut_ref_method());
     check!(ffi2::c_take_opaque_ptr(e));
 
     let f = ffi2::c_return_ns_opaque_ptr();
     check!(ffi2::c_take_opaque_ns_ref(f.as_ref().unwrap()));
     check!(ffi2::c_take_opaque_ns_ptr(f));
 }
+
+#[test]
+fn test_raw_ptr() {
+    let c = ffi::c_return_mut_ptr(2023);
+    let mut c_unique = unsafe { cxx::UniquePtr::from_raw(c) };
+    assert_eq!(2023, c_unique.pin_mut().set_succeed(2023).unwrap());
+    // c will be dropped as it's now in a UniquePtr
+
+    let c2 = ffi::c_return_mut_ptr(2024);
+    assert_eq!(2024, unsafe { ffi::c_take_const_ptr(c2) });
+    assert_eq!(2024, unsafe { ffi::c_take_mut_ptr(c2) }); // deletes c2
+
+    let c3 = ffi::c_return_const_ptr(2025);
+    assert_eq!(2025, unsafe { ffi::c_take_const_ptr(c3) });
+    assert_eq!(2025, unsafe { ffi::c_take_mut_ptr(c3 as *mut ffi::C) }); // deletes c3
+}
diff --git a/tests/ui/array_len_expr.rs b/tests/ui/array_len_expr.rs
new file mode 100644
index 0000000..73da57c
--- /dev/null
+++ b/tests/ui/array_len_expr.rs
@@ -0,0 +1,10 @@
+#[cxx::bridge]
+mod ffi {
+    struct Shared {
+        arraystr: [String; "13"],
+        arraysub: [String; 15 - 1],
+        arrayzero: [String; 0],
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/array_len_expr.stderr b/tests/ui/array_len_expr.stderr
new file mode 100644
index 0000000..4b45a0b
--- /dev/null
+++ b/tests/ui/array_len_expr.stderr
@@ -0,0 +1,17 @@
+error: array length must be an integer literal
+ --> $DIR/array_len_expr.rs:4:28
+  |
+4 |         arraystr: [String; "13"],
+  |                            ^^^^
+
+error: unsupported expression, array length must be an integer literal
+ --> $DIR/array_len_expr.rs:5:28
+  |
+5 |         arraysub: [String; 15 - 1],
+  |                            ^^^^^^
+
+error: array with zero size is not supported
+ --> $DIR/array_len_expr.rs:6:20
+  |
+6 |         arrayzero: [String; 0],
+  |                    ^^^^^^^^^^^
diff --git a/tests/ui/array_len_suffix.rs b/tests/ui/array_len_suffix.rs
new file mode 100644
index 0000000..1e9d514
--- /dev/null
+++ b/tests/ui/array_len_suffix.rs
@@ -0,0 +1,8 @@
+#[cxx::bridge]
+mod ffi {
+    unsafe extern "C++" {
+        fn array() -> [String; 12u16];
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/array_len_suffix.stderr b/tests/ui/array_len_suffix.stderr
new file mode 100644
index 0000000..b72fc02
--- /dev/null
+++ b/tests/ui/array_len_suffix.stderr
@@ -0,0 +1,10 @@
+error[E0308]: mismatched types
+ --> $DIR/array_len_suffix.rs:4:32
+  |
+4 |         fn array() -> [String; 12u16];
+  |                                ^^^^^ expected `usize`, found `u16`
+  |
+help: change the type of the numeric literal from `u16` to `usize`
+  |
+4 |         fn array() -> [String; 12usize];
+  |                                ^^^^^^^
diff --git a/tests/ui/async_fn.rs b/tests/ui/async_fn.rs
new file mode 100644
index 0000000..69f2642
--- /dev/null
+++ b/tests/ui/async_fn.rs
@@ -0,0 +1,10 @@
+#[cxx::bridge]
+mod ffi {
+    extern "Rust" {
+        async fn f();
+    }
+}
+
+async fn f() {}
+
+fn main() {}
diff --git a/tests/ui/async_fn.stderr b/tests/ui/async_fn.stderr
new file mode 100644
index 0000000..dedec7b
--- /dev/null
+++ b/tests/ui/async_fn.stderr
@@ -0,0 +1,5 @@
+error: async function is not directly supported yet, but see https://cxx.rs/async.html for a working approach
+ --> $DIR/async_fn.rs:4:9
+  |
+4 |         async fn f();
+  |         ^^^^^^^^^^^^^
diff --git a/tests/ui/by_value_not_supported.rs b/tests/ui/by_value_not_supported.rs
index 3ff950a..62bf8d4 100644
--- a/tests/ui/by_value_not_supported.rs
+++ b/tests/ui/by_value_not_supported.rs
@@ -6,7 +6,7 @@
         s: CxxString,
     }
 
-    extern "C" {
+    extern "C++" {
         type C;
     }
 
diff --git a/tests/ui/by_value_not_supported.stderr b/tests/ui/by_value_not_supported.stderr
index 0a56dd4..7288c93 100644
--- a/tests/ui/by_value_not_supported.stderr
+++ b/tests/ui/by_value_not_supported.stderr
@@ -16,7 +16,7 @@
 6 |         s: CxxString,
   |         ^^^^^^^^^^^^
 
-error: needs a cxx::ExternType impl in order to be used as a field of `S`
+error: needs a cxx::ExternType impl in order to be used as a field of `S`, argument of `f` or return value of `f`
   --> $DIR/by_value_not_supported.rs:10:9
    |
 10 |         type C;
diff --git a/tests/ui/const_fn.rs b/tests/ui/const_fn.rs
new file mode 100644
index 0000000..1ad4894
--- /dev/null
+++ b/tests/ui/const_fn.rs
@@ -0,0 +1,10 @@
+#[cxx::bridge]
+mod ffi {
+    extern "Rust" {
+        const fn f();
+    }
+}
+
+const fn f() {}
+
+fn main() {}
diff --git a/tests/ui/const_fn.stderr b/tests/ui/const_fn.stderr
new file mode 100644
index 0000000..a62ca83
--- /dev/null
+++ b/tests/ui/const_fn.stderr
@@ -0,0 +1,5 @@
+error: const extern function is not supported
+ --> $DIR/const_fn.rs:4:9
+  |
+4 |         const fn f();
+  |         ^^^^^^^^^^^^^
diff --git a/tests/ui/deny_missing_docs.rs b/tests/ui/deny_missing_docs.rs
new file mode 100644
index 0000000..037894b
--- /dev/null
+++ b/tests/ui/deny_missing_docs.rs
@@ -0,0 +1,94 @@
+// TODO: More work is needed so that the missing_docs lints produced by rustc
+// are properly positioned inside of the bridge.
+
+//! ...
+
+#![deny(missing_docs)]
+
+/// ...
+#[cxx::bridge]
+pub mod ffi {
+    pub struct UndocumentedStruct {
+        pub undocumented_field: u8,
+    }
+
+    /// ...
+    pub struct DocumentedStruct {
+        /// ...
+        pub documented_field: u8,
+    }
+
+    pub enum UndocumentedEnum {
+        UndocumentedVariant = 0,
+    }
+
+    /// ...
+    pub enum DocumentedEnum {
+        /// ...
+        DocumentedVariant = 0,
+    }
+
+    extern "Rust" {
+        pub type UndocumentedRustType;
+
+        /// ...
+        pub type DocumentedRustType;
+
+        pub fn undocumented_rust_fn() -> u8;
+
+        /// ...
+        pub fn documented_rust_fn() -> u8;
+    }
+
+    unsafe extern "C++" {
+        pub type UndocumentedForeignType;
+
+        /// ...
+        pub type DocumentedForeignType;
+
+        pub type UndocumentedTypeAlias = crate::bindgen::UndocumentedTypeAlias;
+
+        /// ...
+        pub type DocumentedTypeAlias = crate::bindgen::DocumentedTypeAlias;
+
+        pub fn undocumented_foreign_fn() -> u8;
+
+        /// ...
+        pub fn documented_foreign_fn() -> u8;
+    }
+
+    #[allow(missing_docs)]
+    pub struct SuppressUndocumentedStruct {
+        pub undocumented_field: u8,
+    }
+}
+
+struct UndocumentedRustType;
+struct DocumentedRustType;
+
+mod bindgen {
+    use cxx::{type_id, ExternType};
+
+    pub struct UndocumentedTypeAlias;
+    pub struct DocumentedTypeAlias;
+
+    unsafe impl ExternType for UndocumentedTypeAlias {
+        type Id = type_id!("UndocumentedTypeAlias");
+        type Kind = cxx::kind::Opaque;
+    }
+
+    unsafe impl ExternType for DocumentedTypeAlias {
+        type Id = type_id!("DocumentedTypeAlias");
+        type Kind = cxx::kind::Opaque;
+    }
+}
+
+fn undocumented_rust_fn() -> u8 {
+    0
+}
+
+fn documented_rust_fn() -> u8 {
+    0
+}
+
+fn main() {}
diff --git a/tests/ui/deny_missing_docs.stderr b/tests/ui/deny_missing_docs.stderr
new file mode 100644
index 0000000..e7aadfb
--- /dev/null
+++ b/tests/ui/deny_missing_docs.stderr
@@ -0,0 +1,47 @@
+error: missing documentation for a struct
+  --> $DIR/deny_missing_docs.rs:11:5
+   |
+11 |     pub struct UndocumentedStruct {
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+note: the lint level is defined here
+  --> $DIR/deny_missing_docs.rs:6:9
+   |
+6  | #![deny(missing_docs)]
+   |         ^^^^^^^^^^^^
+
+error: missing documentation for a struct field
+  --> $DIR/deny_missing_docs.rs:12:9
+   |
+12 |         pub undocumented_field: u8,
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: missing documentation for a struct
+  --> $DIR/deny_missing_docs.rs:21:5
+   |
+21 |     pub enum UndocumentedEnum {
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: missing documentation for an associated constant
+  --> $DIR/deny_missing_docs.rs:22:9
+   |
+22 |         UndocumentedVariant = 0,
+   |         ^^^^^^^^^^^^^^^^^^^
+
+error: missing documentation for a struct
+  --> $DIR/deny_missing_docs.rs:44:9
+   |
+44 |         pub type UndocumentedForeignType;
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: missing documentation for a type alias
+  --> $DIR/deny_missing_docs.rs:49:9
+   |
+49 |         pub type UndocumentedTypeAlias = crate::bindgen::UndocumentedTypeAlias;
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: missing documentation for a function
+  --> $DIR/deny_missing_docs.rs:54:9
+   |
+54 |         pub fn undocumented_foreign_fn() -> u8;
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/tests/ui/derive_duplicate.rs b/tests/ui/derive_duplicate.rs
new file mode 100644
index 0000000..3061f76
--- /dev/null
+++ b/tests/ui/derive_duplicate.rs
@@ -0,0 +1,9 @@
+#[cxx::bridge]
+mod ffi {
+    #[derive(Clone, Clone)]
+    struct Struct {
+        x: usize,
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/derive_duplicate.stderr b/tests/ui/derive_duplicate.stderr
new file mode 100644
index 0000000..ce27163
--- /dev/null
+++ b/tests/ui/derive_duplicate.stderr
@@ -0,0 +1,7 @@
+error[E0119]: conflicting implementations of trait `std::clone::Clone` for type `ffi::Struct`
+ --> $DIR/derive_duplicate.rs:3:21
+  |
+3 |     #[derive(Clone, Clone)]
+  |              -----  ^^^^^ conflicting implementation for `ffi::Struct`
+  |              |
+  |              first implementation here
diff --git a/tests/ui/derive_nonclone.rs b/tests/ui/derive_nonclone.rs
new file mode 100644
index 0000000..5811028
--- /dev/null
+++ b/tests/ui/derive_nonclone.rs
@@ -0,0 +1,13 @@
+#[cxx::bridge]
+mod ffi {
+    #[derive(Clone)]
+    struct TryClone {
+        other: Other,
+    }
+
+    struct Other {
+        x: usize,
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/derive_nonclone.stderr b/tests/ui/derive_nonclone.stderr
new file mode 100644
index 0000000..04eae4f
--- /dev/null
+++ b/tests/ui/derive_nonclone.stderr
@@ -0,0 +1,7 @@
+error[E0277]: the trait bound `ffi::Other: Clone` is not satisfied
+ --> $DIR/derive_nonclone.rs:5:9
+  |
+5 |         other: Other,
+  |         ^^^^^^^^^^^^ the trait `Clone` is not implemented for `ffi::Other`
+  |
+  = note: required by `clone`
diff --git a/tests/ui/derive_noncopy.rs b/tests/ui/derive_noncopy.rs
new file mode 100644
index 0000000..ae705fd
--- /dev/null
+++ b/tests/ui/derive_noncopy.rs
@@ -0,0 +1,13 @@
+#[cxx::bridge]
+mod ffi {
+    #[derive(Copy)]
+    struct TryCopy {
+        other: Other,
+    }
+
+    struct Other {
+        x: usize,
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/derive_noncopy.stderr b/tests/ui/derive_noncopy.stderr
new file mode 100644
index 0000000..d641028
--- /dev/null
+++ b/tests/ui/derive_noncopy.stderr
@@ -0,0 +1,8 @@
+error[E0204]: the trait `Copy` may not be implemented for this type
+ --> $DIR/derive_noncopy.rs:3:14
+  |
+3 |     #[derive(Copy)]
+  |              ^^^^
+4 |     struct TryCopy {
+5 |         other: Other,
+  |         ------------ this field does not implement `Copy`
diff --git a/tests/ui/disallow_lifetime.rs b/tests/ui/disallow_lifetime.rs
deleted file mode 100644
index ee39fef..0000000
--- a/tests/ui/disallow_lifetime.rs
+++ /dev/null
@@ -1,13 +0,0 @@
-#[cxx::bridge]
-mod ffi {
-    extern "C" {
-        type C;
-        fn f(&'static self);
-    }
-
-    extern "Rust" {
-        fn f(string: &'a String);
-    }
-}
-
-fn main() {}
diff --git a/tests/ui/disallow_lifetime.stderr b/tests/ui/disallow_lifetime.stderr
deleted file mode 100644
index d7c42e4..0000000
--- a/tests/ui/disallow_lifetime.stderr
+++ /dev/null
@@ -1,11 +0,0 @@
-error: references with explicit lifetimes are not supported
- --> $DIR/disallow_lifetime.rs:9:22
-  |
-9 |         fn f(string: &'a String);
-  |                      ^^^^^^^^^^
-
-error: references with explicit lifetimes are not supported
- --> $DIR/disallow_lifetime.rs:5:14
-  |
-5 |         fn f(&'static self);
-  |              ^^^^^^^^^^^^^
diff --git a/tests/ui/drop_shared.rs b/tests/ui/drop_shared.rs
new file mode 100644
index 0000000..5e3de4a
--- /dev/null
+++ b/tests/ui/drop_shared.rs
@@ -0,0 +1,14 @@
+#[cxx::bridge]
+mod ffi {
+    struct Shared {
+        fd: i32,
+    }
+}
+
+impl Drop for ffi::Shared {
+    fn drop(&mut self) {
+        println!("close({})", self.fd);
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/drop_shared.stderr b/tests/ui/drop_shared.stderr
new file mode 100644
index 0000000..f2724e3
--- /dev/null
+++ b/tests/ui/drop_shared.stderr
@@ -0,0 +1,8 @@
+error[E0119]: conflicting implementations of trait `ffi::_::forbid::Drop` for type `ffi::Shared`
+ --> $DIR/drop_shared.rs:3:5
+  |
+1 | #[cxx::bridge]
+  | -------------- first implementation here
+2 | mod ffi {
+3 |     struct Shared {
+  |     ^^^^^^^^^^^^^ conflicting implementation for `ffi::Shared`
diff --git a/tests/ui/duplicate_enum_discriminants.rs b/tests/ui/duplicate_enum_discriminants.rs
deleted file mode 100644
index ec3d61a..0000000
--- a/tests/ui/duplicate_enum_discriminants.rs
+++ /dev/null
@@ -1,15 +0,0 @@
-#[cxx::bridge]
-mod ffi {
-    enum A {
-        V1 = 10,
-        V2 = 10,
-    }
-
-    enum B {
-        V1 = 10,
-        V2,
-        V3 = 11,
-    }
-}
-
-fn main() {}
diff --git a/tests/ui/duplicate_enum_discriminants.stderr b/tests/ui/duplicate_enum_discriminants.stderr
deleted file mode 100644
index 14505e3..0000000
--- a/tests/ui/duplicate_enum_discriminants.stderr
+++ /dev/null
@@ -1,11 +0,0 @@
-error: discriminant value `10` already exists
- --> $DIR/duplicate_enum_discriminants.rs:5:9
-  |
-5 |         V2 = 10,
-  |         ^^^^^^^
-
-error: discriminant value `11` already exists
-  --> $DIR/duplicate_enum_discriminants.rs:11:9
-   |
-11 |         V3 = 11,
-   |         ^^^^^^^
diff --git a/tests/ui/empty_enum.stderr b/tests/ui/empty_enum.stderr
index 7f35019..0556da9 100644
--- a/tests/ui/empty_enum.stderr
+++ b/tests/ui/empty_enum.stderr
@@ -1,4 +1,4 @@
-error: enums without any variants are not supported
+error: explicit #[repr(...)] is required for enum without any variants
  --> $DIR/empty_enum.rs:3:5
   |
 3 |     enum A {}
diff --git a/tests/ui/enum_match_without_wildcard.stderr b/tests/ui/enum_match_without_wildcard.stderr
index 85cc0c5..f43678f 100644
--- a/tests/ui/enum_match_without_wildcard.stderr
+++ b/tests/ui/enum_match_without_wildcard.stderr
@@ -1,8 +1,8 @@
 error[E0004]: non-exhaustive patterns: `A { repr: 2_u8..=u8::MAX }` not covered
   --> $DIR/enum_match_without_wildcard.rs:12:11
    |
-1  | #[cxx::bridge]
-   | -------------- `ffi::A` defined here
+3  |     enum A {
+   |     ------ `ffi::A` defined here
 ...
 12 |     match a {
    |           ^ pattern `A { repr: 2_u8..=u8::MAX }` not covered
diff --git a/tests/ui/enum_receiver.rs b/tests/ui/enum_receiver.rs
new file mode 100644
index 0000000..a27f274
--- /dev/null
+++ b/tests/ui/enum_receiver.rs
@@ -0,0 +1,11 @@
+#[cxx::bridge]
+mod ffi {
+    enum Enum {
+        Variant,
+    }
+    extern "Rust" {
+        fn f(self: &Enum);
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/enum_receiver.stderr b/tests/ui/enum_receiver.stderr
new file mode 100644
index 0000000..e09b4e3
--- /dev/null
+++ b/tests/ui/enum_receiver.stderr
@@ -0,0 +1,5 @@
+error: unsupported receiver type; C++ does not allow member functions on enums
+ --> $DIR/enum_receiver.rs:7:20
+  |
+7 |         fn f(self: &Enum);
+  |                    ^^^^^
diff --git a/tests/ui/expected_named.rs b/tests/ui/expected_named.rs
new file mode 100644
index 0000000..31626d1
--- /dev/null
+++ b/tests/ui/expected_named.rs
@@ -0,0 +1,9 @@
+#[cxx::bridge]
+mod ffi {
+    unsafe extern "C++" {
+        type Borrowed<'a>;
+        fn borrowed() -> UniquePtr<Borrowed>;
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/expected_named.stderr b/tests/ui/expected_named.stderr
new file mode 100644
index 0000000..4676401
--- /dev/null
+++ b/tests/ui/expected_named.stderr
@@ -0,0 +1,11 @@
+error[E0106]: missing lifetime specifier
+ --> $DIR/expected_named.rs:5:36
+  |
+5 |         fn borrowed() -> UniquePtr<Borrowed>;
+  |                                    ^^^^^^^^ expected named lifetime parameter
+  |
+  = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from
+help: consider using the `'static` lifetime
+  |
+5 |         fn borrowed() -> UniquePtr<Borrowed<'static>>;
+  |                                    ^^^^^^^^^^^^^^^^^
diff --git a/tests/ui/extern_fn_abi.rs b/tests/ui/extern_fn_abi.rs
new file mode 100644
index 0000000..1f93338
--- /dev/null
+++ b/tests/ui/extern_fn_abi.rs
@@ -0,0 +1,8 @@
+#[cxx::bridge]
+mod ffi {
+    extern "C++" {
+        extern "Java" fn f();
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/extern_fn_abi.stderr b/tests/ui/extern_fn_abi.stderr
new file mode 100644
index 0000000..3abf47a
--- /dev/null
+++ b/tests/ui/extern_fn_abi.stderr
@@ -0,0 +1,5 @@
+error: explicit ABI on extern function is not supported
+ --> $DIR/extern_fn_abi.rs:4:9
+  |
+4 |         extern "Java" fn f();
+  |         ^^^^^^^^^^^^^
diff --git a/tests/ui/extern_type_bound.rs b/tests/ui/extern_type_bound.rs
new file mode 100644
index 0000000..958accd
--- /dev/null
+++ b/tests/ui/extern_type_bound.rs
@@ -0,0 +1,15 @@
+#[cxx::bridge]
+mod ffi {
+    extern "C++" {
+        type Opaque: PartialEq + PartialOrd;
+    }
+}
+
+#[cxx::bridge]
+mod ffi {
+    extern "C++" {
+        type Opaque: for<'de> Deserialize<'de>;
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/extern_type_bound.stderr b/tests/ui/extern_type_bound.stderr
new file mode 100644
index 0000000..ca07ef7
--- /dev/null
+++ b/tests/ui/extern_type_bound.stderr
@@ -0,0 +1,11 @@
+error: extern type bounds are not implemented yet
+ --> $DIR/extern_type_bound.rs:4:22
+  |
+4 |         type Opaque: PartialEq + PartialOrd;
+  |                      ^^^^^^^^^^^^^^^^^^^^^^
+
+error: unsupported trait
+  --> $DIR/extern_type_bound.rs:11:22
+   |
+11 |         type Opaque: for<'de> Deserialize<'de>;
+   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/tests/ui/extern_type_generic.rs b/tests/ui/extern_type_generic.rs
new file mode 100644
index 0000000..4de2c98
--- /dev/null
+++ b/tests/ui/extern_type_generic.rs
@@ -0,0 +1,8 @@
+#[cxx::bridge]
+mod ffi {
+    extern "C++" {
+        type Generic<T>;
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/extern_type_generic.stderr b/tests/ui/extern_type_generic.stderr
new file mode 100644
index 0000000..95faec6
--- /dev/null
+++ b/tests/ui/extern_type_generic.stderr
@@ -0,0 +1,5 @@
+error: extern type with generic type parameter is not supported yet
+ --> $DIR/extern_type_generic.rs:4:22
+  |
+4 |         type Generic<T>;
+  |                      ^
diff --git a/tests/ui/extern_type_lifetime_bound.rs b/tests/ui/extern_type_lifetime_bound.rs
new file mode 100644
index 0000000..ad58181
--- /dev/null
+++ b/tests/ui/extern_type_lifetime_bound.rs
@@ -0,0 +1,8 @@
+#[cxx::bridge]
+mod ffi {
+    extern "C++" {
+        type Complex<'a, 'b: 'a>;
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/extern_type_lifetime_bound.stderr b/tests/ui/extern_type_lifetime_bound.stderr
new file mode 100644
index 0000000..8c58050
--- /dev/null
+++ b/tests/ui/extern_type_lifetime_bound.stderr
@@ -0,0 +1,5 @@
+error: lifetime parameter with bounds is not supported yet
+ --> $DIR/extern_type_lifetime_bound.rs:4:26
+  |
+4 |         type Complex<'a, 'b: 'a>;
+  |                          ^^^^^^
diff --git a/tests/ui/fallible_fnptr.rs b/tests/ui/fallible_fnptr.rs
new file mode 100644
index 0000000..c45813d
--- /dev/null
+++ b/tests/ui/fallible_fnptr.rs
@@ -0,0 +1,8 @@
+#[cxx::bridge]
+mod ffi {
+    unsafe extern "C++" {
+        fn f(callback: fn() -> Result<()>);
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/fallible_fnptr.stderr b/tests/ui/fallible_fnptr.stderr
new file mode 100644
index 0000000..1d3fbe4
--- /dev/null
+++ b/tests/ui/fallible_fnptr.stderr
@@ -0,0 +1,5 @@
+error: function pointer returning Result is not supported yet
+ --> $DIR/fallible_fnptr.rs:4:24
+  |
+4 |         fn f(callback: fn() -> Result<()>);
+  |                        ^^^^^^^^^^^^^^^^^^
diff --git a/tests/ui/function_with_body.rs b/tests/ui/function_with_body.rs
new file mode 100644
index 0000000..52be79a
--- /dev/null
+++ b/tests/ui/function_with_body.rs
@@ -0,0 +1,8 @@
+#[cxx::bridge]
+mod ffi {
+    unsafe extern "C++" {
+        fn f() {}
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/function_with_body.stderr b/tests/ui/function_with_body.stderr
new file mode 100644
index 0000000..c5641db
--- /dev/null
+++ b/tests/ui/function_with_body.stderr
@@ -0,0 +1,5 @@
+error: expected `;`
+ --> $DIR/function_with_body.rs:4:16
+  |
+4 |         fn f() {}
+  |                ^
diff --git a/tests/ui/generic_enum.rs b/tests/ui/generic_enum.rs
index 808856e..feeb94b 100644
--- a/tests/ui/generic_enum.rs
+++ b/tests/ui/generic_enum.rs
@@ -3,6 +3,14 @@
     enum A<T> {
         Field,
     }
+
+    enum B<T> where T: Copy {
+        Field,
+    }
+
+    enum C where void: Copy {
+        Field,
+    }
 }
 
 fn main() {}
diff --git a/tests/ui/generic_enum.stderr b/tests/ui/generic_enum.stderr
index bf267f7..ea83d7c 100644
--- a/tests/ui/generic_enum.stderr
+++ b/tests/ui/generic_enum.stderr
@@ -1,5 +1,17 @@
-error: enums with generic parameters are not allowed
+error: enum with generic parameters is not supported
  --> $DIR/generic_enum.rs:3:5
   |
 3 |     enum A<T> {
   |     ^^^^^^^^^
+
+error: enum with generic parameters is not supported
+ --> $DIR/generic_enum.rs:7:5
+  |
+7 |     enum B<T> where T: Copy {
+  |     ^^^^^^^^^
+
+error: enum with where-clause is not supported
+  --> $DIR/generic_enum.rs:11:12
+   |
+11 |     enum C where void: Copy {
+   |            ^^^^^^^^^^^^^^^^
diff --git a/tests/ui/include.rs b/tests/ui/include.rs
index 82fa8de..3e848b5 100644
--- a/tests/ui/include.rs
+++ b/tests/ui/include.rs
@@ -1,6 +1,6 @@
 #[cxx::bridge]
 mod ffi {
-    extern "C" {
+    extern "C++" {
         include!("path/to" what);
         include!(<path/to> what);
         include!(<path/to);
diff --git a/tests/ui/lifetime_extern_cxx.rs b/tests/ui/lifetime_extern_cxx.rs
new file mode 100644
index 0000000..e85b339
--- /dev/null
+++ b/tests/ui/lifetime_extern_cxx.rs
@@ -0,0 +1,9 @@
+#[cxx::bridge]
+mod ffi {
+    extern "C++" {
+        type Opaque;
+        unsafe fn f<'a>(&'a self, arg: &str) -> &'a str;
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/lifetime_extern_cxx.stderr b/tests/ui/lifetime_extern_cxx.stderr
new file mode 100644
index 0000000..099c6ce
--- /dev/null
+++ b/tests/ui/lifetime_extern_cxx.stderr
@@ -0,0 +1,5 @@
+error: extern C++ function with lifetimes must be declared in `unsafe extern "C++"` block
+ --> $DIR/lifetime_extern_cxx.rs:5:9
+  |
+5 |         unsafe fn f<'a>(&'a self, arg: &str) -> &'a str;
+  |         ^^^^^^^^^^^^^^^
diff --git a/tests/ui/lifetime_extern_rust.rs b/tests/ui/lifetime_extern_rust.rs
new file mode 100644
index 0000000..72c37cb
--- /dev/null
+++ b/tests/ui/lifetime_extern_rust.rs
@@ -0,0 +1,17 @@
+#[cxx::bridge]
+mod ffi {
+    extern "Rust" {
+        type Opaque;
+        fn f<'a>(&'a self, arg: &str) -> &'a str;
+    }
+}
+
+pub struct Opaque;
+
+impl Opaque {
+    fn f(&self, _arg: &str) -> &str {
+        ""
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/lifetime_extern_rust.stderr b/tests/ui/lifetime_extern_rust.stderr
new file mode 100644
index 0000000..0fc806a
--- /dev/null
+++ b/tests/ui/lifetime_extern_rust.stderr
@@ -0,0 +1,5 @@
+error: must be `unsafe fn f` in order to expose explicit lifetimes to C++
+ --> $DIR/lifetime_extern_rust.rs:5:9
+  |
+5 |         fn f<'a>(&'a self, arg: &str) -> &'a str;
+  |         ^^^^^^^^
diff --git a/tests/ui/multiple_parse_error.stderr b/tests/ui/multiple_parse_error.stderr
index 4189507..f736fd1 100644
--- a/tests/ui/multiple_parse_error.stderr
+++ b/tests/ui/multiple_parse_error.stderr
@@ -1,10 +1,10 @@
-error: struct with generic parameters is not supported yet
+error: unit structs are not supported
  --> $DIR/multiple_parse_error.rs:3:5
   |
 3 |     struct Monad<T>;
-  |     ^^^^^^^^^^^^^^^
+  |     ^^^^^^^^^^^^^^^^
 
-error: unrecognized ABI
+error: unrecognized ABI, requires either "C++" or "Rust"
  --> $DIR/multiple_parse_error.rs:5:5
   |
 5 |     extern "Haskell" {}
diff --git a/tests/ui/mut_return.rs b/tests/ui/mut_return.rs
new file mode 100644
index 0000000..f30988b
--- /dev/null
+++ b/tests/ui/mut_return.rs
@@ -0,0 +1,18 @@
+#[cxx::bridge]
+mod ffi {
+    extern "Rust" {
+        type Mut<'a>;
+    }
+
+    unsafe extern "C++" {
+        type Thing;
+
+        fn f(t: &Thing) -> Pin<&mut CxxString>;
+        unsafe fn g(t: &Thing) -> Pin<&mut CxxString>;
+        fn h(t: Box<Mut>) -> Pin<&mut CxxString>;
+        fn i<'a>(t: Box<Mut<'a>>) -> Pin<&'a mut CxxString>;
+        fn j(t: &Thing) -> &mut [u8];
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/mut_return.stderr b/tests/ui/mut_return.stderr
new file mode 100644
index 0000000..e33393d
--- /dev/null
+++ b/tests/ui/mut_return.stderr
@@ -0,0 +1,11 @@
+error: &mut return type is not allowed unless there is a &mut argument
+  --> $DIR/mut_return.rs:10:9
+   |
+10 |         fn f(t: &Thing) -> Pin<&mut CxxString>;
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: &mut return type is not allowed unless there is a &mut argument
+  --> $DIR/mut_return.rs:14:9
+   |
+14 |         fn j(t: &Thing) -> &mut [u8];
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/tests/ui/nonlocal_rust_type.rs b/tests/ui/nonlocal_rust_type.rs
new file mode 100644
index 0000000..d8a238a
--- /dev/null
+++ b/tests/ui/nonlocal_rust_type.rs
@@ -0,0 +1,18 @@
+pub struct MyBuilder<'a> {
+    _s: &'a str,
+}
+
+type OptBuilder<'a> = Option<MyBuilder<'a>>;
+
+#[cxx::bridge]
+mod ffi {
+    extern "Rust" {
+        type OptBuilder<'a>;
+    }
+
+    struct MyBuilder<'a> {
+        rs: Box<OptBuilder<'a>>,
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/nonlocal_rust_type.stderr b/tests/ui/nonlocal_rust_type.stderr
new file mode 100644
index 0000000..d266353
--- /dev/null
+++ b/tests/ui/nonlocal_rust_type.stderr
@@ -0,0 +1,21 @@
+error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
+  --> $DIR/nonlocal_rust_type.rs:10:9
+   |
+10 |         type OptBuilder<'a>;
+   |         ^^^^^--------------
+   |         |    |
+   |         |    `Option` is not defined in the current crate
+   |         impl doesn't use only types from inside the current crate
+   |
+   = note: define and implement a trait or new type instead
+
+error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
+  --> $DIR/nonlocal_rust_type.rs:14:13
+   |
+14 |         rs: Box<OptBuilder<'a>>,
+   |             ^^^^---------------
+   |             |   |
+   |             |   `Option` is not defined in the current crate
+   |             impl doesn't use only types from inside the current crate
+   |
+   = note: define and implement a trait or new type instead
diff --git a/tests/ui/opaque_autotraits.rs b/tests/ui/opaque_autotraits.rs
new file mode 100644
index 0000000..99406d7
--- /dev/null
+++ b/tests/ui/opaque_autotraits.rs
@@ -0,0 +1,16 @@
+#[cxx::bridge]
+mod ffi {
+    extern "C++" {
+        type Opaque;
+    }
+}
+
+fn assert_send<T: Send>() {}
+fn assert_sync<T: Sync>() {}
+fn assert_unpin<T: Unpin>() {}
+
+fn main() {
+    assert_send::<ffi::Opaque>();
+    assert_sync::<ffi::Opaque>();
+    assert_unpin::<ffi::Opaque>();
+}
diff --git a/tests/ui/opaque_autotraits.stderr b/tests/ui/opaque_autotraits.stderr
new file mode 100644
index 0000000..4f469ef
--- /dev/null
+++ b/tests/ui/opaque_autotraits.stderr
@@ -0,0 +1,40 @@
+error[E0277]: `*const u8` cannot be sent between threads safely
+  --> $DIR/opaque_autotraits.rs:13:5
+   |
+8  | fn assert_send<T: Send>() {}
+   |                   ---- required by this bound in `assert_send`
+...
+13 |     assert_send::<ffi::Opaque>();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^ `*const u8` cannot be sent between threads safely
+   |
+   = help: within `ffi::Opaque`, the trait `Send` is not implemented for `*const u8`
+   = note: required because it appears within the type `[*const u8; 0]`
+   = note: required because it appears within the type `cxx::private::Opaque`
+   = note: required because it appears within the type `ffi::Opaque`
+
+error[E0277]: `*const u8` cannot be shared between threads safely
+  --> $DIR/opaque_autotraits.rs:14:5
+   |
+9  | fn assert_sync<T: Sync>() {}
+   |                   ---- required by this bound in `assert_sync`
+...
+14 |     assert_sync::<ffi::Opaque>();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^ `*const u8` cannot be shared between threads safely
+   |
+   = help: within `ffi::Opaque`, the trait `Sync` is not implemented for `*const u8`
+   = note: required because it appears within the type `[*const u8; 0]`
+   = note: required because it appears within the type `cxx::private::Opaque`
+   = note: required because it appears within the type `ffi::Opaque`
+
+error[E0277]: `PhantomPinned` cannot be unpinned
+  --> $DIR/opaque_autotraits.rs:15:5
+   |
+10 | fn assert_unpin<T: Unpin>() {}
+   |                    ----- required by this bound in `assert_unpin`
+...
+15 |     assert_unpin::<ffi::Opaque>();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^ within `ffi::Opaque`, the trait `Unpin` is not implemented for `PhantomPinned`
+   |
+   = note: required because it appears within the type `PhantomData<PhantomPinned>`
+   = note: required because it appears within the type `cxx::private::Opaque`
+   = note: required because it appears within the type `ffi::Opaque`
diff --git a/tests/ui/pin_mut_opaque.rs b/tests/ui/pin_mut_opaque.rs
new file mode 100644
index 0000000..ac1ca43
--- /dev/null
+++ b/tests/ui/pin_mut_opaque.rs
@@ -0,0 +1,14 @@
+#[cxx::bridge]
+mod ffi {
+    unsafe extern "C++" {
+        type Opaque;
+        fn f(arg: &mut Opaque);
+        fn g(&mut self);
+        fn h(self: &mut Opaque);
+        fn s(s: &mut CxxString);
+        fn v(v: &mut CxxVector<u8>);
+    }
+
+}
+
+fn main() {}
diff --git a/tests/ui/pin_mut_opaque.stderr b/tests/ui/pin_mut_opaque.stderr
new file mode 100644
index 0000000..95af3cd
--- /dev/null
+++ b/tests/ui/pin_mut_opaque.stderr
@@ -0,0 +1,35 @@
+error: mutable reference to C++ type requires a pin -- use Pin<&mut Opaque>
+ --> $DIR/pin_mut_opaque.rs:5:19
+  |
+5 |         fn f(arg: &mut Opaque);
+  |                   ^^^^^^^^^^^
+
+error: mutable reference to C++ type requires a pin -- use Pin<&mut CxxString>
+ --> $DIR/pin_mut_opaque.rs:8:17
+  |
+8 |         fn s(s: &mut CxxString);
+  |                 ^^^^^^^^^^^^^^
+
+error: mutable reference to C++ type requires a pin -- use Pin<&mut CxxVector<...>>
+ --> $DIR/pin_mut_opaque.rs:9:17
+  |
+9 |         fn v(v: &mut CxxVector<u8>);
+  |                 ^^^^^^^^^^^^^^^^^^
+
+error: needs a cxx::ExternType impl in order to be used as a non-pinned mutable reference in signature of `f`, `g`, `h`
+ --> $DIR/pin_mut_opaque.rs:4:9
+  |
+4 |         type Opaque;
+  |         ^^^^^^^^^^^
+
+error: mutable reference to opaque C++ type requires a pin -- use `self: Pin<&mut Opaque>`
+ --> $DIR/pin_mut_opaque.rs:6:14
+  |
+6 |         fn g(&mut self);
+  |              ^^^^^^^^^
+
+error: mutable reference to opaque C++ type requires a pin -- use `self: Pin<&mut Opaque>`
+ --> $DIR/pin_mut_opaque.rs:7:20
+  |
+7 |         fn h(self: &mut Opaque);
+  |                    ^^^^^^^^^^^
diff --git a/tests/ui/ptr_in_fnptr.rs b/tests/ui/ptr_in_fnptr.rs
new file mode 100644
index 0000000..73c97c6
--- /dev/null
+++ b/tests/ui/ptr_in_fnptr.rs
@@ -0,0 +1,8 @@
+#[cxx::bridge]
+mod ffi {
+    unsafe extern "C++" {
+        fn f(callback: fn(p: *const u8));
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/ptr_in_fnptr.stderr b/tests/ui/ptr_in_fnptr.stderr
new file mode 100644
index 0000000..372d4cd
--- /dev/null
+++ b/tests/ui/ptr_in_fnptr.stderr
@@ -0,0 +1,5 @@
+error: pointer argument requires that the function pointer be marked unsafe
+ --> $DIR/ptr_in_fnptr.rs:4:27
+  |
+4 |         fn f(callback: fn(p: *const u8));
+  |                           ^^^^^^^^^^^^
diff --git a/tests/ui/ptr_missing_unsafe.rs b/tests/ui/ptr_missing_unsafe.rs
new file mode 100644
index 0000000..56f0007
--- /dev/null
+++ b/tests/ui/ptr_missing_unsafe.rs
@@ -0,0 +1,10 @@
+#[cxx::bridge]
+mod ffi {
+    unsafe extern "C++" {
+        type C;
+
+        fn not_unsafe_ptr(c: *mut C);
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/ptr_missing_unsafe.stderr b/tests/ui/ptr_missing_unsafe.stderr
new file mode 100644
index 0000000..65f53a9
--- /dev/null
+++ b/tests/ui/ptr_missing_unsafe.stderr
@@ -0,0 +1,5 @@
+error: pointer argument requires that the function be marked unsafe
+ --> $DIR/ptr_missing_unsafe.rs:6:27
+  |
+6 |         fn not_unsafe_ptr(c: *mut C);
+  |                           ^^^^^^^^^
diff --git a/tests/ui/ptr_no_const_mut.rs b/tests/ui/ptr_no_const_mut.rs
new file mode 100644
index 0000000..eecec3f
--- /dev/null
+++ b/tests/ui/ptr_no_const_mut.rs
@@ -0,0 +1,10 @@
+#[cxx::bridge]
+mod ffi {
+    unsafe extern "C++" {
+        type C;
+
+        fn get_neither_const_nor_mut() -> *C;
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/ptr_no_const_mut.stderr b/tests/ui/ptr_no_const_mut.stderr
new file mode 100644
index 0000000..8a39185
--- /dev/null
+++ b/tests/ui/ptr_no_const_mut.stderr
@@ -0,0 +1,13 @@
+error: expected mut or const in raw pointer type
+ --> $DIR/ptr_no_const_mut.rs:6:43
+  |
+6 |         fn get_neither_const_nor_mut() -> *C;
+  |                                           ^ expected mut or const in raw pointer type
+  |
+  = help: use `*mut T` or `*const T` as appropriate
+
+error: expected `const` or `mut`
+ --> $DIR/ptr_no_const_mut.rs:6:44
+  |
+6 |         fn get_neither_const_nor_mut() -> *C;
+  |                                            ^
diff --git a/tests/ui/ptr_unsupported.rs b/tests/ui/ptr_unsupported.rs
new file mode 100644
index 0000000..9d59c03
--- /dev/null
+++ b/tests/ui/ptr_unsupported.rs
@@ -0,0 +1,12 @@
+#[cxx::bridge]
+mod ffi {
+    unsafe extern "C++" {
+        type C;
+
+        fn get_ptr_to_reference() -> *mut &C;
+        fn get_uniqueptr_to_ptr() -> UniquePtr<*mut C>;
+        fn get_vector_of_ptr() -> UniquePtr<CxxVector<*mut C>>;
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/ptr_unsupported.stderr b/tests/ui/ptr_unsupported.stderr
new file mode 100644
index 0000000..a3e9343
--- /dev/null
+++ b/tests/ui/ptr_unsupported.stderr
@@ -0,0 +1,17 @@
+error: C++ does not allow pointer to reference as a type
+ --> $DIR/ptr_unsupported.rs:6:38
+  |
+6 |         fn get_ptr_to_reference() -> *mut &C;
+  |                                      ^^^^^^^
+
+error: unsupported unique_ptr target type
+ --> $DIR/ptr_unsupported.rs:7:38
+  |
+7 |         fn get_uniqueptr_to_ptr() -> UniquePtr<*mut C>;
+  |                                      ^^^^^^^^^^^^^^^^^
+
+error: unsupported vector element type
+ --> $DIR/ptr_unsupported.rs:8:45
+  |
+8 |         fn get_vector_of_ptr() -> UniquePtr<CxxVector<*mut C>>;
+  |                                             ^^^^^^^^^^^^^^^^^^
diff --git a/tests/ui/reference_to_reference.rs b/tests/ui/reference_to_reference.rs
index 526d036..91fe160 100644
--- a/tests/ui/reference_to_reference.rs
+++ b/tests/ui/reference_to_reference.rs
@@ -1,6 +1,6 @@
 #[cxx::bridge]
 mod ffi {
-    extern "C" {
+    unsafe extern "C++" {
         type ThingC;
         fn repro_c(t: &&ThingC);
     }
diff --git a/tests/ui/reserved_name.rs b/tests/ui/reserved_name.rs
index 27acca5..409e67c 100644
--- a/tests/ui/reserved_name.rs
+++ b/tests/ui/reserved_name.rs
@@ -4,7 +4,7 @@
         val: usize,
     }
 
-    extern "C" {
+    extern "C++" {
         type Box;
     }
 
diff --git a/tests/ui/root_namespace.rs b/tests/ui/root_namespace.rs
new file mode 100644
index 0000000..886fbd9
--- /dev/null
+++ b/tests/ui/root_namespace.rs
@@ -0,0 +1,13 @@
+#[cxx::bridge]
+mod ffi {
+    #[namespace = "::"]
+    extern "Rust" {}
+
+    #[namespace = ""]
+    extern "Rust" {}
+
+    #[namespace = ]
+    extern "Rust" {}
+}
+
+fn main() {}
diff --git a/tests/ui/root_namespace.stderr b/tests/ui/root_namespace.stderr
new file mode 100644
index 0000000..a9c01a2
--- /dev/null
+++ b/tests/ui/root_namespace.stderr
@@ -0,0 +1,5 @@
+error: expected expression, found `]`
+ --> $DIR/root_namespace.rs:9:19
+  |
+9 |     #[namespace = ]
+  |                   ^ expected expression
diff --git a/tests/ui/rust_pinned.rs b/tests/ui/rust_pinned.rs
new file mode 100644
index 0000000..34ca7e3
--- /dev/null
+++ b/tests/ui/rust_pinned.rs
@@ -0,0 +1,14 @@
+use std::marker::PhantomPinned;
+
+#[cxx::bridge]
+mod ffi {
+    extern "Rust" {
+        type Pinned;
+    }
+}
+
+pub struct Pinned {
+    _pinned: PhantomPinned,
+}
+
+fn main() {}
diff --git a/tests/ui/rust_pinned.stderr b/tests/ui/rust_pinned.stderr
new file mode 100644
index 0000000..f16f9d5
--- /dev/null
+++ b/tests/ui/rust_pinned.stderr
@@ -0,0 +1,10 @@
+error[E0277]: `PhantomPinned` cannot be unpinned
+ --> $DIR/rust_pinned.rs:6:14
+  |
+6 |         type Pinned;
+  |         -----^^^^^^-
+  |         |    |
+  |         |    within `Pinned`, the trait `Unpin` is not implemented for `PhantomPinned`
+  |         required by this bound in `__AssertUnpin`
+  |
+  = note: required because it appears within the type `Pinned`
diff --git a/tests/ui/slice_unsupported.rs b/tests/ui/slice_unsupported.rs
new file mode 100644
index 0000000..7a148dd
--- /dev/null
+++ b/tests/ui/slice_unsupported.rs
@@ -0,0 +1,10 @@
+#[cxx::bridge]
+mod ffi {
+    unsafe extern "C++" {
+        type Opaque;
+
+        fn f(_: &mut [Opaque]);
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/slice_unsupported.stderr b/tests/ui/slice_unsupported.stderr
new file mode 100644
index 0000000..787076f
--- /dev/null
+++ b/tests/ui/slice_unsupported.stderr
@@ -0,0 +1,5 @@
+error: unsupported &mut [T] element type: opaque C++ type is not supported yet
+ --> $DIR/slice_unsupported.rs:6:17
+  |
+6 |         fn f(_: &mut [Opaque]);
+  |                 ^^^^^^^^^^^^^
diff --git a/tests/ui/unique_ptr_as_mut.rs b/tests/ui/unique_ptr_as_mut.rs
new file mode 100644
index 0000000..d2c758a
--- /dev/null
+++ b/tests/ui/unique_ptr_as_mut.rs
@@ -0,0 +1,23 @@
+use cxx::UniquePtr;
+
+#[cxx::bridge]
+mod ffi {
+    struct Shared {
+        x: i32,
+    }
+
+    extern "C++" {
+        type Opaque;
+    }
+
+    impl UniquePtr<Shared> {}
+    impl UniquePtr<Opaque> {}
+}
+
+fn main() {
+    let mut shared = UniquePtr::<ffi::Shared>::null();
+    let _: &mut ffi::Shared = &mut shared;
+
+    let mut opaque = UniquePtr::<ffi::Opaque>::null();
+    let _: &mut ffi::Opaque = &mut opaque;
+}
diff --git a/tests/ui/unique_ptr_as_mut.stderr b/tests/ui/unique_ptr_as_mut.stderr
new file mode 100644
index 0000000..a41f716
--- /dev/null
+++ b/tests/ui/unique_ptr_as_mut.stderr
@@ -0,0 +1,7 @@
+error[E0596]: cannot borrow data in a dereference of `UniquePtr<ffi::Opaque>` as mutable
+  --> $DIR/unique_ptr_as_mut.rs:22:31
+   |
+22 |     let _: &mut ffi::Opaque = &mut opaque;
+   |                               ^^^^^^^^^^^ cannot borrow as mutable
+   |
+   = help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `UniquePtr<ffi::Opaque>`
diff --git a/tests/ui/unique_ptr_to_opaque.rs b/tests/ui/unique_ptr_to_opaque.rs
index 720ca69..5226ca8 100644
--- a/tests/ui/unique_ptr_to_opaque.rs
+++ b/tests/ui/unique_ptr_to_opaque.rs
@@ -11,7 +11,7 @@
 
 #[cxx::bridge]
 mod ffi {
-    extern "C" {
+    extern "C++" {
         type C = crate::outside::C;
     }
 
diff --git a/tests/ui/unique_ptr_twice.rs b/tests/ui/unique_ptr_twice.rs
index b6cb4d4..14c6f62 100644
--- a/tests/ui/unique_ptr_twice.rs
+++ b/tests/ui/unique_ptr_twice.rs
@@ -1,6 +1,6 @@
 #[cxx::bridge]
 mod here {
-    extern "C" {
+    extern "C++" {
         type C;
     }
 
@@ -9,7 +9,7 @@
 
 #[cxx::bridge]
 mod there {
-    extern "C" {
+    extern "C++" {
         type C = crate::here::C;
     }
 
diff --git a/tests/ui/unique_ptr_twice.stderr b/tests/ui/unique_ptr_twice.stderr
index 5686cf1..ee26b64 100644
--- a/tests/ui/unique_ptr_twice.stderr
+++ b/tests/ui/unique_ptr_twice.stderr
@@ -1,4 +1,4 @@
-error[E0119]: conflicting implementations of trait `cxx::private::UniquePtrTarget` for type `here::C`:
+error[E0119]: conflicting implementations of trait `cxx::memory::UniquePtrTarget` for type `here::C`
   --> $DIR/unique_ptr_twice.rs:16:5
    |
 7  |     impl UniquePtr<C> {}
diff --git a/tests/ui/unnamed_receiver.rs b/tests/ui/unnamed_receiver.rs
index 917e991..5f53a0a 100644
--- a/tests/ui/unnamed_receiver.rs
+++ b/tests/ui/unnamed_receiver.rs
@@ -1,6 +1,6 @@
 #[cxx::bridge]
 mod ffi {
-    extern "C" {
+    unsafe extern "C++" {
         type One;
         type Two;
         fn f(&mut self);
diff --git a/tests/ui/unpin_impl.rs b/tests/ui/unpin_impl.rs
new file mode 100644
index 0000000..129fcb1
--- /dev/null
+++ b/tests/ui/unpin_impl.rs
@@ -0,0 +1,10 @@
+#[cxx::bridge]
+mod ffi {
+    extern "C++" {
+        type Opaque;
+    }
+}
+
+impl Unpin for ffi::Opaque {}
+
+fn main() {}
diff --git a/tests/ui/unpin_impl.stderr b/tests/ui/unpin_impl.stderr
new file mode 100644
index 0000000..01ee19c
--- /dev/null
+++ b/tests/ui/unpin_impl.stderr
@@ -0,0 +1,8 @@
+error[E0282]: type annotations needed for `fn()`
+ --> $DIR/unpin_impl.rs:4:14
+  |
+1 | #[cxx::bridge]
+  | -------------- consider giving this pattern the explicit type `fn()`, with the type parameters specified
+...
+4 |         type Opaque;
+  |              ^^^^^^ cannot infer type
diff --git a/tests/ui/unrecognized_receiver.rs b/tests/ui/unrecognized_receiver.rs
index 823bfcf..eee8259 100644
--- a/tests/ui/unrecognized_receiver.rs
+++ b/tests/ui/unrecognized_receiver.rs
@@ -1,6 +1,6 @@
 #[cxx::bridge]
 mod ffi {
-    extern "C" {
+    unsafe extern "C++" {
         fn f(self: &Unrecognized);
     }
 }
diff --git a/tests/ui/unsupported_elided.stderr b/tests/ui/unsupported_elided.stderr
index 5136390..932076d 100644
--- a/tests/ui/unsupported_elided.stderr
+++ b/tests/ui/unsupported_elided.stderr
@@ -1,3 +1,9 @@
+error[E0726]: implicit elided lifetime not allowed here
+ --> $DIR/unsupported_elided.rs:6:14
+  |
+6 |         type T;
+  |              ^- help: indicate the anonymous lifetime: `<'_>`
+
 error[E0106]: missing lifetime specifier
  --> $DIR/unsupported_elided.rs:8:24
   |
diff --git a/tests/ui/vec_opaque.rs b/tests/ui/vec_opaque.rs
new file mode 100644
index 0000000..d0b279c
--- /dev/null
+++ b/tests/ui/vec_opaque.rs
@@ -0,0 +1,34 @@
+#[cxx::bridge]
+mod handle {
+    extern "C++" {
+        type Job;
+    }
+}
+
+#[cxx::bridge]
+mod ffi1 {
+    extern "C++" {
+        type Job;
+    }
+
+    extern "Rust" {
+        fn f() -> Vec<Job>;
+    }
+}
+
+#[cxx::bridge]
+mod ffi2 {
+    extern "C++" {
+        type Job = crate::handle::Job;
+    }
+
+    extern "Rust" {
+        fn f() -> Vec<Job>;
+    }
+}
+
+fn f() -> Vec<handle::Job> {
+    unimplemented!()
+}
+
+fn main() {}
diff --git a/tests/ui/vec_opaque.stderr b/tests/ui/vec_opaque.stderr
new file mode 100644
index 0000000..b8dad91
--- /dev/null
+++ b/tests/ui/vec_opaque.stderr
@@ -0,0 +1,22 @@
+error: Rust Vec containing C++ type is not supported yet
+  --> $DIR/vec_opaque.rs:15:19
+   |
+15 |         fn f() -> Vec<Job>;
+   |                   ^^^^^^^^
+
+error: needs a cxx::ExternType impl in order to be used as a vector element in Vec<Job>
+  --> $DIR/vec_opaque.rs:11:9
+   |
+11 |         type Job;
+   |         ^^^^^^^^
+
+error[E0271]: type mismatch resolving `<handle::Job as ExternType>::Kind == Trivial`
+   --> $DIR/vec_opaque.rs:22:9
+    |
+22  |         type Job = crate::handle::Job;
+    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected enum `Trivial`, found enum `cxx::kind::Opaque`
+    |
+   ::: $WORKSPACE/src/extern_type.rs
+    |
+    | pub fn verify_extern_kind<T: ExternType<Kind = Kind>, Kind: self::Kind>() {}
+    |                                         ----------- required by this bound in `verify_extern_kind`
diff --git a/tests/ui/wrong_type_id.rs b/tests/ui/wrong_type_id.rs
index e3d1380..9137e51 100644
--- a/tests/ui/wrong_type_id.rs
+++ b/tests/ui/wrong_type_id.rs
@@ -1,13 +1,13 @@
 #[cxx::bridge(namespace = "folly")]
 mod here {
-    extern "C" {
+    extern "C++" {
         type StringPiece;
     }
 }
 
 #[cxx::bridge(namespace = "folly")]
 mod there {
-    extern "C" {
+    extern "C++" {
         type ByteRange = crate::here::StringPiece;
     }
 }
diff --git a/tests/unique_ptr.rs b/tests/unique_ptr.rs
index e5eb66b..bbaa316 100644
--- a/tests/unique_ptr.rs
+++ b/tests/unique_ptr.rs
@@ -1,3 +1,5 @@
+#![allow(clippy::let_underscore_drop)]
+
 use cxx::{CxxString, UniquePtr};
 
 #[test]
diff --git a/third-party/BUCK b/third-party/BUCK
index a9a3913..84a5e18 100644
--- a/third-party/BUCK
+++ b/third-party/BUCK
@@ -7,7 +7,7 @@
 
 rust_library(
     name = "cc",
-    srcs = glob(["vendor/cc-1.0.62/src/**"]),
+    srcs = glob(["vendor/cc-1.0.67/src/**"]),
     visibility = ["PUBLIC"],
 )
 
@@ -25,7 +25,7 @@
 
 rust_library(
     name = "codespan-reporting",
-    srcs = glob(["vendor/codespan-reporting-0.9.5/src/**"]),
+    srcs = glob(["vendor/codespan-reporting-0.11.1/src/**"]),
     visibility = ["PUBLIC"],
     deps = [
         ":termcolor",
@@ -41,7 +41,7 @@
 
 rust_library(
     name = "proc-macro2",
-    srcs = glob(["vendor/proc-macro2-1.0.24/src/**"]),
+    srcs = glob(["vendor/proc-macro2-1.0.26/src/**"]),
     visibility = ["PUBLIC"],
     features = [
         "proc-macro",
@@ -57,7 +57,7 @@
 
 rust_library(
     name = "quote",
-    srcs = glob(["vendor/quote-1.0.7/src/**"]),
+    srcs = glob(["vendor/quote-1.0.9/src/**"]),
     visibility = ["PUBLIC"],
     features = ["proc-macro"],
     deps = [":proc-macro2"],
@@ -72,7 +72,7 @@
 
 rust_library(
     name = "syn",
-    srcs = glob(["vendor/syn-1.0.48/src/**"]),
+    srcs = glob(["vendor/syn-1.0.68/src/**"]),
     visibility = ["PUBLIC"],
     features = [
         "clone-impls",
@@ -91,7 +91,7 @@
 
 rust_library(
     name = "termcolor",
-    srcs = glob(["vendor/termcolor-1.1.0/src/**"]),
+    srcs = glob(["vendor/termcolor-1.1.2/src/**"]),
 )
 
 rust_library(
diff --git a/third-party/BUILD b/third-party/BUILD
index d72b083..cf1a288 100644
--- a/third-party/BUILD
+++ b/third-party/BUILD
@@ -1,10 +1,8 @@
 load(
     "//tools/bazel:rust.bzl",
     glob = "third_party_glob",
-    rust_binary = "third_party_rust_binary",
     rust_library = "third_party_rust_library",
 )
-load("@bazel_tools//tools/build_defs/pkg:pkg.bzl", "pkg_tar")
 
 rust_library(
     name = "bitflags",
@@ -13,7 +11,7 @@
 
 rust_library(
     name = "cc",
-    srcs = glob(["vendor/cc-1.0.62/src/**"]),
+    srcs = glob(["vendor/cc-1.0.67/src/**"]),
     visibility = ["//visibility:public"],
 )
 
@@ -31,7 +29,7 @@
 
 rust_library(
     name = "codespan-reporting",
-    srcs = glob(["vendor/codespan-reporting-0.9.5/src/**"]),
+    srcs = glob(["vendor/codespan-reporting-0.11.1/src/**"]),
     visibility = ["//visibility:public"],
     deps = [
         ":termcolor",
@@ -47,7 +45,7 @@
 
 rust_library(
     name = "proc-macro2",
-    srcs = glob(["vendor/proc-macro2-1.0.24/src/**"]),
+    srcs = glob(["vendor/proc-macro2-1.0.26/src/**"]),
     crate_features = [
         "proc-macro",
         "span-locations",
@@ -63,7 +61,7 @@
 
 rust_library(
     name = "quote",
-    srcs = glob(["vendor/quote-1.0.7/src/**"]),
+    srcs = glob(["vendor/quote-1.0.9/src/**"]),
     crate_features = ["proc-macro"],
     visibility = ["//visibility:public"],
     deps = [":proc-macro2"],
@@ -78,7 +76,7 @@
 
 rust_library(
     name = "syn",
-    srcs = glob(["vendor/syn-1.0.48/src/**"]),
+    srcs = glob(["vendor/syn-1.0.68/src/**"]),
     crate_features = [
         "clone-impls",
         "derive",
@@ -97,7 +95,7 @@
 
 rust_library(
     name = "termcolor",
-    srcs = glob(["vendor/termcolor-1.1.0/src/**"]),
+    srcs = glob(["vendor/termcolor-1.1.2/src/**"]),
 )
 
 rust_library(
diff --git a/third-party/Cargo.lock b/third-party/Cargo.lock
index 76b95f0..a95e737 100644
--- a/third-party/Cargo.lock
+++ b/third-party/Cargo.lock
@@ -1,5 +1,7 @@
 # This file is automatically @generated by Cargo.
 # It is not intended for manual editing.
+version = 3
+
 [[package]]
 name = "ansi_term"
 version = "0.11.0"
@@ -28,9 +30,12 @@
 
 [[package]]
 name = "cc"
-version = "1.0.62"
+version = "1.0.67"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f1770ced377336a88a67c473594ccc14eca6f4559217c34f64aac8f83d641b40"
+checksum = "e3c69b077ad434294d3ce9f1f6143a2a4b89a8a2d54ef813d85003a4fd1137fd"
+dependencies = [
+ "jobserver",
+]
 
 [[package]]
 name = "clap"
@@ -49,9 +54,9 @@
 
 [[package]]
 name = "codespan-reporting"
-version = "0.9.5"
+version = "0.11.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6e0762455306b1ed42bc651ef6a2197aabda5e1d4a43c34d5eab5c1a3634e81d"
+checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e"
 dependencies = [
  "termcolor",
  "unicode-width",
@@ -59,7 +64,7 @@
 
 [[package]]
 name = "cxx"
-version = "0.5.9"
+version = "1.0.42"
 dependencies = [
  "cc",
  "cxx-build",
@@ -74,7 +79,7 @@
 
 [[package]]
 name = "cxx-build"
-version = "0.5.9"
+version = "1.0.42"
 dependencies = [
  "cc",
  "codespan-reporting",
@@ -89,7 +94,7 @@
 
 [[package]]
 name = "cxx-gen"
-version = "0.6.6"
+version = "0.7.42"
 dependencies = [
  "cc",
  "codespan-reporting",
@@ -109,7 +114,7 @@
 
 [[package]]
 name = "cxxbridge-cmd"
-version = "0.5.9"
+version = "1.0.42"
 dependencies = [
  "clap",
  "codespan-reporting",
@@ -120,11 +125,11 @@
 
 [[package]]
 name = "cxxbridge-flags"
-version = "0.5.9"
+version = "1.0.42"
 
 [[package]]
 name = "cxxbridge-macro"
-version = "0.5.9"
+version = "1.0.42"
 dependencies = [
  "cxx",
  "proc-macro2",
@@ -154,18 +159,27 @@
 
 [[package]]
 name = "hermit-abi"
-version = "0.1.17"
+version = "0.1.18"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5aca5565f760fb5b220e499d72710ed156fdb74e631659e99377d9ebfbd13ae8"
+checksum = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c"
 dependencies = [
  "libc",
 ]
 
 [[package]]
 name = "itoa"
-version = "0.4.6"
+version = "0.4.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dc6f3ad7b9d11a0c00842ff8de1b60ee58661048eb8049ed33c73594f359d7e6"
+checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736"
+
+[[package]]
+name = "jobserver"
+version = "0.1.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c71313ebb9439f74b00d9d2dcec36440beaf57a6aa0623068441dd7cd81a7f2"
+dependencies = [
+ "libc",
+]
 
 [[package]]
 name = "lazy_static"
@@ -175,15 +189,15 @@
 
 [[package]]
 name = "libc"
-version = "0.2.80"
+version = "0.2.92"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4d58d1b70b004888f764dfbf6a26a3b0342a1632d33968e4a179d8011c760614"
+checksum = "56d855069fafbb9b344c0f962150cd2c1187975cb1c22c1522c240d8c4986714"
 
 [[package]]
 name = "link-cplusplus"
-version = "1.0.4"
+version = "1.0.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f96aa785c87218ec773df6c510af203872b34e2df2cf47d6e908e5f36231e354"
+checksum = "8f1becd27d473556dc610b8afa1636ef90747b574a84553bc11e82371d5ef2d1"
 dependencies = [
  "cc",
 ]
@@ -196,18 +210,18 @@
 
 [[package]]
 name = "proc-macro2"
-version = "1.0.24"
+version = "1.0.26"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71"
+checksum = "a152013215dca273577e18d2bf00fa862b89b24169fb78c4c95aeb07992c9cec"
 dependencies = [
  "unicode-xid",
 ]
 
 [[package]]
 name = "quote"
-version = "1.0.7"
+version = "1.0.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37"
+checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7"
 dependencies = [
  "proc-macro2",
 ]
@@ -232,18 +246,18 @@
 
 [[package]]
 name = "serde"
-version = "1.0.117"
+version = "1.0.125"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b88fa983de7720629c9387e9f517353ed404164b1e482c970a90c1a4aaf7dc1a"
+checksum = "558dc50e1a5a5fa7112ca2ce4effcb321b0300c0d4ccf0776a9f60cd89031171"
 dependencies = [
  "serde_derive",
 ]
 
 [[package]]
 name = "serde_derive"
-version = "1.0.117"
+version = "1.0.125"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cbd1ae72adb44aab48f325a02444a5fc079349a8d804c1fc922aed3f7454c74e"
+checksum = "b093b7a2bb58203b5da3056c05b4ec1fed827dcfdb37347a8841695263b3d06d"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -252,9 +266,9 @@
 
 [[package]]
 name = "serde_json"
-version = "1.0.59"
+version = "1.0.64"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dcac07dbffa1c65e7f816ab9eba78eb142c6d44410f4eeba1e26e4f5dfa56b95"
+checksum = "799e97dc9fdae36a5c8b8f2cae9ce2ee9fdce2058c57a93e6099d919fd982f79"
 dependencies = [
  "itoa",
  "ryu",
@@ -269,9 +283,9 @@
 
 [[package]]
 name = "syn"
-version = "1.0.48"
+version = "1.0.68"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cc371affeffc477f42a221a1e4297aedcea33d47d19b61455588bd9d8f6b19ac"
+checksum = "3ce15dd3ed8aa2f8eeac4716d6ef5ab58b6b9256db41d7e1a0224c2788e8fd87"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -280,9 +294,9 @@
 
 [[package]]
 name = "termcolor"
-version = "1.1.0"
+version = "1.1.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bb6bfa289a4d7c5766392812c0a1f4c1ba45afa1ad47803c11e1f407d846d75f"
+checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4"
 dependencies = [
  "winapi-util",
 ]
@@ -298,18 +312,18 @@
 
 [[package]]
 name = "toml"
-version = "0.5.7"
+version = "0.5.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "75cf45bb0bef80604d001caaec0d09da99611b3c0fd39d3080468875cdb65645"
+checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa"
 dependencies = [
  "serde",
 ]
 
 [[package]]
 name = "trybuild"
-version = "1.0.35"
+version = "1.0.41"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b7d30fe369fd650072b352b1a9cb9587669de6b89be3b8225544012c1c45292d"
+checksum = "99471a206425fba51842a9186315f32d91c56eadc21ea4c21f847b59cf778f8b"
 dependencies = [
  "dissimilar",
  "glob",
diff --git a/tools/bazel/BUILD b/tools/bazel/BUILD
index e69de29..d42fc71 100644
--- a/tools/bazel/BUILD
+++ b/tools/bazel/BUILD
@@ -0,0 +1,7 @@
+load("@bazel_skylib//:bzl_library.bzl", "bzl_library")
+
+bzl_library(
+    name = "bzl_srcs",
+    srcs = glob(["**/*.bzl"]),
+    visibility = ["//visibility:public"],
+)
diff --git a/tools/bazel/rust.bzl b/tools/bazel/rust.bzl
index b7b23e5..9f71923 100644
--- a/tools/bazel/rust.bzl
+++ b/tools/bazel/rust.bzl
@@ -1,5 +1,7 @@
+"""A module wrapping the core rules of `rules_rust`"""
+
 load(
-    "@io_bazel_rules_rust//rust:rust.bzl",
+    "@rules_rust//rust:rust.bzl",
     _rust_binary = "rust_binary",
     _rust_library = "rust_library",
     _rust_test = "rust_test",
diff --git a/tools/bazel/rust_cxx_bridge.bzl b/tools/bazel/rust_cxx_bridge.bzl
index 534f6b5..c7d07e8 100644
--- a/tools/bazel/rust_cxx_bridge.bzl
+++ b/tools/bazel/rust_cxx_bridge.bzl
@@ -1,7 +1,15 @@
+# buildifier: disable=module-docstring
 load("@bazel_skylib//rules:run_binary.bzl", "run_binary")
 load("@rules_cc//cc:defs.bzl", "cc_library")
 
 def rust_cxx_bridge(name, src, deps = []):
+    """A macro defining a cxx bridge library
+
+    Args:
+        name (string): The name of the new target
+        src (string): The rust source file to generate a bridge for
+        deps (list, optional): A list of dependencies for the underlying cc_library. Defaults to [].
+    """
     native.alias(
         name = "%s/header" % name,
         actual = src + ".h",
@@ -26,7 +34,7 @@
             "-o",
             "$(location %s.cc)" % src,
         ],
-        tool = "//:codegen",
+        tool = "@cxx.rs//:codegen",
     )
 
     cc_library(
diff --git a/tools/bazel/vendor.bzl b/tools/bazel/vendor.bzl
index e9f10ac..e404ad9 100644
--- a/tools/bazel/vendor.bzl
+++ b/tools/bazel/vendor.bzl
@@ -1,3 +1,7 @@
+"""A module defining a repository rule for vendoring the dependencies
+of a crate in the current workspace.
+"""
+
 def _impl(repository_ctx):
     # Link cxx repository into @third-party.
     lockfile = repository_ctx.path(repository_ctx.attr.lockfile)
@@ -48,7 +52,12 @@
         repository_ctx.execute(print, quiet = False)
 
 vendor = repository_rule(
-    attrs = {"lockfile": attr.label()},
+    doc = "A rule used to vendor the dependencies of a crate in the current workspace",
+    attrs = {
+        "lockfile": attr.label(
+            doc = "A lockfile providing the set of crates to vendor",
+        ),
+    },
     local = True,
     implementation = _impl,
 )
diff --git a/tools/cargo/build.rs b/tools/cargo/build.rs
index 6c9d22d..401c741 100644
--- a/tools/cargo/build.rs
+++ b/tools/cargo/build.rs
@@ -58,11 +58,11 @@
     #[cfg(windows)]
     if let Some(out_dir) = env::var_os("OUT_DIR") {
         let parent_dir = Path::new(&out_dir).join("symlink");
-        let from_dir = parent_dir.join("from");
-        let to_dir = parent_dir.join("to");
-        if fs::create_dir_all(&from_dir).is_ok()
-            && fs::remove_dir(&to_dir).is_ok()
-            && windows::symlink_dir(&from_dir, &to_dir).is_err()
+        let original_dir = parent_dir.join("original");
+        let link_dir = parent_dir.join("link");
+        if fs::create_dir_all(&original_dir).is_ok()
+            && (!link_dir.exists() || fs::remove_dir(&link_dir).is_ok())
+            && windows::symlink_dir(&original_dir, &link_dir).is_err()
         {
             message = DENIED;
         }