Merge "Mark ab/6881855 as merged" into stage-aosp-master
diff --git a/.buckconfig b/.buckconfig
index 33c15ec..5c6c995 100644
--- a/.buckconfig
+++ b/.buckconfig
@@ -4,9 +4,20 @@
   # required downstream.
   allow_symlinks = allow
 
+  # Hide BUCK files under target/package/ from `buck build ...`. Otherwise:
+  #   $ buck build ...
+  #   //target/package/cxx-0.3.0/tests:ffi references non-existing file or directory 'target/package/cxx-0.3.0/tests/ffi/lib.rs'
+  ignore = target
+
 [cxx]
   cxxflags = -std=c++11
 
 [rust]
   default_edition = 2018
-  rustc_flags = -Crelocation-model=dynamic-no-pic --cap-lints=allow
+  rustc_flags = \
+      -Clink-arg=-fuse-ld=lld \
+      -Crelocation-model=dynamic-no-pic \
+      --cap-lints=allow
+
+[defaults.rust_library]
+  type = check
diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile
new file mode 100644
index 0000000..93cfc05
--- /dev/null
+++ b/.devcontainer/Dockerfile
@@ -0,0 +1 @@
+FROM dtolnay/devcontainer:latest
diff --git a/.devcontainer/README.md b/.devcontainer/README.md
new file mode 100644
index 0000000..b30aebc
--- /dev/null
+++ b/.devcontainer/README.md
@@ -0,0 +1,4 @@
+This directory contains the container setup used when developing CXX inside of
+GitHub [Codespaces].
+
+[Codespaces]: https://github.com/features/codespaces
diff --git a/.devcontainer/build.Dockerfile b/.devcontainer/build.Dockerfile
new file mode 100644
index 0000000..6085459
--- /dev/null
+++ b/.devcontainer/build.Dockerfile
@@ -0,0 +1,18 @@
+FROM mcr.microsoft.com/vscode/devcontainers/rust:1
+
+RUN apt-get update \
+    && export DEBIAN_FRONTEND=noninteractive \
+    && 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/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 \
+    && chmod +x bin/install-bazel bin/buck bin/buildifier \
+    && bin/install-bazel \
+    && unzip tmp/watchman.zip -d tmp \
+    && mv tmp/watchman-v2020.09.21.00-linux/bin/watchman bin \
+    && mv tmp/watchman-v2020.09.21.00-linux/lib/* /usr/local/lib \
+    && mkdir -p /usr/local/var/run/watchman \
+    && rm tmp/watchman.zip
diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json
new file mode 100644
index 0000000..b8deba2
--- /dev/null
+++ b/.devcontainer/devcontainer.json
@@ -0,0 +1,20 @@
+{
+    "name": "Rust",
+    "build": {
+        "dockerfile": "Dockerfile"
+    },
+    "runArgs": ["--cap-add=SYS_PTRACE", "--security-opt", "seccomp=unconfined"],
+    "settings": {
+        "terminal.integrated.shell.linux": "/bin/bash",
+        "lldb.executable": "/usr/bin/lldb",
+        "files.watcherExclude": {
+            "**/target/**": true
+        }
+    },
+    "extensions": [
+        "BazelBuild.vscode-bazel",
+        "matklad.rust-analyzer",
+        "ms-vscode.cpptools",
+        "vadimcn.vscode-lldb"
+    ]
+}
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index f0ca230..26b2722 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -34,16 +34,17 @@
       - uses: dtolnay/rust-toolchain@master
         with:
           toolchain: ${{matrix.rust}}
-      - run: cargo run --manifest-path demo-rs/Cargo.toml
-      - run: cargo test
-
-  msrv:
-    name: Rust 1.42.0
-    runs-on: ubuntu-latest
-    steps:
-      - uses: actions/checkout@v2
-      - uses: dtolnay/rust-toolchain@1.42.0
-      - run: cargo run --manifest-path demo-rs/Cargo.toml
+      - name: Determine test suite subset
+        # Our Windows and macOS jobs are the longest running, so exclude the
+        # relatively slow compiletest from them to speed up end-to-end CI time,
+        # except during cron builds when no human is presumably waiting on the
+        # build. The extra coverage is not particularly valuable and we can
+        # still ensure the test is kept passing on the basis of the scheduled
+        # builds.
+        if: matrix.os && github.event_name != 'schedule'
+        run: echo '::set-env name=RUSTFLAGS::--cfg skip_ui_tests'
+      - run: cargo run --manifest-path demo/Cargo.toml
+      - run: cargo test --workspace --exclude cxx-test-suite
 
   buck:
     name: Buck
@@ -53,20 +54,22 @@
       - uses: dtolnay/rust-toolchain@stable
       - uses: actions/setup-java@v1
         with:
-          java-version: 8
+          java-version: 11
           java-package: jre
       - name: Install Buck
         run: |
           mkdir bin
-          wget -q -O bin/buck https://jitpack.io/com/github/facebook/buck/v2019.10.17.01/buck-v2019.10.17.01.pex
+          wget -q -O bin/buck https://jitpack.io/com/github/facebook/buck/a5f0342ae3/buck-a5f0342ae3-java11.pex # dev branch from 2020.10.11
           chmod +x bin/buck
-          echo ::add-path::bin
+          echo bin >> $GITHUB_PATH
+      - name: Install lld
+        run: sudo apt install lld
       - name: Vendor dependencies
         run: |
           cp third-party/Cargo.lock .
           cargo vendor --versioned-dirs --locked third-party/vendor
       - run: buck build :cxx#check --verbose=0
-      - run: buck run demo-rs --verbose=0
+      - run: buck run demo --verbose=0
       - run: buck test ... --verbose=0
 
   bazel:
@@ -79,10 +82,14 @@
           wget -q -O install.sh https://github.com/bazelbuild/bazel/releases/download/2.1.1/bazel-2.1.1-installer-linux-x86_64.sh
           chmod +x install.sh
           ./install.sh --user
-          echo ::add-path::$HOME/bin
-      - name: Vendor dependencies
-        run: |
-          cp third-party/Cargo.lock .
-          cargo vendor --versioned-dirs --locked third-party/vendor
-      - run: bazel run demo-rs --verbose_failures --noshow_progress
+          echo $HOME/bin >> $GITHUB_PATH
+      - run: bazel run demo --verbose_failures --noshow_progress
       - run: bazel test ... --verbose_failures --noshow_progress
+
+  clippy:
+    name: Clippy
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v2
+      - uses: dtolnay/rust-toolchain@clippy
+      - run: cargo clippy --workspace --tests -- -Dclippy::all
diff --git a/.github/workflows/site.yml b/.github/workflows/site.yml
new file mode 100644
index 0000000..73b8186
--- /dev/null
+++ b/.github/workflows/site.yml
@@ -0,0 +1,39 @@
+name: Deploy
+
+on:
+  push:
+    branches:
+      - master
+    paths:
+      - book/**
+
+jobs:
+  deploy:
+    name: Deploy
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v2
+
+      - name: Get mdBook
+        working-directory: book
+        run: |
+          export MDBOOK_VERSION="v0.4.4"
+          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
+
+      - name: Build
+        working-directory: book
+        run: ./mdbook build
+
+      - name: Push to gh-pages
+        working-directory: book/build
+        run: |
+          REV=$(git rev-parse --short HEAD)
+          git init
+          git remote add upstream https://x-access-token:${{secrets.GITHUB_TOKEN}}@github.com/dtolnay/cxx
+          git config user.name "CXX"
+          git config user.email "dtolnay+cxx@gmail.com"
+          git add -A .
+          git commit -qm "Website @ ${{github.repository}}@${REV}"
+          git push -q upstream HEAD:refs/heads/gh-pages --force
diff --git a/.gitignore b/.gitignore
index e7499e5..b036b6f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,7 +4,7 @@
 /bazel-out
 /bazel-testlogs
 /buck-out
-/Cargo.lock
 /expand.cc
 /expand.rs
-/target
+Cargo.lock
+target
diff --git a/.vscode/README.md b/.vscode/README.md
new file mode 100644
index 0000000..5ed5b27
--- /dev/null
+++ b/.vscode/README.md
@@ -0,0 +1,4 @@
+VS Code actions and configuration. Applicable when developing CXX inside of
+GitHub [Codespaces].
+
+[Codespaces]: https://github.com/features/codespaces
diff --git a/.vscode/launch.json b/.vscode/launch.json
new file mode 100644
index 0000000..244f5c4
--- /dev/null
+++ b/.vscode/launch.json
@@ -0,0 +1,17 @@
+{
+    "version": "0.2.0",
+    "configurations": [
+        {
+            "name": "Run cxx demo",
+            "type": "lldb",
+            "request": "launch",
+            "cargo": {
+                "args": ["build", "--manifest-path", "demo/Cargo.toml"],
+                "filter": {
+                    "name": "demo",
+                    "kind": "bin"
+                }
+            }
+        }
+    ]
+}
diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 0000000..8a1c2c1
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,5 @@
+{
+    "search.exclude": {
+        "**/target": true
+    }
+}
diff --git a/.vscode/tasks.json b/.vscode/tasks.json
new file mode 100644
index 0000000..44a2ab7
--- /dev/null
+++ b/.vscode/tasks.json
@@ -0,0 +1,30 @@
+{
+    "version": "2.0.0",
+    "tasks": [
+        {
+            "label": "Cargo test",
+            "type": "shell",
+            "command": "cargo test",
+            "group": "test"
+        },
+        {
+            "label": "Bazel test",
+            "type": "shell",
+            "command": "bazel test ...",
+            "group": "test",
+            "dependsOn": ["Vendor"]
+        },
+        {
+            "label": "Buck test",
+            "type": "shell",
+            "command": "buck test ...",
+            "group": "test",
+            "dependsOn": ["Vendor"]
+        },
+        {
+            "label": "Vendor",
+            "type": "shell",
+            "command": "cp third-party/Cargo.lock . && cargo vendor --versioned-dirs --locked third-party/vendor"
+        }
+    ]
+}
diff --git a/Android.bp b/Android.bp
index 9d57757..985b8e2 100644
--- a/Android.bp
+++ b/Android.bp
@@ -26,9 +26,8 @@
 
 cc_library_static {
     host_supported: true,
-    name: "libcxx-demo-cxx",
-    srcs: ["demo-cxx/demo.cc"],
-    local_include_dirs: ["demo-cxx"],
+    name: "libcxx-demo-blobstore",
+    srcs: ["demo/src/blobstore.cc"],
     generated_headers: ["cxx-demo-bridge-header", "cxx-bridge-header"],
     generated_sources: ["cxx-demo-bridge-code"],
 }
diff --git a/BUCK b/BUCK
index 241d7b3..da6fcb2 100644
--- a/BUCK
+++ b/BUCK
@@ -1,12 +1,10 @@
 rust_library(
     name = "cxx",
-    srcs = glob(["src/**"], exclude = ["src/symbols/**"]),
+    srcs = glob(["src/**"]),
     visibility = ["PUBLIC"],
-    rustc_flags = ["--cfg", "no_export_symbols"],
     deps = [
         ":core",
         ":macro",
-        "//third-party:link-cplusplus",
     ],
 )
 
@@ -16,11 +14,10 @@
     crate = "cxxbridge",
     visibility = ["PUBLIC"],
     deps = [
-        "//third-party:anyhow",
+        "//third-party:clap",
         "//third-party:codespan-reporting",
         "//third-party:proc-macro2",
         "//third-party:quote",
-        "//third-party:structopt",
         "//third-party:syn",
     ],
 )
@@ -34,13 +31,6 @@
         "cxx.h": "include/cxx.h",
     },
     exported_linker_flags = ["-lstdc++"],
-    deps = [":symbols"],
-)
-
-rust_library(
-    name = "symbols",
-    srcs = glob(["src/macros/**", "src/symbols/**"]),
-    crate_root = "src/symbols/symbols.rs",
 )
 
 rust_library(
@@ -60,7 +50,21 @@
     srcs = glob(["gen/build/src/**"]),
     visibility = ["PUBLIC"],
     deps = [
-        "//third-party:anyhow",
+        "//third-party:cc",
+        "//third-party:codespan-reporting",
+        "//third-party:lazy_static",
+        "//third-party:proc-macro2",
+        "//third-party:quote",
+        "//third-party:scratch",
+        "//third-party:syn",
+    ],
+)
+
+rust_library(
+    name = "lib",
+    srcs = glob(["gen/lib/src/**"]),
+    visibility = ["PUBLIC"],
+    deps = [
         "//third-party:cc",
         "//third-party:codespan-reporting",
         "//third-party:proc-macro2",
diff --git a/BUILD b/BUILD
index 4d2b33a..24a1c8b 100644
--- a/BUILD
+++ b/BUILD
@@ -1,3 +1,4 @@
+load("@rules_cc//cc:defs.bzl", "cc_library")
 load("//tools/bazel:rust.bzl", "rust_binary", "rust_library")
 
 rust_library(
@@ -7,10 +8,7 @@
         ":cxxbridge-macro",
     ],
     visibility = ["//visibility:public"],
-    deps = [
-        ":core-lib",
-        "//third-party:link-cplusplus",
-    ],
+    deps = [":core-lib"],
 )
 
 rust_binary(
@@ -19,11 +17,10 @@
     data = ["gen/cmd/src/gen/include/cxx.h"],
     visibility = ["//visibility:public"],
     deps = [
-        "//third-party:anyhow",
+        "//third-party:clap",
         "//third-party:codespan-reporting",
         "//third-party:proc-macro2",
         "//third-party:quote",
-        "//third-party:structopt",
         "//third-party:syn",
     ],
 )
@@ -59,7 +56,22 @@
     data = ["gen/build/src/gen/include/cxx.h"],
     visibility = ["//visibility:public"],
     deps = [
-        "//third-party:anyhow",
+        "//third-party:cc",
+        "//third-party:codespan-reporting",
+        "//third-party:lazy_static",
+        "//third-party:proc-macro2",
+        "//third-party:quote",
+        "//third-party:scratch",
+        "//third-party:syn",
+    ],
+)
+
+rust_library(
+    name = "lib",
+    srcs = glob(["gen/lib/src/**/*.rs"]),
+    data = ["gen/lib/src/gen/include/cxx.h"],
+    visibility = ["//visibility:public"],
+    deps = [
         "//third-party:cc",
         "//third-party:codespan-reporting",
         "//third-party:proc-macro2",
diff --git a/Cargo.toml b/Cargo.toml
index 6a8c1a4..f0692ae 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,38 +1,41 @@
 [package]
 name = "cxx"
-version = "0.3.4" # remember to update html_root_url
+version = "0.5.9" # remember to update html_root_url
 authors = ["David Tolnay <dtolnay@gmail.com>"]
 edition = "2018"
-links = "cxxbridge03"
+links = "cxxbridge05"
 license = "MIT OR Apache-2.0"
 description = "Safe interop between Rust and C++"
 repository = "https://github.com/dtolnay/cxx"
 documentation = "https://docs.rs/cxx"
 readme = "README.md"
-exclude = ["/demo-cxx", "/gen", "/syntax", "/third-party"]
+exclude = ["/demo", "/gen", "/syntax", "/third-party"]
 keywords = ["ffi"]
 categories = ["development-tools::ffi", "api-bindings"]
 
 [features]
-default = [] # c++11
-"c++14" = []
-"c++17" = []
+default = ["cxxbridge-flags/default"] # c++11
+"c++14" = ["cxxbridge-flags/c++14"]
+"c++17" = ["cxxbridge-flags/c++17"]
+"c++20" = ["cxxbridge-flags/c++20"]
 
 [dependencies]
-cxxbridge-macro = { version = "=0.3.4", path = "macro" }
+cxxbridge-macro = { version = "=0.5.9", path = "macro" }
 link-cplusplus = "1.0"
 
 [build-dependencies]
 cc = "1.0.49"
+cxxbridge-flags = { version = "=0.5.9", path = "flags", default-features = false }
 
 [dev-dependencies]
-cxx-build = { version = "=0.3.4", path = "gen/build" }
+cxx-build = { version = "=0.5.9", path = "gen/build" }
+cxx-gen = { version = "0.6", path = "gen/lib" }
 cxx-test-suite = { version = "0", path = "tests/ffi" }
 rustversion = "1.0"
-trybuild = { version = "1.0.27", features = ["diff"] }
+trybuild = { version = "1.0.33", features = ["diff"] }
 
 [workspace]
-members = ["demo-rs", "gen/build", "gen/cmd", "macro", "tests/ffi"]
+members = ["demo", "flags", "gen/build", "gen/cmd", "gen/lib", "macro", "tests/ffi"]
 
 [package.metadata.docs.rs]
 targets = ["x86_64-unknown-linux-gnu"]
diff --git a/README.md b/README.md
index 260c272..9c0289b 100644
--- a/README.md
+++ b/README.md
@@ -18,10 +18,13 @@
 
 ```toml
 [dependencies]
-cxx = "0.3"
+cxx = "0.5"
+
+[build-dependencies]
+cxx-build = "0.5"
 ```
 
-*Compiler support: requires rustc 1.42+ and c++11 or newer*<br>
+*Compiler support: requires rustc 1.43+ and c++11 or newer*<br>
 *[Release notes](https://github.com/dtolnay/cxx/releases)*
 
 <br>
@@ -60,57 +63,62 @@
 
 ## Example
 
-A runnable version of this example is provided under the *demo-rs* directory of
-this repo (with the C++ side of the implementation in the *demo-cxx* directory).
-To try it out, jump into demo-rs and run `cargo run`.
+In this example we are writing a Rust application that wishes to take advantage
+of an existing C++ client for a large-file blobstore service. The blobstore
+supports a `put` operation for a discontiguous buffer upload. For example we
+might be uploading snapshots of a circular buffer which would tend to consist of
+2 chunks, or fragments of a file spread across memory for some other reason.
+
+A runnable version of this example is provided under the *demo* directory of
+this repo. To try it out, run `cargo run` from that directory.
 
 ```rust
 #[cxx::bridge]
 mod ffi {
     // Any shared structs, whose fields will be visible to both languages.
-    struct SharedThing {
-        z: i32,
-        y: Box<ThingR>,
-        x: UniquePtr<ThingC>,
-    }
-
-    extern "C" {
-        // One or more headers with the matching C++ declarations. Our code
-        // generators don't read it but it gets #include'd and used in static
-        // assertions to ensure our picture of the FFI boundary is accurate.
-        include!("demo-cxx/demo.h");
-
-        // Zero or more opaque types which both languages can pass around but
-        // only C++ can see the fields.
-        type ThingC;
-
-        // Functions implemented in C++.
-        fn make_demo(appname: &str) -> UniquePtr<ThingC>;
-        fn get_name(thing: &ThingC) -> &CxxString;
-        fn do_thing(state: SharedThing);
+    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 ThingR;
+        type MultiBuf;
 
         // Functions implemented in Rust.
-        fn print_r(r: &ThingR);
+        fn next_chunk(buf: &mut MultiBuf) -> &[u8];
+    }
+
+    extern "C++" {
+        // One or more headers with the matching C++ declarations. Our code
+        // generators don't read it but it gets #include'd and used in static
+        // assertions to ensure our picture of the FFI boundary is accurate.
+        include!("demo/include/blobstore.h");
+
+        // Zero or more opaque types which both languages can pass around but
+        // only C++ can see the fields.
+        type BlobstoreClient;
+
+        // Functions implemented in C++.
+        fn new_blobstore_client() -> UniquePtr<BlobstoreClient>;
+        fn put(&self, parts: &mut MultiBuf) -> u64;
+        fn tag(&self, blobid: u64, tag: &str);
+        fn metadata(&self, blobid: u64) -> BlobMetadata;
     }
 }
 ```
 
-Now we simply provide C++ definitions of all the things in the `extern "C"`
-block and Rust definitions of all the things in the `extern "Rust"` block, and
-get to call back and forth safely.
+Now we simply provide Rust definitions of all the things in the `extern "Rust"`
+block and C++ definitions of all the things in the `extern "C++"` block, and get
+to call back and forth safely.
 
 Here are links to the complete set of source files involved in the demo:
 
-- [demo-rs/src/main.rs](demo-rs/src/main.rs)
-- [demo-rs/build.rs](demo-rs/build.rs)
-- [demo-cxx/demo.h](demo-cxx/demo.h)
-- [demo-cxx/demo.cc](demo-cxx/demo.cc)
+- [demo/src/main.rs](demo/src/main.rs)
+- [demo/build.rs](demo/build.rs)
+- [demo/include/blobstore.h](demo/include/blobstore.h)
+- [demo/src/blobstore.cc](demo/src/blobstore.cc)
 
 To look at the code generated in both languages for the example by the CXX code
 generators:
@@ -118,10 +126,10 @@
 ```console
    # run Rust code generator and print to stdout
    # (requires https://github.com/dtolnay/cargo-expand)
-$ cargo expand --manifest-path demo-rs/Cargo.toml
+$ cargo expand --manifest-path demo/Cargo.toml
 
    # run C++ code generator and print to stdout
-$ cargo run --manifest-path gen/cmd/Cargo.toml -- demo-rs/src/main.rs
+$ cargo run --manifest-path gen/cmd/Cargo.toml -- demo/src/main.rs
 ```
 
 <br>
@@ -220,7 +228,7 @@
 # Cargo.toml
 
 [build-dependencies]
-cxx-build = "0.3"
+cxx-build = "0.5"
 ```
 
 ```rust
@@ -228,13 +236,13 @@
 
 fn main() {
     cxx_build::bridge("src/main.rs")  // returns a cc::Build
-        .file("../demo-cxx/demo.cc")
+        .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=../demo-cxx/demo.h");
-    println!("cargo:rerun-if-changed=../demo-cxx/demo.cc");
+    println!("cargo:rerun-if-changed=src/demo.cc");
+    println!("cargo:rerun-if-changed=include/demo.h");
 }
 ```
 
@@ -245,7 +253,7 @@
 For use in non-Cargo builds like Bazel or Buck, CXX provides an alternate way of
 invoking the C++ code generator as a standalone command line tool. The tool is
 packaged as the `cxxbridge-cmd` crate on crates.io or can be built from the
-*cmd* directory of this repo.
+*gen/cmd* directory of this repo.
 
 ```bash
 $ cargo install cxxbridge-cmd
@@ -308,11 +316,11 @@
 <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.3/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><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>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.3/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/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>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.3/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/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>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>
@@ -330,6 +338,7 @@
 <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>
 <tr><td><sup><i>tbd</i></sup></td><td>std::shared_ptr&lt;T&gt;</td></tr>
@@ -343,10 +352,9 @@
 to collect feedback on the direction and invite collaborators. Please check the
 open issues.
 
-On the build side, I don't have much experience with the `cc` crate so I expect
-there may be someone who can suggest ways to make that aspect of this crate
-friendlier or more robust. Please report issues if you run into trouble building
-or linking any of this stuff.
+Especially please report issues if you run into trouble building or linking any
+of this stuff. I'm sure there are ways to make the build aspects friendlier or
+more robust.
 
 Finally, I know more about Rust library design than C++ library design so I
 would appreciate help making the C++ APIs in this project more idiomatic where
diff --git a/WORKSPACE b/WORKSPACE
index cd2a2e8..6093eee 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -1,4 +1,5 @@
 load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
+load("//tools/bazel:vendor.bzl", "vendor")
 
 http_archive(
     name = "io_bazel_rules_rust",
@@ -24,13 +25,18 @@
 load("@io_bazel_rules_rust//rust:repositories.bzl", "rust_repository_set")
 
 rust_repository_set(
-    name = "rust_1_44_linux",
+    name = "rust_1_47_linux",
     exec_triple = "x86_64-unknown-linux-gnu",
-    version = "1.44.0",
+    version = "1.47.0",
 )
 
 rust_repository_set(
-    name = "rust_1_44_darwin",
+    name = "rust_1_47_darwin",
     exec_triple = "x86_64-apple-darwin",
-    version = "1.44.0",
+    version = "1.47.0",
+)
+
+vendor(
+    name = "third-party",
+    lockfile = "//third-party:Cargo.lock",
 )
diff --git a/book/.gitignore b/book/.gitignore
new file mode 100644
index 0000000..690b5b8
--- /dev/null
+++ b/book/.gitignore
@@ -0,0 +1,2 @@
+/build
+/mdbook
diff --git a/book/README.md b/book/README.md
new file mode 100644
index 0000000..e4916e0
--- /dev/null
+++ b/book/README.md
@@ -0,0 +1,9 @@
+Published automatically to https://cxx.rs from master branch.
+
+To build and view locally:
+
+- Install [mdBook]: `cargo install mdbook`.
+- Run `mdbook build` in this directory.
+- Open the generated *build/index.html*.
+
+[mdBook]: https://github.com/rust-lang/mdBook
diff --git a/book/book.toml b/book/book.toml
new file mode 100644
index 0000000..d3187e0
--- /dev/null
+++ b/book/book.toml
@@ -0,0 +1,14 @@
+[book]
+title = "CXX"
+authors = ["David Tolnay"]
+description = "Guide for the `cxx` crate, a safe approach to FFI between Rust and C++."
+
+[rust]
+edition = "2018"
+
+[build]
+build-dir = "build"
+
+[output.html]
+cname = "cxx.rs"
+git-repository-url = "https://github.com/dtolnay/cxx"
diff --git a/book/src/SUMMARY.md b/book/src/SUMMARY.md
new file mode 100644
index 0000000..f3fa924
--- /dev/null
+++ b/book/src/SUMMARY.md
@@ -0,0 +1,3 @@
+# Summary
+
+[Rust ❤️ C++](about.md)
diff --git a/book/src/about.md b/book/src/about.md
new file mode 100644
index 0000000..6e63bf5
--- /dev/null
+++ b/book/src/about.md
@@ -0,0 +1 @@
+### Coming soon
diff --git a/build.rs b/build.rs
index a412dbd..57b3e52 100644
--- a/build.rs
+++ b/build.rs
@@ -1,16 +1,18 @@
+use std::env;
+use std::path::Path;
+
 fn main() {
     cc::Build::new()
         .file("src/cxx.cc")
         .cpp(true)
         .cpp_link_stdlib(None) // linked via link-cplusplus crate
-        .flag_if_supported(if cfg!(feature = "c++17") {
-            "-std=c++17"
-        } else if cfg!(feature = "c++14") {
-            "-std=c++14"
-        } else {
-            "-std=c++11"
-        })
-        .compile("cxxbridge03");
+        .flag_if_supported(cxxbridge_flags::STD)
+        .compile("cxxbridge05");
     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());
+    }
 }
diff --git a/demo-cxx/BUCK b/demo-cxx/BUCK
deleted file mode 100644
index f60200b..0000000
--- a/demo-cxx/BUCK
+++ /dev/null
@@ -1,17 +0,0 @@
-cxx_library(
-    name = "demo-cxx",
-    srcs = ["demo.cc"],
-    compiler_flags = ["-std=c++14"],
-    visibility = ["PUBLIC"],
-    deps = [
-        ":include",
-        "//demo-rs:include",
-    ],
-)
-
-cxx_library(
-    name = "include",
-    exported_headers = ["demo.h"],
-    visibility = ["PUBLIC"],
-    deps = ["//:core"],
-)
diff --git a/demo-cxx/BUILD b/demo-cxx/BUILD
deleted file mode 100644
index 7b1860a..0000000
--- a/demo-cxx/BUILD
+++ /dev/null
@@ -1,17 +0,0 @@
-cc_library(
-    name = "demo-cxx",
-    srcs = ["demo.cc"],
-    copts = ["-std=c++14"],
-    visibility = ["//visibility:public"],
-    deps = [
-        ":include",
-        "//demo-rs:include",
-    ],
-)
-
-cc_library(
-    name = "include",
-    hdrs = ["demo.h"],
-    visibility = ["//visibility:public"],
-    deps = ["//:core"],
-)
diff --git a/demo-cxx/demo.cc b/demo-cxx/demo.cc
deleted file mode 100644
index 21bdad4..0000000
--- a/demo-cxx/demo.cc
+++ /dev/null
@@ -1,21 +0,0 @@
-#include "demo-cxx/demo.h"
-#include "demo-rs/src/main.rs.h"
-#include <iostream>
-
-namespace org {
-namespace example {
-
-ThingC::ThingC(std::string appname) : appname(std::move(appname)) {}
-
-ThingC::~ThingC() { std::cout << "done with ThingC" << std::endl; }
-
-std::unique_ptr<ThingC> make_demo(rust::Str appname) {
-  return std::make_unique<ThingC>(std::string(appname));
-}
-
-const std::string &get_name(const ThingC &thing) { return thing.appname; }
-
-void do_thing(SharedThing state) { print_r(*state.y); }
-
-} // namespace example
-} // namespace org
diff --git a/demo-cxx/demo.h b/demo-cxx/demo.h
deleted file mode 100644
index fafc474..0000000
--- a/demo-cxx/demo.h
+++ /dev/null
@@ -1,24 +0,0 @@
-#pragma once
-#include "rust/cxx.h"
-#include <memory>
-#include <string>
-
-namespace org {
-namespace example {
-
-class ThingC {
-public:
-  ThingC(std::string appname);
-  ~ThingC();
-
-  std::string appname;
-};
-
-struct SharedThing;
-
-std::unique_ptr<ThingC> make_demo(rust::Str appname);
-const std::string &get_name(const ThingC &thing);
-void do_thing(SharedThing state);
-
-} // namespace example
-} // namespace org
diff --git a/demo-rs/BUCK b/demo-rs/BUCK
deleted file mode 100644
index d4164d3..0000000
--- a/demo-rs/BUCK
+++ /dev/null
@@ -1,42 +0,0 @@
-rust_binary(
-    name = "demo-rs",
-    srcs = glob(["src/**"]),
-    deps = [
-        ":gen",
-        "//:cxx",
-        "//demo-cxx:demo-cxx",
-    ],
-)
-
-cxx_library(
-    name = "gen",
-    srcs = [":gen-source"],
-    deps = [
-        ":include",
-        "//demo-cxx:include",
-    ],
-)
-
-genrule(
-    name = "gen-header",
-    srcs = ["src/main.rs"],
-    cmd = "$(exe //:codegen) --header ${SRCS} > ${OUT}",
-    type = "cxxbridge",
-    out = "generated.h",
-)
-
-genrule(
-    name = "gen-source",
-    srcs = ["src/main.rs"],
-    cmd = "$(exe //:codegen) ${SRCS} > ${OUT}",
-    type = "cxxbridge",
-    out = "generated.cc",
-)
-
-cxx_library(
-    name = "include",
-    exported_headers = {
-        "src/main.rs.h": ":gen-header",
-    },
-    visibility = ["PUBLIC"],
-)
diff --git a/demo-rs/BUILD b/demo-rs/BUILD
deleted file mode 100644
index e3ebb96..0000000
--- a/demo-rs/BUILD
+++ /dev/null
@@ -1,43 +0,0 @@
-load("//tools/bazel:rust.bzl", "rust_binary", "rust_library")
-
-rust_binary(
-    name = "demo-rs",
-    srcs = glob(["src/**"]),
-    deps = [
-        ":gen",
-        "//:cxx",
-        "//demo-cxx",
-    ],
-)
-
-cc_library(
-    name = "gen",
-    srcs = [":gen-source"],
-    deps = [
-        ":include",
-        "//demo-cxx:include",
-    ],
-)
-
-genrule(
-    name = "gen-header",
-    srcs = ["src/main.rs"],
-    outs = ["main.rs.h"],
-    cmd = "$(location //:codegen) --header $< > $@",
-    tools = ["//:codegen"],
-)
-
-genrule(
-    name = "gen-source",
-    srcs = ["src/main.rs"],
-    outs = ["generated.cc"],
-    cmd = "$(location //:codegen) $< > $@",
-    tools = ["//:codegen"],
-)
-
-cc_library(
-    name = "include",
-    hdrs = [":gen-header"],
-    include_prefix = "demo-rs/src",
-    visibility = ["//visibility:public"],
-)
diff --git a/demo-rs/build.rs b/demo-rs/build.rs
deleted file mode 100644
index f32b8ef..0000000
--- a/demo-rs/build.rs
+++ /dev/null
@@ -1,10 +0,0 @@
-fn main() {
-    cxx_build::bridge("src/main.rs")
-        .file("../demo-cxx/demo.cc")
-        .flag_if_supported("-std=c++14")
-        .compile("cxxbridge-demo");
-
-    println!("cargo:rerun-if-changed=src/main.rs");
-    println!("cargo:rerun-if-changed=../demo-cxx/demo.h");
-    println!("cargo:rerun-if-changed=../demo-cxx/demo.cc");
-}
diff --git a/demo-rs/src/main.rs b/demo-rs/src/main.rs
deleted file mode 100644
index 66dfc79..0000000
--- a/demo-rs/src/main.rs
+++ /dev/null
@@ -1,39 +0,0 @@
-#[cxx::bridge(namespace = org::example)]
-mod ffi {
-    struct SharedThing {
-        z: i32,
-        y: Box<ThingR>,
-        x: UniquePtr<ThingC>,
-    }
-
-    extern "C" {
-        include!("demo-cxx/demo.h");
-
-        type ThingC;
-        fn make_demo(appname: &str) -> UniquePtr<ThingC>;
-        fn get_name(thing: &ThingC) -> &CxxString;
-        fn do_thing(state: SharedThing);
-    }
-
-    extern "Rust" {
-        type ThingR;
-        fn print_r(r: &ThingR);
-    }
-}
-
-pub struct ThingR(usize);
-
-fn print_r(r: &ThingR) {
-    println!("called back with r={}", r.0);
-}
-
-fn main() {
-    let x = ffi::make_demo("demo of cxx::bridge");
-    println!("this is a {}", ffi::get_name(x.as_ref().unwrap()));
-
-    ffi::do_thing(ffi::SharedThing {
-        z: 222,
-        y: Box::new(ThingR(333)),
-        x,
-    });
-}
diff --git a/demo-rs/Android.bp b/demo/Android.bp
similarity index 78%
rename from demo-rs/Android.bp
rename to demo/Android.bp
index 2fcdf6f..fabfaa7 100644
--- a/demo-rs/Android.bp
+++ b/demo/Android.bp
@@ -3,7 +3,7 @@
     tools: ["cxxbridge"],
     cmd: "$(location cxxbridge) $(in) --header > $(out)",
     srcs: ["src/main.rs"],
-    out: ["demo-rs/src/main.rs.h"],
+    out: ["demo/src/main.rs.h"],
 }
 
 genrule {
@@ -11,12 +11,12 @@
     tools: ["cxxbridge"],
     cmd: "$(location cxxbridge) $(in) >> $(out)",
     srcs: ["src/main.rs"],
-    out: ["demo-rs/generated.cc"],
+    out: ["demo/generated.cc"],
 }
 
 rust_binary {
     name: "cxx-demo-rs",
     srcs: ["src/main.rs"],
     rlibs: ["libcxx"],
-    static_libs: ["libcxx-demo-cxx"],
+    static_libs: ["libcxx-demo-blobstore"],
 }
diff --git a/demo/BUCK b/demo/BUCK
new file mode 100644
index 0000000..43b83aa
--- /dev/null
+++ b/demo/BUCK
@@ -0,0 +1,33 @@
+load("//tools/buck: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"],
+)
+
+cxx_library(
+    name = "blobstore-sys",
+    srcs = ["src/blobstore.cc"],
+    compiler_flags = ["-std=c++14"],
+    deps = [
+        ":blobstore-include",
+        ":bridge/include",
+    ],
+)
+
+cxx_library(
+    name = "blobstore-include",
+    exported_headers = ["include/blobstore.h"],
+    deps = ["//:core"],
+)
diff --git a/demo/BUILD b/demo/BUILD
new file mode 100644
index 0000000..cce8119
--- /dev/null
+++ b/demo/BUILD
@@ -0,0 +1,35 @@
+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"],
+    copts = ["-std=c++14"],
+    deps = [
+        ":blobstore-include",
+        ":bridge/include",
+    ],
+)
+
+cc_library(
+    name = "blobstore-include",
+    hdrs = ["include/blobstore.h"],
+    deps = ["//:core"],
+)
diff --git a/demo-rs/Cargo.toml b/demo/Cargo.toml
similarity index 89%
rename from demo-rs/Cargo.toml
rename to demo/Cargo.toml
index d2147ab..dc94861 100644
--- a/demo-rs/Cargo.toml
+++ b/demo/Cargo.toml
@@ -1,5 +1,5 @@
 [package]
-name = "cxxbridge-demo"
+name = "demo"
 version = "0.0.0"
 authors = ["David Tolnay <dtolnay@gmail.com>"]
 edition = "2018"
diff --git a/demo/build.rs b/demo/build.rs
new file mode 100644
index 0000000..c1b55cc
--- /dev/null
+++ b/demo/build.rs
@@ -0,0 +1,10 @@
+fn main() {
+    cxx_build::bridge("src/main.rs")
+        .file("src/blobstore.cc")
+        .flag_if_supported("-std=c++14")
+        .compile("cxxbridge-demo");
+
+    println!("cargo:rerun-if-changed=src/main.rs");
+    println!("cargo:rerun-if-changed=src/blobstore.cc");
+    println!("cargo:rerun-if-changed=include/blobstore.h");
+}
diff --git a/demo/include/blobstore.h b/demo/include/blobstore.h
new file mode 100644
index 0000000..68a7fc2
--- /dev/null
+++ b/demo/include/blobstore.h
@@ -0,0 +1,26 @@
+#pragma once
+#include "rust/cxx.h"
+#include <memory>
+
+namespace org {
+namespace blobstore {
+
+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();
+
+} // namespace blobstore
+} // namespace org
diff --git a/demo/src/blobstore.cc b/demo/src/blobstore.cc
new file mode 100644
index 0000000..0036a32
--- /dev/null
+++ b/demo/src/blobstore.cc
@@ -0,0 +1,71 @@
+#include "demo/include/blobstore.h"
+#include "demo/src/main.rs.h"
+#include <algorithm>
+#include <functional>
+#include <set>
+#include <string>
+#include <unordered_map>
+
+namespace org {
+namespace blobstore {
+
+// 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 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 {
+  std::string contents;
+
+  // Traverse the caller's chunk iterator.
+  //
+  // In reality there might be sophisticated batching of chunks and/or parallel
+  // upload implemented by the blobstore's C++ client.
+  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.begin(), blob->second.tags.end(),
+                  [&](auto &t) { metadata.tags.emplace_back(t); });
+  }
+  return metadata;
+}
+
+std::unique_ptr<BlobstoreClient> new_blobstore_client() {
+  return std::make_unique<BlobstoreClient>();
+}
+
+} // namespace blobstore
+} // namespace org
diff --git a/demo/src/main.rs b/demo/src/main.rs
new file mode 100644
index 0000000..10f57e5
--- /dev/null
+++ b/demo/src/main.rs
@@ -0,0 +1,59 @@
+#[cxx::bridge(namespace = "org::blobstore")]
+mod ffi {
+    // Shared structs with fields visible to both languages.
+    struct BlobMetadata {
+        size: usize,
+        tags: Vec<String>,
+    }
+
+    // Rust types and signatures exposed to C++.
+    extern "Rust" {
+        type MultiBuf;
+
+        fn next_chunk(buf: &mut MultiBuf) -> &[u8];
+    }
+
+    // C++ types and signatures exposed to Rust.
+    extern "C++" {
+        include!("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;
+    }
+}
+
+// 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(Vec::as_slice).unwrap_or(&[])
+}
+
+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);
+}
diff --git a/flags/Cargo.toml b/flags/Cargo.toml
new file mode 100644
index 0000000..cc5f230
--- /dev/null
+++ b/flags/Cargo.toml
@@ -0,0 +1,17 @@
+[package]
+name = "cxxbridge-flags"
+version = "0.5.9"
+authors = ["David Tolnay <dtolnay@gmail.com>"]
+edition = "2018"
+license = "MIT OR Apache-2.0"
+description = "Compiler configuration of the `cxx` crate (implementation detail)"
+repository = "https://github.com/dtolnay/cxx"
+
+[features]
+default = [] # c++11
+"c++14" = []
+"c++17" = []
+"c++20" = []
+
+[package.metadata.docs.rs]
+targets = ["x86_64-unknown-linux-gnu"]
diff --git a/flags/src/impl.rs b/flags/src/impl.rs
new file mode 100644
index 0000000..4f7b8fb
--- /dev/null
+++ b/flags/src/impl.rs
@@ -0,0 +1,20 @@
+#[allow(unused_assignments, unused_mut, unused_variables)]
+pub const STD: &str = {
+    let mut flags = ["-std=c++11", "/std:c++11"];
+
+    #[cfg(feature = "c++14")]
+    (flags = ["-std=c++14", "/std:c++14"]);
+
+    #[cfg(feature = "c++17")]
+    (flags = ["-std=c++17", "/std:c++17"]);
+
+    #[cfg(feature = "c++20")]
+    (flags = ["-std=c++20", "/std:c++20"]);
+
+    let [mut flag, msvc_flag] = flags;
+
+    #[cfg(target_env = "msvc")]
+    (flag = msvc_flag);
+
+    flag
+};
diff --git a/flags/src/lib.rs b/flags/src/lib.rs
new file mode 100644
index 0000000..55172b2
--- /dev/null
+++ b/flags/src/lib.rs
@@ -0,0 +1,7 @@
+//! This crate is an implementation detail of the `cxx` and `cxx-build` crates,
+//! and does not expose any public API.
+
+mod r#impl;
+
+#[doc(hidden)]
+pub use r#impl::*;
diff --git a/gen/README.md b/gen/README.md
index 9786911..d00b98f 100644
--- a/gen/README.md
+++ b/gen/README.md
@@ -2,3 +2,6 @@
 public frontends, one a command-line application (binary) in the *cmd* directory
 and the other a library intended to be used from a build.rs in the *build*
 directory.
+
+There's also a 'lib' frontend which is intended to allow higher level code
+generators to embed cxx. This is not yet recommended for general use.
diff --git a/gen/build/Cargo.toml b/gen/build/Cargo.toml
index b43a37e..3f7b0ea 100644
--- a/gen/build/Cargo.toml
+++ b/gen/build/Cargo.toml
@@ -1,21 +1,27 @@
 [package]
 name = "cxx-build"
-version = "0.3.4"
+version = "0.5.9"
 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"
+exclude = ["build.rs"]
 keywords = ["ffi"]
 categories = ["development-tools::ffi"]
 
 [dependencies]
-anyhow = "1.0"
 cc = "1.0.49"
 codespan-reporting = "0.9"
+lazy_static = "1.4"
 proc-macro2 = { version = "1.0.17", 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"] }
 
+[dev-dependencies]
+cxx-gen = { version = "0.6", path = "../lib" }
+pkg-config = "0.3"
+
 [package.metadata.docs.rs]
 targets = ["x86_64-unknown-linux-gnu"]
diff --git a/gen/build/build.rs b/gen/build/build.rs
new file mode 100644
index 0000000..c53bef7
--- /dev/null
+++ b/gen/build/build.rs
@@ -0,0 +1 @@
+include!("../../tools/cargo/build.rs");
diff --git a/gen/build/src/cfg.rs b/gen/build/src/cfg.rs
new file mode 100644
index 0000000..c15a173
--- /dev/null
+++ b/gen/build/src/cfg.rs
@@ -0,0 +1,407 @@
+use std::fmt::{self, Debug};
+use std::marker::PhantomData;
+use std::path::Path;
+
+/// Build configuration. See [CFG].
+pub struct Cfg<'a> {
+    /// See [`CFG.include_prefix`][CFG#cfginclude_prefix].
+    pub include_prefix: &'a str,
+    /// See [`CFG.exported_header_dirs`][CFG#cfgexported_header_dirs].
+    pub exported_header_dirs: Vec<&'a Path>,
+    /// See [`CFG.exported_header_prefixes`][CFG#cfgexported_header_prefixes].
+    pub exported_header_prefixes: Vec<&'a str>,
+    /// See [`CFG.exported_header_links`][CFG#cfgexported_header_links].
+    pub exported_header_links: Vec<&'a str>,
+    marker: PhantomData<*const ()>, // !Send + !Sync
+}
+
+/// Global configuration of the current build.
+///
+/// <br>
+///
+/// <div style="float:right;margin:22px 50px 0;font-size:1.15em;color:#444"><strong>&amp;str</strong></div>
+///
+/// ## **`CFG.include_prefix`**
+///
+/// The prefix at which C++ code from your crate as well as directly dependent
+/// crates can access the code generated during this build.
+///
+/// By default, the `include_prefix` is equal to the name of the current crate.
+/// That means if your crate is called `demo` and has Rust source files in a
+/// *src/* directory and maybe some handwritten C++ header files in an
+/// *include/* directory, then the current crate as well as downstream crates
+/// might include them as follows:
+///
+/// ```
+/// # const _: &str = stringify! {
+///   // include one of the handwritten headers:
+/// #include "demo/include/wow.h"
+///
+///   // include a header generated from Rust cxx::bridge:
+/// #include "demo/src/lib.rs.h"
+/// # };
+/// ```
+///
+/// By modifying `CFG.include_prefix` we can substitute a prefix that is
+/// different from the crate name if desired. Here we'll change it to
+/// `"path/to"` which will make import paths take the form
+/// `"path/to/include/wow.h"` and `"path/to/src/lib.rs.h"`.
+///
+/// ```no_run
+/// // build.rs
+///
+/// use cxx_build::CFG;
+///
+/// fn main() {
+///     CFG.include_prefix = "path/to";
+///
+///     cxx_build::bridge("src/lib.rs")
+///         .file("src/demo.cc") // probably contains `#include "path/to/src/lib.rs.h"`
+///         /* ... */
+///         .compile("demo");
+/// }
+/// ```
+///
+/// Note that cross-crate imports are only made available between **direct
+/// dependencies**. Another crate must directly depend on your 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.
+///
+/// <br>
+///
+/// <div style="float:right;margin:22px 50px 0;font-size:1.15em;color:#444"><strong>Vec&lt;&amp;Path&gt;</strong></div>
+///
+/// ## **`CFG.exported_header_dirs`**
+///
+/// A vector of absolute paths. The current crate, directly dependent crates,
+/// and further crates to which this crate's headers are exported (see below)
+/// will be able to `#include` headers from these directories.
+///
+/// Adding a directory to `exported_header_dirs` is similar to adding it to the
+/// current build via the `cc` crate's [`Build::include`][cc::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.
+///
+/// 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
+/// execute before yours decides what to put into `exported_header_dirs`.
+///
+/// [links]: https://doc.rust-lang.org/cargo/reference/build-scripts.html#the-links-manifest-key
+///
+/// ### Example
+///
+/// One of your crate's headers wants to include a system library, such as
+/// `#include "Python.h"`.
+///
+/// ```no_run
+/// // 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"`.
+///
+/// ```no_run
+/// // 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");
+/// }
+/// ```
+///
+/// <p style="margin:0"><br><br></p>
+///
+/// <div style="float:right;margin:22px 50px 0;font-size:1.15em;color:#444"><strong>Vec&lt;&amp;str&gt;</strong></div>
+///
+/// ## **`CFG.exported_header_prefixes`**
+///
+/// Vector of strings. These 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:
+///
+/// ```no_run
+/// // 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");
+/// }
+/// ```
+///
+/// <p style="margin:0"><br><br></p>
+///
+/// <div style="float:right;margin:22px 50px 0;font-size:1.15em;color:#444"><strong>Vec&lt;&amp;str&gt;</strong></div>
+///
+/// ## **`CFG.exported_header_links`**
+///
+/// Vector of strings. These 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 dependency as part of your crate's public API, except with
+/// finer grained 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
+///
+/// ```no_run
+/// // build.rs
+///
+/// use cxx_build::CFG;
+///
+/// fn main() {
+///     CFG.exported_header_links.push("git2");
+///
+///     cxx_build::bridge("src/bridge.rs").compile("demo");
+/// }
+/// ```
+#[cfg(doc)]
+pub static mut CFG: Cfg = Cfg {
+    include_prefix: "",
+    exported_header_dirs: Vec::new(),
+    exported_header_prefixes: Vec::new(),
+    exported_header_links: Vec::new(),
+    marker: PhantomData,
+};
+
+impl<'a> Debug for Cfg<'a> {
+    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+        let Self {
+            include_prefix,
+            exported_header_dirs,
+            exported_header_prefixes,
+            exported_header_links,
+            marker: _,
+        } = self;
+        formatter
+            .debug_struct("Cfg")
+            .field("include_prefix", include_prefix)
+            .field("exported_header_dirs", exported_header_dirs)
+            .field("exported_header_prefixes", exported_header_prefixes)
+            .field("exported_header_links", exported_header_links)
+            .finish()
+    }
+}
+
+#[cfg(not(doc))]
+pub use self::r#impl::Cfg::CFG;
+
+#[cfg(not(doc))]
+mod r#impl {
+    use crate::intern::{intern, InternedString};
+    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};
+    use std::sync::{PoisonError, RwLock};
+
+    struct CurrentCfg {
+        include_prefix: InternedString,
+        exported_header_dirs: Vec<InternedString>,
+        exported_header_prefixes: Vec<InternedString>,
+        exported_header_links: Vec<InternedString>,
+    }
+
+    impl CurrentCfg {
+        fn default() -> Self {
+            let include_prefix = crate::env_os("CARGO_PKG_NAME")
+                .map(|pkg| intern(&pkg.to_string_lossy()))
+                .unwrap_or_default();
+            let exported_header_dirs = Vec::new();
+            let exported_header_prefixes = Vec::new();
+            let exported_header_links = Vec::new();
+            CurrentCfg {
+                include_prefix,
+                exported_header_dirs,
+                exported_header_prefixes,
+                exported_header_links,
+            }
+        }
+    }
+
+    lazy_static! {
+        static ref CURRENT: RwLock<CurrentCfg> = RwLock::new(CurrentCfg::default());
+    }
+
+    thread_local! {
+        // FIXME: If https://github.com/rust-lang/rust/issues/77425 is resolved,
+        // we can delete this thread local side table and instead make each CFG
+        // instance directly own the associated super::Cfg.
+        //
+        //     #[allow(const_item_mutation)]
+        //     pub const CFG: Cfg = Cfg {
+        //         cfg: AtomicPtr::new(ptr::null_mut()),
+        //     };
+        //     pub struct Cfg {
+        //         cfg: AtomicPtr<super::Cfg>,
+        //     }
+        //
+        static CONST_DEREFS: RefCell<HashMap<Handle, Box<super::Cfg<'static>>>> = RefCell::default();
+    }
+
+    #[derive(Eq, PartialEq, Hash)]
+    struct Handle(*const Cfg<'static>);
+
+    impl<'a> Cfg<'a> {
+        fn current() -> super::Cfg<'a> {
+            let current = CURRENT.read().unwrap_or_else(PoisonError::into_inner);
+            let include_prefix = current.include_prefix.str();
+            let exported_header_dirs = current.exported_header_dirs.vec();
+            let exported_header_prefixes = current.exported_header_prefixes.vec();
+            let exported_header_links = current.exported_header_links.vec();
+            super::Cfg {
+                include_prefix,
+                exported_header_dirs,
+                exported_header_prefixes,
+                exported_header_links,
+                marker: PhantomData,
+            }
+        }
+
+        const fn handle(self: &Cfg<'a>) -> Handle {
+            Handle(<*const Cfg>::cast(self))
+        }
+    }
+
+    // Since super::Cfg is !Send and !Sync, all Cfg are thread local and will
+    // drop on the same thread where they were created.
+    pub enum Cfg<'a> {
+        Mut(super::Cfg<'a>),
+        CFG,
+    }
+
+    impl<'a> Debug for Cfg<'a> {
+        fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+            if let Cfg::Mut(cfg) = self {
+                Debug::fmt(cfg, formatter)
+            } else {
+                Debug::fmt(&Cfg::current(), formatter)
+            }
+        }
+    }
+
+    impl<'a> Deref for Cfg<'a> {
+        type Target = super::Cfg<'a>;
+
+        fn deref(&self) -> &Self::Target {
+            if let Cfg::Mut(cfg) = self {
+                cfg
+            } else {
+                let cfg = CONST_DEREFS.with(|derefs| -> *mut super::Cfg {
+                    &mut **derefs
+                        .borrow_mut()
+                        .entry(self.handle())
+                        .or_insert_with(|| Box::new(Cfg::current()))
+                });
+                unsafe { &mut *cfg }
+            }
+        }
+    }
+
+    impl<'a> DerefMut for Cfg<'a> {
+        fn deref_mut(&mut self) -> &mut Self::Target {
+            if let Cfg::CFG = self {
+                CONST_DEREFS.with(|derefs| derefs.borrow_mut().remove(&self.handle()));
+                *self = Cfg::Mut(Cfg::current());
+            }
+            match self {
+                Cfg::Mut(cfg) => cfg,
+                Cfg::CFG => unreachable!(),
+            }
+        }
+    }
+
+    impl<'a> Drop for Cfg<'a> {
+        fn drop(&mut self) {
+            if let Cfg::Mut(cfg) = self {
+                let mut current = CURRENT.write().unwrap_or_else(PoisonError::into_inner);
+                current.include_prefix = intern(cfg.include_prefix);
+                current.exported_header_dirs = vec::intern(&cfg.exported_header_dirs);
+                current.exported_header_prefixes = vec::intern(&cfg.exported_header_prefixes);
+                current.exported_header_links = vec::intern(&cfg.exported_header_links);
+            } else {
+                CONST_DEREFS.with(|derefs| derefs.borrow_mut().remove(&self.handle()));
+            }
+        }
+    }
+}
diff --git a/gen/build/src/deps.rs b/gen/build/src/deps.rs
new file mode 100644
index 0000000..fb80072
--- /dev/null
+++ b/gen/build/src/deps.rs
@@ -0,0 +1,107 @@
+use std::collections::BTreeMap;
+use std::env;
+use std::ffi::OsString;
+use std::path::PathBuf;
+
+#[derive(Default)]
+pub struct Crate {
+    pub include_prefix: Option<PathBuf>,
+    pub links: Option<OsString>,
+    pub header_dirs: Vec<HeaderDir>,
+}
+
+pub struct HeaderDir {
+    pub exported: bool,
+    pub path: PathBuf,
+}
+
+impl Crate {
+    pub fn print_to_cargo(&self) {
+        if let Some(include_prefix) = &self.include_prefix {
+            println!(
+                "cargo:CXXBRIDGE_PREFIX={}",
+                include_prefix.to_string_lossy(),
+            );
+        }
+        if let Some(links) = &self.links {
+            println!("cargo:CXXBRIDGE_LINKS={}", links.to_string_lossy());
+        }
+        for (i, header_dir) in self.header_dirs.iter().enumerate() {
+            if header_dir.exported {
+                println!(
+                    "cargo:CXXBRIDGE_DIR{}={}",
+                    i,
+                    header_dir.path.to_string_lossy(),
+                );
+            }
+        }
+    }
+}
+
+pub fn direct_dependencies() -> Vec<Crate> {
+    let mut crates: BTreeMap<String, Crate> = BTreeMap::new();
+    let mut exported_header_dirs: BTreeMap<String, Vec<(usize, PathBuf)>> = BTreeMap::new();
+
+    // Only variables set from a build script of direct dependencies are
+    // observable. That's exactly what we want! Your crate needs to declare a
+    // direct dependency on the other crate in order to be able to #include its
+    // headers.
+    //
+    // Also, they're only observable if the dependency's manifest contains a
+    // `links` key. This is important because Cargo imposes no ordering on the
+    // execution of build scripts without a `links` key. When exposing a
+    // generated header for the current crate to #include, we need to be sure
+    // the dependency's build script has already executed and emitted that
+    // generated header.
+    //
+    // References:
+    //   - https://doc.rust-lang.org/cargo/reference/build-scripts.html#the-links-manifest-key
+    //   - https://doc.rust-lang.org/cargo/reference/build-script-examples.html#using-another-sys-crate
+    for (k, v) in env::vars_os() {
+        let mut k = k.to_string_lossy().into_owned();
+        if !k.starts_with("DEP_") {
+            continue;
+        }
+
+        if k.ends_with("_CXXBRIDGE_PREFIX") {
+            k.truncate(k.len() - "_CXXBRIDGE_PREFIX".len());
+            crates.entry(k).or_default().include_prefix = Some(PathBuf::from(v));
+            continue;
+        }
+
+        if k.ends_with("_CXXBRIDGE_LINKS") {
+            k.truncate(k.len() - "_CXXBRIDGE_LINKS".len());
+            crates.entry(k).or_default().links = Some(v);
+            continue;
+        }
+
+        let without_counter = k.trim_end_matches(|ch: char| ch.is_ascii_digit());
+        let counter_len = k.len() - without_counter.len();
+        if counter_len == 0 || !without_counter.ends_with("_CXXBRIDGE_DIR") {
+            continue;
+        }
+
+        let sort_key = k[k.len() - counter_len..]
+            .parse::<usize>()
+            .unwrap_or(usize::MAX);
+        k.truncate(k.len() - counter_len - "_CXXBRIDGE_DIR".len());
+        exported_header_dirs
+            .entry(k)
+            .or_default()
+            .push((sort_key, PathBuf::from(v)));
+    }
+
+    for (k, mut dirs) in exported_header_dirs {
+        dirs.sort_by_key(|(sort_key, _dir)| *sort_key);
+        crates
+            .entry(k)
+            .or_default()
+            .header_dirs
+            .extend(dirs.into_iter().map(|(_sort_key, dir)| HeaderDir {
+                exported: true,
+                path: dir,
+            }));
+    }
+
+    crates.into_iter().map(|entry| entry.1).collect()
+}
diff --git a/gen/build/src/error.rs b/gen/build/src/error.rs
index 740ab94..99d7a30 100644
--- a/gen/build/src/error.rs
+++ b/gen/build/src/error.rs
@@ -1,22 +1,83 @@
+use crate::cfg::CFG;
+use crate::gen::fs;
 use std::error::Error as StdError;
+use std::ffi::OsString;
 use std::fmt::{self, Display};
-use std::io;
+use std::path::Path;
 
 pub(super) type Result<T, E = Error> = std::result::Result<T, E>;
 
 #[derive(Debug)]
 pub(super) enum Error {
-    MissingOutDir,
-    TargetDir,
-    Io(io::Error),
+    NoEnv(OsString),
+    Fs(fs::Error),
+    ExportedDirNotAbsolute(&'static Path),
+    ExportedEmptyPrefix,
+    ExportedDirsWithoutLinks,
+    ExportedPrefixesWithoutLinks,
+    ExportedLinksWithoutLinks,
+    UnusedExportedPrefix(&'static str),
+    UnusedExportedLinks(&'static str),
 }
 
+macro_rules! expr {
+    ($expr:expr) => {{
+        let _ = $expr; // ensure it doesn't fall out of sync with CFG definition
+        stringify!($expr)
+    }};
+}
+
+const LINKS_DOCUMENTATION: &str =
+    "https://doc.rust-lang.org/cargo/reference/build-scripts.html#the-links-manifest-key";
+
 impl Display for Error {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         match self {
-            Error::MissingOutDir => write!(f, "missing OUT_DIR environment variable"),
-            Error::TargetDir => write!(f, "failed to locate target dir"),
-            Error::Io(err) => err.fmt(f),
+            Error::NoEnv(var) => {
+                write!(f, "missing {} environment variable", var.to_string_lossy())
+            }
+            Error::Fs(err) => err.fmt(f),
+            Error::ExportedDirNotAbsolute(path) => write!(
+                f,
+                "element of {} must be absolute path, but was: {:?}",
+                expr!(CFG.exported_header_dirs),
+                path,
+            ),
+            Error::ExportedEmptyPrefix => write!(
+                f,
+                "element of {} must not be empty string",
+                expr!(CFG.exported_header_prefixes),
+            ),
+            Error::ExportedDirsWithoutLinks => write!(
+                f,
+                "if {} is nonempty then `links` needs to be set in Cargo.toml; see {}",
+                expr!(CFG.exported_header_dirs),
+                LINKS_DOCUMENTATION,
+            ),
+            Error::ExportedPrefixesWithoutLinks => write!(
+                f,
+                "if {} is nonempty then `links` needs to be set in Cargo.toml; see {}",
+                expr!(CFG.exported_header_prefixes),
+                LINKS_DOCUMENTATION,
+            ),
+            Error::ExportedLinksWithoutLinks => write!(
+                f,
+                "if {} is nonempty then `links` needs to be set in Cargo.toml; see {}",
+                expr!(CFG.exported_header_links),
+                LINKS_DOCUMENTATION,
+            ),
+            Error::UnusedExportedPrefix(unused) => write!(
+                f,
+                "unused element in {}: {:?} does not match the include prefix of any direct dependency",
+                expr!(CFG.exported_header_prefixes),
+                unused,
+            ),
+            Error::UnusedExportedLinks(unused) => write!(
+                f,
+                "unused element in {}: {:?} does not match the `links` attribute any direct dependency",
+                expr!(CFG.exported_header_links),
+                unused,
+            ),
         }
     }
 }
@@ -24,14 +85,14 @@
 impl StdError for Error {
     fn source(&self) -> Option<&(dyn StdError + 'static)> {
         match self {
-            Error::Io(err) => Some(err),
+            Error::Fs(err) => err.source(),
             _ => None,
         }
     }
 }
 
-impl From<io::Error> for Error {
-    fn from(err: io::Error) -> Self {
-        Error::Io(err)
+impl From<fs::Error> for Error {
+    fn from(err: fs::Error) -> Self {
+        Error::Fs(err)
     }
 }
diff --git a/gen/build/src/intern.rs b/gen/build/src/intern.rs
new file mode 100644
index 0000000..51bc07c
--- /dev/null
+++ b/gen/build/src/intern.rs
@@ -0,0 +1,28 @@
+use lazy_static::lazy_static;
+use std::collections::HashSet;
+use std::sync::{Mutex, PoisonError};
+
+#[derive(Copy, Clone, Default)]
+pub struct InternedString(&'static str);
+
+impl InternedString {
+    pub fn str(self) -> &'static str {
+        self.0
+    }
+}
+
+pub fn intern(s: &str) -> InternedString {
+    lazy_static! {
+        static ref INTERN: Mutex<HashSet<&'static str>> = Mutex::new(HashSet::new());
+    }
+
+    let mut set = INTERN.lock().unwrap_or_else(PoisonError::into_inner);
+    InternedString(match set.get(s) {
+        Some(interned) => *interned,
+        None => {
+            let interned = Box::leak(Box::from(s));
+            set.insert(interned);
+            interned
+        }
+    })
+}
diff --git a/gen/build/src/lib.rs b/gen/build/src/lib.rs
index 14683b6..db15335 100644
--- a/gen/build/src/lib.rs
+++ b/gen/build/src/lib.rs
@@ -15,18 +15,18 @@
 //!
 //! fn main() {
 //!     cxx_build::bridge("src/main.rs")
-//!         .file("../demo-cxx/demo.cc")
+//!         .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=../demo-cxx/demo.h");
-//!     println!("cargo:rerun-if-changed=../demo-cxx/demo.cc");
+//!     println!("cargo:rerun-if-changed=src/demo.cc");
+//!     println!("cargo:rerun-if-changed=include/demo.h");
 //! }
 //! ```
 //!
-//! A runnable working setup with this build script is shown in the
-//! *demo-rs* and *demo-cxx* directories of [https://github.com/dtolnay/cxx].
+//! A runnable working setup with this build script is shown in the *demo*
+//! directory of [https://github.com/dtolnay/cxx].
 //!
 //! [https://github.com/dtolnay/cxx]: https://github.com/dtolnay/cxx
 //!
@@ -46,33 +46,50 @@
 //! ```
 
 #![allow(
+    clippy::drop_copy,
     clippy::inherent_to_string,
     clippy::needless_doctest_main,
     clippy::new_without_default,
+    clippy::or_fun_call,
     clippy::toplevel_ref_arg
 )]
 
+mod cfg;
+mod deps;
 mod error;
 mod gen;
+mod intern;
+mod out;
 mod paths;
 mod syntax;
+mod target;
+mod vec;
 
-use crate::error::Result;
+use crate::deps::{Crate, HeaderDir};
+use crate::error::{Error, Result};
+use crate::gen::error::report;
 use crate::gen::Opt;
-use anyhow::anyhow;
-use std::fs;
+use crate::paths::PathExt;
+use crate::target::TargetDir;
+use cc::Build;
+use std::collections::btree_map::Entry;
+use std::collections::{BTreeMap, BTreeSet};
+use std::env;
+use std::ffi::{OsStr, OsString};
 use std::io::{self, Write};
 use std::iter;
-use std::path::Path;
+use std::path::{Path, PathBuf};
 use std::process;
 
+pub use crate::cfg::{Cfg, CFG};
+
 /// This returns a [`cc::Build`] on which you should continue to set up any
 /// additional source files or compiler flags, and lastly call its [`compile`]
 /// method to execute the C++ build.
 ///
 /// [`compile`]: https://docs.rs/cc/1.0.49/cc/struct.Build.html#method.compile
 #[must_use]
-pub fn bridge(rust_source_file: impl AsRef<Path>) -> cc::Build {
+pub fn bridge(rust_source_file: impl AsRef<Path>) -> Build {
     bridges(iter::once(rust_source_file))
 }
 
@@ -82,41 +99,301 @@
 /// ```no_run
 /// let source_files = vec!["src/main.rs", "src/path/to/other.rs"];
 /// cxx_build::bridges(source_files)
-///     .file("../demo-cxx/demo.cc")
+///     .file("src/demo.cc")
 ///     .flag_if_supported("-std=c++11")
 ///     .compile("cxxbridge-demo");
 /// ```
-pub fn bridges(rust_source_files: impl IntoIterator<Item = impl AsRef<Path>>) -> cc::Build {
-    let mut build = paths::cc_build();
+#[must_use]
+pub fn bridges(rust_source_files: impl IntoIterator<Item = impl AsRef<Path>>) -> Build {
+    let ref mut rust_source_files = rust_source_files.into_iter();
+    build(rust_source_files).unwrap_or_else(|err| {
+        let _ = writeln!(io::stderr(), "\n\ncxxbridge error: {}\n\n", report(err));
+        process::exit(1);
+    })
+}
+
+struct Project {
+    include_prefix: PathBuf,
+    manifest_dir: PathBuf,
+    // The `links = "..."` value from Cargo.toml.
+    links_attribute: Option<OsString>,
+    // Output directory as received from Cargo.
+    out_dir: PathBuf,
+    // Directory into which to symlink all generated code.
+    //
+    // This is *not* used for an #include path, only as a debugging convenience.
+    // Normally available at target/cxxbridge/ if we are able to know where the
+    // target dir is, otherwise under a common scratch dir.
+    //
+    // The reason this isn't the #include dir is that we do not want builds to
+    // have access to headers from arbitrary other parts of the dependency
+    // graph. Using a global directory for all builds would be both a race
+    // condition depending on what order Cargo randomly executes the build
+    // scripts, as well as semantically undesirable for builds not to have to
+    // declare their real dependencies.
+    shared_dir: PathBuf,
+}
+
+impl Project {
+    fn init() -> Result<Self> {
+        let include_prefix = Path::new(CFG.include_prefix);
+        assert!(include_prefix.is_relative());
+        let include_prefix = include_prefix.components().collect();
+
+        let links_attribute = env::var_os("CARGO_MANIFEST_LINKS");
+
+        let manifest_dir = paths::manifest_dir()?;
+        let out_dir = paths::out_dir()?;
+
+        let shared_dir = match target::find_target_dir(&out_dir) {
+            TargetDir::Path(target_dir) => target_dir.join("cxxbridge"),
+            TargetDir::Unknown => scratch::path("cxxbridge"),
+        };
+
+        Ok(Project {
+            include_prefix,
+            manifest_dir,
+            links_attribute,
+            out_dir,
+            shared_dir,
+        })
+    }
+}
+
+// We lay out the OUT_DIR as follows. Everything is namespaced under a cxxbridge
+// subdirectory to avoid stomping on other things that the caller's build script
+// might be doing inside OUT_DIR.
+//
+//     $OUT_DIR/
+//        cxxbridge/
+//           crate/
+//              $CARGO_PKG_NAME -> $CARGO_MANIFEST_DIR
+//           include/
+//              rust/
+//                 cxx.h
+//              $CARGO_PKG_NAME/
+//                 .../
+//                    lib.rs.h
+//           sources/
+//              $CARGO_PKG_NAME/
+//                 .../
+//                    lib.rs.cc
+//
+// The crate/ and include/ directories are placed on the #include path for the
+// current build as well as for downstream builds that have a direct dependency
+// on the current crate.
+fn build(rust_source_files: &mut dyn Iterator<Item = impl AsRef<Path>>) -> Result<Build> {
+    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);
     build.cpp_link_stdlib(None); // linked via link-cplusplus crate
 
     for path in rust_source_files {
-        if let Err(err) = try_generate_bridge(&mut build, path.as_ref()) {
-            let _ = writeln!(io::stderr(), "\n\ncxxbridge error: {:?}\n\n", anyhow!(err));
-            process::exit(1);
+        generate_bridge(prj, &mut build, path.as_ref())?;
+    }
+
+    eprintln!("\nCXX include path:");
+    for header_dir in this_crate.header_dirs {
+        build.include(&header_dir.path);
+        if header_dir.exported {
+            eprintln!("  {}", header_dir.path.display());
+        } else {
+            eprintln!("  {} (private)", header_dir.path.display());
         }
     }
 
-    build
+    Ok(build)
 }
 
-fn try_generate_bridge(build: &mut cc::Build, rust_source_file: &Path) -> Result<()> {
-    let header = gen::do_generate_header(rust_source_file, Opt::default());
-    let header_path = paths::out_with_extension(rust_source_file, ".h")?;
-    fs::create_dir_all(header_path.parent().unwrap())?;
-    fs::write(&header_path, header)?;
-    paths::symlink_header(&header_path, rust_source_file);
+fn validate_cfg(prj: &Project) -> Result<()> {
+    for exported_dir in &CFG.exported_header_dirs {
+        if !exported_dir.is_absolute() {
+            return Err(Error::ExportedDirNotAbsolute(exported_dir));
+        }
+    }
 
-    let bridge = gen::do_generate_bridge(rust_source_file, Opt::default());
-    let bridge_path = paths::out_with_extension(rust_source_file, ".cc")?;
-    fs::write(&bridge_path, bridge)?;
-    build.file(&bridge_path);
+    for prefix in &CFG.exported_header_prefixes {
+        if prefix.is_empty() {
+            return Err(Error::ExportedEmptyPrefix);
+        }
+    }
 
-    let ref cxx_h = paths::include_dir()?.join("rust").join("cxx.h");
-    let _ = fs::create_dir_all(cxx_h.parent().unwrap());
-    let _ = fs::remove_file(cxx_h);
-    let _ = fs::write(cxx_h, gen::include::HEADER);
+    if prj.links_attribute.is_none() {
+        if !CFG.exported_header_dirs.is_empty() {
+            return Err(Error::ExportedDirsWithoutLinks);
+        }
+        if !CFG.exported_header_prefixes.is_empty() {
+            return Err(Error::ExportedPrefixesWithoutLinks);
+        }
+        if !CFG.exported_header_links.is_empty() {
+            return Err(Error::ExportedLinksWithoutLinks);
+        }
+    }
 
     Ok(())
 }
+
+fn make_this_crate(prj: &Project) -> Result<Crate> {
+    let crate_dir = make_crate_dir(prj);
+    let include_dir = make_include_dir(prj)?;
+
+    let mut this_crate = Crate {
+        include_prefix: Some(prj.include_prefix.clone()),
+        links: prj.links_attribute.clone(),
+        header_dirs: Vec::new(),
+    };
+
+    // The generated code directory (include_dir) is placed in front of
+    // crate_dir on the include line so that `#include "path/to/file.rs"` from
+    // C++ "magically" works and refers to the API generated from that Rust
+    // source file.
+    this_crate.header_dirs.push(HeaderDir {
+        exported: true,
+        path: include_dir,
+    });
+
+    if let Some(crate_dir) = 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 {
+            exported: true,
+            path: PathBuf::from(exported_dir),
+        });
+    }
+
+    let mut header_dirs_index = BTreeMap::new();
+    let mut used_header_links = BTreeSet::new();
+    let mut used_header_prefixes = BTreeSet::new();
+    for krate in deps::direct_dependencies() {
+        let mut is_link_exported = || match &krate.links {
+            None => false,
+            Some(links_attribute) => CFG.exported_header_links.iter().any(|&exported| {
+                let matches = links_attribute == exported;
+                if matches {
+                    used_header_links.insert(exported);
+                }
+                matches
+            }),
+        };
+
+        let mut is_prefix_exported = || match &krate.include_prefix {
+            None => false,
+            Some(include_prefix) => CFG.exported_header_prefixes.iter().any(|&exported| {
+                let matches = include_prefix.starts_with(exported);
+                if matches {
+                    used_header_prefixes.insert(exported);
+                }
+                matches
+            }),
+        };
+
+        let exported = is_link_exported() || is_prefix_exported();
+
+        for dir in krate.header_dirs {
+            // Deduplicate dirs reachable via multiple transitive dependencies.
+            match header_dirs_index.entry(dir.path.clone()) {
+                Entry::Vacant(entry) => {
+                    entry.insert(this_crate.header_dirs.len());
+                    this_crate.header_dirs.push(HeaderDir {
+                        exported,
+                        path: dir.path,
+                    });
+                }
+                Entry::Occupied(entry) => {
+                    let index = *entry.get();
+                    this_crate.header_dirs[index].exported |= exported;
+                }
+            }
+        }
+    }
+
+    if let Some(unused) = CFG
+        .exported_header_links
+        .iter()
+        .find(|&exported| !used_header_links.contains(exported))
+    {
+        return Err(Error::UnusedExportedLinks(unused));
+    }
+
+    if let Some(unused) = CFG
+        .exported_header_prefixes
+        .iter()
+        .find(|&exported| !used_header_prefixes.contains(exported))
+    {
+        return Err(Error::UnusedExportedPrefix(unused));
+    }
+
+    Ok(this_crate)
+}
+
+fn make_crate_dir(prj: &Project) -> Option<PathBuf> {
+    if prj.include_prefix.as_os_str().is_empty() {
+        return Some(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
+    }
+}
+
+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") {
+        out::symlink_file(original, cxx_h)?;
+        out::symlink_file(original, shared_cxx_h)?;
+    } else {
+        out::write(shared_cxx_h, gen::include::HEADER.as_bytes())?;
+        out::symlink_file(shared_cxx_h, cxx_h)?;
+    }
+    Ok(include_dir)
+}
+
+fn generate_bridge(prj: &Project, build: &mut Build, rust_source_file: &Path) -> Result<()> {
+    let opt = Opt {
+        allow_dot_includes: false,
+        ..Opt::default()
+    };
+    let generated = gen::generate_from_path(rust_source_file, &opt);
+    let ref rel_path = paths::local_relative_path(rust_source_file);
+
+    let cxxbridge = prj.out_dir.join("cxxbridge");
+    let include_dir = cxxbridge.join("include").join(&prj.include_prefix);
+    let sources_dir = cxxbridge.join("sources").join(&prj.include_prefix);
+
+    let ref rel_path_h = rel_path.with_appended_extension(".h");
+    let ref header_path = include_dir.join(rel_path_h);
+    out::write(header_path, &generated.header)?;
+
+    let ref link_path = include_dir.join(rel_path);
+    let _ = out::symlink_file(header_path, link_path);
+
+    let ref rel_path_cc = rel_path.with_appended_extension(".cc");
+    let ref implementation_path = sources_dir.join(rel_path_cc);
+    out::write(implementation_path, &generated.implementation)?;
+    build.file(implementation_path);
+
+    let shared_h = prj.shared_dir.join(&prj.include_prefix).join(rel_path_h);
+    let shared_cc = prj.shared_dir.join(&prj.include_prefix).join(rel_path_cc);
+    let _ = out::symlink_file(header_path, shared_h);
+    let _ = out::symlink_file(implementation_path, shared_cc);
+    Ok(())
+}
+
+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
new file mode 100644
index 0000000..b97e992
--- /dev/null
+++ b/gen/build/src/out.rs
@@ -0,0 +1,69 @@
+use crate::error::{Error, Result};
+use crate::gen::fs;
+use crate::paths;
+use std::path::Path;
+
+pub(crate) fn write(path: impl AsRef<Path>, content: &[u8]) -> Result<()> {
+    let path = path.as_ref();
+
+    let mut create_dir_error = None;
+    if path.exists() {
+        if let Ok(existing) = fs::read(path) {
+            if existing == content {
+                // Avoid bumping modified time with unchanged contents.
+                return Ok(());
+            }
+        }
+        let _ = fs::remove_file(path);
+    } else {
+        let parent = path.parent().unwrap();
+        create_dir_error = fs::create_dir_all(parent).err();
+    }
+
+    match fs::write(path, content) {
+        // As long as write succeeded, ignore any create_dir_all error.
+        Ok(()) => Ok(()),
+        // If create_dir_all and write both failed, prefer the first error.
+        Err(err) => Err(Error::Fs(create_dir_error.unwrap_or(err))),
+    }
+}
+
+pub(crate) fn symlink_file(src: impl AsRef<Path>, dst: impl AsRef<Path>) -> Result<()> {
+    let src = src.as_ref();
+    let dst = dst.as_ref();
+
+    let mut create_dir_error = None;
+    if dst.exists() {
+        let _ = fs::remove_file(dst).unwrap();
+    } else {
+        let parent = dst.parent().unwrap();
+        create_dir_error = fs::create_dir_all(parent).err();
+    }
+
+    match paths::symlink_or_copy(src, dst) {
+        // 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.
+        Err(err) => Err(Error::Fs(create_dir_error.unwrap_or(err))),
+    }
+}
+
+pub(crate) fn symlink_dir(src: impl AsRef<Path>, dst: impl AsRef<Path>) -> Result<()> {
+    let src = src.as_ref();
+    let dst = dst.as_ref();
+
+    let mut create_dir_error = None;
+    if dst.exists() {
+        let _ = paths::remove_symlink_dir(dst).unwrap();
+    } else {
+        let parent = dst.parent().unwrap();
+        create_dir_error = fs::create_dir_all(parent).err();
+    }
+
+    match paths::symlink_dir(src, dst) {
+        // 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))),
+    }
+}
diff --git a/gen/build/src/paths.rs b/gen/build/src/paths.rs
index ca183d9..4459363 100644
--- a/gen/build/src/paths.rs
+++ b/gen/build/src/paths.rs
@@ -1,116 +1,74 @@
-use crate::error::{Error, Result};
-use std::env;
-use std::fs;
-use std::path::{Path, PathBuf};
+use crate::error::Result;
+use crate::gen::fs;
+use std::ffi::OsStr;
+use std::path::{Component, Path, PathBuf};
 
-fn out_dir() -> Result<PathBuf> {
-    env::var_os("OUT_DIR")
-        .map(PathBuf::from)
-        .ok_or(Error::MissingOutDir)
+pub(crate) fn manifest_dir() -> Result<PathBuf> {
+    crate::env_os("CARGO_MANIFEST_DIR").map(PathBuf::from)
 }
 
-pub(crate) fn cc_build() -> cc::Build {
-    try_cc_build().unwrap_or_default()
+pub(crate) fn out_dir() -> Result<PathBuf> {
+    crate::env_os("OUT_DIR").map(PathBuf::from)
 }
 
-fn try_cc_build() -> Result<cc::Build> {
-    let mut build = cc::Build::new();
-    build.include(include_dir()?);
-    build.include(target_dir()?.parent().unwrap());
-    Ok(build)
-}
-
-// Symlink the header file into a predictable place. The header generated from
-// path/to/mod.rs gets linked to targets/cxxbridge/path/to/mod.rs.h.
-pub(crate) fn symlink_header(path: &Path, original: &Path) {
-    let _ = try_symlink_header(path, original);
-}
-
-fn try_symlink_header(path: &Path, original: &Path) -> Result<()> {
-    let suffix = relative_to_parent_of_target_dir(original)?;
-    let ref dst = include_dir()?.join(suffix);
-
-    fs::create_dir_all(dst.parent().unwrap())?;
-    let _ = fs::remove_file(dst);
-    symlink_or_copy(path, dst)?;
-
-    let mut file_name = dst.file_name().unwrap().to_os_string();
-    file_name.push(".h");
-    let ref dst2 = dst.with_file_name(file_name);
-    symlink_or_copy(path, dst2)?;
-
-    Ok(())
-}
-
-fn relative_to_parent_of_target_dir(original: &Path) -> Result<PathBuf> {
-    let target_dir = target_dir()?;
-    let mut outer = target_dir.parent().unwrap();
-    let original = canonicalize(original)?;
-    loop {
-        if let Ok(suffix) = original.strip_prefix(outer) {
-            return Ok(suffix.to_owned());
-        }
-        match outer.parent() {
-            Some(parent) => outer = parent,
-            None => return Ok(original.components().skip(1).collect()),
+// Given a path provided by the user, determines where generated files related
+// to that path should go in our out dir. In particular we don't want to
+// accidentally write generated code upward of our out dir, even if the user
+// passed a path containing lots of `..` or an absolute path.
+pub(crate) fn local_relative_path(path: &Path) -> PathBuf {
+    let mut rel_path = PathBuf::new();
+    for component in path.components() {
+        match component {
+            Component::Prefix(_) | Component::RootDir | Component::CurDir => {}
+            Component::ParentDir => drop(rel_path.pop()), // noop if empty
+            Component::Normal(name) => rel_path.push(name),
         }
     }
+    rel_path
 }
 
-pub(crate) fn out_with_extension(path: &Path, ext: &str) -> Result<PathBuf> {
-    let mut file_name = path.file_name().unwrap().to_owned();
-    file_name.push(ext);
-
-    let out_dir = out_dir()?;
-    let rel = relative_to_parent_of_target_dir(path)?;
-    Ok(out_dir.join(rel).with_file_name(file_name))
+pub(crate) trait PathExt {
+    fn with_appended_extension(&self, suffix: impl AsRef<OsStr>) -> PathBuf;
 }
 
-pub(crate) fn include_dir() -> Result<PathBuf> {
-    let target_dir = target_dir()?;
-    Ok(target_dir.join("cxxbridge"))
-}
-
-fn target_dir() -> Result<PathBuf> {
-    let mut dir = out_dir().and_then(canonicalize)?;
-    loop {
-        if dir.ends_with("target") {
-            return Ok(dir);
-        }
-        if !dir.pop() {
-            return Err(Error::TargetDir);
-        }
+impl PathExt for Path {
+    fn with_appended_extension(&self, suffix: impl AsRef<OsStr>) -> PathBuf {
+        let mut file_name = self.file_name().unwrap().to_owned();
+        file_name.push(suffix);
+        self.with_file_name(file_name)
     }
 }
 
-#[cfg(not(windows))]
-fn canonicalize(path: impl AsRef<Path>) -> Result<PathBuf> {
-    Ok(fs::canonicalize(path)?)
-}
-
-#[cfg(windows)]
-fn canonicalize(path: impl AsRef<Path>) -> Result<PathBuf> {
-    // Real fs::canonicalize on Windows produces UNC paths which cl.exe is
-    // unable to handle in includes. Use a poor approximation instead.
-    // https://github.com/rust-lang/rust/issues/42869
-    // https://github.com/alexcrichton/cc-rs/issues/169
-    Ok(env::current_dir()?.join(path))
-}
-
 #[cfg(unix)]
-use std::os::unix::fs::symlink as symlink_or_copy;
+pub(crate) use self::fs::symlink_file as symlink_or_copy;
 
 #[cfg(windows)]
-fn symlink_or_copy(src: &Path, dst: &Path) -> Result<()> {
-    use std::os::windows::fs::symlink_file;
-
+pub(crate) fn symlink_or_copy(src: impl AsRef<Path>, dst: 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.
-    if symlink_file(src, dst).is_err() {
+    let src = src.as_ref();
+    let dst = dst.as_ref();
+    if fs::symlink_file(src, dst).is_err() {
         fs::copy(src, dst)?;
     }
     Ok(())
 }
 
 #[cfg(not(any(unix, windows)))]
-use std::fs::copy as symlink_or_copy;
+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<()> {
+    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
new file mode 100644
index 0000000..58ada3a
--- /dev/null
+++ b/gen/build/src/target.rs
@@ -0,0 +1,42 @@
+use std::env;
+use std::path::{Path, PathBuf};
+
+pub(crate) enum TargetDir {
+    Path(PathBuf),
+    Unknown,
+}
+
+pub(crate) fn find_target_dir(out_dir: &Path) -> TargetDir {
+    if let Some(target_dir) = env::var_os("CARGO_TARGET_DIR") {
+        let target_dir = PathBuf::from(target_dir);
+        if target_dir.is_absolute() {
+            return TargetDir::Path(target_dir);
+        } else {
+            return TargetDir::Unknown;
+        };
+    }
+
+    // fs::canonicalize on Windows produces UNC paths which cl.exe is unable to
+    // handle in includes.
+    // https://github.com/rust-lang/rust/issues/42869
+    // https://github.com/alexcrichton/cc-rs/issues/169
+    let mut also_try_canonical = cfg!(not(windows));
+
+    let mut dir = out_dir.to_owned();
+    loop {
+        if dir.join(".rustc_info.json").exists() || dir.join("CACHEDIR.TAG").exists() {
+            return TargetDir::Path(dir);
+        }
+        if dir.pop() {
+            continue;
+        }
+        if also_try_canonical {
+            if let Ok(canonical_dir) = out_dir.canonicalize() {
+                dir = canonical_dir;
+                also_try_canonical = false;
+                continue;
+            }
+        }
+        return TargetDir::Unknown;
+    }
+}
diff --git a/gen/build/src/vec.rs b/gen/build/src/vec.rs
new file mode 100644
index 0000000..ac9235e
--- /dev/null
+++ b/gen/build/src/vec.rs
@@ -0,0 +1,50 @@
+use crate::intern::{self, InternedString};
+use std::path::Path;
+
+pub trait InternedVec<T>
+where
+    T: ?Sized,
+{
+    fn vec(&self) -> Vec<&'static T>;
+}
+
+impl<T> InternedVec<T> for Vec<InternedString>
+where
+    T: ?Sized + Element,
+{
+    fn vec(&self) -> Vec<&'static T> {
+        self.iter().copied().map(Element::unintern).collect()
+    }
+}
+
+pub fn intern<T>(elements: &[&T]) -> Vec<InternedString>
+where
+    T: ?Sized + Element,
+{
+    elements.iter().copied().map(Element::intern).collect()
+}
+
+pub trait Element {
+    fn intern(&self) -> InternedString;
+    fn unintern(_: InternedString) -> &'static Self;
+}
+
+impl Element for str {
+    fn intern(&self) -> InternedString {
+        intern::intern(self)
+    }
+
+    fn unintern(interned: InternedString) -> &'static Self {
+        interned.str()
+    }
+}
+
+impl Element for Path {
+    fn intern(&self) -> InternedString {
+        intern::intern(&self.to_string_lossy())
+    }
+
+    fn unintern(interned: InternedString) -> &'static Self {
+        Path::new(interned.str())
+    }
+}
diff --git a/gen/cmd/Android.bp b/gen/cmd/Android.bp
index 30d13e3..14e48e9 100644
--- a/gen/cmd/Android.bp
+++ b/gen/cmd/Android.bp
@@ -6,12 +6,11 @@
     srcs: ["src/main.rs"],
     edition: "2018",
     rustlibs: [
-        "libanyhow",
+        "libclap",
         "libcodespan_reporting",
         "libcxxbridge_cmd",
         "libproc_macro2",
         "libquote",
-        "libstructopt",
         "libsyn",
     ],
     compile_multilib: "first",
@@ -23,11 +22,10 @@
     srcs: ["src/lib.rs"],
     edition: "2018",
     rustlibs: [
-        "libanyhow",
+        "libclap",
         "libcodespan_reporting",
         "libproc_macro2",
         "libquote",
-        "libstructopt",
         "libsyn",
     ],
     compile_multilib: "first",
@@ -35,26 +33,17 @@
 
 // dependent_library ["feature_list"]
 //   ansi_term-0.11.0
-//   anyhow-1.0.32 "default,std"
 //   atty-0.2.14
 //   bitflags-1.2.1 "default"
-//   clap-2.33.1 "ansi_term,atty,color,default,strsim,suggestions,vec_map"
+//   clap-2.33.3 "ansi_term,atty,color,default,strsim,suggestions,vec_map"
 //   codespan-reporting-0.9.5
-//   heck-0.3.1
-//   lazy_static-1.4.0
-//   libc-0.2.74
-//   proc-macro-error-1.0.4 "default,syn,syn-error"
-//   proc-macro-error-attr-1.0.4
-//   proc-macro2-1.0.19 "default,proc-macro,span-locations"
-//   quote-1.0.7 "default,proc-macro"
+//   libc-0.2.80
+//   proc-macro2-1.0.24 "span-locations"
+//   quote-1.0.7
 //   strsim-0.8.0
-//   structopt-0.3.15 "default"
-//   structopt-derive-0.4.8
-//   syn-1.0.36 "clone-impls,default,derive,full,parsing,printing,proc-macro,quote"
-//   termcolor-1.1.0
+//   syn-1.0.50 "clone-impls,full,parsing,printing,quote"
+//   termcolor-1.1.2
 //   textwrap-0.11.0
-//   unicode-segmentation-1.6.0
 //   unicode-width-0.1.8 "default"
 //   unicode-xid-0.2.1 "default"
 //   vec_map-0.8.2
-//   version_check-0.9.2
diff --git a/gen/cmd/Cargo.toml b/gen/cmd/Cargo.toml
index 7a80a2f..f3bdebf 100644
--- a/gen/cmd/Cargo.toml
+++ b/gen/cmd/Cargo.toml
@@ -1,11 +1,12 @@
 [package]
 name = "cxxbridge-cmd"
-version = "0.3.4"
+version = "0.5.9"
 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"
+exclude = ["build.rs"]
 keywords = ["ffi"]
 categories = ["development-tools::ffi"]
 
@@ -14,11 +15,10 @@
 path = "src/main.rs"
 
 [dependencies]
-anyhow = "1.0"
+clap = "2.33"
 codespan-reporting = "0.9"
 proc-macro2 = { version = "1.0.17", default-features = false, features = ["span-locations"] }
 quote = { version = "1.0", default-features = false }
-structopt = "0.3"
 syn = { version = "1.0.20", default-features = false, features = ["parsing", "printing", "clone-impls", "full"] }
 
 [package.metadata.docs.rs]
diff --git a/gen/cmd/build.rs b/gen/cmd/build.rs
new file mode 100644
index 0000000..c53bef7
--- /dev/null
+++ b/gen/cmd/build.rs
@@ -0,0 +1 @@
+include!("../../tools/cargo/build.rs");
diff --git a/gen/cmd/src/app.rs b/gen/cmd/src/app.rs
new file mode 100644
index 0000000..e2945a1
--- /dev/null
+++ b/gen/cmd/src/app.rs
@@ -0,0 +1,171 @@
+#[cfg(test)]
+#[path = "test.rs"]
+mod test;
+
+use super::{Opt, Output};
+use crate::gen::include::Include;
+use crate::syntax::IncludeKind;
+use clap::AppSettings;
+use std::ffi::{OsStr, OsString};
+use std::path::PathBuf;
+
+type App = clap::App<'static, 'static>;
+type Arg = clap::Arg<'static, 'static>;
+
+const USAGE: &str = "\
+    cxxbridge <input>.rs              Emit .cc file for bridge to stdout
+    cxxbridge <input>.rs --header     Emit .h file for bridge to stdout
+    cxxbridge --header                Emit \"rust/cxx.h\" header to stdout\
+";
+
+const TEMPLATE: &str = "\
+{bin} {version}
+David Tolnay <dtolnay@gmail.com>
+https://github.com/dtolnay/cxx
+
+USAGE:
+    {usage}
+
+ARGS:
+{positionals}
+OPTIONS:
+{unified}\
+";
+
+fn app() -> App {
+    let mut app = App::new("cxxbridge")
+        .usage(USAGE)
+        .template(TEMPLATE)
+        .setting(AppSettings::NextLineHelp)
+        .arg(arg_input())
+        .arg(arg_cxx_impl_annotations())
+        .arg(arg_header())
+        .arg(arg_include())
+        .arg(arg_output())
+        .help_message("Print help information.")
+        .version_message("Print version information.");
+    if let Some(version) = option_env!("CARGO_PKG_VERSION") {
+        app = app.version(version);
+    }
+    app
+}
+
+const INPUT: &str = "input";
+const CXX_IMPL_ANNOTATIONS: &str = "cxx-impl-annotations";
+const HEADER: &str = "header";
+const INCLUDE: &str = "include";
+const OUTPUT: &str = "output";
+
+pub(super) fn from_args() -> Opt {
+    let matches = app().get_matches();
+
+    let input = matches.value_of_os(INPUT).map(PathBuf::from);
+    let cxx_impl_annotations = matches.value_of(CXX_IMPL_ANNOTATIONS).map(str::to_owned);
+    let header = matches.is_present(HEADER);
+    let include = matches
+        .values_of(INCLUDE)
+        .unwrap_or_default()
+        .map(|include| {
+            if include.starts_with('<') && include.ends_with('>') {
+                Include {
+                    path: include[1..include.len() - 1].to_owned(),
+                    kind: IncludeKind::Bracketed,
+                }
+            } else {
+                Include {
+                    path: include.to_owned(),
+                    kind: IncludeKind::Quoted,
+                }
+            }
+        })
+        .collect();
+
+    let mut outputs = Vec::new();
+    for path in matches.values_of_os(OUTPUT).unwrap_or_default() {
+        outputs.push(if path == "-" {
+            Output::Stdout
+        } else {
+            Output::File(PathBuf::from(path))
+        });
+    }
+    if outputs.is_empty() {
+        outputs.push(Output::Stdout);
+    }
+
+    Opt {
+        input,
+        cxx_impl_annotations,
+        header,
+        include,
+        outputs,
+    }
+}
+
+fn validate_utf8(arg: &OsStr) -> Result<(), OsString> {
+    if arg.to_str().is_some() {
+        Ok(())
+    } else {
+        Err(OsString::from("invalid utf-8 sequence"))
+    }
+}
+
+fn arg_input() -> Arg {
+    Arg::with_name(INPUT)
+        .help("Input Rust source file containing #[cxx::bridge].")
+        .required_unless(HEADER)
+}
+
+fn arg_cxx_impl_annotations() -> Arg {
+    const HELP: &str = "\
+Optional annotation for implementations of C++ function wrappers
+that may be exposed to Rust. You may for example need to provide
+__declspec(dllexport) or __attribute__((visibility(\"default\")))
+if Rust code from one shared object or executable depends on
+these C++ functions in another.
+    ";
+    Arg::with_name(CXX_IMPL_ANNOTATIONS)
+        .long(CXX_IMPL_ANNOTATIONS)
+        .takes_value(true)
+        .value_name("annotation")
+        .validator_os(validate_utf8)
+        .help(HELP)
+}
+
+fn arg_header() -> Arg {
+    const HELP: &str = "\
+Emit header with declarations only. Optional if using `-o` with
+a path ending in `.h`.
+    ";
+    Arg::with_name(HEADER).long(HEADER).help(HELP)
+}
+
+fn arg_include() -> Arg {
+    const HELP: &str = "\
+Any additional headers to #include. The cxxbridge tool does not
+parse or even require the given paths to exist; they simply go
+into the generated C++ code as #include lines.
+    ";
+    Arg::with_name(INCLUDE)
+        .long(INCLUDE)
+        .short("i")
+        .takes_value(true)
+        .multiple(true)
+        .number_of_values(1)
+        .validator_os(validate_utf8)
+        .help(HELP)
+}
+
+fn arg_output() -> Arg {
+    const HELP: &str = "\
+Path of file to write as output. Output goes to stdout if -o is
+not specified.
+    ";
+    Arg::with_name(OUTPUT)
+        .long(OUTPUT)
+        .short("o")
+        .takes_value(true)
+        .multiple(true)
+        .number_of_values(1)
+        .validator_os(validate_utf8)
+        .help(HELP)
+}
diff --git a/gen/cmd/src/main.rs b/gen/cmd/src/main.rs
index 701f77f..c723039 100644
--- a/gen/cmd/src/main.rs
+++ b/gen/cmd/src/main.rs
@@ -3,68 +3,89 @@
     clippy::inherent_to_string,
     clippy::large_enum_variant,
     clippy::new_without_default,
+    clippy::or_fun_call,
     clippy::toplevel_ref_arg
 )]
 
+mod app;
 mod gen;
+mod output;
 mod syntax;
 
-use gen::include;
+use crate::gen::error::{report, Result};
+use crate::gen::fs;
+use crate::gen::include::{self, Include};
+use crate::output::Output;
 use std::io::{self, Write};
 use std::path::PathBuf;
-use structopt::StructOpt;
+use std::process;
 
-#[derive(StructOpt, Debug)]
-#[structopt(
-    name = "cxxbridge",
-    author = "David Tolnay <dtolnay@gmail.com>",
-    about = "https://github.com/dtolnay/cxx",
-    usage = "\
-    cxxbridge <input>.rs              Emit .cc file for bridge to stdout
-    cxxbridge <input>.rs --header     Emit .h file for bridge to stdout
-    cxxbridge --header                Emit rust/cxx.h header to stdout",
-    help_message = "Print help information",
-    version_message = "Print version information"
-)]
+#[derive(Debug)]
 struct Opt {
-    /// Input Rust source file containing #[cxx::bridge]
-    #[structopt(parse(from_os_str), required_unless = "header")]
     input: Option<PathBuf>,
-
-    /// Emit header with declarations only
-    #[structopt(long)]
     header: bool,
-
-    /// Optional annotation for implementations of C++ function
-    /// wrappers that may be exposed to Rust. You may for example
-    /// need to provide __declspec(dllexport) or
-    /// __attribute__((visibility("default"))) if Rust code from
-    /// one shared object or executable depends on these C++ functions
-    /// in another.
-    #[structopt(long)]
     cxx_impl_annotations: Option<String>,
-
-    /// Any additional headers to #include
-    #[structopt(short, long)]
-    include: Vec<String>,
-}
-
-fn write(content: impl AsRef<[u8]>) {
-    let _ = io::stdout().lock().write_all(content.as_ref());
+    include: Vec<Include>,
+    outputs: Vec<Output>,
 }
 
 fn main() {
-    let opt = Opt::from_args();
+    if let Err(err) = try_main() {
+        let _ = writeln!(io::stderr(), "cxxbridge: {}", report(err));
+        process::exit(1);
+    }
+}
+
+enum Kind {
+    GeneratedHeader,
+    GeneratedImplementation,
+    Header,
+}
+
+fn try_main() -> Result<()> {
+    let opt = app::from_args();
+
+    let mut outputs = Vec::new();
+    let mut gen_header = false;
+    let mut gen_implementation = false;
+    for output in opt.outputs {
+        let kind = if opt.input.is_none() {
+            Kind::Header
+        } else if opt.header || output.ends_with(".h") {
+            gen_header = true;
+            Kind::GeneratedHeader
+        } else {
+            gen_implementation = true;
+            Kind::GeneratedImplementation
+        };
+        outputs.push((output, kind));
+    }
 
     let gen = gen::Opt {
         include: opt.include,
         cxx_impl_annotations: opt.cxx_impl_annotations,
+        gen_header,
+        gen_implementation,
+        ..Default::default()
     };
 
-    match (opt.input, opt.header) {
-        (Some(input), true) => write(gen::do_generate_header(&input, gen)),
-        (Some(input), false) => write(gen::do_generate_bridge(&input, gen)),
-        (None, true) => write(include::HEADER),
-        (None, false) => unreachable!(), // enforced by required_unless
+    let generated_code = if let Some(input) = opt.input {
+        gen::generate_from_path(&input, &gen)
+    } else {
+        Default::default()
+    };
+
+    for (output, kind) in outputs {
+        let content = match kind {
+            Kind::GeneratedHeader => &generated_code.header,
+            Kind::GeneratedImplementation => &generated_code.implementation,
+            Kind::Header => include::HEADER.as_bytes(),
+        };
+        match output {
+            Output::Stdout => drop(io::stdout().write_all(content)),
+            Output::File(path) => fs::write(path, content)?,
+        }
     }
+
+    Ok(())
 }
diff --git a/gen/cmd/src/output.rs b/gen/cmd/src/output.rs
new file mode 100644
index 0000000..a46581b
--- /dev/null
+++ b/gen/cmd/src/output.rs
@@ -0,0 +1,16 @@
+use std::path::PathBuf;
+
+#[derive(Debug)]
+pub(crate) enum Output {
+    Stdout,
+    File(PathBuf),
+}
+
+impl Output {
+    pub(crate) fn ends_with(&self, suffix: &str) -> bool {
+        match self {
+            Output::Stdout => false,
+            Output::File(path) => path.to_string_lossy().ends_with(suffix),
+        }
+    }
+}
diff --git a/gen/cmd/src/test.rs b/gen/cmd/src/test.rs
new file mode 100644
index 0000000..17023e4
--- /dev/null
+++ b/gen/cmd/src/test.rs
@@ -0,0 +1,52 @@
+const EXPECTED: &str = "\
+cxxbridge $VERSION
+David Tolnay <dtolnay@gmail.com>
+https://github.com/dtolnay/cxx
+
+USAGE:
+    cxxbridge <input>.rs              Emit .cc file for bridge to stdout
+    cxxbridge <input>.rs --header     Emit .h file for bridge to stdout
+    cxxbridge --header                Emit \"rust/cxx.h\" header to stdout
+
+ARGS:
+    <input>
+            Input Rust source file containing #[cxx::bridge].
+
+OPTIONS:
+        --cxx-impl-annotations <annotation>
+            Optional annotation for implementations of C++ function wrappers
+            that may be exposed to Rust. You may for example need to provide
+            __declspec(dllexport) or __attribute__((visibility(\"default\")))
+            if Rust code from one shared object or executable depends on
+            these C++ functions in another.
+               \x20
+    -h, --help
+            Print help information.
+
+        --header
+            Emit header with declarations only. Optional if using `-o` with
+            a path ending in `.h`.
+               \x20
+    -i, --include <include>...
+            Any additional headers to #include. The cxxbridge tool does not
+            parse or even require the given paths to exist; they simply go
+            into the generated C++ code as #include lines.
+               \x20
+    -o, --output <output>...
+            Path of file to write as output. Output goes to stdout if -o is
+            not specified.
+               \x20
+    -V, --version
+            Print version information.
+";
+
+#[test]
+fn test_help() {
+    let mut app = super::app();
+    let mut out = Vec::new();
+    app.write_long_help(&mut out).unwrap();
+    let help = String::from_utf8(out).unwrap();
+    let version = option_env!("CARGO_PKG_VERSION").unwrap_or_default();
+    let expected = EXPECTED.replace("$VERSION", version);
+    assert_eq!(help, expected);
+}
diff --git a/gen/lib/Cargo.toml b/gen/lib/Cargo.toml
new file mode 100644
index 0000000..2de0f28
--- /dev/null
+++ b/gen/lib/Cargo.toml
@@ -0,0 +1,21 @@
+[package]
+name = "cxx-gen"
+version = "0.6.6"
+authors = ["Adrian Taylor <adetaylor@chromium.org>"]
+edition = "2018"
+license = "MIT OR Apache-2.0"
+description = "C++ code generator for integrating `cxx` crate into higher level tools."
+repository = "https://github.com/dtolnay/cxx"
+exclude = ["build.rs"]
+keywords = ["ffi"]
+categories = ["development-tools::ffi"]
+
+[dependencies]
+cc = "1.0.49"
+codespan-reporting = "0.9"
+proc-macro2 = { version = "1.0.17", default-features = false, features = ["span-locations"] }
+quote = { version = "1.0", default-features = false }
+syn = { version = "1.0.20", default-features = false, features = ["parsing", "printing", "clone-impls", "full"] }
+
+[package.metadata.docs.rs]
+targets = ["x86_64-unknown-linux-gnu"]
diff --git a/gen/lib/LICENSE-APACHE b/gen/lib/LICENSE-APACHE
new file mode 120000
index 0000000..1cd601d
--- /dev/null
+++ b/gen/lib/LICENSE-APACHE
@@ -0,0 +1 @@
+../../LICENSE-APACHE
\ No newline at end of file
diff --git a/gen/lib/LICENSE-MIT b/gen/lib/LICENSE-MIT
new file mode 120000
index 0000000..b2cfbdc
--- /dev/null
+++ b/gen/lib/LICENSE-MIT
@@ -0,0 +1 @@
+../../LICENSE-MIT
\ No newline at end of file
diff --git a/gen/lib/build.rs b/gen/lib/build.rs
new file mode 100644
index 0000000..c53bef7
--- /dev/null
+++ b/gen/lib/build.rs
@@ -0,0 +1 @@
+include!("../../tools/cargo/build.rs");
diff --git a/gen/lib/src/error.rs b/gen/lib/src/error.rs
new file mode 100644
index 0000000..26249be
--- /dev/null
+++ b/gen/lib/src/error.rs
@@ -0,0 +1,33 @@
+// We can expose more detail on the error as the need arises, but start with an
+// opaque error type for now.
+
+use std::error::Error as StdError;
+use std::fmt::{self, Debug, Display};
+
+pub struct Error {
+    pub(crate) err: crate::gen::Error,
+}
+
+impl From<crate::gen::Error> for Error {
+    fn from(err: crate::gen::Error) -> Self {
+        Error { err }
+    }
+}
+
+impl Display for Error {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        Display::fmt(&self.err, f)
+    }
+}
+
+impl Debug for Error {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        Debug::fmt(&self.err, f)
+    }
+}
+
+impl StdError for Error {
+    fn source(&self) -> Option<&(dyn StdError + 'static)> {
+        self.err.source()
+    }
+}
diff --git a/gen/lib/src/gen b/gen/lib/src/gen
new file mode 120000
index 0000000..929cb3d
--- /dev/null
+++ b/gen/lib/src/gen
@@ -0,0 +1 @@
+../../src
\ No newline at end of file
diff --git a/gen/lib/src/lib.rs b/gen/lib/src/lib.rs
new file mode 100644
index 0000000..963e870
--- /dev/null
+++ b/gen/lib/src/lib.rs
@@ -0,0 +1,35 @@
+//! The CXX code generator for constructing and compiling C++ code.
+//!
+//! This is intended as a mechanism for embedding the `cxx` crate into
+//! higher-level code generators. See [dtolnay/cxx#235] and
+//! [https://github.com/google/autocxx].
+//!
+//! [dtolnay/cxx#235]: https://github.com/dtolnay/cxx/issues/235
+//! [https://github.com/google/autocxx]: https://github.com/google/autocxx
+
+#![allow(dead_code)]
+#![allow(
+    clippy::inherent_to_string,
+    clippy::new_without_default,
+    clippy::or_fun_call,
+    clippy::toplevel_ref_arg
+)]
+
+mod error;
+mod gen;
+mod syntax;
+
+pub use crate::error::Error;
+pub use crate::gen::include::{Include, HEADER};
+pub use crate::gen::{GeneratedCode, Opt};
+pub use crate::syntax::IncludeKind;
+use proc_macro2::TokenStream;
+
+/// Generate C++ bindings code from a Rust token stream. This should be a Rust
+/// token stream which somewhere contains a `#[cxx::bridge] mod {}`.
+pub fn generate_header_and_cc(rust_source: TokenStream, opt: &Opt) -> Result<GeneratedCode, Error> {
+    let syntax = syn::parse2(rust_source)
+        .map_err(crate::gen::Error::from)
+        .map_err(Error::from)?;
+    gen::generate(syntax, opt).map_err(Error::from)
+}
diff --git a/gen/lib/src/syntax b/gen/lib/src/syntax
new file mode 120000
index 0000000..a6fe06c
--- /dev/null
+++ b/gen/lib/src/syntax
@@ -0,0 +1 @@
+../../../syntax
\ No newline at end of file
diff --git a/gen/lib/tests/test.rs b/gen/lib/tests/test.rs
new file mode 100644
index 0000000..73a25f3
--- /dev/null
+++ b/gen/lib/tests/test.rs
@@ -0,0 +1,28 @@
+use cxx_gen::Opt;
+use quote::quote;
+
+#[test]
+fn test_positive() {
+    let rs = quote! {
+        #[cxx::bridge]
+        mod ffi {
+            extern "C" {
+                fn in_C();
+            }
+            extern "Rust" {
+                fn in_rs();
+            }
+        }
+    };
+    let opt = Opt::default();
+    let code = cxx_gen::generate_header_and_cc(rs, &opt).unwrap();
+    assert!(!code.header.is_empty());
+    assert!(!code.implementation.is_empty());
+}
+
+#[test]
+fn test_negative() {
+    let rs = quote! {};
+    let opt = Opt::default();
+    assert!(cxx_gen::generate_header_and_cc(rs, &opt).is_err())
+}
diff --git a/gen/src/block.rs b/gen/src/block.rs
new file mode 100644
index 0000000..96a9a6e
--- /dev/null
+++ b/gen/src/block.rs
@@ -0,0 +1,45 @@
+use proc_macro2::Ident;
+
+#[derive(Copy, Clone, PartialEq, Debug)]
+pub enum Block<'a> {
+    AnonymousNamespace,
+    Namespace(&'static str),
+    UserDefinedNamespace(&'a Ident),
+    InlineNamespace(&'static str),
+    ExternC,
+}
+
+impl<'a> Block<'a> {
+    pub fn write_begin(self, out: &mut String) {
+        if let Block::InlineNamespace(_) = self {
+            out.push_str("inline ");
+        }
+        self.write_common(out);
+        out.push_str(" {\n");
+    }
+
+    pub fn write_end(self, out: &mut String) {
+        out.push_str("} // ");
+        self.write_common(out);
+        out.push('\n');
+    }
+
+    fn write_common(self, out: &mut String) {
+        match self {
+            Block::AnonymousNamespace => out.push_str("namespace"),
+            Block::Namespace(name) => {
+                out.push_str("namespace ");
+                out.push_str(name);
+            }
+            Block::UserDefinedNamespace(name) => {
+                out.push_str("namespace ");
+                out.push_str(&name.to_string());
+            }
+            Block::InlineNamespace(name) => {
+                out.push_str("namespace ");
+                out.push_str(name);
+            }
+            Block::ExternC => out.push_str("extern \"C\""),
+        }
+    }
+}
diff --git a/gen/src/builtin.rs b/gen/src/builtin.rs
new file mode 100644
index 0000000..41d037a
--- /dev/null
+++ b/gen/src/builtin.rs
@@ -0,0 +1,259 @@
+use crate::gen::block::Block;
+use crate::gen::ifndef;
+use crate::gen::out::{Content, OutFile};
+
+#[derive(Default, PartialEq)]
+pub struct Builtins<'a> {
+    pub panic: bool,
+    pub rust_string: bool,
+    pub rust_str: bool,
+    pub rust_slice: bool,
+    pub rust_box: bool,
+    pub rust_vec: bool,
+    pub rust_fn: bool,
+    pub rust_isize: bool,
+    pub unsafe_bitcopy: bool,
+    pub rust_error: bool,
+    pub manually_drop: bool,
+    pub maybe_uninit: bool,
+    pub trycatch: bool,
+    pub ptr_len: bool,
+    pub rust_str_new_unchecked: bool,
+    pub rust_str_repr: bool,
+    pub rust_slice_new: bool,
+    pub rust_slice_repr: bool,
+    pub exception: bool,
+    pub relocatable: bool,
+    pub friend_impl: bool,
+    pub content: Content<'a>,
+}
+
+impl<'a> Builtins<'a> {
+    pub fn new() -> Self {
+        Builtins::default()
+    }
+}
+
+pub(super) fn write(out: &mut OutFile) {
+    if out.builtin == Default::default() {
+        return;
+    }
+
+    let include = &mut out.include;
+    let builtin = &mut out.builtin;
+    let out = &mut builtin.content;
+
+    if builtin.rust_string {
+        include.array = true;
+        include.cstdint = true;
+        include.string = true;
+    }
+
+    if builtin.rust_str {
+        include.cstdint = true;
+        include.string = true;
+        builtin.friend_impl = true;
+    }
+
+    if builtin.rust_slice {
+        builtin.friend_impl = true;
+    }
+
+    if builtin.rust_box {
+        include.new = true;
+        include.type_traits = true;
+        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;
+    }
+
+    if builtin.rust_error {
+        include.exception = true;
+        builtin.friend_impl = true;
+    }
+
+    if builtin.rust_isize {
+        include.basetsd = true;
+    }
+
+    if builtin.relocatable {
+        include.type_traits = true;
+    }
+
+    out.begin_block(Block::Namespace("rust"));
+    out.begin_block(Block::InlineNamespace("cxxbridge05"));
+    writeln!(out, "// #include \"rust/cxx.h\"");
+
+    ifndef::write(out, builtin.panic, "CXXBRIDGE05_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);
+    }
+
+    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");
+
+    if builtin.manually_drop {
+        out.next_section();
+        include.utility = true;
+        writeln!(out, "template <typename T>");
+        writeln!(out, "union ManuallyDrop {{");
+        writeln!(out, "  T value;");
+        writeln!(
+            out,
+            "  ManuallyDrop(T &&value) : value(::std::move(value)) {{}}",
+        );
+        writeln!(out, "  ~ManuallyDrop() {{}}");
+        writeln!(out, "}};");
+    }
+
+    if builtin.maybe_uninit {
+        out.next_section();
+        writeln!(out, "template <typename T>");
+        writeln!(out, "union MaybeUninit {{");
+        writeln!(out, "  T value;");
+        writeln!(out, "  MaybeUninit() {{}}");
+        writeln!(out, "  ~MaybeUninit() {{}}");
+        writeln!(out, "}};");
+    }
+
+    out.begin_block(Block::AnonymousNamespace);
+
+    if builtin.ptr_len {
+        out.begin_block(Block::Namespace("repr"));
+        writeln!(out, "struct PtrLen final {{");
+        writeln!(out, "  const void *ptr;");
+        writeln!(out, "  size_t len;");
+        writeln!(out, "}};");
+        out.end_block(Block::Namespace("repr"));
+    }
+
+    if builtin.rust_str_new_unchecked || builtin.rust_str_repr {
+        out.next_section();
+        writeln!(out, "template <>");
+        writeln!(out, "class impl<Str> final {{");
+        writeln!(out, "public:");
+        if builtin.rust_str_new_unchecked {
+            writeln!(
+                out,
+                "  static Str new_unchecked(repr::PtrLen repr) noexcept {{",
+            );
+            writeln!(out, "    Str str;");
+            writeln!(out, "    str.ptr = static_cast<const char *>(repr.ptr);");
+            writeln!(out, "    str.len = repr.len;");
+            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, "  }}");
+        }
+        writeln!(out, "}};");
+    }
+
+    if builtin.rust_slice_new || builtin.rust_slice_repr {
+        out.next_section();
+        writeln!(out, "template <typename T>");
+        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, "  }}");
+        }
+        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, "  }}");
+        }
+        writeln!(out, "}};");
+    }
+
+    if builtin.rust_error {
+        out.next_section();
+        writeln!(out, "template <>");
+        writeln!(out, "class impl<Error> final {{");
+        writeln!(out, "public:");
+        writeln!(out, "  static Error error(repr::PtrLen repr) noexcept {{");
+        writeln!(out, "    Error error;");
+        writeln!(out, "    error.msg = static_cast<const char *>(repr.ptr);");
+        writeln!(out, "    error.len = repr.len;");
+        writeln!(out, "    return error;");
+        writeln!(out, "  }}");
+        writeln!(out, "}};");
+    }
+
+    out.end_block(Block::AnonymousNamespace);
+    out.end_block(Block::InlineNamespace("cxxbridge05"));
+
+    if builtin.trycatch {
+        out.begin_block(Block::Namespace("behavior"));
+        include.exception = true;
+        include.type_traits = true;
+        include.utility = true;
+        writeln!(out, "class missing {{}};");
+        writeln!(out, "missing trycatch(...);");
+        writeln!(out);
+        writeln!(out, "template <typename Try, typename Fail>");
+        writeln!(out, "static typename ::std::enable_if<");
+        writeln!(
+            out,
+            "    ::std::is_same<decltype(trycatch(::std::declval<Try>(), ::std::declval<Fail>())),",
+        );
+        writeln!(out, "                 missing>::value>::type");
+        writeln!(out, "trycatch(Try &&func, Fail &&fail) noexcept try {{");
+        writeln!(out, "  func();");
+        writeln!(out, "}} catch (const ::std::exception &e) {{");
+        writeln!(out, "  fail(e.what());");
+        writeln!(out, "}}");
+        out.end_block(Block::Namespace("behavior"));
+    }
+
+    out.end_block(Block::Namespace("rust"));
+
+    if builtin.exception {
+        out.begin_block(Block::ExternC);
+        writeln!(
+            out,
+            "const char *cxxbridge05$exception(const char *, size_t);",
+        );
+        out.end_block(Block::ExternC);
+    }
+}
diff --git a/gen/src/check.rs b/gen/src/check.rs
new file mode 100644
index 0000000..35929ad
--- /dev/null
+++ b/gen/src/check.rs
@@ -0,0 +1,27 @@
+use crate::gen::Opt;
+use crate::syntax::report::Errors;
+use crate::syntax::{error, Api};
+use quote::{quote, quote_spanned};
+use std::path::{Component, Path};
+
+pub(super) use crate::syntax::check::typecheck;
+
+pub(super) fn precheck(cx: &mut Errors, apis: &[Api], opt: &Opt) {
+    if !opt.allow_dot_includes {
+        check_dot_includes(cx, apis);
+    }
+}
+
+fn check_dot_includes(cx: &mut Errors, apis: &[Api]) {
+    for api in apis {
+        if let Api::Include(include) = api {
+            let first_component = Path::new(&include.path).components().next();
+            if let Some(Component::CurDir) | Some(Component::ParentDir) = first_component {
+                let begin = quote_spanned!(include.begin_span=> .);
+                let end = quote_spanned!(include.end_span=> .);
+                let span = quote!(#begin #end);
+                cx.error(span, error::DOT_INCLUDE.msg);
+            }
+        }
+    }
+}
diff --git a/gen/src/error.rs b/gen/src/error.rs
index 51dbbe7..2335d43 100644
--- a/gen/src/error.rs
+++ b/gen/src/error.rs
@@ -1,23 +1,25 @@
+use crate::gen::fs;
 use crate::syntax;
-use anyhow::anyhow;
 use codespan_reporting::diagnostic::{Diagnostic, Label};
 use codespan_reporting::files::SimpleFiles;
 use codespan_reporting::term::termcolor::{ColorChoice, StandardStream, WriteColor};
 use codespan_reporting::term::{self, Config};
+use std::borrow::Cow;
 use std::error::Error as StdError;
 use std::fmt::{self, Display};
 use std::io::{self, Write};
 use std::ops::Range;
-use std::path::Path;
+use std::path::{Path, PathBuf};
 use std::process;
+use std::str::Utf8Error;
 
-pub(super) type Result<T, E = Error> = std::result::Result<T, E>;
+pub(crate) type Result<T, E = Error> = std::result::Result<T, E>;
 
 #[derive(Debug)]
-pub(super) enum Error {
+pub(crate) enum Error {
     NoBridgeMod,
-    OutOfLineMod,
-    Io(io::Error),
+    Fs(fs::Error),
+    Utf8(PathBuf, Utf8Error),
     Syn(syn::Error),
 }
 
@@ -25,8 +27,8 @@
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         match self {
             Error::NoBridgeMod => write!(f, "no #[cxx::bridge] module found"),
-            Error::OutOfLineMod => write!(f, "#[cxx::bridge] module must have inline contents"),
-            Error::Io(err) => err.fmt(f),
+            Error::Fs(err) => err.fmt(f),
+            Error::Utf8(path, _) => write!(f, "Failed to read file `{}`", path.display()),
             Error::Syn(err) => err.fmt(f),
         }
     }
@@ -35,16 +37,17 @@
 impl StdError for Error {
     fn source(&self) -> Option<&(dyn StdError + 'static)> {
         match self {
-            Error::Io(err) => Some(err),
-            Error::Syn(err) => Some(err),
+            Error::Fs(err) => err.source(),
+            Error::Utf8(_, err) => Some(err),
+            Error::Syn(err) => err.source(),
             _ => None,
         }
     }
 }
 
-impl From<io::Error> for Error {
-    fn from(err: io::Error) -> Self {
-        Error::Io(err)
+impl From<fs::Error> for Error {
+    fn from(err: fs::Error) -> Self {
+        Error::Fs(err)
     }
 }
 
@@ -65,11 +68,34 @@
                 display_syn_error(stderr, path, source, error);
             }
         }
-        _ => eprintln!("cxxbridge: {:?}", anyhow!(error)),
+        _ => {
+            let _ = writeln!(io::stderr(), "cxxbridge: {}", report(error));
+        }
     }
     process::exit(1);
 }
 
+pub(crate) fn report(error: impl StdError) -> impl Display {
+    struct Report<E>(E);
+
+    impl<E: StdError> Display for Report<E> {
+        fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+            Display::fmt(&self.0, formatter)?;
+            let mut error: &dyn StdError = &self.0;
+
+            while let Some(cause) = error.source() {
+                formatter.write_str("\n\nCaused by:\n    ")?;
+                Display::fmt(cause, formatter)?;
+                error = cause;
+            }
+
+            Ok(())
+        }
+    }
+
+    Report(error)
+}
+
 fn sort_syn_errors(error: syn::Error) -> Vec<syn::Error> {
     let mut errors: Vec<_> = error.into_iter().collect();
     errors.sort_by_key(|e| {
@@ -109,8 +135,13 @@
         .map(char::len_utf8)
         .sum::<usize>();
 
+    let mut path = path.to_string_lossy();
+    if path == "-" {
+        path = Cow::Borrowed(if cfg!(unix) { "/dev/stdin" } else { "stdin" });
+    }
+
     let mut files = SimpleFiles::new();
-    let file = files.add(path.to_string_lossy(), source);
+    let file = files.add(path, source);
 
     let diagnostic = diagnose(file, start_offset..end_offset, error);
 
diff --git a/gen/src/file.rs b/gen/src/file.rs
new file mode 100644
index 0000000..46616fb
--- /dev/null
+++ b/gen/src/file.rs
@@ -0,0 +1,72 @@
+use crate::syntax::file::Module;
+use crate::syntax::namespace::Namespace;
+use syn::parse::discouraged::Speculative;
+use syn::parse::{Error, Parse, ParseStream, Result};
+use syn::{braced, Attribute, Ident, Item, Token, Visibility};
+
+pub struct File {
+    pub modules: Vec<Module>,
+}
+
+impl Parse for File {
+    fn parse(input: ParseStream) -> Result<Self> {
+        let mut modules = Vec::new();
+        input.call(Attribute::parse_inner)?;
+        parse(input, &mut modules)?;
+        Ok(File { modules })
+    }
+}
+
+fn parse(input: ParseStream, modules: &mut Vec<Module>) -> Result<()> {
+    while !input.is_empty() {
+        let mut cxx_bridge = false;
+        let mut namespace = Namespace::ROOT;
+        let mut attrs = input.call(Attribute::parse_outer)?;
+        for attr in &attrs {
+            let path = &attr.path.segments;
+            if path.len() == 2 && path[0].ident == "cxx" && path[1].ident == "bridge" {
+                cxx_bridge = true;
+                namespace = parse_args(attr)?;
+                break;
+            }
+        }
+
+        let ahead = input.fork();
+        ahead.parse::<Visibility>()?;
+        ahead.parse::<Option<Token![unsafe]>>()?;
+        if !ahead.peek(Token![mod]) {
+            let item: Item = input.parse()?;
+            if cxx_bridge {
+                return Err(Error::new_spanned(item, "expected a module"));
+            }
+            continue;
+        }
+
+        if cxx_bridge {
+            let mut module: Module = input.parse()?;
+            module.namespace = namespace;
+            attrs.extend(module.attrs);
+            module.attrs = attrs;
+            modules.push(module);
+        } else {
+            input.advance_to(&ahead);
+            input.parse::<Token![mod]>()?;
+            input.parse::<Ident>()?;
+            let semi: Option<Token![;]> = input.parse()?;
+            if semi.is_none() {
+                let content;
+                braced!(content in input);
+                parse(&content, modules)?;
+            }
+        }
+    }
+    Ok(())
+}
+
+fn parse_args(attr: &Attribute) -> Result<Namespace> {
+    if attr.tokens.is_empty() {
+        Ok(Namespace::ROOT)
+    } else {
+        attr.parse_args_with(Namespace::parse_bridge_attr_namespace)
+    }
+}
diff --git a/gen/src/find.rs b/gen/src/find.rs
deleted file mode 100644
index 86e1dc7..0000000
--- a/gen/src/find.rs
+++ /dev/null
@@ -1,48 +0,0 @@
-use crate::gen::{Error, Input, Result};
-use crate::syntax::namespace::Namespace;
-use quote::quote;
-use syn::{Attribute, File, Item};
-
-pub(super) fn find_bridge_mod(syntax: File) -> Result<Input> {
-    match scan(syntax.items)? {
-        Some(input) => Ok(input),
-        None => Err(Error::NoBridgeMod),
-    }
-}
-
-fn scan(items: Vec<Item>) -> Result<Option<Input>> {
-    for item in items {
-        if let Item::Mod(item) = item {
-            for attr in &item.attrs {
-                let path = &attr.path;
-                if quote!(#path).to_string() == "cxx :: bridge" {
-                    let module = match item.content {
-                        Some(module) => module.1,
-                        None => {
-                            return Err(Error::Syn(syn::Error::new_spanned(
-                                item,
-                                Error::OutOfLineMod,
-                            )));
-                        }
-                    };
-                    let namespace = parse_args(attr)?;
-                    return Ok(Some(Input { namespace, module }));
-                }
-            }
-            if let Some(module) = item.content {
-                if let Some(input) = scan(module.1)? {
-                    return Ok(Some(input));
-                }
-            }
-        }
-    }
-    Ok(None)
-}
-
-fn parse_args(attr: &Attribute) -> syn::Result<Namespace> {
-    if attr.tokens.is_empty() {
-        Ok(Namespace::none())
-    } else {
-        attr.parse_args()
-    }
-}
diff --git a/gen/src/fs.rs b/gen/src/fs.rs
new file mode 100644
index 0000000..8f94f00
--- /dev/null
+++ b/gen/src/fs.rs
@@ -0,0 +1,142 @@
+#![allow(dead_code)]
+
+use std::error::Error as StdError;
+use std::fmt::{self, Display};
+use std::io::{self, Read};
+use std::path::{Path, PathBuf};
+
+pub(crate) type Result<T> = std::result::Result<T, Error>;
+
+#[derive(Debug)]
+pub(crate) struct Error {
+    source: io::Error,
+    message: String,
+}
+
+impl Display for Error {
+    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+        formatter.write_str(&self.message)
+    }
+}
+
+impl StdError for Error {
+    fn source(&self) -> Option<&(dyn StdError + 'static)> {
+        Some(&self.source)
+    }
+}
+
+macro_rules! err {
+    ($io_error:expr, $fmt:expr $(, $path:expr)* $(,)?) => {
+        Err(Error {
+            source: $io_error,
+            message: format!($fmt $(, $path.display())*),
+        })
+    }
+}
+
+pub(crate) fn copy(from: impl AsRef<Path>, to: impl AsRef<Path>) -> Result<u64> {
+    let from = from.as_ref();
+    let to = to.as_ref();
+    match std::fs::copy(from, to) {
+        Ok(n) => Ok(n),
+        Err(e) => err!(e, "Failed to copy `{}` -> `{}`", from, to),
+    }
+}
+
+pub(crate) fn create_dir_all(path: impl AsRef<Path>) -> Result<()> {
+    let path = path.as_ref();
+    match std::fs::create_dir_all(path) {
+        Ok(()) => Ok(()),
+        Err(e) => err!(e, "Failed to create directory `{}`", path),
+    }
+}
+
+pub(crate) fn current_dir() -> Result<PathBuf> {
+    match std::env::current_dir() {
+        Ok(dir) => Ok(dir),
+        Err(e) => err!(e, "Failed to determine current directory"),
+    }
+}
+
+pub(crate) fn read(path: impl AsRef<Path>) -> Result<Vec<u8>> {
+    let path = path.as_ref();
+    match std::fs::read(path) {
+        Ok(string) => Ok(string),
+        Err(e) => err!(e, "Failed to read file `{}`", path),
+    }
+}
+
+pub(crate) fn read_stdin() -> Result<Vec<u8>> {
+    let mut bytes = Vec::new();
+    match io::stdin().read_to_end(&mut bytes) {
+        Ok(_len) => Ok(bytes),
+        Err(e) => err!(e, "Failed to read input from stdin"),
+    }
+}
+
+pub(crate) fn remove_file(path: impl AsRef<Path>) -> Result<()> {
+    let path = path.as_ref();
+    match std::fs::remove_file(path) {
+        Ok(()) => Ok(()),
+        Err(e) => err!(e, "Failed to remove file `{}`", path),
+    }
+}
+
+pub(crate) fn remove_dir(path: impl AsRef<Path>) -> Result<()> {
+    let path = path.as_ref();
+    match std::fs::remove_dir(path) {
+        Ok(()) => Ok(()),
+        Err(e) => err!(e, "Failed to remove directory `{}`", path),
+    }
+}
+
+fn symlink<'a>(
+    src: &'a Path,
+    dst: &'a Path,
+    fun: fn(&'a Path, &'a Path) -> io::Result<()>,
+) -> Result<()> {
+    match fun(src, dst) {
+        Ok(()) => Ok(()),
+        Err(e) => err!(
+            e,
+            "Failed to create symlink `{}` pointing to `{}`",
+            dst,
+            src,
+        ),
+    }
+}
+
+#[cfg(unix)]
+#[allow(unused_imports)]
+pub(crate) use self::symlink_file 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)
+}
+
+#[cfg(windows)]
+pub(crate) fn symlink_file(src: impl AsRef<Path>, dst: impl AsRef<Path>) -> Result<()> {
+    symlink(
+        src.as_ref(),
+        dst.as_ref(),
+        std::os::windows::fs::symlink_file,
+    )
+}
+
+#[cfg(windows)]
+pub(crate) fn symlink_dir(src: impl AsRef<Path>, dst: impl AsRef<Path>) -> Result<()> {
+    symlink(
+        src.as_ref(),
+        dst.as_ref(),
+        std::os::windows::fs::symlink_dir,
+    )
+}
+
+pub(crate) fn write(path: impl AsRef<Path>, contents: impl AsRef<[u8]>) -> Result<()> {
+    let path = path.as_ref();
+    match std::fs::write(path, contents) {
+        Ok(()) => Ok(()),
+        Err(e) => err!(e, "Failed to write file `{}`", path),
+    }
+}
diff --git a/gen/src/ifndef.rs b/gen/src/ifndef.rs
new file mode 100644
index 0000000..b436266
--- /dev/null
+++ b/gen/src/ifndef.rs
@@ -0,0 +1,46 @@
+use crate::gen::include::HEADER;
+use crate::gen::out::Content;
+
+pub(super) fn write(out: &mut Content, needed: bool, guard: &str) {
+    let ifndef = format!("#ifndef {}", guard);
+    let define = format!("#define {}", guard);
+    let endif = format!("#endif // {}", guard);
+
+    let mut offset = 0;
+    loop {
+        let begin = find_line(offset, &ifndef);
+        let end = find_line(offset, &endif);
+        if let (Some(begin), Some(end)) = (begin, end) {
+            if !needed {
+                return;
+            }
+            out.next_section();
+            if offset == 0 {
+                writeln!(out, "{}", ifndef);
+                writeln!(out, "{}", define);
+            }
+            for line in HEADER[begin + ifndef.len()..end].trim().lines() {
+                if line != define && !line.trim_start().starts_with("//") {
+                    writeln!(out, "{}", line);
+                }
+            }
+            offset = end + endif.len();
+        } else if offset == 0 {
+            panic!("not found in cxx.h header: {}", guard)
+        } else {
+            writeln!(out, "{}", endif);
+            return;
+        }
+    }
+}
+
+fn find_line(mut offset: usize, line: &str) -> Option<usize> {
+    loop {
+        offset += HEADER[offset..].find(line)?;
+        let rest = &HEADER[offset + line.len()..];
+        if rest.starts_with('\n') || rest.starts_with('\r') {
+            return Some(offset);
+        }
+        offset += line.len();
+    }
+}
diff --git a/gen/src/include.rs b/gen/src/include.rs
index 4309c9c..cf718da 100644
--- a/gen/src/include.rs
+++ b/gen/src/include.rs
@@ -1,55 +1,26 @@
-use crate::gen::out::OutFile;
-use std::fmt::{self, Display};
+use crate::gen::out::{Content, OutFile};
+use crate::syntax::{self, IncludeKind};
+use std::ops::{Deref, DerefMut};
 
+/// The complete contents of the "rust/cxx.h" header.
 pub static HEADER: &str = include_str!("include/cxx.h");
 
-pub(super) fn write(out: &mut OutFile, needed: bool, guard: &str) {
-    let ifndef = format!("#ifndef {}", guard);
-    let define = format!("#define {}", guard);
-    let endif = format!("#endif // {}", guard);
-
-    let mut offset = 0;
-    loop {
-        let begin = find_line(offset, &ifndef);
-        let end = find_line(offset, &endif);
-        if let (Some(begin), Some(end)) = (begin, end) {
-            if !needed {
-                return;
-            }
-            out.next_section();
-            if offset == 0 {
-                writeln!(out, "{}", ifndef);
-                writeln!(out, "{}", define);
-            }
-            for line in HEADER[begin + ifndef.len()..end].trim().lines() {
-                if line != define && !line.trim_start().starts_with("//") {
-                    writeln!(out, "{}", line);
-                }
-            }
-            offset = end + endif.len();
-        } else if offset == 0 {
-            panic!("not found in cxx.h header: {}", guard)
-        } else {
-            writeln!(out, "{}", endif);
-            return;
-        }
-    }
-}
-
-fn find_line(mut offset: usize, line: &str) -> Option<usize> {
-    loop {
-        offset += HEADER[offset..].find(line)?;
-        let rest = &HEADER[offset + line.len()..];
-        if rest.starts_with('\n') || rest.starts_with('\r') {
-            return Some(offset);
-        }
-        offset += line.len();
-    }
+/// A header to #include.
+///
+/// The cxxbridge tool does not parse or even require the given paths to exist;
+/// they simply go into the generated C++ code as #include lines.
+#[derive(Clone, PartialEq, Debug)]
+pub struct Include {
+    /// The header's path, not including the enclosing quotation marks or angle
+    /// brackets.
+    pub path: String,
+    /// Whether to emit `#include "path"` or `#include <path>`.
+    pub kind: IncludeKind,
 }
 
 #[derive(Default, PartialEq)]
-pub struct Includes {
-    custom: Vec<String>,
+pub struct Includes<'a> {
+    pub custom: Vec<Include>,
     pub array: bool,
     pub cstddef: bool,
     pub cstdint: bool,
@@ -61,75 +32,105 @@
     pub type_traits: bool,
     pub utility: bool,
     pub vector: bool,
-    pub base_tsd: bool,
+    pub basetsd: bool,
+    pub content: Content<'a>,
 }
 
-impl Includes {
+impl<'a> Includes<'a> {
     pub fn new() -> Self {
         Includes::default()
     }
 
-    pub fn insert(&mut self, include: impl AsRef<str>) {
-        self.custom.push(include.as_ref().to_owned());
+    pub fn insert(&mut self, include: impl Into<Include>) {
+        self.custom.push(include.into());
     }
 }
 
-impl Extend<String> for Includes {
-    fn extend<I: IntoIterator<Item = String>>(&mut self, iter: I) {
-        self.custom.extend(iter);
-    }
-}
+pub(super) fn write(out: &mut OutFile) {
+    let header = out.header;
+    let include = &mut out.include;
+    let out = &mut include.content;
 
-impl Display for Includes {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        for include in &self.custom {
-            if include.starts_with('<') && include.ends_with('>') {
-                writeln!(f, "#include {}", include)?;
-            } else {
-                writeln!(f, "#include \"{}\"", include.escape_default())?;
+    if header {
+        writeln!(out, "#pragma once");
+    }
+
+    for include in &include.custom {
+        match include.kind {
+            IncludeKind::Quoted => {
+                writeln!(out, "#include \"{}\"", include.path.escape_default());
+            }
+            IncludeKind::Bracketed => {
+                writeln!(out, "#include <{}>", include.path);
             }
         }
-        if self.array {
-            writeln!(f, "#include <array>")?;
+    }
+
+    if include.array {
+        writeln!(out, "#include <array>");
+    }
+    if include.cstddef {
+        writeln!(out, "#include <cstddef>");
+    }
+    if include.cstdint {
+        writeln!(out, "#include <cstdint>");
+    }
+    if include.cstring {
+        writeln!(out, "#include <cstring>");
+    }
+    if include.exception {
+        writeln!(out, "#include <exception>");
+    }
+    if include.memory {
+        writeln!(out, "#include <memory>");
+    }
+    if include.new {
+        writeln!(out, "#include <new>");
+    }
+    if include.string {
+        writeln!(out, "#include <string>");
+    }
+    if include.type_traits {
+        writeln!(out, "#include <type_traits>");
+    }
+    if include.utility {
+        writeln!(out, "#include <utility>");
+    }
+    if include.vector {
+        writeln!(out, "#include <vector>");
+    }
+    if include.basetsd {
+        writeln!(out, "#if defined(_WIN32)");
+        writeln!(out, "#include <basetsd.h>");
+        writeln!(out, "#endif");
+    }
+}
+
+impl<'i, 'a> Extend<&'i Include> for Includes<'a> {
+    fn extend<I: IntoIterator<Item = &'i Include>>(&mut self, iter: I) {
+        self.custom.extend(iter.into_iter().cloned());
+    }
+}
+
+impl<'i> From<&'i syntax::Include> for Include {
+    fn from(include: &syntax::Include) -> Self {
+        Include {
+            path: include.path.clone(),
+            kind: include.kind,
         }
-        if self.cstddef {
-            writeln!(f, "#include <cstddef>")?;
-        }
-        if self.cstdint {
-            writeln!(f, "#include <cstdint>")?;
-        }
-        if self.cstring {
-            writeln!(f, "#include <cstring>")?;
-        }
-        if self.exception {
-            writeln!(f, "#include <exception>")?;
-        }
-        if self.memory {
-            writeln!(f, "#include <memory>")?;
-        }
-        if self.new {
-            writeln!(f, "#include <new>")?;
-        }
-        if self.string {
-            writeln!(f, "#include <string>")?;
-        }
-        if self.type_traits {
-            writeln!(f, "#include <type_traits>")?;
-        }
-        if self.utility {
-            writeln!(f, "#include <utility>")?;
-        }
-        if self.vector {
-            writeln!(f, "#include <vector>")?;
-        }
-        if self.base_tsd {
-            writeln!(f, "#if defined(_WIN32)")?;
-            writeln!(f, "#include <BaseTsd.h>")?;
-            writeln!(f, "#endif")?;
-        }
-        if *self != Self::default() {
-            writeln!(f)?;
-        }
-        Ok(())
+    }
+}
+
+impl<'a> Deref for Includes<'a> {
+    type Target = Content<'a>;
+
+    fn deref(&self) -> &Self::Target {
+        &self.content
+    }
+}
+
+impl<'a> DerefMut for Includes<'a> {
+    fn deref_mut(&mut self) -> &mut Self::Target {
+        &mut self.content
     }
 }
diff --git a/gen/src/mod.rs b/gen/src/mod.rs
index 710bca2..3d12c71 100644
--- a/gen/src/mod.rs
+++ b/gen/src/mod.rs
@@ -1,69 +1,150 @@
 // Functionality that is shared between the cxx_build::bridge entry point and
 // the cxxbridge CLI command.
 
-mod error;
-mod find;
+mod block;
+mod builtin;
+mod check;
+pub(super) mod error;
+mod file;
+pub(super) mod fs;
+mod ifndef;
 pub(super) mod include;
+mod namespace;
+mod nested;
 pub(super) mod out;
 mod write;
 
-#[cfg(test)]
-mod tests;
-
-use self::error::{format_err, Error, Result};
-use crate::syntax::namespace::Namespace;
+pub(super) use self::error::Error;
+use self::error::{format_err, Result};
+use self::file::File;
+use self::include::Include;
 use crate::syntax::report::Errors;
-use crate::syntax::{self, check, Types};
-use std::fs;
+use crate::syntax::{self, Types};
 use std::path::Path;
-use syn::Item;
 
-struct Input {
-    namespace: Namespace,
-    module: Vec<Item>,
-}
-
-#[derive(Default)]
-pub(super) struct Opt {
-    /// Any additional headers to #include
-    pub include: Vec<String>,
-    /// Whether to set __attribute__((visibility("default")))
-    /// or similar annotations on function implementations.
+/// Options for C++ code generation.
+///
+/// We expect options to be added over time, so this is a non-exhaustive struct.
+/// To instantiate one you need to crate a default value and mutate those fields
+/// that you want to modify.
+///
+/// ```
+/// # use cxx_gen::Opt;
+/// #
+/// let impl_annotations = r#"__attribute__((visibility("default")))"#.to_owned();
+///
+/// let mut opt = Opt::default();
+/// opt.cxx_impl_annotations = Some(impl_annotations);
+/// ```
+#[non_exhaustive]
+pub struct Opt {
+    /// Any additional headers to #include. The cxxbridge tool does not parse or
+    /// even require the given paths to exist; they simply go into the generated
+    /// C++ code as #include lines.
+    pub include: Vec<Include>,
+    /// Optional annotation for implementations of C++ function wrappers that
+    /// may be exposed to Rust. You may for example need to provide
+    /// `__declspec(dllexport)` or `__attribute__((visibility("default")))` if
+    /// Rust code from one shared object or executable depends on these C++
+    /// functions in another.
     pub cxx_impl_annotations: Option<String>,
+
+    pub(super) gen_header: bool,
+    pub(super) gen_implementation: bool,
+    pub(super) allow_dot_includes: bool,
 }
 
-pub(super) fn do_generate_bridge(path: &Path, opt: Opt) -> Vec<u8> {
-    let header = false;
-    generate_from_path(path, opt, header)
+/// Results of code generation.
+#[derive(Default)]
+pub struct GeneratedCode {
+    /// The bytes of a C++ header file.
+    pub header: Vec<u8>,
+    /// The bytes of a C++ implementation file (e.g. .cc, cpp etc.)
+    pub implementation: Vec<u8>,
 }
 
-pub(super) fn do_generate_header(path: &Path, opt: Opt) -> Vec<u8> {
-    let header = true;
-    generate_from_path(path, opt, header)
+impl Default for Opt {
+    fn default() -> Self {
+        Opt {
+            include: Vec::new(),
+            cxx_impl_annotations: None,
+            gen_header: true,
+            gen_implementation: true,
+            allow_dot_includes: true,
+        }
+    }
 }
 
-fn generate_from_path(path: &Path, opt: Opt, header: bool) -> Vec<u8> {
-    let source = match fs::read_to_string(path) {
+pub(super) fn generate_from_path(path: &Path, opt: &Opt) -> GeneratedCode {
+    let source = match read_to_string(path) {
         Ok(source) => source,
-        Err(err) => format_err(path, "", Error::Io(err)),
+        Err(err) => format_err(path, "", err),
     };
-    match generate(&source, opt, header) {
+    match generate_from_string(&source, opt) {
         Ok(out) => out,
         Err(err) => format_err(path, &source, err),
     }
 }
 
-fn generate(source: &str, opt: Opt, header: bool) -> Result<Vec<u8>> {
+fn read_to_string(path: &Path) -> Result<String> {
+    let bytes = if path == Path::new("-") {
+        fs::read_stdin()
+    } else {
+        fs::read(path)
+    }?;
+    match String::from_utf8(bytes) {
+        Ok(string) => Ok(string),
+        Err(err) => Err(Error::Utf8(path.to_owned(), err.utf8_error())),
+    }
+}
+
+fn generate_from_string(source: &str, opt: &Opt) -> Result<GeneratedCode> {
+    let mut source = source;
+    if source.starts_with("#!") && !source.starts_with("#![") {
+        let shebang_end = source.find('\n').unwrap_or(source.len());
+        source = &source[shebang_end..];
+    }
     proc_macro2::fallback::force();
+    let syntax: File = syn::parse_str(source)?;
+    generate(syntax, opt)
+}
+
+pub(super) fn generate(syntax: File, opt: &Opt) -> Result<GeneratedCode> {
+    if syntax.modules.is_empty() {
+        return Err(Error::NoBridgeMod);
+    }
+
+    let ref mut apis = Vec::new();
     let ref mut errors = Errors::new();
-    let syntax = syn::parse_file(&source)?;
-    let bridge = find::find_bridge_mod(syntax)?;
-    let ref namespace = bridge.namespace;
-    let ref apis = syntax::parse_items(errors, bridge.module);
+    for bridge in syntax.modules {
+        let ref namespace = bridge.namespace;
+        let trusted = bridge.unsafety.is_some();
+        apis.extend(syntax::parse_items(
+            errors,
+            bridge.content,
+            trusted,
+            namespace,
+        ));
+    }
+
     let ref types = Types::collect(errors, apis);
+    check::precheck(errors, apis, opt);
     errors.propagate()?;
-    check::typecheck(errors, namespace, apis, types);
+    check::typecheck(errors, apis, types);
     errors.propagate()?;
-    let out = write::gen(namespace, apis, types, opt, header);
-    Ok(out.content())
+
+    // Some callers may wish to generate both header and implementation from the
+    // same token stream to avoid parsing twice. Others only need to generate
+    // one or the other.
+    let (mut header, mut implementation) = Default::default();
+    if opt.gen_header {
+        header = write::gen(apis, types, opt, true);
+    }
+    if opt.gen_implementation {
+        implementation = write::gen(apis, types, opt, false);
+    }
+    Ok(GeneratedCode {
+        header,
+        implementation,
+    })
 }
diff --git a/gen/src/namespace.rs b/gen/src/namespace.rs
new file mode 100644
index 0000000..b79c38f
--- /dev/null
+++ b/gen/src/namespace.rs
@@ -0,0 +1,14 @@
+use crate::syntax::namespace::Namespace;
+use crate::syntax::Api;
+
+impl Api {
+    pub fn namespace(&self) -> &Namespace {
+        match self {
+            Api::CxxFunction(efn) | Api::RustFunction(efn) => &efn.name.namespace,
+            Api::CxxType(ety) | Api::RustType(ety) => &ety.name.namespace,
+            Api::Enum(enm) => &enm.name.namespace,
+            Api::Struct(strct) => &strct.name.namespace,
+            Api::Impl(_) | Api::Include(_) | Api::TypeAlias(_) => Default::default(),
+        }
+    }
+}
diff --git a/gen/src/nested.rs b/gen/src/nested.rs
new file mode 100644
index 0000000..22b0c9f
--- /dev/null
+++ b/gen/src/nested.rs
@@ -0,0 +1,136 @@
+use crate::syntax::Api;
+use proc_macro2::Ident;
+use std::collections::HashMap as Map;
+
+pub struct NamespaceEntries<'a> {
+    direct: Vec<&'a Api>,
+    nested: Vec<(&'a Ident, NamespaceEntries<'a>)>,
+}
+
+impl<'a> NamespaceEntries<'a> {
+    pub fn new(apis: Vec<&'a Api>) -> Self {
+        sort_by_inner_namespace(apis, 0)
+    }
+
+    pub fn direct_content(&self) -> &[&'a Api] {
+        &self.direct
+    }
+
+    pub fn nested_content(&self) -> impl Iterator<Item = (&'a Ident, &NamespaceEntries<'a>)> {
+        self.nested.iter().map(|(k, entries)| (*k, entries))
+    }
+}
+
+fn sort_by_inner_namespace(apis: Vec<&Api>, depth: usize) -> NamespaceEntries {
+    let mut direct = Vec::new();
+    let mut nested_namespaces = Vec::new();
+    let mut index_of_namespace = Map::new();
+
+    for api in &apis {
+        if let Some(first_ns_elem) = api.namespace().iter().nth(depth) {
+            match index_of_namespace.get(first_ns_elem) {
+                None => {
+                    index_of_namespace.insert(first_ns_elem, nested_namespaces.len());
+                    nested_namespaces.push((first_ns_elem, vec![*api]));
+                }
+                Some(&index) => nested_namespaces[index].1.push(*api),
+            }
+            continue;
+        }
+        direct.push(*api);
+    }
+
+    let nested = nested_namespaces
+        .into_iter()
+        .map(|(k, apis)| (k, sort_by_inner_namespace(apis, depth + 1)))
+        .collect();
+
+    NamespaceEntries { direct, nested }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::NamespaceEntries;
+    use crate::syntax::namespace::Namespace;
+    use crate::syntax::{Api, Doc, ExternType, Pair};
+    use proc_macro2::{Ident, Span};
+    use std::iter::FromIterator;
+    use syn::Token;
+
+    #[test]
+    fn test_ns_entries_sort() {
+        let apis = &[
+            make_api(None, "C"),
+            make_api(None, "A"),
+            make_api(Some("G"), "E"),
+            make_api(Some("D"), "F"),
+            make_api(Some("G"), "H"),
+            make_api(Some("D::K"), "L"),
+            make_api(Some("D::K"), "M"),
+            make_api(None, "B"),
+            make_api(Some("D"), "I"),
+            make_api(Some("D"), "J"),
+        ];
+
+        let root = NamespaceEntries::new(Vec::from_iter(apis));
+
+        // ::
+        let root_direct = root.direct_content();
+        assert_eq!(root_direct.len(), 3);
+        assert_ident(root_direct[0], "C");
+        assert_ident(root_direct[1], "A");
+        assert_ident(root_direct[2], "B");
+
+        let mut root_nested = root.nested_content();
+        let (id, g) = root_nested.next().unwrap();
+        assert_eq!(id, "G");
+        let (id, d) = root_nested.next().unwrap();
+        assert_eq!(id, "D");
+        assert!(root_nested.next().is_none());
+
+        // ::G
+        let g_direct = g.direct_content();
+        assert_eq!(g_direct.len(), 2);
+        assert_ident(g_direct[0], "E");
+        assert_ident(g_direct[1], "H");
+
+        let mut g_nested = g.nested_content();
+        assert!(g_nested.next().is_none());
+
+        // ::D
+        let d_direct = d.direct_content();
+        assert_eq!(d_direct.len(), 3);
+        assert_ident(d_direct[0], "F");
+        assert_ident(d_direct[1], "I");
+        assert_ident(d_direct[2], "J");
+
+        let mut d_nested = d.nested_content();
+        let (id, k) = d_nested.next().unwrap();
+        assert_eq!(id, "K");
+
+        // ::D::K
+        let k_direct = k.direct_content();
+        assert_eq!(k_direct.len(), 2);
+        assert_ident(k_direct[0], "L");
+        assert_ident(k_direct[1], "M");
+    }
+
+    fn assert_ident(api: &Api, expected: &str) {
+        if let Api::CxxType(cxx_type) = api {
+            assert_eq!(cxx_type.name.cxx, expected);
+        } else {
+            unreachable!()
+        }
+    }
+
+    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 {
+            doc: Doc::new(),
+            type_token: Token![type](Span::call_site()),
+            name: Pair::new(ns, Ident::new(ident, Span::call_site())),
+            semi_token: Token![;](Span::call_site()),
+            trusted: false,
+        })
+    }
+}
diff --git a/gen/src/out.rs b/gen/src/out.rs
index 08bf85f..bf880cc 100644
--- a/gen/src/out.rs
+++ b/gen/src/out.rs
@@ -1,59 +1,63 @@
+use crate::gen::block::Block;
+use crate::gen::builtin::Builtins;
 use crate::gen::include::Includes;
+use crate::gen::Opt;
 use crate::syntax::namespace::Namespace;
+use crate::syntax::Types;
 use std::cell::RefCell;
 use std::fmt::{self, Arguments, Write};
 
-pub(crate) struct OutFile {
-    pub namespace: Namespace,
+pub(crate) struct OutFile<'a> {
     pub header: bool,
-    pub include: Includes,
-    content: RefCell<Content>,
+    pub opt: &'a Opt,
+    pub types: &'a Types<'a>,
+    pub include: Includes<'a>,
+    pub builtin: Builtins<'a>,
+    content: RefCell<Content<'a>>,
 }
 
-struct Content {
-    bytes: Vec<u8>,
+#[derive(Default)]
+pub struct Content<'a> {
+    bytes: String,
+    namespace: &'a Namespace,
+    blocks: Vec<BlockBoundary<'a>>,
     section_pending: bool,
-    blocks_pending: Vec<&'static str>,
+    blocks_pending: usize,
 }
 
-impl OutFile {
-    pub fn new(namespace: Namespace, header: bool) -> Self {
+#[derive(Copy, Clone, PartialEq, Debug)]
+enum BlockBoundary<'a> {
+    Begin(Block<'a>),
+    End(Block<'a>),
+}
+
+impl<'a> OutFile<'a> {
+    pub fn new(header: bool, opt: &'a Opt, types: &'a Types) -> Self {
         OutFile {
-            namespace,
             header,
+            opt,
+            types,
             include: Includes::new(),
-            content: RefCell::new(Content {
-                bytes: Vec::new(),
-                section_pending: false,
-                blocks_pending: Vec::new(),
-            }),
+            builtin: Builtins::new(),
+            content: RefCell::new(Content::new()),
         }
     }
 
     // Write a blank line if the preceding section had any contents.
     pub fn next_section(&mut self) {
-        let content = self.content.get_mut();
-        content.section_pending = true;
+        self.content.get_mut().next_section();
     }
 
-    pub fn begin_block(&mut self, block: &'static str) {
-        let content = self.content.get_mut();
-        content.blocks_pending.push(block);
+    pub fn begin_block(&mut self, block: Block<'a>) {
+        self.content.get_mut().begin_block(block);
     }
 
-    pub fn end_block(&mut self, block: &'static str) {
-        let content = self.content.get_mut();
-        if content.blocks_pending.pop().is_none() {
-            content.bytes.extend_from_slice(b"} // ");
-            content.bytes.extend_from_slice(block.as_bytes());
-            content.bytes.push(b'\n');
-            content.section_pending = true;
-        }
+    pub fn end_block(&mut self, block: Block<'a>) {
+        self.content.get_mut().end_block(block);
     }
 
-    pub fn prepend(&mut self, section: String) {
-        let content = self.content.get_mut();
-        content.bytes.splice(..0, section.into_bytes());
+    pub fn set_namespace(&mut self, namespace: &'a Namespace) {
+        self.content.get_mut().set_namespace(namespace);
     }
 
     pub fn write_fmt(&self, args: Arguments) {
@@ -61,31 +65,146 @@
         Write::write_fmt(content, args).unwrap();
     }
 
-    pub fn content(&self) -> Vec<u8> {
-        self.content.borrow().bytes.clone()
+    pub fn content(&mut self) -> Vec<u8> {
+        self.flush();
+        let include = &self.include.content.bytes;
+        let builtin = &self.builtin.content.bytes;
+        let content = &self.content.get_mut().bytes;
+        let len = include.len() + builtin.len() + content.len() + 2;
+        let mut out = String::with_capacity(len);
+        out.push_str(include);
+        if !out.is_empty() && !builtin.is_empty() {
+            out.push('\n');
+        }
+        out.push_str(builtin);
+        if !out.is_empty() && !content.is_empty() {
+            out.push('\n');
+        }
+        out.push_str(content);
+        if out.is_empty() {
+            out.push_str("// empty\n");
+        }
+        out.into_bytes()
+    }
+
+    fn flush(&mut self) {
+        self.include.content.flush();
+        self.builtin.content.flush();
+        self.content.get_mut().flush();
     }
 }
 
-impl Write for Content {
+impl<'a> Write for Content<'a> {
     fn write_str(&mut self, s: &str) -> fmt::Result {
-        if !s.is_empty() {
-            if !self.blocks_pending.is_empty() {
-                if !self.bytes.is_empty() {
-                    self.bytes.push(b'\n');
-                }
-                for block in self.blocks_pending.drain(..) {
-                    self.bytes.extend_from_slice(block.as_bytes());
-                    self.bytes.extend_from_slice(b" {\n");
-                }
-                self.section_pending = false;
-            } else if self.section_pending {
-                if !self.bytes.is_empty() {
-                    self.bytes.push(b'\n');
-                }
-                self.section_pending = false;
-            }
-            self.bytes.extend_from_slice(s.as_bytes());
-        }
+        self.write(s);
         Ok(())
     }
 }
+
+impl<'a> PartialEq for Content<'a> {
+    fn eq(&self, _other: &Content) -> bool {
+        true
+    }
+}
+
+impl<'a> Content<'a> {
+    fn new() -> Self {
+        Content::default()
+    }
+
+    pub fn next_section(&mut self) {
+        self.section_pending = true;
+    }
+
+    pub fn begin_block(&mut self, block: Block<'a>) {
+        self.push_block_boundary(BlockBoundary::Begin(block));
+    }
+
+    pub fn end_block(&mut self, block: Block<'a>) {
+        self.push_block_boundary(BlockBoundary::End(block));
+    }
+
+    pub fn set_namespace(&mut self, namespace: &'a Namespace) {
+        for name in self.namespace.iter().rev() {
+            self.end_block(Block::UserDefinedNamespace(name));
+        }
+        for name in namespace {
+            self.begin_block(Block::UserDefinedNamespace(name));
+        }
+        self.namespace = namespace;
+    }
+
+    pub fn write_fmt(&mut self, args: Arguments) {
+        Write::write_fmt(self, args).unwrap();
+    }
+
+    fn write(&mut self, b: &str) {
+        if !b.is_empty() {
+            if self.blocks_pending > 0 {
+                self.flush_blocks();
+            }
+            if self.section_pending && !self.bytes.is_empty() {
+                self.bytes.push('\n');
+            }
+            self.bytes.push_str(b);
+            self.section_pending = false;
+            self.blocks_pending = 0;
+        }
+    }
+
+    fn push_block_boundary(&mut self, boundary: BlockBoundary<'a>) {
+        if self.blocks_pending > 0 && boundary == self.blocks.last().unwrap().rev() {
+            self.blocks.pop();
+            self.blocks_pending -= 1;
+        } else {
+            self.blocks.push(boundary);
+            self.blocks_pending += 1;
+        }
+    }
+
+    fn flush(&mut self) {
+        self.set_namespace(Default::default());
+        if self.blocks_pending > 0 {
+            self.flush_blocks();
+        }
+    }
+
+    fn flush_blocks(&mut self) {
+        self.section_pending = !self.bytes.is_empty();
+        let mut read = self.blocks.len() - self.blocks_pending;
+        let mut write = read;
+
+        while read < self.blocks.len() {
+            match self.blocks[read] {
+                BlockBoundary::Begin(begin_block) => {
+                    if self.section_pending {
+                        self.bytes.push('\n');
+                        self.section_pending = false;
+                    }
+                    Block::write_begin(begin_block, &mut self.bytes);
+                    self.blocks[write] = BlockBoundary::Begin(begin_block);
+                    write += 1;
+                }
+                BlockBoundary::End(end_block) => {
+                    write = write.checked_sub(1).unwrap();
+                    let begin_block = self.blocks[write];
+                    assert_eq!(begin_block, BlockBoundary::Begin(end_block));
+                    Block::write_end(end_block, &mut self.bytes);
+                    self.section_pending = true;
+                }
+            }
+            read += 1;
+        }
+
+        self.blocks.truncate(write);
+    }
+}
+
+impl<'a> BlockBoundary<'a> {
+    fn rev(self) -> BlockBoundary<'a> {
+        match self {
+            BlockBoundary::Begin(block) => BlockBoundary::End(block),
+            BlockBoundary::End(block) => BlockBoundary::Begin(block),
+        }
+    }
+}
diff --git a/gen/src/tests.rs b/gen/src/tests.rs
deleted file mode 100644
index 0e7a910..0000000
--- a/gen/src/tests.rs
+++ /dev/null
@@ -1,34 +0,0 @@
-use crate::gen::{generate, Opt};
-
-const CPP_EXAMPLE: &'static str = r#"
-    #[cxx::bridge]
-    mod ffi {
-        extern "C" {
-            pub fn do_cpp_thing(foo: &str);
-        }
-    }
-    "#;
-
-#[test]
-fn test_cpp() {
-    let opts = Opt {
-        include: Vec::new(),
-        cxx_impl_annotations: None,
-    };
-    let output = generate(CPP_EXAMPLE, opts, false).unwrap();
-    let output = std::str::from_utf8(&output).unwrap();
-    // To avoid continual breakage we won't test every byte.
-    // Let's look for the major features.
-    assert!(output.contains("void cxxbridge03$do_cpp_thing(::rust::Str::Repr foo)"));
-}
-
-#[test]
-fn test_annotation() {
-    let opts = Opt {
-        include: Vec::new(),
-        cxx_impl_annotations: Some("ANNOTATION".to_string()),
-    };
-    let output = generate(CPP_EXAMPLE, opts, false).unwrap();
-    let output = std::str::from_utf8(&output).unwrap();
-    assert!(output.contains("ANNOTATION void cxxbridge03$do_cpp_thing(::rust::Str::Repr foo)"));
-}
diff --git a/gen/src/write.rs b/gen/src/write.rs
index 04d8e3e..5fe8ee3 100644
--- a/gen/src/write.rs
+++ b/gen/src/write.rs
@@ -1,79 +1,111 @@
+use crate::gen::block::Block;
+use crate::gen::nested::NamespaceEntries;
 use crate::gen::out::OutFile;
-use crate::gen::{include, Opt};
+use crate::gen::{builtin, include, Opt};
 use crate::syntax::atom::Atom::{self, *};
-use crate::syntax::namespace::Namespace;
 use crate::syntax::symbol::Symbol;
-use crate::syntax::{mangle, Api, Enum, ExternFn, ExternType, Signature, Struct, Type, Types, Var};
+use crate::syntax::{
+    mangle, Api, Enum, ExternFn, ExternType, Pair, ResolvableName, Signature, Struct, Type, Types,
+    Var,
+};
 use proc_macro2::Ident;
-use std::collections::HashMap;
+use std::collections::{HashMap, HashSet};
 
-pub(super) fn gen(
-    namespace: &Namespace,
-    apis: &[Api],
-    types: &Types,
-    opt: Opt,
-    header: bool,
-) -> OutFile {
-    let mut out_file = OutFile::new(namespace.clone(), header);
+pub(super) fn gen(apis: &[Api], types: &Types, opt: &Opt, header: bool) -> Vec<u8> {
+    let mut out_file = OutFile::new(header, opt, types);
     let out = &mut out_file;
 
-    if header {
-        writeln!(out, "#pragma once");
-    }
+    pick_includes_and_builtins(out, apis);
+    out.include.extend(&opt.include);
 
-    out.include.extend(opt.include);
-    for api in apis {
-        if let Api::Include(include) = api {
-            out.include.insert(include);
+    write_forward_declarations(out, apis);
+    write_data_structures(out, apis);
+    write_functions(out, apis);
+    write_generic_instantiations(out);
+
+    builtin::write(out);
+    include::write(out);
+
+    out_file.content()
+}
+
+fn write_forward_declarations(out: &mut OutFile, apis: &[Api]) {
+    let needs_forward_declaration = |api: &&Api| match api {
+        Api::Struct(_) | Api::CxxType(_) | Api::RustType(_) => true,
+        Api::Enum(enm) => !out.types.cxx.contains(&enm.name.rust),
+        _ => false,
+    };
+
+    let apis_by_namespace =
+        NamespaceEntries::new(apis.iter().filter(needs_forward_declaration).collect());
+
+    write(out, &apis_by_namespace, 0);
+
+    fn write(out: &mut OutFile, ns_entries: &NamespaceEntries, indent: usize) {
+        let apis = ns_entries.direct_content();
+
+        for api in apis {
+            write!(out, "{:1$}", "", indent);
+            match api {
+                Api::Struct(strct) => write_struct_decl(out, &strct.name.cxx),
+                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),
+                _ => unreachable!(),
+            }
+        }
+
+        for (namespace, nested_ns_entries) in ns_entries.nested_content() {
+            writeln!(out, "{:2$}namespace {} {{", "", namespace, indent);
+            write(out, nested_ns_entries, indent + 2);
+            writeln!(out, "{:1$}}}", "", indent);
         }
     }
+}
 
-    write_includes(out, types);
-    write_include_cxxbridge(out, apis, types);
-
-    out.next_section();
-    for name in namespace {
-        writeln!(out, "namespace {} {{", name);
-    }
-
-    out.next_section();
-    for api in apis {
-        match api {
-            Api::Struct(strct) => write_struct_decl(out, &strct.ident),
-            Api::CxxType(ety) => write_struct_using(out, &ety.ident),
-            Api::RustType(ety) => write_struct_decl(out, &ety.ident),
-            _ => {}
-        }
-    }
-
+fn write_data_structures<'a>(out: &mut OutFile<'a>, apis: &'a [Api]) {
     let mut methods_for_type = HashMap::new();
     for api in apis {
-        if let Api::RustFunction(efn) = api {
+        if let Api::CxxFunction(efn) | Api::RustFunction(efn) = api {
             if let Some(receiver) = &efn.sig.receiver {
                 methods_for_type
-                    .entry(&receiver.ty)
+                    .entry(&receiver.ty.rust)
                     .or_insert_with(Vec::new)
                     .push(efn);
             }
         }
     }
 
+    let mut structs_written = HashSet::new();
+    let mut toposorted_structs = out.types.toposorted_structs.iter();
     for api in apis {
         match api {
-            Api::Struct(strct) => {
-                out.next_section();
-                write_struct(out, strct);
+            Api::Struct(strct) if !structs_written.contains(&strct.name.rust) => {
+                for next in &mut toposorted_structs {
+                    if !out.types.cxx.contains(&strct.name.rust) {
+                        out.next_section();
+                        let methods = methods_for_type
+                            .get(&strct.name.rust)
+                            .map(Vec::as_slice)
+                            .unwrap_or_default();
+                        write_struct(out, next, methods);
+                    }
+                    structs_written.insert(&next.name.rust);
+                    if next.name.rust == strct.name.rust {
+                        break;
+                    }
+                }
             }
             Api::Enum(enm) => {
                 out.next_section();
-                if types.cxx.contains(&enm.ident) {
+                if out.types.cxx.contains(&enm.name.rust) {
                     check_enum(out, enm);
                 } else {
                     write_enum(out, enm);
                 }
             }
             Api::RustType(ety) => {
-                if let Some(methods) = methods_for_type.get(&ety.ident) {
+                if let Some(methods) = methods_for_type.get(&ety.name.rust) {
                     out.next_section();
                     write_struct_with_methods(out, ety, methods);
                 }
@@ -82,290 +114,162 @@
         }
     }
 
-    if !header {
-        out.begin_block("extern \"C\"");
-        write_exception_glue(out, apis);
-        for api in apis {
-            let (efn, write): (_, fn(_, _, _, _)) = match api {
-                Api::CxxFunction(efn) => (efn, write_cxx_function_shim),
-                Api::RustFunction(efn) => (efn, write_rust_function_decl),
-                _ => continue,
-            };
-            out.next_section();
-            write(out, efn, types, &opt.cxx_impl_annotations);
+    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)
+            }
         }
-        out.end_block("extern \"C\"");
+    }
+}
+
+fn write_functions<'a>(out: &mut OutFile<'a>, apis: &'a [Api]) {
+    if !out.header {
+        for api in apis {
+            match api {
+                Api::CxxFunction(efn) => write_cxx_function_shim(out, efn),
+                Api::RustFunction(efn) => write_rust_function_decl(out, efn),
+                _ => {}
+            }
+        }
     }
 
     for api in apis {
         if let Api::RustFunction(efn) = api {
             out.next_section();
-            write_rust_function_shim(out, efn, types);
+            write_rust_function_shim(out, efn);
+        }
+    }
+}
+
+fn pick_includes_and_builtins(out: &mut OutFile, apis: &[Api]) {
+    for api in apis {
+        if let Api::Include(include) = api {
+            out.include.insert(include);
         }
     }
 
-    out.next_section();
-    for name in namespace.iter().rev() {
-        writeln!(out, "}} // namespace {}", name);
-    }
-
-    if !header {
-        out.next_section();
-        write_generic_instantiations(out, types);
-    }
-
-    out.prepend(out.include.to_string());
-
-    out_file
-}
-
-fn write_includes(out: &mut OutFile, types: &Types) {
-    for ty in types {
+    for ty in out.types {
         match ty {
-            Type::Ident(ident) => match Atom::from(ident) {
+            Type::Ident(ident) => match Atom::from(&ident.rust) {
                 Some(U8) | Some(U16) | Some(U32) | Some(U64) | Some(I8) | Some(I16) | Some(I32)
                 | Some(I64) => out.include.cstdint = true,
                 Some(Usize) => out.include.cstddef = true,
+                Some(Isize) => out.builtin.rust_isize = true,
                 Some(CxxString) => out.include.string = true,
-                Some(Bool) | Some(Isize) | Some(F32) | Some(F64) | Some(RustString) | None => {}
+                Some(RustString) => out.builtin.rust_string = true,
+                Some(Bool) | Some(F32) | Some(F64) | None => {}
             },
-            Type::RustBox(_) => out.include.type_traits = true,
+            Type::RustBox(_) => out.builtin.rust_box = true,
+            Type::RustVec(_) => out.builtin.rust_vec = true,
             Type::UniquePtr(_) => out.include.memory = true,
+            Type::Str(_) => out.builtin.rust_str = true,
             Type::CxxVector(_) => out.include.vector = true,
-            Type::SliceRefU8(_) => out.include.cstdint = 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(_) => {}
         }
     }
 }
 
-fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api], types: &Types) {
-    let mut needs_rust_string = false;
-    let mut needs_rust_str = false;
-    let mut needs_rust_slice = false;
-    let mut needs_rust_box = false;
-    let mut needs_rust_vec = false;
-    let mut needs_rust_fn = false;
-    let mut needs_rust_isize = false;
-    for ty in types {
-        match ty {
-            Type::RustBox(_) => {
-                out.include.new = true;
-                out.include.type_traits = true;
-                needs_rust_box = true;
-            }
-            Type::RustVec(_) => {
-                out.include.array = true;
-                out.include.new = true;
-                out.include.type_traits = true;
-                needs_rust_vec = true;
-            }
-            Type::Str(_) => {
-                out.include.cstdint = true;
-                out.include.string = true;
-                needs_rust_str = true;
-            }
-            Type::Fn(_) => {
-                needs_rust_fn = true;
-            }
-            Type::Slice(_) | Type::SliceRefU8(_) => {
-                needs_rust_slice = true;
-            }
-            ty if ty == Isize => {
-                out.include.base_tsd = true;
-                needs_rust_isize = true;
-            }
-            ty if ty == RustString => {
-                out.include.array = true;
-                out.include.cstdint = true;
-                out.include.string = true;
-                needs_rust_string = true;
-            }
-            _ => {}
-        }
-    }
-
-    let mut needs_rust_error = false;
-    let mut needs_unsafe_bitcopy = false;
-    let mut needs_manually_drop = false;
-    let mut needs_maybe_uninit = false;
-    let mut needs_trycatch = false;
-    for api in apis {
-        match api {
-            Api::CxxFunction(efn) if !out.header => {
-                if efn.throws {
-                    needs_trycatch = true;
-                }
-                for arg in &efn.args {
-                    let bitcopy = match arg.ty {
-                        Type::RustVec(_) => true,
-                        _ => arg.ty == RustString,
-                    };
-                    if bitcopy {
-                        needs_unsafe_bitcopy = true;
-                        break;
-                    }
-                }
-            }
-            Api::RustFunction(efn) if !out.header => {
-                if efn.throws {
-                    out.include.exception = true;
-                    needs_rust_error = true;
-                }
-                for arg in &efn.args {
-                    if arg.ty != RustString && types.needs_indirect_abi(&arg.ty) {
-                        needs_manually_drop = true;
-                        break;
-                    }
-                }
-                if let Some(ret) = &efn.ret {
-                    if types.needs_indirect_abi(ret) {
-                        needs_maybe_uninit = true;
-                    }
-                }
-            }
-            _ => {}
-        }
-    }
-
-    out.begin_block("namespace rust");
-    out.begin_block("inline namespace cxxbridge03");
-
-    if needs_rust_string
-        || needs_rust_str
-        || needs_rust_slice
-        || needs_rust_box
-        || needs_rust_vec
-        || needs_rust_fn
-        || needs_rust_error
-        || needs_rust_isize
-        || needs_unsafe_bitcopy
-        || needs_manually_drop
-        || needs_maybe_uninit
-        || needs_trycatch
-    {
-        writeln!(out, "// #include \"rust/cxx.h\"");
-    }
-
-    if needs_rust_string || needs_rust_vec {
-        out.next_section();
-        writeln!(out, "struct unsafe_bitcopy_t;");
-    }
-
-    include::write(out, needs_rust_string, "CXXBRIDGE03_RUST_STRING");
-    include::write(out, needs_rust_str, "CXXBRIDGE03_RUST_STR");
-    include::write(out, needs_rust_slice, "CXXBRIDGE03_RUST_SLICE");
-    include::write(out, needs_rust_box, "CXXBRIDGE03_RUST_BOX");
-    include::write(out, needs_rust_vec, "CXXBRIDGE03_RUST_VEC");
-    include::write(out, needs_rust_fn, "CXXBRIDGE03_RUST_FN");
-    include::write(out, needs_rust_error, "CXXBRIDGE03_RUST_ERROR");
-    include::write(out, needs_rust_isize, "CXXBRIDGE03_RUST_ISIZE");
-    include::write(out, needs_unsafe_bitcopy, "CXXBRIDGE03_RUST_BITCOPY");
-
-    if needs_manually_drop {
-        out.next_section();
-        out.include.utility = true;
-        writeln!(out, "template <typename T>");
-        writeln!(out, "union ManuallyDrop {{");
-        writeln!(out, "  T value;");
-        writeln!(
-            out,
-            "  ManuallyDrop(T &&value) : value(::std::move(value)) {{}}",
-        );
-        writeln!(out, "  ~ManuallyDrop() {{}}");
-        writeln!(out, "}};");
-    }
-
-    if needs_maybe_uninit {
-        out.next_section();
-        writeln!(out, "template <typename T>");
-        writeln!(out, "union MaybeUninit {{");
-        writeln!(out, "  T value;");
-        writeln!(out, "  MaybeUninit() {{}}");
-        writeln!(out, "  ~MaybeUninit() {{}}");
-        writeln!(out, "}};");
-    }
-
-    out.end_block("namespace cxxbridge03");
-
-    if needs_trycatch {
-        out.begin_block("namespace behavior");
-        out.include.exception = true;
-        out.include.type_traits = true;
-        out.include.utility = true;
-        writeln!(out, "class missing {{}};");
-        writeln!(out, "missing trycatch(...);");
-        writeln!(out);
-        writeln!(out, "template <typename Try, typename Fail>");
-        writeln!(out, "static typename std::enable_if<");
-        writeln!(
-            out,
-            "    std::is_same<decltype(trycatch(std::declval<Try>(), std::declval<Fail>())),",
-        );
-        writeln!(out, "                 missing>::value>::type");
-        writeln!(out, "trycatch(Try &&func, Fail &&fail) noexcept try {{");
-        writeln!(out, "  func();");
-        writeln!(out, "}} catch (const ::std::exception &e) {{");
-        writeln!(out, "  fail(e.what());");
-        writeln!(out, "}}");
-        out.end_block("namespace behavior");
-    }
-
-    out.end_block("namespace rust");
-}
-
-fn write_struct(out: &mut OutFile, strct: &Struct) {
+fn write_struct<'a>(out: &mut OutFile<'a>, strct: &'a Struct, methods: &[&ExternFn]) {
+    out.set_namespace(&strct.name.namespace);
+    let guard = format!("CXXBRIDGE05_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.ident);
+    writeln!(out, "struct {} final {{", strct.name.cxx);
     for field in &strct.fields {
         write!(out, "  ");
         write_type_space(out, &field.ty);
         writeln!(out, "{};", field.ident);
     }
+    if !methods.is_empty() {
+        writeln!(out);
+    }
+    for method in methods {
+        write!(out, "  ");
+        let sig = &method.sig;
+        let local_name = method.name.cxx.to_string();
+        write_rust_function_shim_decl(out, &local_name, sig, false);
+        writeln!(out, ";");
+    }
     writeln!(out, "}};");
+    writeln!(out, "#endif // {}", guard);
 }
 
 fn write_struct_decl(out: &mut OutFile, ident: &Ident) {
     writeln!(out, "struct {};", ident);
 }
 
-fn write_struct_using(out: &mut OutFile, ident: &Ident) {
-    writeln!(out, "using {} = {};", ident, ident);
+fn write_enum_decl(out: &mut OutFile, enm: &Enum) {
+    write!(out, "enum class {} : ", enm.name.cxx);
+    write_atom(out, enm.repr);
+    writeln!(out, ";");
 }
 
-fn write_struct_with_methods(out: &mut OutFile, ety: &ExternType, methods: &[&ExternFn]) {
+fn write_struct_using(out: &mut OutFile, ident: &Pair) {
+    writeln!(out, "using {} = {};", ident.cxx, ident.to_fully_qualified());
+}
+
+fn write_struct_with_methods<'a>(
+    out: &mut OutFile<'a>,
+    ety: &'a ExternType,
+    methods: &[&ExternFn],
+) {
+    out.set_namespace(&ety.name.namespace);
+    let guard = format!("CXXBRIDGE05_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.ident);
-    writeln!(out, "  {}() = delete;", ety.ident);
-    writeln!(out, "  {}(const {} &) = delete;", ety.ident, ety.ident);
+    writeln!(out, "struct {} final {{", ety.name.cxx);
+    writeln!(out, "  {}() = delete;", ety.name.cxx);
+    writeln!(
+        out,
+        "  {}(const {} &) = delete;",
+        ety.name.cxx, ety.name.cxx,
+    );
     for method in methods {
         write!(out, "  ");
         let sig = &method.sig;
-        let local_name = method.ident.to_string();
+        let local_name = method.name.cxx.to_string();
         write_rust_function_shim_decl(out, &local_name, sig, false);
         writeln!(out, ";");
     }
     writeln!(out, "}};");
+    writeln!(out, "#endif // {}", guard);
 }
 
-fn write_enum(out: &mut OutFile, enm: &Enum) {
+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());
+    writeln!(out, "#ifndef {}", guard);
+    writeln!(out, "#define {}", guard);
     for line in enm.doc.to_string().lines() {
         writeln!(out, "//{}", line);
     }
-    write!(out, "enum class {} : ", enm.ident);
+    write!(out, "enum class {} : ", enm.name.cxx);
     write_atom(out, enm.repr);
     writeln!(out, " {{");
     for variant in &enm.variants {
         writeln!(out, "  {} = {},", variant.ident, variant.discriminant);
     }
     writeln!(out, "}};");
+    writeln!(out, "#endif // {}", guard);
 }
 
-fn check_enum(out: &mut OutFile, enm: &Enum) {
-    write!(out, "static_assert(sizeof({}) == sizeof(", enm.ident);
+fn check_enum<'a>(out: &mut OutFile<'a>, enm: &'a Enum) {
+    out.set_namespace(&enm.name.namespace);
+    write!(out, "static_assert(sizeof({}) == sizeof(", enm.name.cxx);
     write_atom(out, enm.repr);
     writeln!(out, "), \"incorrect size\");");
     for variant in &enm.variants {
@@ -374,54 +278,80 @@
         writeln!(
             out,
             ">({}::{}) == {}, \"disagrees with the value in #[cxx::bridge]\");",
-            enm.ident, variant.ident, variant.discriminant,
+            enm.name.cxx, variant.ident, variant.discriminant,
         );
     }
 }
 
-fn write_exception_glue(out: &mut OutFile, apis: &[Api]) {
-    let mut has_cxx_throws = false;
-    for api in apis {
-        if let Api::CxxFunction(efn) = api {
-            if efn.throws {
-                has_cxx_throws = true;
-                break;
-            }
-        }
-    }
+fn check_trivial_extern_type(out: &mut OutFile, id: &Pair) {
+    // 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:
+    //
+    //     unsafe impl ExternType for MyType {
+    //         type Id = cxx::type_id!("...");
+    //         type Kind = cxx::kind::Trivial;
+    //     }
+    //
+    // Since the user went on the record with their unsafe impl to unsafely
+    // claim they KNOW that the type is trivial, it's fine for that to be on
+    // them if that were wrong. However, in practice correctly reasoning about
+    // the relocatability of C++ types is challenging, particularly if the type
+    // definition were to change over time, so for now we add this check.
+    //
+    // There may be legitimate reasons to opt out of this assertion for support
+    // of types that the programmer knows are soundly Rust-movable despite not
+    // being recognized as such by the C++ type system due to a move constructor
+    // or destructor. To opt out of the relocatability check, they need to do
+    // one of the following things in any header used by `include!` in their
+    // bridge.
+    //
+    //      --- if they define the type:
+    //      struct MyType {
+    //        ...
+    //    +   using IsRelocatable = std::true_type;
+    //      };
+    //
+    //      --- otherwise:
+    //    + template <>
+    //    + struct rust::IsRelocatable<MyType> : std::true_type {};
+    //
 
-    if has_cxx_throws {
-        out.next_section();
-        writeln!(
-            out,
-            "const char *cxxbridge03$exception(const char *, size_t);",
-        );
-    }
+    let id = id.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,
+    );
 }
 
-fn write_cxx_function_shim(
-    out: &mut OutFile,
-    efn: &ExternFn,
-    types: &Types,
-    impl_annotations: &Option<String>,
-) {
-    if !out.header {
-        if let Some(annotation) = 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);
     }
     if efn.throws {
-        write!(out, "::rust::Str::Repr ");
+        out.builtin.ptr_len = true;
+        write!(out, "::rust::repr::PtrLen ");
     } else {
-        write_extern_return_type_space(out, &efn.ret, types);
+        write_extern_return_type_space(out, &efn.ret);
     }
-    let mangled = mangle::extern_fn(&out.namespace, efn);
+    let mangled = mangle::extern_fn(efn, out.types);
     write!(out, "{}(", mangled);
     if let Some(receiver) = &efn.receiver {
         if receiver.mutability.is_none() {
             write!(out, "const ");
         }
-        write!(out, "{} &self", receiver.ty);
+        write!(
+            out,
+            "{} &self",
+            out.types.resolve(&receiver.ty).to_fully_qualified(),
+        );
     }
     for (i, arg) in efn.args.iter().enumerate() {
         if i > 0 || efn.receiver.is_some() {
@@ -432,9 +362,9 @@
         } else if let Type::RustVec(_) = arg.ty {
             write!(out, "const ");
         }
-        write_extern_arg(out, arg, types);
+        write_extern_arg(out, arg);
     }
-    let indirect_return = indirect_return(efn, types);
+    let indirect_return = indirect_return(efn, out.types);
     if indirect_return {
         if !efn.args.is_empty() || efn.receiver.is_some() {
             write!(out, ", ");
@@ -446,8 +376,13 @@
     write!(out, "  ");
     write_return_type(out, &efn.ret);
     match &efn.receiver {
-        None => write!(out, "(*{}$)(", efn.ident),
-        Some(receiver) => write!(out, "({}::*{}$)(", receiver.ty, efn.ident),
+        None => write!(out, "(*{}$)(", efn.name.rust),
+        Some(receiver) => write!(
+            out,
+            "({}::*{}$)(",
+            out.types.resolve(&receiver.ty).to_fully_qualified(),
+            efn.name.rust,
+        ),
     }
     for (i, arg) in efn.args.iter().enumerate() {
         if i > 0 {
@@ -463,13 +398,20 @@
     }
     write!(out, " = ");
     match &efn.receiver {
-        None => write!(out, "{}", efn.ident),
-        Some(receiver) => write!(out, "&{}::{}", receiver.ty, efn.ident),
+        None => write!(out, "{}", efn.name.to_fully_qualified()),
+        Some(receiver) => write!(
+            out,
+            "&{}::{}",
+            out.types.resolve(&receiver.ty).to_fully_qualified(),
+            efn.name.cxx,
+        ),
     }
     writeln!(out, ";");
     write!(out, "  ");
     if efn.throws {
-        writeln!(out, "::rust::Str::Repr throw$;");
+        out.builtin.ptr_len = true;
+        out.builtin.trycatch = true;
+        writeln!(out, "::rust::repr::PtrLen throw$;");
         writeln!(out, "  ::rust::behavior::trycatch(");
         writeln!(out, "      [&] {{");
         write!(out, "        ");
@@ -484,15 +426,19 @@
     }
     match &efn.ret {
         Some(Type::Ref(_)) => write!(out, "&"),
-        Some(Type::Str(_)) if !indirect_return => write!(out, "::rust::Str::Repr("),
+        Some(Type::Str(_)) if !indirect_return => {
+            out.builtin.rust_str_repr = true;
+            write!(out, "::rust::impl<::rust::Str>::repr(");
+        }
         Some(Type::SliceRefU8(_)) if !indirect_return => {
-            write!(out, "::rust::Slice<uint8_t>::Repr(")
+            out.builtin.rust_slice_repr = true;
+            write!(out, "::rust::impl<::rust::Slice<uint8_t>>::repr(")
         }
         _ => {}
     }
     match &efn.receiver {
-        None => write!(out, "{}$(", efn.ident),
-        Some(_) => write!(out, "(self.*{}$)(", efn.ident),
+        None => write!(out, "{}$(", efn.name.rust),
+        Some(_) => write!(out, "(self.*{}$)(", efn.name.rust),
     }
     for (i, arg) in efn.args.iter().enumerate() {
         if i > 0 {
@@ -504,16 +450,31 @@
         } 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,
+            );
         } else if arg.ty == RustString {
+            out.builtin.unsafe_bitcopy = true;
             write!(
                 out,
                 "::rust::String(::rust::unsafe_bitcopy, *{})",
                 arg.ident,
             );
         } 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 types.needs_indirect_abi(&arg.ty) {
+        } else if let Type::SliceRefU8(_) = arg.ty {
+            write!(
+                out,
+                "::rust::Slice<uint8_t>(static_cast<const uint8_t *>({0}.ptr), {0}.len)",
+                arg.ident,
+            );
+        } else if out.types.needs_indirect_abi(&arg.ty) {
             out.include.utility = true;
             write!(out, "::std::move(*{})", arg.ident);
         } else {
@@ -533,13 +494,14 @@
     writeln!(out, ";");
     if efn.throws {
         out.include.cstring = true;
+        out.builtin.exception = true;
         writeln!(out, "        throw$.ptr = nullptr;");
         writeln!(out, "      }},");
         writeln!(out, "      [&](const char *catch$) noexcept {{");
         writeln!(out, "        throw$.len = ::std::strlen(catch$);");
         writeln!(
             out,
-            "        throw$.ptr = cxxbridge03$exception(catch$, throw$.len);",
+            "        throw$.ptr = ::cxxbridge05$exception(catch$, throw$.len);",
         );
         writeln!(out, "      }});");
         writeln!(out, "  return throw$;");
@@ -548,9 +510,10 @@
     for arg in &efn.args {
         if let Type::Fn(f) = &arg.ty {
             let var = &arg.ident;
-            write_function_pointer_trampoline(out, efn, var, f, types);
+            write_function_pointer_trampoline(out, efn, var, f);
         }
     }
+    out.end_block(Block::ExternC);
 }
 
 fn write_function_pointer_trampoline(
@@ -558,35 +521,37 @@
     efn: &ExternFn,
     var: &Ident,
     f: &Signature,
-    types: &Types,
 ) {
-    out.next_section();
-    let r_trampoline = mangle::r_trampoline(&out.namespace, efn, var);
+    let r_trampoline = mangle::r_trampoline(efn, var, out.types);
     let indirect_call = true;
-    write_rust_function_decl_impl(out, &r_trampoline, f, types, indirect_call);
+    write_rust_function_decl_impl(out, &r_trampoline, f, indirect_call);
 
     out.next_section();
-    let c_trampoline = mangle::c_trampoline(&out.namespace, efn, var).to_string();
-    write_rust_function_shim_impl(out, &c_trampoline, f, types, &r_trampoline, indirect_call);
+    let c_trampoline = mangle::c_trampoline(efn, var, out.types).to_string();
+    write_rust_function_shim_impl(out, &c_trampoline, f, &r_trampoline, indirect_call);
 }
 
-fn write_rust_function_decl(out: &mut OutFile, efn: &ExternFn, types: &Types, _: &Option<String>) {
-    let link_name = mangle::extern_fn(&out.namespace, efn);
+fn write_rust_function_decl<'a>(out: &mut OutFile<'a>, efn: &'a ExternFn) {
+    out.set_namespace(&efn.name.namespace);
+    out.begin_block(Block::ExternC);
+    let link_name = mangle::extern_fn(efn, out.types);
     let indirect_call = false;
-    write_rust_function_decl_impl(out, &link_name, efn, types, indirect_call);
+    write_rust_function_decl_impl(out, &link_name, efn, indirect_call);
+    out.end_block(Block::ExternC);
 }
 
 fn write_rust_function_decl_impl(
     out: &mut OutFile,
     link_name: &Symbol,
     sig: &Signature,
-    types: &Types,
     indirect_call: bool,
 ) {
+    out.next_section();
     if sig.throws {
-        write!(out, "::rust::Str::Repr ");
+        out.builtin.ptr_len = true;
+        write!(out, "::rust::repr::PtrLen ");
     } else {
-        write_extern_return_type_space(out, &sig.ret, types);
+        write_extern_return_type_space(out, &sig.ret);
     }
     write!(out, "{}(", link_name);
     let mut needs_comma = false;
@@ -594,17 +559,21 @@
         if receiver.mutability.is_none() {
             write!(out, "const ");
         }
-        write!(out, "{} &self", receiver.ty);
+        write!(
+            out,
+            "{} &self",
+            out.types.resolve(&receiver.ty).to_fully_qualified(),
+        );
         needs_comma = true;
     }
     for arg in &sig.args {
         if needs_comma {
             write!(out, ", ");
         }
-        write_extern_arg(out, arg, types);
+        write_extern_arg(out, arg);
         needs_comma = true;
     }
-    if indirect_return(sig, types) {
+    if indirect_return(sig, out.types) {
         if needs_comma {
             write!(out, ", ");
         }
@@ -621,17 +590,18 @@
     writeln!(out, ") noexcept;");
 }
 
-fn write_rust_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) {
+fn write_rust_function_shim<'a>(out: &mut OutFile<'a>, efn: &'a ExternFn) {
+    out.set_namespace(&efn.name.namespace);
     for line in efn.doc.to_string().lines() {
         writeln!(out, "//{}", line);
     }
     let local_name = match &efn.sig.receiver {
-        None => efn.ident.to_string(),
-        Some(receiver) => format!("{}::{}", receiver.ty, efn.ident),
+        None => efn.name.cxx.to_string(),
+        Some(receiver) => format!("{}::{}", out.types.resolve(&receiver.ty).cxx, efn.name.cxx),
     };
-    let invoke = mangle::extern_fn(&out.namespace, efn);
+    let invoke = mangle::extern_fn(efn, out.types);
     let indirect_call = false;
-    write_rust_function_shim_impl(out, &local_name, efn, types, &invoke, indirect_call);
+    write_rust_function_shim_impl(out, &local_name, efn, &invoke, indirect_call);
 }
 
 fn write_rust_function_shim_decl(
@@ -670,7 +640,6 @@
     out: &mut OutFile,
     local_name: &str,
     sig: &Signature,
-    types: &Types,
     invoke: &Symbol,
     indirect_call: bool,
 ) {
@@ -685,16 +654,18 @@
     }
     writeln!(out, " {{");
     for arg in &sig.args {
-        if arg.ty != RustString && types.needs_indirect_abi(&arg.ty) {
+        if arg.ty != RustString && out.types.needs_indirect_abi(&arg.ty) {
             out.include.utility = true;
+            out.builtin.manually_drop = true;
             write!(out, "  ::rust::ManuallyDrop<");
             write_type(out, &arg.ty);
             writeln!(out, "> {}$(::std::move({0}));", arg.ident);
         }
     }
     write!(out, "  ");
-    let indirect_return = indirect_return(sig, types);
+    let indirect_return = indirect_return(sig, out.types);
     if indirect_return {
+        out.builtin.maybe_uninit = true;
         write!(out, "::rust::MaybeUninit<");
         write_type(out, sig.ret.as_ref().unwrap());
         writeln!(out, "> return$;");
@@ -711,24 +682,41 @@
                 write!(out, "(");
             }
             Type::Ref(_) => write!(out, "*"),
+            Type::Str(_) => {
+                out.builtin.rust_str_new_unchecked = true;
+                write!(out, "::rust::impl<::rust::Str>::new_unchecked(");
+            }
+            Type::SliceRefU8(_) => {
+                out.builtin.rust_slice_new = true;
+                write!(out, "::rust::impl<::rust::Slice<uint8_t>>::slice(");
+            }
             _ => {}
         }
     }
     if sig.throws {
-        write!(out, "::rust::Str::Repr error$ = ");
+        out.builtin.ptr_len = true;
+        write!(out, "::rust::repr::PtrLen error$ = ");
     }
     write!(out, "{}(", invoke);
+    let mut needs_comma = false;
     if sig.receiver.is_some() {
         write!(out, "*this");
+        needs_comma = true;
     }
-    for (i, arg) in sig.args.iter().enumerate() {
-        if i > 0 || sig.receiver.is_some() {
+    for arg in &sig.args {
+        if needs_comma {
             write!(out, ", ");
         }
         match &arg.ty {
-            Type::Str(_) => write!(out, "::rust::Str::Repr("),
-            Type::SliceRefU8(_) => write!(out, "::rust::Slice<uint8_t>::Repr("),
-            ty if types.needs_indirect_abi(ty) => write!(out, "&"),
+            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, "&"),
             _ => {}
         }
         write!(out, "{}", arg.ident);
@@ -736,32 +724,38 @@
             Type::RustBox(_) => write!(out, ".into_raw()"),
             Type::UniquePtr(_) => write!(out, ".release()"),
             Type::Str(_) | Type::SliceRefU8(_) => write!(out, ")"),
-            ty if ty != RustString && types.needs_indirect_abi(ty) => write!(out, "$.value"),
+            ty if ty != RustString && out.types.needs_indirect_abi(ty) => write!(out, "$.value"),
             _ => {}
         }
+        needs_comma = true;
     }
     if indirect_return {
-        if !sig.args.is_empty() {
+        if needs_comma {
             write!(out, ", ");
         }
         write!(out, "&return$.value");
+        needs_comma = true;
     }
     if indirect_call {
-        if !sig.args.is_empty() || indirect_return {
+        if needs_comma {
             write!(out, ", ");
         }
         write!(out, "extern$");
     }
     write!(out, ")");
-    if let Some(ret) = &sig.ret {
-        if let Type::RustBox(_) | Type::UniquePtr(_) = ret {
-            write!(out, ")");
+    if !indirect_return {
+        if let Some(ret) = &sig.ret {
+            if let Type::RustBox(_) | Type::UniquePtr(_) | Type::Str(_) | Type::SliceRefU8(_) = ret
+            {
+                write!(out, ")");
+            }
         }
     }
     writeln!(out, ";");
     if sig.throws {
+        out.builtin.rust_error = true;
         writeln!(out, "  if (error$.ptr) {{");
-        writeln!(out, "    throw ::rust::Error(error$);");
+        writeln!(out, "    throw ::rust::impl<::rust::Error>::error(error$);");
         writeln!(out, "  }}");
     }
     if indirect_return {
@@ -797,8 +791,6 @@
             write_type(out, &ty.inner);
             write!(out, " *");
         }
-        Type::Str(_) => write!(out, "::rust::Str::Repr"),
-        Type::SliceRefU8(_) => write!(out, "::rust::Slice<uint8_t>::Repr"),
         _ => write_type(out, ty),
     }
 }
@@ -812,7 +804,7 @@
     }
 }
 
-fn write_extern_return_type_space(out: &mut OutFile, ty: &Option<Type>, types: &Types) {
+fn write_extern_return_type_space(out: &mut OutFile, ty: &Option<Type>) {
     match ty {
         Some(Type::RustBox(ty)) | Some(Type::UniquePtr(ty)) => {
             write_type_space(out, &ty.inner);
@@ -825,24 +817,28 @@
             write_type(out, &ty.inner);
             write!(out, " *");
         }
-        Some(Type::Str(_)) => write!(out, "::rust::Str::Repr "),
-        Some(Type::SliceRefU8(_)) => write!(out, "::rust::Slice<uint8_t>::Repr "),
-        Some(ty) if types.needs_indirect_abi(ty) => write!(out, "void "),
+        Some(Type::Str(_)) | Some(Type::SliceRefU8(_)) => {
+            out.builtin.ptr_len = true;
+            write!(out, "::rust::repr::PtrLen ");
+        }
+        Some(ty) if out.types.needs_indirect_abi(ty) => write!(out, "void "),
         _ => write_return_type(out, ty),
     }
 }
 
-fn write_extern_arg(out: &mut OutFile, arg: &Var, types: &Types) {
+fn write_extern_arg(out: &mut OutFile, arg: &Var) {
     match &arg.ty {
         Type::RustBox(ty) | Type::UniquePtr(ty) | Type::CxxVector(ty) => {
             write_type_space(out, &ty.inner);
             write!(out, "*");
         }
-        Type::Str(_) => write!(out, "::rust::Str::Repr "),
-        Type::SliceRefU8(_) => write!(out, "::rust::Slice<uint8_t>::Repr "),
+        Type::Str(_) | Type::SliceRefU8(_) => {
+            out.builtin.ptr_len = true;
+            write!(out, "::rust::repr::PtrLen ");
+        }
         _ => write_type_space(out, &arg.ty),
     }
-    if types.needs_indirect_abi(&arg.ty) {
+    if out.types.needs_indirect_abi(&arg.ty) {
         write!(out, "*");
     }
     write!(out, "{}", arg.ident);
@@ -850,9 +846,9 @@
 
 fn write_type(out: &mut OutFile, ty: &Type) {
     match ty {
-        Type::Ident(ident) => match Atom::from(ident) {
+        Type::Ident(ident) => match Atom::from(&ident.rust) {
             Some(atom) => write_atom(out, atom),
-            None => write!(out, "{}", ident),
+            None => write!(out, "{}", out.types.resolve(ident).to_fully_qualified()),
         },
         Type::RustBox(ty) => {
             write!(out, "::rust::Box<");
@@ -952,223 +948,256 @@
 
 // Only called for legal referent types of unique_ptr and element types of
 // std::vector and Vec.
-fn to_typename(namespace: &Namespace, ty: &Type) -> String {
+fn to_typename(ty: &Type, types: &Types) -> String {
     match ty {
-        Type::Ident(ident) => {
-            let mut path = String::new();
-            for name in namespace {
-                path += &name.to_string();
-                path += "::";
-            }
-            path += &ident.to_string();
-            path
-        }
-        Type::CxxVector(ptr) => format!("::std::vector<{}>", to_typename(namespace, &ptr.inner)),
+        Type::Ident(ident) => types.resolve(&ident).to_fully_qualified(),
+        Type::CxxVector(ptr) => format!("::std::vector<{}>", to_typename(&ptr.inner, types)),
         _ => unreachable!(),
     }
 }
 
 // Only called for legal referent types of unique_ptr and element types of
 // std::vector and Vec.
-fn to_mangled(namespace: &Namespace, ty: &Type) -> String {
+fn to_mangled(ty: &Type, types: &Types) -> Symbol {
     match ty {
-        Type::Ident(_) => to_typename(namespace, ty).replace("::", "$"),
-        Type::CxxVector(ptr) => format!("std$vector${}", to_mangled(namespace, &ptr.inner)),
+        Type::Ident(ident) => ident.to_symbol(types),
+        Type::CxxVector(ptr) => to_mangled(&ptr.inner, types).prefix_with("std$vector$"),
         _ => unreachable!(),
     }
 }
 
-fn write_generic_instantiations(out: &mut OutFile, types: &Types) {
-    out.begin_block("extern \"C\"");
-    for ty in types {
+fn write_generic_instantiations(out: &mut OutFile) {
+    if out.header {
+        return;
+    }
+
+    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, inner);
+                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).is_none() {
+                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).is_none() && !types.aliases.contains_key(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, types);
+                    write_unique_ptr(out, inner);
                 }
             }
         } else if let Type::CxxVector(ptr) = ty {
             if let Type::Ident(inner) = &ptr.inner {
-                if Atom::from(inner).is_none() && !types.aliases.contains_key(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, types);
+                    write_cxx_vector(out, ty, inner);
                 }
             }
         }
     }
-    out.end_block("extern \"C\"");
+    out.end_block(Block::ExternC);
 
-    out.begin_block("namespace rust");
-    out.begin_block("inline namespace cxxbridge03");
-    for ty in types {
+    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, 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).is_none() {
+                if Atom::from(&inner.rust).is_none() {
                     write_rust_vec_impl(out, inner);
                 }
             }
         }
     }
-    out.end_block("namespace cxxbridge03");
-    out.end_block("namespace rust");
+    out.end_block(Block::InlineNamespace("cxxbridge05"));
+    out.end_block(Block::Namespace("rust"));
 }
 
-fn write_rust_box_extern(out: &mut OutFile, ident: &Ident) {
-    let mut inner = String::new();
-    for name in &out.namespace {
-        inner += &name.to_string();
-        inner += "::";
-    }
-    inner += &ident.to_string();
-    let instance = inner.replace("::", "$");
+fn write_rust_box_extern(out: &mut OutFile, ident: &Pair) {
+    let inner = ident.to_fully_qualified();
+    let instance = ident.to_symbol();
 
-    writeln!(out, "#ifndef CXXBRIDGE03_RUST_BOX_{}", instance);
-    writeln!(out, "#define CXXBRIDGE03_RUST_BOX_{}", instance);
+    writeln!(out, "#ifndef CXXBRIDGE05_RUST_BOX_{}", instance);
+    writeln!(out, "#define CXXBRIDGE05_RUST_BOX_{}", instance);
     writeln!(
         out,
-        "void cxxbridge03$box${}$uninit(::rust::Box<{}> *ptr) noexcept;",
+        "void cxxbridge05$box${}$uninit(::rust::Box<{}> *ptr) noexcept;",
         instance, inner,
     );
     writeln!(
         out,
-        "void cxxbridge03$box${}$drop(::rust::Box<{}> *ptr) noexcept;",
+        "void cxxbridge05$box${}$drop(::rust::Box<{}> *ptr) noexcept;",
         instance, inner,
     );
-    writeln!(out, "#endif // CXXBRIDGE03_RUST_BOX_{}", instance);
+    writeln!(out, "#endif // CXXBRIDGE05_RUST_BOX_{}", instance);
 }
 
-fn write_rust_vec_extern(out: &mut OutFile, element: &Ident) {
+fn write_rust_vec_extern(out: &mut OutFile, element: &ResolvableName) {
     let element = Type::Ident(element.clone());
-    let inner = to_typename(&out.namespace, &element);
-    let instance = to_mangled(&out.namespace, &element);
+    let inner = to_typename(&element, out.types);
+    let instance = to_mangled(&element, out.types);
 
-    writeln!(out, "#ifndef CXXBRIDGE03_RUST_VEC_{}", instance);
-    writeln!(out, "#define CXXBRIDGE03_RUST_VEC_{}", instance);
+    writeln!(out, "#ifndef CXXBRIDGE05_RUST_VEC_{}", instance);
+    writeln!(out, "#define CXXBRIDGE05_RUST_VEC_{}", instance);
     writeln!(
         out,
-        "void cxxbridge03$rust_vec${}$new(const ::rust::Vec<{}> *ptr) noexcept;",
+        "void cxxbridge05$rust_vec${}$new(const ::rust::Vec<{}> *ptr) noexcept;",
         instance, inner,
     );
     writeln!(
         out,
-        "void cxxbridge03$rust_vec${}$drop(::rust::Vec<{}> *ptr) noexcept;",
+        "void cxxbridge05$rust_vec${}$drop(::rust::Vec<{}> *ptr) noexcept;",
         instance, inner,
     );
     writeln!(
         out,
-        "size_t cxxbridge03$rust_vec${}$len(const ::rust::Vec<{}> *ptr) noexcept;",
+        "size_t cxxbridge05$rust_vec${}$len(const ::rust::Vec<{}> *ptr) noexcept;",
         instance, inner,
     );
     writeln!(
         out,
-        "const {} *cxxbridge03$rust_vec${}$data(const ::rust::Vec<{0}> *ptr) noexcept;",
+        "const {} *cxxbridge05$rust_vec${}$data(const ::rust::Vec<{0}> *ptr) noexcept;",
         inner, instance,
     );
     writeln!(
         out,
-        "size_t cxxbridge03$rust_vec${}$stride() noexcept;",
+        "void cxxbridge05$rust_vec${}$reserve_total(::rust::Vec<{}> *ptr, size_t cap) noexcept;",
+        instance, inner,
+    );
+    writeln!(
+        out,
+        "void cxxbridge05$rust_vec${}$set_len(::rust::Vec<{}> *ptr, size_t len) noexcept;",
+        instance, inner,
+    );
+    writeln!(
+        out,
+        "size_t cxxbridge05$rust_vec${}$stride() noexcept;",
         instance,
     );
-    writeln!(out, "#endif // CXXBRIDGE03_RUST_VEC_{}", instance);
+    writeln!(out, "#endif // CXXBRIDGE05_RUST_VEC_{}", instance);
 }
 
-fn write_rust_box_impl(out: &mut OutFile, ident: &Ident) {
-    let mut inner = String::new();
-    for name in &out.namespace {
-        inner += &name.to_string();
-        inner += "::";
-    }
-    inner += &ident.to_string();
-    let instance = inner.replace("::", "$");
+fn write_rust_box_impl(out: &mut OutFile, ident: &Pair) {
+    let inner = ident.to_fully_qualified();
+    let instance = ident.to_symbol();
 
     writeln!(out, "template <>");
     writeln!(out, "void Box<{}>::uninit() noexcept {{", inner);
-    writeln!(out, "  cxxbridge03$box${}$uninit(this);", instance);
+    writeln!(out, "  cxxbridge05$box${}$uninit(this);", instance);
     writeln!(out, "}}");
 
     writeln!(out, "template <>");
     writeln!(out, "void Box<{}>::drop() noexcept {{", inner);
-    writeln!(out, "  cxxbridge03$box${}$drop(this);", instance);
+    writeln!(out, "  cxxbridge05$box${}$drop(this);", instance);
     writeln!(out, "}}");
 }
 
-fn write_rust_vec_impl(out: &mut OutFile, element: &Ident) {
+fn write_rust_vec_impl(out: &mut OutFile, element: &ResolvableName) {
     let element = Type::Ident(element.clone());
-    let inner = to_typename(&out.namespace, &element);
-    let instance = to_mangled(&out.namespace, &element);
+    let inner = to_typename(&element, out.types);
+    let instance = to_mangled(&element, out.types);
 
     writeln!(out, "template <>");
     writeln!(out, "Vec<{}>::Vec() noexcept {{", inner);
-    writeln!(out, "  cxxbridge03$rust_vec${}$new(this);", instance);
+    writeln!(out, "  cxxbridge05$rust_vec${}$new(this);", instance);
     writeln!(out, "}}");
 
     writeln!(out, "template <>");
     writeln!(out, "void Vec<{}>::drop() noexcept {{", inner);
     writeln!(
         out,
-        "  return cxxbridge03$rust_vec${}$drop(this);",
+        "  return cxxbridge05$rust_vec${}$drop(this);",
         instance,
     );
     writeln!(out, "}}");
 
     writeln!(out, "template <>");
     writeln!(out, "size_t Vec<{}>::size() const noexcept {{", inner);
-    writeln!(out, "  return cxxbridge03$rust_vec${}$len(this);", instance);
+    writeln!(out, "  return cxxbridge05$rust_vec${}$len(this);", instance);
     writeln!(out, "}}");
 
     writeln!(out, "template <>");
     writeln!(out, "const {} *Vec<{0}>::data() const noexcept {{", inner);
     writeln!(
         out,
-        "  return cxxbridge03$rust_vec${}$data(this);",
+        "  return cxxbridge05$rust_vec${}$data(this);",
+        instance,
+    );
+    writeln!(out, "}}");
+
+    writeln!(out, "template <>");
+    writeln!(
+        out,
+        "void Vec<{}>::reserve_total(size_t cap) noexcept {{",
+        inner,
+    );
+    writeln!(
+        out,
+        "  return cxxbridge05$rust_vec${}$reserve_total(this, cap);",
+        instance,
+    );
+    writeln!(out, "}}");
+
+    writeln!(out, "template <>");
+    writeln!(out, "void Vec<{}>::set_len(size_t len) noexcept {{", inner);
+    writeln!(
+        out,
+        "  return cxxbridge05$rust_vec${}$set_len(this, len);",
         instance,
     );
     writeln!(out, "}}");
 
     writeln!(out, "template <>");
     writeln!(out, "size_t Vec<{}>::stride() noexcept {{", inner);
-    writeln!(out, "  return cxxbridge03$rust_vec${}$stride();", instance);
+    writeln!(out, "  return cxxbridge05$rust_vec${}$stride();", instance);
     writeln!(out, "}}");
 }
 
-fn write_unique_ptr(out: &mut OutFile, ident: &Ident, types: &Types) {
+fn write_unique_ptr(out: &mut OutFile, ident: &ResolvableName) {
     let ty = Type::Ident(ident.clone());
-    let instance = to_mangled(&out.namespace, &ty);
+    let instance = to_mangled(&ty, out.types);
 
-    writeln!(out, "#ifndef CXXBRIDGE03_UNIQUE_PTR_{}", instance);
-    writeln!(out, "#define CXXBRIDGE03_UNIQUE_PTR_{}", instance);
+    writeln!(out, "#ifndef CXXBRIDGE05_UNIQUE_PTR_{}", instance);
+    writeln!(out, "#define CXXBRIDGE05_UNIQUE_PTR_{}", instance);
 
-    write_unique_ptr_common(out, &ty, types);
+    write_unique_ptr_common(out, &ty);
 
-    writeln!(out, "#endif // CXXBRIDGE03_UNIQUE_PTR_{}", instance);
+    writeln!(out, "#endif // CXXBRIDGE05_UNIQUE_PTR_{}", instance);
 }
 
 // Shared by UniquePtr<T> and UniquePtr<CxxVector<T>>.
-fn write_unique_ptr_common(out: &mut OutFile, ty: &Type, types: &Types) {
+fn write_unique_ptr_common(out: &mut OutFile, ty: &Type) {
     out.include.new = true;
     out.include.utility = true;
-    let inner = to_typename(&out.namespace, ty);
-    let instance = to_mangled(&out.namespace, ty);
+    let inner = to_typename(ty, out.types);
+    let instance = to_mangled(ty, out.types);
 
     let can_construct_from_value = match ty {
-        Type::Ident(ident) => types.structs.contains_key(ident),
+        // 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)
+        }
         _ => false,
     };
 
@@ -1184,7 +1213,7 @@
     );
     writeln!(
         out,
-        "void cxxbridge03$unique_ptr${}$null(::std::unique_ptr<{}> *ptr) noexcept {{",
+        "void cxxbridge05$unique_ptr${}$null(::std::unique_ptr<{}> *ptr) noexcept {{",
         instance, inner,
     );
     writeln!(out, "  new (ptr) ::std::unique_ptr<{}>();", inner);
@@ -1192,7 +1221,7 @@
     if can_construct_from_value {
         writeln!(
             out,
-            "void cxxbridge03$unique_ptr${}$new(::std::unique_ptr<{}> *ptr, {} *value) noexcept {{",
+            "void cxxbridge05$unique_ptr${}$new(::std::unique_ptr<{}> *ptr, {} *value) noexcept {{",
             instance, inner, inner,
         );
         writeln!(
@@ -1204,57 +1233,57 @@
     }
     writeln!(
         out,
-        "void cxxbridge03$unique_ptr${}$raw(::std::unique_ptr<{}> *ptr, {} *raw) noexcept {{",
+        "void cxxbridge05$unique_ptr${}$raw(::std::unique_ptr<{}> *ptr, {} *raw) noexcept {{",
         instance, inner, inner,
     );
     writeln!(out, "  new (ptr) ::std::unique_ptr<{}>(raw);", inner);
     writeln!(out, "}}");
     writeln!(
         out,
-        "const {} *cxxbridge03$unique_ptr${}$get(const ::std::unique_ptr<{}>& ptr) noexcept {{",
+        "const {} *cxxbridge05$unique_ptr${}$get(const ::std::unique_ptr<{}>& ptr) noexcept {{",
         inner, instance, inner,
     );
     writeln!(out, "  return ptr.get();");
     writeln!(out, "}}");
     writeln!(
         out,
-        "{} *cxxbridge03$unique_ptr${}$release(::std::unique_ptr<{}>& ptr) noexcept {{",
+        "{} *cxxbridge05$unique_ptr${}$release(::std::unique_ptr<{}>& ptr) noexcept {{",
         inner, instance, inner,
     );
     writeln!(out, "  return ptr.release();");
     writeln!(out, "}}");
     writeln!(
         out,
-        "void cxxbridge03$unique_ptr${}$drop(::std::unique_ptr<{}> *ptr) noexcept {{",
+        "void cxxbridge05$unique_ptr${}$drop(::std::unique_ptr<{}> *ptr) noexcept {{",
         instance, inner,
     );
     writeln!(out, "  ptr->~unique_ptr();");
     writeln!(out, "}}");
 }
 
-fn write_cxx_vector(out: &mut OutFile, vector_ty: &Type, element: &Ident, types: &Types) {
+fn write_cxx_vector(out: &mut OutFile, vector_ty: &Type, element: &ResolvableName) {
     let element = Type::Ident(element.clone());
-    let inner = to_typename(&out.namespace, &element);
-    let instance = to_mangled(&out.namespace, &element);
+    let inner = to_typename(&element, out.types);
+    let instance = to_mangled(&element, out.types);
 
-    writeln!(out, "#ifndef CXXBRIDGE03_VECTOR_{}", instance);
-    writeln!(out, "#define CXXBRIDGE03_VECTOR_{}", instance);
+    writeln!(out, "#ifndef CXXBRIDGE05_VECTOR_{}", instance);
+    writeln!(out, "#define CXXBRIDGE05_VECTOR_{}", instance);
     writeln!(
         out,
-        "size_t cxxbridge03$std$vector${}$size(const ::std::vector<{}> &s) noexcept {{",
+        "size_t cxxbridge05$std$vector${}$size(const ::std::vector<{}> &s) noexcept {{",
         instance, inner,
     );
     writeln!(out, "  return s.size();");
     writeln!(out, "}}");
     writeln!(
         out,
-        "const {} *cxxbridge03$std$vector${}$get_unchecked(const ::std::vector<{}> &s, size_t pos) noexcept {{",
+        "const {} *cxxbridge05$std$vector${}$get_unchecked(const ::std::vector<{}> &s, size_t pos) noexcept {{",
         inner, instance, inner,
     );
     writeln!(out, "  return &s[pos];");
     writeln!(out, "}}");
 
-    write_unique_ptr_common(out, vector_ty, types);
+    write_unique_ptr_common(out, vector_ty);
 
-    writeln!(out, "#endif // CXXBRIDGE03_VECTOR_{}", instance);
+    writeln!(out, "#endif // CXXBRIDGE05_VECTOR_{}", instance);
 }
diff --git a/include/cxx.h b/include/cxx.h
index c3231fd..7dfbbca 100644
--- a/include/cxx.h
+++ b/include/cxx.h
@@ -5,21 +5,27 @@
 #include <exception>
 #include <iosfwd>
 #include <new>
+#include <stdexcept>
 #include <string>
 #include <type_traits>
 #include <utility>
 #include <vector>
 #if defined(_WIN32)
-#include <BaseTsd.h>
+#include <basetsd.h>
 #endif
 
 namespace rust {
-inline namespace cxxbridge03 {
+inline namespace cxxbridge05 {
 
 struct unsafe_bitcopy_t;
 
-#ifndef CXXBRIDGE03_RUST_STRING
-#define CXXBRIDGE03_RUST_STRING
+namespace {
+template <typename T>
+class impl;
+}
+
+#ifndef CXXBRIDGE05_RUST_STRING
+#define CXXBRIDGE05_RUST_STRING
 class String final {
 public:
   String() noexcept;
@@ -48,21 +54,18 @@
   // Size and alignment statically verified by rust_string.rs.
   std::array<uintptr_t, 3> repr;
 };
-#endif // CXXBRIDGE03_RUST_STRING
+#endif // CXXBRIDGE05_RUST_STRING
 
-#ifndef CXXBRIDGE03_RUST_STR
-#define CXXBRIDGE03_RUST_STR
+#ifndef CXXBRIDGE05_RUST_STR
 class Str final {
 public:
   Str() noexcept;
-  Str(const Str &) noexcept;
-
   Str(const std::string &);
   Str(const char *);
   Str(const char *, size_t);
   Str(std::string &&) = delete;
 
-  Str &operator=(Str) noexcept;
+  Str &operator=(const Str &) noexcept = default;
 
   explicit operator std::string() const;
 
@@ -71,54 +74,46 @@
   size_t size() const noexcept;
   size_t length() const noexcept;
 
-  // Repr is PRIVATE; must not be used other than by our generated code.
-  //
-  // Not necessarily ABI compatible with &str. Codegen will translate to
-  // cxx::rust_str::RustStr which matches this layout.
-  struct Repr {
-    const char *ptr;
-    size_t len;
-  };
-  Str(Repr) noexcept;
-  explicit operator Repr() noexcept;
+  // Important in order for System V ABI to pass in registers.
+  Str(const Str &) noexcept = default;
+  ~Str() noexcept = default;
 
 private:
-  Repr repr;
+  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;
 };
-#endif // CXXBRIDGE03_RUST_STR
+#endif // CXXBRIDGE05_RUST_STR
 
-#ifndef CXXBRIDGE03_RUST_SLICE
+#ifndef CXXBRIDGE05_RUST_SLICE
 template <typename T>
 class Slice final {
 public:
   Slice() noexcept;
-  Slice(const Slice<T> &) noexcept;
   Slice(const T *, size_t count) noexcept;
 
-  Slice &operator=(Slice<T>) noexcept;
+  Slice &operator=(const Slice<T> &) noexcept = default;
 
   const T *data() const noexcept;
   size_t size() const noexcept;
   size_t length() const noexcept;
 
-  // Repr is PRIVATE; must not be used other than by our generated code.
-  //
-  // At present this class is only used for &[u8] slices.
-  // Not necessarily ABI compatible with &[u8]. Codegen will translate to
-  // cxx::rust_sliceu8::RustSliceU8 which matches this layout.
-  struct Repr {
-    const T *ptr;
-    size_t len;
-  };
-  Slice(Repr) noexcept;
-  explicit operator Repr() noexcept;
+  // Important in order for System V ABI to pass in registers.
+  Slice(const Slice<T> &) noexcept = default;
+  ~Slice() noexcept = default;
 
 private:
-  Repr repr;
+  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 // CXXBRIDGE03_RUST_SLICE
+#endif // CXXBRIDGE05_RUST_SLICE
 
-#ifndef CXXBRIDGE03_RUST_BOX
+#ifndef CXXBRIDGE05_RUST_BOX
 template <typename T>
 class Box final {
 public:
@@ -157,9 +152,9 @@
   void drop() noexcept;
   T *ptr;
 };
-#endif // CXXBRIDGE03_RUST_BOX
+#endif // CXXBRIDGE05_RUST_BOX
 
-#ifndef CXXBRIDGE03_RUST_VEC
+#ifndef CXXBRIDGE05_RUST_VEC
 template <typename T>
 class Vec final {
 public:
@@ -174,8 +169,21 @@
   size_t size() const noexcept;
   bool empty() const noexcept;
   const T *data() const noexcept;
+  T *data() noexcept;
 
-  class const_iterator {
+  const T &operator[](size_t n) const noexcept;
+  const T &at(size_t n) const;
+
+  const T &front() const;
+  const T &back() const;
+
+  void reserve(size_t new_cap);
+  void push_back(const T &value);
+  void push_back(T &&value);
+  template <class... Args>
+  void emplace_back(Args &&... args);
+
+  class const_iterator final {
   public:
     using difference_type = ptrdiff_t;
     using value_type = typename std::add_const<T>::type;
@@ -206,20 +214,21 @@
 
 private:
   static size_t stride() noexcept;
+  void reserve_total(size_t cap) noexcept;
+  void set_len(size_t len) noexcept;
   void drop() noexcept;
 
   // Size and alignment statically verified by rust_vec.rs.
   std::array<uintptr_t, 3> repr;
 };
-#endif // CXXBRIDGE03_RUST_VEC
+#endif // CXXBRIDGE05_RUST_VEC
 
-#ifndef CXXBRIDGE03_RUST_FN
-#define CXXBRIDGE03_RUST_FN
+#ifndef CXXBRIDGE05_RUST_FN
 template <typename Signature, bool Throws = false>
 class Fn;
 
 template <typename Ret, typename... Args, bool Throws>
-class Fn<Ret(Args...), Throws> {
+class Fn<Ret(Args...), Throws> final {
 public:
   Ret operator()(Args... args) const noexcept(!Throws);
   Fn operator*() const noexcept;
@@ -231,51 +240,92 @@
 
 template <typename Signature>
 using TryFn = Fn<Signature, true>;
-#endif // CXXBRIDGE03_RUST_FN
+#endif // CXXBRIDGE05_RUST_FN
 
-#ifndef CXXBRIDGE03_RUST_ERROR
-#define CXXBRIDGE03_RUST_ERROR
-class Error final : std::exception {
+#ifndef CXXBRIDGE05_RUST_ERROR
+#define CXXBRIDGE05_RUST_ERROR
+class Error final : public std::exception {
 public:
   Error(const Error &);
   Error(Error &&) noexcept;
-  Error(Str::Repr) noexcept;
   ~Error() noexcept;
+
+  Error &operator=(const Error &);
+  Error &operator=(Error &&) noexcept;
+
   const char *what() const noexcept override;
 
 private:
-  Str::Repr msg;
+  Error() noexcept = default;
+  friend impl<Error>;
+  const char *msg;
+  size_t len;
 };
-#endif // CXXBRIDGE03_RUST_ERROR
+#endif // CXXBRIDGE05_RUST_ERROR
 
-#ifndef CXXBRIDGE03_RUST_ISIZE
-#define CXXBRIDGE03_RUST_ISIZE
+#ifndef CXXBRIDGE05_RUST_ISIZE
+#define CXXBRIDGE05_RUST_ISIZE
 #if defined(_WIN32)
 using isize = SSIZE_T;
 #else
 using isize = ssize_t;
 #endif
-#endif // CXXBRIDGE03_RUST_ISIZE
+#endif // CXXBRIDGE05_RUST_ISIZE
 
 std::ostream &operator<<(std::ostream &, const String &);
 std::ostream &operator<<(std::ostream &, const Str &);
 
+// IsRelocatable<T> is used in assertions that a C++ type passed by value
+// between Rust and C++ is soundly relocatable by Rust.
+//
+// There may be legitimate reasons to opt out of the check for support of types
+// that the programmer knows are soundly Rust-movable despite not being
+// recognized as such by the C++ type system due to a move constructor or
+// destructor. To opt out of the relocatability check, do either of the
+// following things in any header used by `include!` in the bridge.
+//
+//      --- if you define the type:
+//      struct MyType {
+//        ...
+//    +   using IsRelocatable = std::true_type;
+//      };
+//
+//      --- otherwise:
+//    + template <>
+//    + struct rust::IsRelocatable<MyType> : std::true_type {};
+template <typename T>
+struct IsRelocatable;
+
 // Snake case aliases for use in code that uses this style for type names.
 using string = String;
 using str = Str;
 template <class T>
+using slice = Slice<T>;
+template <class T>
 using box = Box<T>;
+template <class 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>;
+template <typename T>
+using is_relocatable = IsRelocatable<T>;
 
 
 
 ////////////////////////////////////////////////////////////////////////////////
 /// end public API, begin implementation details
 
+#ifndef CXXBRIDGE05_PANIC
+#define CXXBRIDGE05_PANIC
+template <typename Exception>
+void panic [[noreturn]] (const char *msg);
+#endif // CXXBRIDGE05_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) {
   return (*this->trampoline)(std::move(args)..., this->fn);
@@ -285,59 +335,52 @@
 Fn<Ret(Args...), Throws> Fn<Ret(Args...), Throws>::operator*() const noexcept {
   return *this;
 }
+#endif // CXXBRIDGE05_RUST_FN
 
-#ifndef CXXBRIDGE03_RUST_BITCOPY
-#define CXXBRIDGE03_RUST_BITCOPY
-struct unsafe_bitcopy_t {
+#ifndef CXXBRIDGE05_RUST_BITCOPY
+#define CXXBRIDGE05_RUST_BITCOPY
+struct unsafe_bitcopy_t final {
   explicit unsafe_bitcopy_t() = default;
 };
 
 constexpr unsafe_bitcopy_t unsafe_bitcopy{};
-#endif // CXXBRIDGE03_RUST_BITCOPY
+#endif // CXXBRIDGE05_RUST_BITCOPY
 
-#ifndef CXXBRIDGE03_RUST_SLICE
-#define CXXBRIDGE03_RUST_SLICE
+#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
 template <typename T>
-Slice<T>::Slice() noexcept : repr(Repr{reinterpret_cast<const T *>(this), 0}) {}
+Slice<T>::Slice() noexcept : ptr(reinterpret_cast<const T *>(this)), len(0) {}
 
 template <typename T>
-Slice<T>::Slice(const Slice<T> &) noexcept = default;
-
-template <typename T>
-Slice<T>::Slice(const T *s, size_t count) noexcept : repr(Repr{s, count}) {}
-
-template <typename T>
-Slice<T> &Slice<T>::operator=(Slice<T> other) noexcept {
-  this->repr = other.repr;
-  return *this;
-}
+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->repr.ptr;
+  return this->ptr;
 }
 
 template <typename T>
 size_t Slice<T>::size() const noexcept {
-  return this->repr.len;
+  return this->len;
 }
 
 template <typename T>
 size_t Slice<T>::length() const noexcept {
-  return this->repr.len;
+  return this->len;
 }
+#endif // CXXBRIDGE05_RUST_SLICE
 
-template <typename T>
-Slice<T>::Slice(Repr repr_) noexcept : repr(repr_) {}
-
-template <typename T>
-Slice<T>::operator Repr() noexcept {
-  return this->repr;
-}
-#endif // CXXBRIDGE03_RUST_SLICE
-
-#ifndef CXXBRIDGE03_RUST_BOX
-#define CXXBRIDGE03_RUST_BOX
+#ifndef CXXBRIDGE05_RUST_BOX
+#define CXXBRIDGE05_RUST_BOX
 template <typename T>
 Box<T>::Box(const Box &other) : Box(*other) {}
 
@@ -433,10 +476,10 @@
 
 template <typename T>
 Box<T>::Box() noexcept {}
-#endif // CXXBRIDGE03_RUST_BOX
+#endif // CXXBRIDGE05_RUST_BOX
 
-#ifndef CXXBRIDGE03_RUST_VEC
-#define CXXBRIDGE03_RUST_VEC
+#ifndef CXXBRIDGE05_RUST_VEC
+#define CXXBRIDGE05_RUST_VEC
 template <typename T>
 Vec<T>::Vec(Vec &&other) noexcept {
   this->repr = other.repr;
@@ -464,6 +507,61 @@
 }
 
 template <typename T>
+T *Vec<T>::data() noexcept {
+  return const_cast<T *>(const_cast<const Vec<T> *>(this)->data());
+}
+
+template <typename T>
+const T &Vec<T>::operator[](size_t n) const noexcept {
+  auto data = reinterpret_cast<const char *>(this->data());
+  return *reinterpret_cast<const T *>(data + n * this->stride());
+}
+
+template <typename T>
+const T &Vec<T>::at(size_t n) const {
+  if (n >= this->size()) {
+    panic<std::out_of_range>("rust::Vec index out of range");
+  }
+  return (*this)[n];
+}
+
+template <typename T>
+const T &Vec<T>::front() const {
+  return (*this)[0];
+}
+
+template <typename T>
+const T &Vec<T>::back() const {
+  return (*this)[this->size() - 1];
+}
+
+template <typename T>
+void Vec<T>::reserve(size_t new_cap) {
+  this->reserve_total(new_cap);
+}
+
+template <typename T>
+void Vec<T>::push_back(const T &value) {
+  this->emplace_back(value);
+}
+
+template <typename T>
+void Vec<T>::push_back(T &&value) {
+  this->emplace_back(std::move(value));
+}
+
+template <typename T>
+template <typename... Args>
+void Vec<T>::emplace_back(Args &&... args) {
+  auto size = this->size();
+  this->reserve_total(size + 1);
+  ::new (reinterpret_cast<T *>(reinterpret_cast<char *>(this->data()) +
+                               size * this->stride()))
+      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);
 }
@@ -488,14 +586,14 @@
 }
 
 template <typename T>
-bool Vec<T>::const_iterator::operator==(const const_iterator &other) const
-    noexcept {
+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 {
+bool Vec<T>::const_iterator::operator!=(
+    const const_iterator &other) const noexcept {
   return this->pos != other.pos;
 }
 
@@ -517,7 +615,44 @@
 // 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 // CXXBRIDGE03_RUST_VEC
+#endif // CXXBRIDGE05_RUST_VEC
 
-} // namespace cxxbridge03
+#ifndef CXXBRIDGE05_RELOCATABLE
+#define CXXBRIDGE05_RELOCATABLE
+namespace detail {
+template <typename... Ts>
+struct make_void {
+  using type = void;
+};
+
+template <typename... Ts>
+using void_t = typename make_void<Ts...>::type;
+
+template <typename Void, template <typename...> class, typename...>
+struct detect : std::false_type {};
+template <template <typename...> class T, typename... A>
+struct detect<void_t<T<A...>>, T, A...> : std::true_type {};
+
+template <template <typename...> class T, typename... A>
+using is_detected = detect<void, T, A...>;
+
+template <typename T>
+using detect_IsRelocatable = typename T::IsRelocatable;
+
+template <typename T>
+struct get_IsRelocatable
+    : std::is_same<typename T::IsRelocatable, std::true_type> {};
+} // namespace detail
+
+template <typename T>
+struct IsRelocatable
+    : std::conditional<
+          detail::is_detected<detail::detect_IsRelocatable, T>::value,
+          detail::get_IsRelocatable<T>,
+          std::integral_constant<
+              bool, std::is_trivially_move_constructible<T>::value &&
+                        std::is_trivially_destructible<T>::value>>::type {};
+#endif // CXXBRIDGE05_RELOCATABLE
+
+} // namespace cxxbridge05
 } // namespace rust
diff --git a/macro/Cargo.toml b/macro/Cargo.toml
index 21c4318..6b07a07 100644
--- a/macro/Cargo.toml
+++ b/macro/Cargo.toml
@@ -1,12 +1,12 @@
 [package]
 name = "cxxbridge-macro"
-version = "0.3.4"
+version = "0.5.9"
 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"
-exclude = ["README.md"]
+exclude = ["build.rs", "README.md"]
 keywords = ["ffi"]
 categories = ["development-tools::ffi"]
 
@@ -19,7 +19,7 @@
 syn = { version = "1.0.20", features = ["full"] }
 
 [dev-dependencies]
-cxx = { version = "0.3", path = ".." }
+cxx = { version = "0.5", path = ".." }
 
 [package.metadata.docs.rs]
 targets = ["x86_64-unknown-linux-gnu"]
diff --git a/macro/build.rs b/macro/build.rs
new file mode 100644
index 0000000..927f72b
--- /dev/null
+++ b/macro/build.rs
@@ -0,0 +1 @@
+include!("../tools/cargo/build.rs");
diff --git a/macro/src/derive.rs b/macro/src/derive.rs
new file mode 100644
index 0000000..1abc5da
--- /dev/null
+++ b/macro/src/derive.rs
@@ -0,0 +1,14 @@
+use crate::syntax::Derive;
+use proc_macro2::TokenStream;
+use quote::{quote, ToTokens};
+
+pub struct DeriveAttribute<'a>(pub &'a [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),*)]));
+        }
+    }
+}
diff --git a/macro/src/expand.rs b/macro/src/expand.rs
index f28d955..903585e 100644
--- a/macro/src/expand.rs
+++ b/macro/src/expand.rs
@@ -1,93 +1,95 @@
+use crate::derive::DeriveAttribute;
 use crate::syntax::atom::Atom::{self, *};
-use crate::syntax::namespace::Namespace;
+use crate::syntax::file::Module;
 use crate::syntax::report::Errors;
 use crate::syntax::symbol::Symbol;
 use crate::syntax::{
-    self, check, mangle, Api, Enum, ExternFn, ExternType, Signature, Struct, Type, TypeAlias, Types,
+    self, check, mangle, Api, Enum, ExternFn, ExternType, Impl, Pair, ResolvableName, Signature,
+    Struct, Type, TypeAlias, Types,
 };
 use proc_macro2::{Ident, Span, TokenStream};
 use quote::{format_ident, quote, quote_spanned, ToTokens};
-use syn::{parse_quote, Error, ItemMod, Result, Token};
+use std::mem;
+use syn::{parse_quote, Result, Token};
 
-pub fn bridge(namespace: &Namespace, mut ffi: ItemMod) -> Result<TokenStream> {
+pub fn bridge(mut ffi: Module) -> Result<TokenStream> {
     let ref mut errors = Errors::new();
-    let content = ffi.content.take().ok_or(Error::new(
-        Span::call_site(),
-        "#[cxx::bridge] module must have inline contents",
-    ))?;
-    let ref apis = syntax::parse_items(errors, content.1);
+    let content = mem::take(&mut ffi.content);
+    let trusted = ffi.unsafety.is_some();
+    let namespace = &ffi.namespace;
+    let ref apis = syntax::parse_items(errors, content, trusted, namespace);
     let ref types = Types::collect(errors, apis);
     errors.propagate()?;
-    check::typecheck(errors, namespace, apis, types);
+    check::typecheck(errors, apis, types);
     errors.propagate()?;
 
-    Ok(expand(namespace, ffi, apis, types))
+    Ok(expand(ffi, apis, types))
 }
 
-fn expand(namespace: &Namespace, ffi: ItemMod, apis: &[Api], types: &Types) -> TokenStream {
+fn expand(ffi: Module, apis: &[Api], types: &Types) -> TokenStream {
     let mut expanded = TokenStream::new();
     let mut hidden = TokenStream::new();
 
     for api in apis {
         if let Api::RustType(ety) = api {
             expanded.extend(expand_rust_type(ety));
-            let ident = &ety.ident;
-            let span = ident.span();
-            hidden.extend(quote_spanned! {span=>
-                let _ = ::std::ptr::read::<#ident>;
-            });
+            hidden.extend(expand_rust_type_assert_sized(ety));
         }
     }
 
     for api in apis {
         match api {
-            Api::Include(_) | Api::RustType(_) => {}
+            Api::Include(_) | Api::RustType(_) | Api::Impl(_) => {}
             Api::Struct(strct) => expanded.extend(expand_struct(strct)),
             Api::Enum(enm) => expanded.extend(expand_enum(enm)),
             Api::CxxType(ety) => {
-                if !types.enums.contains_key(&ety.ident) {
-                    expanded.extend(expand_cxx_type(namespace, ety));
+                let ident = &ety.name.rust;
+                if !types.structs.contains_key(ident) && !types.enums.contains_key(ident) {
+                    expanded.extend(expand_cxx_type(ety));
                 }
             }
             Api::CxxFunction(efn) => {
-                expanded.extend(expand_cxx_function_shim(namespace, efn, types));
+                expanded.extend(expand_cxx_function_shim(efn, types));
             }
-            Api::RustFunction(efn) => {
-                hidden.extend(expand_rust_function_shim(namespace, efn, types))
-            }
+            Api::RustFunction(efn) => hidden.extend(expand_rust_function_shim(efn, types)),
             Api::TypeAlias(alias) => {
                 expanded.extend(expand_type_alias(alias));
-                hidden.extend(expand_type_alias_verify(namespace, alias));
+                hidden.extend(expand_type_alias_verify(alias, types));
             }
         }
     }
 
     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).is_none() {
-                    hidden.extend(expand_rust_box(namespace, ident));
+                if Atom::from(&ident.rust).is_none() {
+                    hidden.extend(expand_rust_box(ident, types));
                 }
             }
         } else if let Type::RustVec(ty) = ty {
             if let Type::Ident(ident) = &ty.inner {
-                if Atom::from(ident).is_none() {
-                    hidden.extend(expand_rust_vec(namespace, ident));
+                if Atom::from(&ident.rust).is_none() {
+                    hidden.extend(expand_rust_vec(ident, types));
                 }
             }
         } else if let Type::UniquePtr(ptr) = ty {
             if let Type::Ident(ident) = &ptr.inner {
-                if Atom::from(ident).is_none() && !types.aliases.contains_key(ident) {
-                    expanded.extend(expand_unique_ptr(namespace, ident, types));
+                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));
                 }
             }
         } else if let Type::CxxVector(ptr) = ty {
             if let Type::Ident(ident) = &ptr.inner {
-                if Atom::from(ident).is_none() && !types.aliases.contains_key(ident) {
+                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(namespace, ident));
+                    expanded.extend(expand_cxx_vector(ident, explicit_impl, types));
                 }
             }
         }
@@ -121,29 +123,37 @@
 }
 
 fn expand_struct(strct: &Struct) -> TokenStream {
-    let ident = &strct.ident;
+    let ident = &strct.name.rust;
     let doc = &strct.doc;
-    let derives = &strct.derives;
+    let derives = DeriveAttribute(&strct.derives);
+    let type_id = type_id(&strct.name);
     let fields = strct.fields.iter().map(|field| {
         // 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)
     });
+
     quote! {
         #doc
-        #[derive(#(#derives),*)]
+        #derives
         #[repr(C)]
         pub struct #ident {
             #(#fields,)*
         }
+
+        unsafe impl ::cxx::ExternType for #ident {
+            type Id = #type_id;
+            type Kind = ::cxx::kind::Trivial;
+        }
     }
 }
 
 fn expand_enum(enm: &Enum) -> TokenStream {
-    let ident = &enm.ident;
+    let ident = &enm.name.rust;
     let doc = &enm.doc;
     let repr = enm.repr;
+    let type_id = type_id(&enm.name);
     let variants = enm.variants.iter().map(|variant| {
         let variant_ident = &variant.ident;
         let discriminant = &variant.discriminant;
@@ -151,6 +161,7 @@
             pub const #variant_ident: Self = #ident { repr: #discriminant };
         })
     });
+
     quote! {
         #doc
         #[derive(Copy, Clone, PartialEq, Eq)]
@@ -163,13 +174,18 @@
         impl #ident {
             #(#variants)*
         }
+
+        unsafe impl ::cxx::ExternType for #ident {
+            type Id = #type_id;
+            type Kind = ::cxx::kind::Trivial;
+        }
     }
 }
 
-fn expand_cxx_type(namespace: &Namespace, ety: &ExternType) -> TokenStream {
-    let ident = &ety.ident;
+fn expand_cxx_type(ety: &ExternType) -> TokenStream {
+    let ident = &ety.name.rust;
     let doc = &ety.doc;
-    let type_id = type_id(namespace, ident);
+    let type_id = type_id(&ety.name);
 
     quote! {
         #doc
@@ -180,19 +196,19 @@
 
         unsafe impl ::cxx::ExternType for #ident {
             type Id = #type_id;
+            type Kind = ::cxx::kind::Opaque;
         }
     }
 }
 
-fn expand_cxx_function_decl(namespace: &Namespace, efn: &ExternFn, types: &Types) -> TokenStream {
-    let ident = &efn.ident;
+fn expand_cxx_function_decl(efn: &ExternFn, types: &Types) -> TokenStream {
     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 ty = expand_extern_type(&arg.ty);
+        let ty = expand_extern_type(&arg.ty, types, true);
         if arg.ty == RustString {
             quote!(#ident: *const #ty)
         } else if let Type::RustVec(_) = arg.ty {
@@ -209,25 +225,24 @@
     let ret = if efn.throws {
         quote!(-> ::cxx::private::Result)
     } else {
-        expand_extern_return_type(&efn.ret, types)
+        expand_extern_return_type(&efn.ret, types, true)
     };
     let mut outparam = None;
     if indirect_return(efn, types) {
-        let ret = expand_extern_type(efn.ret.as_ref().unwrap());
+        let ret = expand_extern_type(efn.ret.as_ref().unwrap(), types, true);
         outparam = Some(quote!(__return: *mut #ret));
     }
-    let link_name = mangle::extern_fn(namespace, efn);
-    let local_name = format_ident!("__{}", ident);
+    let link_name = mangle::extern_fn(efn, types);
+    let local_name = format_ident!("__{}", efn.name.rust);
     quote! {
         #[link_name = #link_name]
         fn #local_name(#(#all_args,)* #outparam) #ret;
     }
 }
 
-fn expand_cxx_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types) -> TokenStream {
-    let ident = &efn.ident;
+fn expand_cxx_function_shim(efn: &ExternFn, types: &Types) -> TokenStream {
     let doc = &efn.doc;
-    let decl = expand_cxx_function_decl(namespace, efn, types);
+    let decl = expand_cxx_function_decl(efn, types);
     let receiver = efn.receiver.iter().map(|receiver| {
         let ampersand = receiver.ampersand;
         let mutability = receiver.mutability;
@@ -253,17 +268,29 @@
     let arg_vars = efn.args.iter().map(|arg| {
         let var = &arg.ident;
         match &arg.ty {
-            Type::Ident(ident) if ident == RustString => {
+            Type::Ident(ident) if ident.rust == RustString => {
                 quote!(#var.as_mut_ptr() as *const ::cxx::private::RustString)
             }
             Type::RustBox(_) => quote!(::std::boxed::Box::into_raw(#var)),
             Type::UniquePtr(_) => quote!(::cxx::UniquePtr::into_raw(#var)),
             Type::RustVec(_) => quote!(#var.as_mut_ptr() as *const ::cxx::private::RustVec<_>),
             Type::Ref(ty) => match &ty.inner {
-                Type::Ident(ident) if ident == RustString => {
-                    quote!(::cxx::private::RustString::from_ref(#var))
-                }
-                Type::RustVec(_) => quote!(::cxx::private::RustVec::from_ref(#var)),
+                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::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(_) => match ty.mutability {
+                    None => quote!(::cxx::private::RustVec::from_ref(#var)),
+                    Some(_) => quote!(::cxx::private::RustVec::from_mut(#var)),
+                },
+                inner if types.is_considered_improper_ctype(inner) => match ty.mutability {
+                    None => quote!(#var as *const #inner as *const ::std::ffi::c_void),
+                    Some(_) => quote!(#var as *mut #inner as *mut ::std::ffi::c_void),
+                },
                 _ => quote!(#var),
             },
             Type::Str(_) => quote!(::cxx::private::RustStr::from(#var)),
@@ -279,9 +306,7 @@
         .filter_map(|arg| {
             if let Type::Fn(f) = &arg.ty {
                 let var = &arg.ident;
-                Some(expand_function_pointer_trampoline(
-                    namespace, efn, var, f, types,
-                ))
+                Some(expand_function_pointer_trampoline(efn, var, f, types))
             } else {
                 None
             }
@@ -300,23 +325,22 @@
             }
         })
         .collect::<TokenStream>();
-    let local_name = format_ident!("__{}", ident);
+    let local_name = format_ident!("__{}", efn.name.rust);
     let call = if indirect_return {
-        let ret = expand_extern_type(efn.ret.as_ref().unwrap());
+        let ret = expand_extern_type(efn.ret.as_ref().unwrap(), types, true);
         setup.extend(quote! {
             let mut __return = ::std::mem::MaybeUninit::<#ret>::uninit();
         });
-        if efn.throws {
-            setup.extend(quote! {
+        setup.extend(if efn.throws {
+            quote! {
                 #local_name(#(#vars,)* __return.as_mut_ptr()).exception()?;
-            });
-            quote!(::std::result::Result::Ok(__return.assume_init()))
+            }
         } else {
-            setup.extend(quote! {
+            quote! {
                 #local_name(#(#vars,)* __return.as_mut_ptr());
-            });
-            quote!(__return.assume_init())
-        }
+            }
+        });
+        quote!(__return.assume_init())
     } else if efn.throws {
         quote! {
             #local_name(#(#vars),*).exception()
@@ -326,53 +350,65 @@
             #local_name(#(#vars),*)
         }
     };
-    let expr = if efn.throws {
-        efn.ret.as_ref().and_then(|ret| match ret {
-            Type::Ident(ident) if ident == RustString => {
-                Some(quote!(#call.map(|r| r.into_string())))
-            }
-            Type::RustBox(_) => Some(quote!(#call.map(|r| ::std::boxed::Box::from_raw(r)))),
-            Type::RustVec(_) => Some(quote!(#call.map(|r| r.into_vec()))),
-            Type::UniquePtr(_) => Some(quote!(#call.map(|r| ::cxx::UniquePtr::from_raw(r)))),
-            Type::Ref(ty) => match &ty.inner {
-                Type::Ident(ident) if ident == RustString => {
-                    Some(quote!(#call.map(|r| r.as_string())))
-                }
-                Type::RustVec(_) => Some(quote!(#call.map(|r| r.as_vec()))),
-                _ => None,
-            },
-            Type::Str(_) => Some(quote!(#call.map(|r| r.as_str()))),
-            Type::SliceRefU8(_) => Some(quote!(#call.map(|r| r.as_slice()))),
-            _ => None,
-        })
+    let mut expr;
+    if efn.throws && efn.sig.ret.is_none() {
+        expr = call;
     } else {
-        efn.ret.as_ref().and_then(|ret| match ret {
-            Type::Ident(ident) if ident == RustString => Some(quote!(#call.into_string())),
-            Type::RustBox(_) => Some(quote!(::std::boxed::Box::from_raw(#call))),
-            Type::RustVec(_) => Some(quote!(#call.into_vec())),
-            Type::UniquePtr(_) => Some(quote!(::cxx::UniquePtr::from_raw(#call))),
-            Type::Ref(ty) => match &ty.inner {
-                Type::Ident(ident) if ident == RustString => Some(quote!(#call.as_string())),
-                Type::RustVec(_) => Some(quote!(#call.as_vec())),
-                _ => None,
+        expr = match &efn.ret {
+            None => call,
+            Some(ret) => match ret {
+                Type::Ident(ident) if ident.rust == RustString => quote!(#call.into_string()),
+                Type::RustBox(_) => quote!(::std::boxed::Box::from_raw(#call)),
+                Type::RustVec(vec) => {
+                    if vec.inner == RustString {
+                        quote!(#call.into_vec_string())
+                    } else {
+                        quote!(#call.into_vec())
+                    }
+                }
+                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::RustVec(vec) if vec.inner == RustString => match ty.mutability {
+                        None => quote!(#call.as_vec_string()),
+                        Some(_) => quote!(#call.as_mut_vec_string()),
+                    },
+                    Type::RustVec(_) => match ty.mutability {
+                        None => quote!(#call.as_vec()),
+                        Some(_) => quote!(#call.as_mut_vec()),
+                    },
+                    inner if types.is_considered_improper_ctype(inner) => {
+                        let mutability = ty.mutability;
+                        quote!(&#mutability *#call.cast())
+                    }
+                    _ => call,
+                },
+                Type::Str(_) => quote!(#call.as_str()),
+                Type::SliceRefU8(_) => quote!(#call.as_slice()),
+                _ => call,
             },
-            Type::Str(_) => Some(quote!(#call.as_str())),
-            Type::SliceRefU8(_) => Some(quote!(#call.as_slice())),
-            _ => None,
-        })
+        };
+        if efn.throws {
+            expr = quote!(::std::result::Result::Ok(#expr));
+        }
+    };
+    let mut dispatch = quote!(#setup #expr);
+    let unsafety = &efn.sig.unsafety;
+    if unsafety.is_none() {
+        dispatch = quote!(unsafe { #dispatch });
     }
-    .unwrap_or(call);
+    let ident = &efn.name.rust;
     let function_shim = quote! {
         #doc
-        pub fn #ident(#(#all_args,)*) #ret {
+        pub #unsafety fn #ident(#(#all_args,)*) #ret {
             extern "C" {
                 #decl
             }
             #trampolines
-            unsafe {
-                #setup
-                #expr
-            }
+            #dispatch
         }
     };
     match &efn.receiver {
@@ -385,16 +421,15 @@
 }
 
 fn expand_function_pointer_trampoline(
-    namespace: &Namespace,
     efn: &ExternFn,
     var: &Ident,
     sig: &Signature,
     types: &Types,
 ) -> TokenStream {
-    let c_trampoline = mangle::c_trampoline(namespace, efn, var);
-    let r_trampoline = mangle::r_trampoline(namespace, efn, var);
+    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.ident, var);
+    let catch_unwind_label = format!("::{}::{}", efn.name.rust, var);
     let shim = expand_rust_function_shim_impl(
         sig,
         types,
@@ -420,18 +455,39 @@
 }
 
 fn expand_rust_type(ety: &ExternType) -> TokenStream {
-    let ident = &ety.ident;
+    let ident = &ety.name.rust;
     quote! {
         use super::#ident;
     }
 }
 
-fn expand_rust_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types) -> TokenStream {
-    let ident = &efn.ident;
-    let link_name = mangle::extern_fn(namespace, efn);
-    let local_name = format_ident!("__{}", ident);
-    let catch_unwind_label = format!("::{}", ident);
-    let invoke = Some(ident);
+fn expand_rust_type_assert_sized(ety: &ExternType) -> TokenStream {
+    // Rustc will render as follows if not sized:
+    //
+    //     type TheirType;
+    //     -----^^^^^^^^^-
+    //     |    |
+    //     |    doesn't have a size known at compile-time
+    //     required by this bound in `ffi::_::__AssertSized`
+
+    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
+    };
+    quote_spanned! {ident.span()=>
+        let _ = {
+            fn __AssertSized<T: ?#sized + #sized>() {}
+            __AssertSized::<#ident>
+        };
+    }
+}
+
+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 invoke = Some(&efn.name.rust);
     expand_rust_function_shim_impl(
         efn,
         types,
@@ -460,7 +516,7 @@
     });
     let args = sig.args.iter().map(|arg| {
         let ident = &arg.ident;
-        let ty = expand_extern_type(&arg.ty);
+        let ty = expand_extern_type(&arg.ty, types, false);
         if types.needs_indirect_abi(&arg.ty) {
             quote!(#ident: *mut #ty)
         } else {
@@ -472,15 +528,31 @@
     let arg_vars = sig.args.iter().map(|arg| {
         let ident = &arg.ident;
         match &arg.ty {
-            Type::Ident(i) if i == RustString => {
+            Type::Ident(i) if i.rust == RustString => {
                 quote!(::std::mem::take((*#ident).as_mut_string()))
             }
             Type::RustBox(_) => quote!(::std::boxed::Box::from_raw(#ident)),
-            Type::RustVec(_) => quote!(::std::mem::take((*#ident).as_mut_vec())),
+            Type::RustVec(vec) => {
+                if vec.inner == RustString {
+                    quote!(::std::mem::take((*#ident).as_mut_vec_string()))
+                } else {
+                    quote!(::std::mem::take((*#ident).as_mut_vec()))
+                }
+            }
             Type::UniquePtr(_) => quote!(::cxx::UniquePtr::from_raw(#ident)),
             Type::Ref(ty) => match &ty.inner {
-                Type::Ident(i) if i == RustString => quote!(#ident.as_string()),
-                Type::RustVec(_) => quote!(#ident.as_vec()),
+                Type::Ident(i) if i.rust == RustString => match ty.mutability {
+                    None => quote!(#ident.as_string()),
+                    Some(_) => 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(_) => match ty.mutability {
+                    None => quote!(#ident.as_vec()),
+                    Some(_) => quote!(#ident.as_mut_vec()),
+                },
                 _ => quote!(#ident),
             },
             Type::Str(_) => quote!(#ident.as_str()),
@@ -491,45 +563,57 @@
     });
     let vars = receiver_var.into_iter().chain(arg_vars);
 
+    let wrap_super = invoke.map(|invoke| expand_rust_function_shim_super(sig, &local_name, invoke));
+
     let mut call = match invoke {
-        Some(ident) => match &sig.receiver {
-            None => quote!(super::#ident),
-            Some(receiver) => {
-                let receiver_type = &receiver.ty;
-                quote!(#receiver_type::#ident)
-            }
-        },
-        None => quote!(__extern),
+        Some(_) => quote!(#local_name),
+        None => quote!(::std::mem::transmute::<*const (), #sig>(__extern)),
     };
     call.extend(quote! { (#(#vars),*) });
 
-    let mut expr = sig
-        .ret
-        .as_ref()
-        .and_then(|ret| match ret {
-            Type::Ident(ident) if ident == RustString => {
-                Some(quote!(::cxx::private::RustString::from(#call)))
+    let conversion = sig.ret.as_ref().and_then(|ret| match ret {
+        Type::Ident(ident) if ident.rust == RustString => {
+            Some(quote!(::cxx::private::RustString::from))
+        }
+        Type::RustBox(_) => Some(quote!(::std::boxed::Box::into_raw)),
+        Type::RustVec(vec) => {
+            if vec.inner == RustString {
+                Some(quote!(::cxx::private::RustVec::from_vec_string))
+            } else {
+                Some(quote!(::cxx::private::RustVec::from))
             }
-            Type::RustBox(_) => Some(quote!(::std::boxed::Box::into_raw(#call))),
-            Type::RustVec(_) => Some(quote!(::cxx::private::RustVec::from(#call))),
-            Type::UniquePtr(_) => Some(quote!(::cxx::UniquePtr::into_raw(#call))),
-            Type::Ref(ty) => match &ty.inner {
-                Type::Ident(ident) if ident == RustString => {
-                    Some(quote!(::cxx::private::RustString::from_ref(#call)))
-                }
-                Type::RustVec(_) => Some(quote!(::cxx::private::RustVec::from_ref(#call))),
-                _ => None,
+        }
+        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::Str(_) => Some(quote!(::cxx::private::RustStr::from(#call))),
-            Type::SliceRefU8(_) => Some(quote!(::cxx::private::RustSliceU8::from(#call))),
+            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(_) => match ty.mutability {
+                None => Some(quote!(::cxx::private::RustVec::from_ref)),
+                Some(_) => Some(quote!(::cxx::private::RustVec::from_mut)),
+            },
             _ => None,
-        })
-        .unwrap_or(call);
+        },
+        Type::Str(_) => Some(quote!(::cxx::private::RustStr::from)),
+        Type::SliceRefU8(_) => Some(quote!(::cxx::private::RustSliceU8::from)),
+        _ => None,
+    });
+
+    let mut expr = match conversion {
+        None => call,
+        Some(conversion) if !sig.throws => quote!(#conversion(#call)),
+        Some(conversion) => quote!(::std::result::Result::map(#call, #conversion)),
+    };
 
     let mut outparam = None;
     let indirect_return = indirect_return(sig, types);
     if indirect_return {
-        let ret = expand_extern_type(sig.ret.as_ref().unwrap());
+        let ret = expand_extern_type(sig.ret.as_ref().unwrap(), types, false);
         outparam = Some(quote!(__return: *mut #ret,));
     }
     if sig.throws {
@@ -547,11 +631,11 @@
     let ret = if sig.throws {
         quote!(-> ::cxx::private::Result)
     } else {
-        expand_extern_return_type(&sig.ret, types)
+        expand_extern_return_type(&sig.ret, types, false)
     };
 
     let pointer = match invoke {
-        None => Some(quote!(__extern: #sig)),
+        None => Some(quote!(__extern: *const ())),
         Some(_) => None,
     };
 
@@ -560,51 +644,108 @@
         #[export_name = #link_name]
         unsafe extern "C" fn #local_name(#(#all_args,)* #outparam #pointer) #ret {
             let __fn = concat!(module_path!(), #catch_unwind_label);
+            #wrap_super
             #expr
         }
     }
 }
 
+// A wrapper like `fn f(x: Arg) { super::f(x) }` just to ensure we have the
+// accurate unsafety declaration and no problematic elided lifetimes.
+fn expand_rust_function_shim_super(
+    sig: &Signature,
+    local_name: &Ident,
+    invoke: &Ident,
+) -> TokenStream {
+    let unsafety = sig.unsafety;
+
+    let receiver_var = sig
+        .receiver
+        .as_ref()
+        .map(|receiver| Ident::new("__self", receiver.var.span));
+    let receiver = sig.receiver.iter().map(|receiver| {
+        let receiver_type = receiver.ty();
+        quote!(#receiver_var: #receiver_type)
+    });
+    let args = sig.args.iter().map(|arg| quote!(#arg));
+    let all_args = receiver.chain(args);
+
+    let ret = if let Some((result, _langle, rangle)) = sig.throws_tokens {
+        let ok = match &sig.ret {
+            Some(ret) => quote!(#ret),
+            None => quote!(()),
+        };
+        let impl_trait = quote_spanned!(result.span=> impl);
+        let display = quote_spanned!(rangle.span=> ::std::fmt::Display);
+        quote!(-> ::std::result::Result<#ok, #impl_trait #display>)
+    } else {
+        expand_return_type(&sig.ret)
+    };
+
+    let arg_vars = sig.args.iter().map(|arg| &arg.ident);
+    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;
+            quote_spanned!(span=> #receiver_type::#invoke)
+        }
+    };
+
+    quote_spanned! {span=>
+        #unsafety fn #local_name(#(#all_args,)*) #ret {
+            #call(#(#vars,)*)
+        }
+    }
+}
+
 fn expand_type_alias(alias: &TypeAlias) -> TokenStream {
-    let ident = &alias.ident;
+    let doc = &alias.doc;
+    let ident = &alias.name.rust;
     let ty = &alias.ty;
     quote! {
+        #doc
         pub type #ident = #ty;
     }
 }
 
-fn expand_type_alias_verify(namespace: &Namespace, alias: &TypeAlias) -> TokenStream {
-    let ident = &alias.ident;
-    let type_id = type_id(namespace, ident);
+fn expand_type_alias_verify(alias: &TypeAlias, types: &Types) -> TokenStream {
+    let ident = &alias.name.rust;
+    let type_id = type_id(&alias.name);
     let begin_span = alias.type_token.span;
     let end_span = alias.semi_token.span;
     let begin = quote_spanned!(begin_span=> ::cxx::private::verify_extern_type::<);
     let end = quote_spanned!(end_span=> >);
 
-    quote! {
+    let mut verify = quote! {
         const _: fn() = #begin #ident, #type_id #end;
+    };
+
+    if types.required_trivial.contains_key(&alias.name.rust) {
+        let begin = quote_spanned!(begin_span=> ::cxx::private::verify_extern_kind::<);
+        verify.extend(quote! {
+            const _: fn() = #begin #ident, ::cxx::kind::Trivial #end;
+        });
     }
+
+    verify
 }
 
-fn type_id(namespace: &Namespace, ident: &Ident) -> TokenStream {
-    let mut path = String::new();
-    for name in namespace {
-        path += &name.to_string();
-        path += "::";
-    }
-    path += &ident.to_string();
-
+fn type_id(name: &Pair) -> TokenStream {
+    let path = name.to_fully_qualified();
     quote! {
         ::cxx::type_id!(#path)
     }
 }
 
-fn expand_rust_box(namespace: &Namespace, ident: &Ident) -> TokenStream {
-    let link_prefix = format!("cxxbridge03$box${}{}$", namespace, ident);
+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);
     let link_drop = format!("{}drop", link_prefix);
 
-    let local_prefix = format_ident!("{}__box_", ident);
+    let local_prefix = format_ident!("{}__box_", &ident.rust);
     let local_uninit = format_ident!("{}uninit", local_prefix);
     let local_drop = format_ident!("{}drop", local_prefix);
 
@@ -628,19 +769,23 @@
     }
 }
 
-fn expand_rust_vec(namespace: &Namespace, elem: &Ident) -> TokenStream {
-    let link_prefix = format!("cxxbridge03$rust_vec${}{}$", namespace, elem);
+fn expand_rust_vec(elem: &ResolvableName, types: &Types) -> TokenStream {
+    let link_prefix = format!("cxxbridge05$rust_vec${}$", elem.to_symbol(types));
     let link_new = format!("{}new", link_prefix);
     let link_drop = format!("{}drop", link_prefix);
     let link_len = format!("{}len", 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);
+    let local_prefix = format_ident!("{}__vec_", elem.rust);
     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_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();
@@ -666,6 +811,16 @@
             (*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) {
+            (*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) {
+            (*this).set_len(len);
+        }
+        #[doc(hidden)]
         #[export_name = #link_stride]
         unsafe extern "C" fn #local_stride() -> usize {
             ::std::mem::size_of::<#elem>()
@@ -673,9 +828,13 @@
     }
 }
 
-fn expand_unique_ptr(namespace: &Namespace, ident: &Ident, types: &Types) -> TokenStream {
-    let name = ident.to_string();
-    let prefix = format!("cxxbridge03$unique_ptr${}{}$", namespace, ident);
+fn expand_unique_ptr(
+    ident: &ResolvableName,
+    types: &Types,
+    explicit_impl: Option<&Impl>,
+) -> TokenStream {
+    let name = ident.rust.to_string();
+    let prefix = format!("cxxbridge05$unique_ptr${}$", ident.to_symbol(types));
     let link_null = format!("{}null", prefix);
     let link_new = format!("{}new", prefix);
     let link_raw = format!("{}raw", prefix);
@@ -683,24 +842,30 @@
     let link_release = format!("{}release", prefix);
     let link_drop = format!("{}drop", prefix);
 
-    let new_method = if types.structs.contains_key(ident) {
-        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 new_method =
+        if types.structs.contains_key(&ident.rust) || types.aliases.contains_key(&ident.rust) {
+            Some(quote! {
+                fn __new(mut value: Self) -> *mut ::std::ffi::c_void {
+                    extern "C" {
+                        #[link_name = #link_new]
+                        fn __new(this: *mut *mut ::std::ffi::c_void, value: *mut #ident);
+                    }
+                    let mut repr = ::std::ptr::null_mut::<::std::ffi::c_void>();
+                    unsafe { __new(&mut repr, &mut value) }
+                    repr
                 }
-                let mut repr = ::std::ptr::null_mut::<::std::ffi::c_void>();
-                unsafe { __new(&mut repr, &mut value) }
-                repr
-            }
-        })
-    } else {
-        None
-    };
+            })
+        } else {
+            None
+        };
 
-    quote! {
-        unsafe impl ::cxx::private::UniquePtrTarget for #ident {
+    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 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;
             fn __null() -> *mut ::std::ffi::c_void {
                 extern "C" {
@@ -746,20 +911,33 @@
     }
 }
 
-fn expand_cxx_vector(namespace: &Namespace, elem: &Ident) -> TokenStream {
-    let name = elem.to_string();
-    let prefix = format!("cxxbridge03$std$vector${}{}$", namespace, elem);
+fn expand_cxx_vector(
+    elem: &ResolvableName,
+    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 link_size = format!("{}size", prefix);
     let link_get_unchecked = format!("{}get_unchecked", prefix);
-    let unique_ptr_prefix = format!("cxxbridge03$unique_ptr$std$vector${}{}$", namespace, elem);
+    let unique_ptr_prefix = format!(
+        "cxxbridge05$unique_ptr$std$vector${}$",
+        elem.to_symbol(types)
+    );
     let link_unique_ptr_null = format!("{}null", unique_ptr_prefix);
     let link_unique_ptr_raw = format!("{}raw", unique_ptr_prefix);
     let link_unique_ptr_get = format!("{}get", unique_ptr_prefix);
     let link_unique_ptr_release = format!("{}release", unique_ptr_prefix);
     let link_unique_ptr_drop = format!("{}drop", unique_ptr_prefix);
 
-    quote! {
-        unsafe impl ::cxx::private::VectorElement for #elem {
+    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 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;
             fn __vector_size(v: &::cxx::CxxVector<Self>) -> usize {
                 extern "C" {
@@ -768,12 +946,12 @@
                 }
                 unsafe { __vector_size(v) }
             }
-            unsafe fn __get_unchecked(v: &::cxx::CxxVector<Self>, pos: usize) -> &Self {
+            unsafe fn __get_unchecked(v: &::cxx::CxxVector<Self>, pos: usize) -> *const Self {
                 extern "C" {
                     #[link_name = #link_get_unchecked]
                     fn __get_unchecked(_: &::cxx::CxxVector<#elem>, _: usize) -> *const #elem;
                 }
-                &*__get_unchecked(v, pos)
+                __get_unchecked(v, pos)
             }
             fn __unique_ptr_null() -> *mut ::std::ffi::c_void {
                 extern "C" {
@@ -831,36 +1009,45 @@
         .map_or(false, |ret| sig.throws || types.needs_indirect_abi(ret))
 }
 
-fn expand_extern_type(ty: &Type) -> TokenStream {
+fn expand_extern_type(ty: &Type, types: &Types, proper: bool) -> TokenStream {
     match ty {
-        Type::Ident(ident) if ident == RustString => quote!(::cxx::private::RustString),
+        Type::Ident(ident) if ident.rust == RustString => quote!(::cxx::private::RustString),
         Type::RustBox(ty) | Type::UniquePtr(ty) => {
-            let inner = expand_extern_type(&ty.inner);
+            let inner = expand_extern_type(&ty.inner, types, proper);
             quote!(*mut #inner)
         }
         Type::RustVec(ty) => {
-            let elem = expand_extern_type(&ty.inner);
+            let elem = expand_extern_type(&ty.inner, types, proper);
             quote!(::cxx::private::RustVec<#elem>)
         }
-        Type::Ref(ty) => match &ty.inner {
-            Type::Ident(ident) if ident == RustString => quote!(&::cxx::private::RustString),
-            Type::RustVec(ty) => {
-                let inner = expand_extern_type(&ty.inner);
-                quote!(&::cxx::private::RustVec<#inner>)
+        Type::Ref(ty) => {
+            let mutability = ty.mutability;
+            match &ty.inner {
+                Type::Ident(ident) if ident.rust == RustString => {
+                    quote!(&#mutability ::cxx::private::RustString)
+                }
+                Type::RustVec(ty) => {
+                    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),
+                },
+                _ => quote!(#ty),
             }
-            _ => quote!(#ty),
-        },
+        }
         Type::Str(_) => quote!(::cxx::private::RustStr),
         Type::SliceRefU8(_) => quote!(::cxx::private::RustSliceU8),
         _ => quote!(#ty),
     }
 }
 
-fn expand_extern_return_type(ret: &Option<Type>, types: &Types) -> TokenStream {
+fn expand_extern_return_type(ret: &Option<Type>, types: &Types, proper: bool) -> TokenStream {
     let ret = match ret {
         Some(ret) if !types.needs_indirect_abi(ret) => ret,
         _ => return TokenStream::new(),
     };
-    let ty = expand_extern_type(ret);
+    let ty = expand_extern_type(ret, types, proper);
     quote!(-> #ty)
 }
diff --git a/macro/src/lib.rs b/macro/src/lib.rs
index bc8d19b..e55a08a 100644
--- a/macro/src/lib.rs
+++ b/macro/src/lib.rs
@@ -9,13 +9,17 @@
 
 extern crate proc_macro;
 
+mod derive;
 mod expand;
 mod syntax;
 mod type_id;
 
+use crate::syntax::file::Module;
 use crate::syntax::namespace::Namespace;
+use crate::syntax::qualified::QualifiedName;
 use proc_macro::TokenStream;
-use syn::{parse_macro_input, ItemMod, LitStr};
+use syn::parse::{Parse, ParseStream, Parser, Result};
+use syn::parse_macro_input;
 
 /// `#[cxx::bridge] mod ffi { ... }`
 ///
@@ -28,7 +32,7 @@
 /// attribute macro.
 ///
 /// ```
-/// #[cxx::bridge(namespace = mycompany::rust)]
+/// #[cxx::bridge(namespace = "mycompany::rust")]
 /// # mod ffi {}
 /// ```
 ///
@@ -38,16 +42,28 @@
 pub fn bridge(args: TokenStream, input: TokenStream) -> TokenStream {
     let _ = syntax::error::ERRORS;
 
-    let namespace = parse_macro_input!(args as Namespace);
-    let ffi = parse_macro_input!(input as ItemMod);
+    let namespace = match Namespace::parse_bridge_attr_namespace.parse(args) {
+        Ok(namespace) => namespace,
+        Err(err) => return err.to_compile_error().into(),
+    };
+    let mut ffi = parse_macro_input!(input as Module);
+    ffi.namespace = namespace;
 
-    expand::bridge(&namespace, ffi)
+    expand::bridge(ffi)
         .unwrap_or_else(|err| err.to_compile_error())
         .into()
 }
 
 #[proc_macro]
 pub fn type_id(input: TokenStream) -> TokenStream {
-    let arg = parse_macro_input!(input as LitStr);
-    type_id::expand(arg).into()
+    struct TypeId(QualifiedName);
+
+    impl Parse for TypeId {
+        fn parse(input: ParseStream) -> Result<Self> {
+            QualifiedName::parse_quoted_or_unquoted(input).map(TypeId)
+        }
+    }
+
+    let arg = parse_macro_input!(input as TypeId);
+    type_id::expand(arg.0).into()
 }
diff --git a/macro/src/type_id.rs b/macro/src/type_id.rs
index 445da2b..5c5d9cc 100644
--- a/macro/src/type_id.rs
+++ b/macro/src/type_id.rs
@@ -1,16 +1,16 @@
+use crate::syntax::qualified::QualifiedName;
 use proc_macro2::TokenStream;
 use quote::{format_ident, quote};
-use syn::LitStr;
 
 // "folly::File" => `(f, o, l, l, y, (), F, i, l, e)`
-pub fn expand(arg: LitStr) -> TokenStream {
+pub fn expand(arg: QualifiedName) -> TokenStream {
     let mut ids = Vec::new();
 
-    for word in arg.value().split("::") {
+    for word in arg.segments {
         if !ids.is_empty() {
             ids.push(quote!(()));
         }
-        for ch in word.chars() {
+        for ch in word.to_string().chars() {
             ids.push(match ch {
                 'A'..='Z' | 'a'..='z' => {
                     let t = format_ident!("{}", ch);
diff --git a/src/cxx.cc b/src/cxx.cc
index da05ee2..cbaecbf 100644
--- a/src/cxx.cc
+++ b/src/cxx.cc
@@ -1,13 +1,41 @@
 #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 {
+  return s.data();
+}
+
+size_t cxxbridge05$cxx_string$length(const std::string &s) noexcept {
+  return s.length();
+}
+
+// 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;
+
+// rust::Str
+bool cxxbridge05$str$valid(const char *ptr, size_t len) noexcept;
+} // extern "C"
+
+namespace rust {
+inline namespace cxxbridge05 {
+
 template <typename Exception>
-static void panic [[noreturn]] (const char *msg) {
+void panic [[noreturn]] (const char *msg) {
 #if defined(RUST_CXX_NO_EXCEPTIONS)
   std::cerr << "Error: " << msg << ". Aborting." << std::endl;
   std::terminate();
@@ -16,68 +44,54 @@
 #endif
 }
 
-extern "C" {
-const char *cxxbridge03$cxx_string$data(const std::string &s) noexcept {
-  return s.data();
-}
+template void panic<std::out_of_range>[[noreturn]] (const char *msg);
 
-size_t cxxbridge03$cxx_string$length(const std::string &s) noexcept {
-  return s.length();
-}
-
-// rust::String
-void cxxbridge03$string$new(rust::String *self) noexcept;
-void cxxbridge03$string$clone(rust::String *self,
-                              const rust::String &other) noexcept;
-bool cxxbridge03$string$from(rust::String *self, const char *ptr,
-                             size_t len) noexcept;
-void cxxbridge03$string$drop(rust::String *self) noexcept;
-const char *cxxbridge03$string$ptr(const rust::String *self) noexcept;
-size_t cxxbridge03$string$len(const rust::String *self) noexcept;
-
-// rust::Str
-bool cxxbridge03$str$valid(const char *ptr, size_t len) noexcept;
-} // extern "C"
-
-namespace rust {
-inline namespace cxxbridge03 {
-
-String::String() noexcept { cxxbridge03$string$new(this); }
+String::String() noexcept { cxxbridge05$string$new(this); }
 
 String::String(const String &other) noexcept {
-  cxxbridge03$string$clone(this, other);
+  cxxbridge05$string$clone(this, other);
 }
 
 String::String(String &&other) noexcept {
   this->repr = other.repr;
-  cxxbridge03$string$new(&other);
+  cxxbridge05$string$new(&other);
 }
 
-String::~String() noexcept { cxxbridge03$string$drop(this); }
+String::~String() noexcept { cxxbridge05$string$drop(this); }
 
-String::String(const std::string &s) : String(s.data(), s.length()) {}
-
-String::String(const char *s) : String(s, std::strlen(s)) {}
-
-String::String(const char *s, size_t len) {
-  if (!cxxbridge03$string$from(this, s, len)) {
+static void initString(String *self, const char *s, size_t len) {
+  if (!cxxbridge05$string$from(self, s, len)) {
     panic<std::invalid_argument>("data for rust::String is not utf-8");
   }
 }
 
+String::String(const std::string &s) { initString(this, s.data(), s.length()); }
+
+String::String(const char *s) {
+  assert(s != nullptr);
+  initString(this, s, std::strlen(s));
+}
+
+String::String(const char *s, 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 {
   if (this != &other) {
-    cxxbridge03$string$drop(this);
-    cxxbridge03$string$clone(this, other);
+    cxxbridge05$string$drop(this);
+    cxxbridge05$string$clone(this, other);
   }
   return *this;
 }
 
 String &String::operator=(String &&other) noexcept {
   if (this != &other) {
-    cxxbridge03$string$drop(this);
+    cxxbridge05$string$drop(this);
     this->repr = other.repr;
-    cxxbridge03$string$new(&other);
+    cxxbridge05$string$new(&other);
   }
   return *this;
 }
@@ -87,12 +101,12 @@
 }
 
 const char *String::data() const noexcept {
-  return cxxbridge03$string$ptr(this);
+  return cxxbridge05$string$ptr(this);
 }
 
-size_t String::size() const noexcept { return cxxbridge03$string$len(this); }
+size_t String::size() const noexcept { return cxxbridge05$string$len(this); }
 
-size_t String::length() const noexcept { return cxxbridge03$string$len(this); }
+size_t String::length() const noexcept { return cxxbridge05$string$len(this); }
 
 String::String(unsafe_bitcopy_t, const String &bits) noexcept
     : repr(bits.repr) {}
@@ -102,160 +116,191 @@
   return os;
 }
 
-Str::Str() noexcept : repr(Repr{reinterpret_cast<const char *>(this), 0}) {}
+Str::Str() noexcept : ptr(reinterpret_cast<const char *>(1)), len(0) {}
 
-Str::Str(const Str &) noexcept = default;
-
-Str::Str(const std::string &s) : Str(s.data(), s.length()) {}
-
-Str::Str(const char *s) : Str(s, std::strlen(s)) {}
-
-Str::Str(const char *s, size_t len) : repr(Repr{s, len}) {
-  if (!cxxbridge03$str$valid(this->repr.ptr, this->repr.len)) {
+static void initStr(const char *ptr, size_t len) {
+  if (!cxxbridge05$str$valid(ptr, len)) {
     panic<std::invalid_argument>("data for rust::Str is not utf-8");
   }
 }
 
-Str &Str::operator=(Str other) noexcept {
-  this->repr = other.repr;
-  return *this;
+Str::Str(const std::string &s) : ptr(s.data()), len(s.length()) {
+  initStr(this->ptr, this->len);
+}
+
+Str::Str(const char *s) : ptr(s), len(std::strlen(s)) {
+  assert(s != nullptr);
+  initStr(this->ptr, this->len);
+}
+
+Str::Str(const char *s, size_t len)
+    : ptr(s == nullptr && len == 0 ? reinterpret_cast<const char *>(1) : s),
+      len(len) {
+  assert(s != nullptr || len == 0);
+  initStr(this->ptr, this->len);
 }
 
 Str::operator std::string() const {
   return std::string(this->data(), this->size());
 }
 
-const char *Str::data() const noexcept { return this->repr.ptr; }
-
-size_t Str::size() const noexcept { return this->repr.len; }
-
-size_t Str::length() const noexcept { return this->repr.len; }
-
-Str::Str(Repr repr_) noexcept : repr(repr_) {}
-
-Str::operator Repr() noexcept { return this->repr; }
-
 std::ostream &operator<<(std::ostream &os, const Str &s) {
   os.write(s.data(), s.size());
   return os;
 }
 
+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 *cxxbridge03$error(const char *ptr, size_t len) {
+const char *cxxbridge05$error(const char *ptr, size_t len) {
   char *copy = new char[len];
-  strncpy(copy, ptr, len);
+  std::strncpy(copy, ptr, len);
   return copy;
 }
 } // extern "C"
 
-Error::Error(Str::Repr msg) noexcept : msg(msg) {}
+Error::Error(const Error &other)
+    : std::exception(other), msg(cxxbridge05$error(other.msg, other.len)),
+      len(other.len) {}
 
-Error::Error(const Error &other) {
-  this->msg.ptr = cxxbridge03$error(other.msg.ptr, other.msg.len);
-  this->msg.len = other.msg.len;
+Error::Error(Error &&other) noexcept
+    : std::exception(std::move(other)), msg(other.msg), len(other.len) {
+  other.msg = nullptr;
+  other.len = 0;
 }
 
-Error::Error(Error &&other) noexcept {
-  delete[] this->msg.ptr;
-  this->msg = other.msg;
-  other.msg.ptr = nullptr;
-  other.msg.len = 0;
+Error::~Error() noexcept { delete[] this->msg; }
+
+Error &Error::operator=(const Error &other) {
+  if (this != &other) {
+    std::exception::operator=(other);
+    delete[] this->msg;
+    this->msg = nullptr;
+    this->msg = cxxbridge05$error(other.msg, other.len);
+    this->len = other.len;
+  }
+  return *this;
 }
 
-Error::~Error() noexcept { delete[] this->msg.ptr; }
+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;
+  }
+  return *this;
+}
 
-const char *Error::what() const noexcept { return this->msg.ptr; }
+const char *Error::what() const noexcept { return this->msg; }
 
-} // namespace cxxbridge03
+} // namespace cxxbridge05
 } // namespace rust
 
 extern "C" {
-void cxxbridge03$unique_ptr$std$string$null(
+void cxxbridge05$unique_ptr$std$string$null(
     std::unique_ptr<std::string> *ptr) noexcept {
   new (ptr) std::unique_ptr<std::string>();
 }
-void cxxbridge03$unique_ptr$std$string$raw(std::unique_ptr<std::string> *ptr,
+void cxxbridge05$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 *cxxbridge03$unique_ptr$std$string$get(
+const std::string *cxxbridge05$unique_ptr$std$string$get(
     const std::unique_ptr<std::string> &ptr) noexcept {
   return ptr.get();
 }
-std::string *cxxbridge03$unique_ptr$std$string$release(
+std::string *cxxbridge05$unique_ptr$std$string$release(
     std::unique_ptr<std::string> &ptr) noexcept {
   return ptr.release();
 }
-void cxxbridge03$unique_ptr$std$string$drop(
+void cxxbridge05$unique_ptr$std$string$drop(
     std::unique_ptr<std::string> *ptr) noexcept {
   ptr->~unique_ptr();
 }
 } // extern "C"
 
 #define STD_VECTOR_OPS(RUST_TYPE, CXX_TYPE)                                    \
-  size_t cxxbridge03$std$vector$##RUST_TYPE##$size(                            \
+  size_t cxxbridge05$std$vector$##RUST_TYPE##$size(                            \
       const std::vector<CXX_TYPE> &s) noexcept {                               \
     return s.size();                                                           \
   }                                                                            \
-  const CXX_TYPE *cxxbridge03$std$vector$##RUST_TYPE##$get_unchecked(          \
+  const CXX_TYPE *cxxbridge05$std$vector$##RUST_TYPE##$get_unchecked(          \
       const std::vector<CXX_TYPE> &s, size_t pos) noexcept {                   \
     return &s[pos];                                                            \
   }                                                                            \
-  void cxxbridge03$unique_ptr$std$vector$##RUST_TYPE##$null(                   \
+  void cxxbridge05$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 cxxbridge03$unique_ptr$std$vector$##RUST_TYPE##$raw(                    \
+  void cxxbridge05$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>                                                  \
-      *cxxbridge03$unique_ptr$std$vector$##RUST_TYPE##$get(                    \
+      *cxxbridge05$unique_ptr$std$vector$##RUST_TYPE##$get(                    \
           const std::unique_ptr<std::vector<CXX_TYPE>> &ptr) noexcept {        \
     return ptr.get();                                                          \
   }                                                                            \
   std::vector<CXX_TYPE>                                                        \
-      *cxxbridge03$unique_ptr$std$vector$##RUST_TYPE##$release(                \
+      *cxxbridge05$unique_ptr$std$vector$##RUST_TYPE##$release(                \
           std::unique_ptr<std::vector<CXX_TYPE>> &ptr) noexcept {              \
     return ptr.release();                                                      \
   }                                                                            \
-  void cxxbridge03$unique_ptr$std$vector$##RUST_TYPE##$drop(                   \
+  void cxxbridge05$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 cxxbridge03$rust_vec$##RUST_TYPE##$new(                                 \
+  void cxxbridge05$rust_vec$##RUST_TYPE##$new(                                 \
       rust::Vec<CXX_TYPE> *ptr) noexcept;                                      \
-  void cxxbridge03$rust_vec$##RUST_TYPE##$drop(                                \
+  void cxxbridge05$rust_vec$##RUST_TYPE##$drop(                                \
       rust::Vec<CXX_TYPE> *ptr) noexcept;                                      \
-  size_t cxxbridge03$rust_vec$##RUST_TYPE##$len(                               \
+  size_t cxxbridge05$rust_vec$##RUST_TYPE##$len(                               \
       const rust::Vec<CXX_TYPE> *ptr) noexcept;                                \
-  const CXX_TYPE *cxxbridge03$rust_vec$##RUST_TYPE##$data(                     \
+  const CXX_TYPE *cxxbridge05$rust_vec$##RUST_TYPE##$data(                     \
       const rust::Vec<CXX_TYPE> *ptr) noexcept;                                \
-  size_t cxxbridge03$rust_vec$##RUST_TYPE##$stride() 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;
 
 #define RUST_VEC_OPS(RUST_TYPE, CXX_TYPE)                                      \
   template <>                                                                  \
   Vec<CXX_TYPE>::Vec() noexcept {                                              \
-    cxxbridge03$rust_vec$##RUST_TYPE##$new(this);                              \
+    cxxbridge05$rust_vec$##RUST_TYPE##$new(this);                              \
   }                                                                            \
   template <>                                                                  \
   void Vec<CXX_TYPE>::drop() noexcept {                                        \
-    return cxxbridge03$rust_vec$##RUST_TYPE##$drop(this);                      \
+    return cxxbridge05$rust_vec$##RUST_TYPE##$drop(this);                      \
   }                                                                            \
   template <>                                                                  \
   size_t Vec<CXX_TYPE>::size() const noexcept {                                \
-    return cxxbridge03$rust_vec$##RUST_TYPE##$len(this);                       \
+    return cxxbridge05$rust_vec$##RUST_TYPE##$len(this);                       \
   }                                                                            \
   template <>                                                                  \
   const CXX_TYPE *Vec<CXX_TYPE>::data() const noexcept {                       \
-    return cxxbridge03$rust_vec$##RUST_TYPE##$data(this);                      \
+    return cxxbridge05$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);               \
+  }                                                                            \
+  template <>                                                                  \
+  void Vec<CXX_TYPE>::set_len(size_t len) noexcept {                           \
+    cxxbridge05$rust_vec$##RUST_TYPE##$set_len(this, len);                     \
   }                                                                            \
   template <>                                                                  \
   size_t Vec<CXX_TYPE>::stride() noexcept {                                    \
-    return cxxbridge03$rust_vec$##RUST_TYPE##$stride();                        \
+    return cxxbridge05$rust_vec$##RUST_TYPE##$stride();                        \
   }
 
 // Usize and isize are the same type as one of the below.
@@ -274,11 +319,13 @@
 #define FOR_EACH_STD_VECTOR(MACRO)                                             \
   FOR_EACH_NUMERIC(MACRO)                                                      \
   MACRO(usize, size_t)                                                         \
-  MACRO(isize, rust::isize)
+  MACRO(isize, rust::isize)                                                    \
+  MACRO(string, std::string)
 
 #define FOR_EACH_RUST_VEC(MACRO)                                               \
   FOR_EACH_NUMERIC(MACRO)                                                      \
-  MACRO(bool, bool)
+  MACRO(bool, bool)                                                            \
+  MACRO(string, rust::String)
 
 extern "C" {
 FOR_EACH_STD_VECTOR(STD_VECTOR_OPS)
@@ -286,7 +333,7 @@
 } // extern "C"
 
 namespace rust {
-inline namespace cxxbridge03 {
+inline namespace cxxbridge05 {
 FOR_EACH_RUST_VEC(RUST_VEC_OPS)
-} // namespace cxxbridge03
+} // namespace cxxbridge05
 } // namespace rust
diff --git a/src/cxx_string.rs b/src/cxx_string.rs
index ffd0c5c..7b47feb 100644
--- a/src/cxx_string.rs
+++ b/src/cxx_string.rs
@@ -1,12 +1,13 @@
-use std::borrow::Cow;
-use std::fmt::{self, Debug, Display};
-use std::slice;
-use std::str::{self, Utf8Error};
+use alloc::borrow::Cow;
+use alloc::string::String;
+use core::fmt::{self, Debug, Display};
+use core::slice;
+use core::str::{self, Utf8Error};
 
 extern "C" {
-    #[link_name = "cxxbridge03$cxx_string$data"]
+    #[link_name = "cxxbridge05$cxx_string$data"]
     fn string_data(_: &CxxString) -> *const u8;
-    #[link_name = "cxxbridge03$cxx_string$length"]
+    #[link_name = "cxxbridge05$cxx_string$length"]
     fn string_length(_: &CxxString) -> usize;
 }
 
diff --git a/src/cxx_vector.rs b/src/cxx_vector.rs
index bfb4a4e..5fb0807 100644
--- a/src/cxx_vector.rs
+++ b/src/cxx_vector.rs
@@ -1,8 +1,10 @@
-use std::ffi::c_void;
-use std::fmt::{self, Display};
-use std::marker::PhantomData;
-use std::mem;
-use std::ptr;
+use crate::cxx_string::CxxString;
+use core::ffi::c_void;
+use core::fmt::{self, Display};
+use core::marker::PhantomData;
+use core::mem;
+use core::ptr;
+use core::slice;
 
 /// Binding to C++ `std::vector<T, std::allocator<T>>`.
 ///
@@ -43,7 +45,7 @@
     /// out of bounds.
     pub fn get(&self, pos: usize) -> Option<&T> {
         if pos < self.len() {
-            Some(unsafe { T::__get_unchecked(self, pos) })
+            Some(unsafe { self.get_unchecked(pos) })
         } else {
             None
         }
@@ -60,7 +62,24 @@
     ///
     /// [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)
+        &*T::__get_unchecked(self, pos)
+    }
+
+    /// Returns a slice to the underlying contiguous array of elements.
+    pub fn as_slice(&self) -> &[T] {
+        let len = self.len();
+        if len == 0 {
+            // The slice::from_raw_parts in the other branch requires a nonnull
+            // and properly aligned data ptr. C++ standard does not guarantee
+            // that data() on a vector with size 0 would return a nonnull
+            // pointer or sufficiently aligned pointer, so using it would be
+            // undefined behavior. Create our own empty slice in Rust instead
+            // which upholds the invariants.
+            &[]
+        } else {
+            let ptr = unsafe { T::__get_unchecked(self, 0) };
+            unsafe { slice::from_raw_parts(ptr, len) }
+        }
     }
 }
 
@@ -121,7 +140,7 @@
 pub unsafe trait VectorElement: Sized {
     const __NAME: &'static dyn Display;
     fn __vector_size(v: &CxxVector<Self>) -> usize;
-    unsafe fn __get_unchecked(v: &CxxVector<Self>, pos: usize) -> &Self;
+    unsafe fn __get_unchecked(v: &CxxVector<Self>, pos: usize) -> *const Self;
     fn __unique_ptr_null() -> *mut c_void;
     unsafe fn __unique_ptr_raw(raw: *mut CxxVector<Self>) -> *mut c_void;
     unsafe fn __unique_ptr_get(repr: *mut c_void) -> *const CxxVector<Self>;
@@ -129,34 +148,34 @@
     unsafe fn __unique_ptr_drop(repr: *mut c_void);
 }
 
-macro_rules! impl_vector_element_for_primitive {
-    ($ty:ident) => {
+macro_rules! impl_vector_element {
+    ($segment:expr, $name:expr, $ty:ty) => {
         const_assert_eq!(1, mem::align_of::<CxxVector<$ty>>());
 
         unsafe impl VectorElement for $ty {
-            const __NAME: &'static dyn Display = &stringify!($ty);
+            const __NAME: &'static dyn Display = &$name;
             fn __vector_size(v: &CxxVector<$ty>) -> usize {
                 extern "C" {
                     attr! {
-                        #[link_name = concat!("cxxbridge03$std$vector$", stringify!($ty), "$size")]
+                        #[link_name = concat!("cxxbridge05$std$vector$", $segment, "$size")]
                         fn __vector_size(_: &CxxVector<$ty>) -> usize;
                     }
                 }
                 unsafe { __vector_size(v) }
             }
-            unsafe fn __get_unchecked(v: &CxxVector<$ty>, pos: usize) -> &$ty {
+            unsafe fn __get_unchecked(v: &CxxVector<$ty>, pos: usize) -> *const $ty {
                 extern "C" {
                     attr! {
-                        #[link_name = concat!("cxxbridge03$std$vector$", stringify!($ty), "$get_unchecked")]
+                        #[link_name = concat!("cxxbridge05$std$vector$", $segment, "$get_unchecked")]
                         fn __get_unchecked(_: &CxxVector<$ty>, _: usize) -> *const $ty;
                     }
                 }
-                &*__get_unchecked(v, pos)
+                __get_unchecked(v, pos)
             }
             fn __unique_ptr_null() -> *mut c_void {
                 extern "C" {
                     attr! {
-                        #[link_name = concat!("cxxbridge03$unique_ptr$std$vector$", stringify!($ty), "$null")]
+                        #[link_name = concat!("cxxbridge05$unique_ptr$std$vector$", $segment, "$null")]
                         fn __unique_ptr_null(this: *mut *mut c_void);
                     }
                 }
@@ -167,7 +186,7 @@
             unsafe fn __unique_ptr_raw(raw: *mut CxxVector<Self>) -> *mut c_void {
                 extern "C" {
                     attr! {
-                        #[link_name = concat!("cxxbridge03$unique_ptr$std$vector$", stringify!($ty), "$raw")]
+                        #[link_name = concat!("cxxbridge05$unique_ptr$std$vector$", $segment, "$raw")]
                         fn __unique_ptr_raw(this: *mut *mut c_void, raw: *mut CxxVector<$ty>);
                     }
                 }
@@ -178,7 +197,7 @@
             unsafe fn __unique_ptr_get(repr: *mut c_void) -> *const CxxVector<Self> {
                 extern "C" {
                     attr! {
-                        #[link_name = concat!("cxxbridge03$unique_ptr$std$vector$", stringify!($ty), "$get")]
+                        #[link_name = concat!("cxxbridge05$unique_ptr$std$vector$", $segment, "$get")]
                         fn __unique_ptr_get(this: *const *mut c_void) -> *const CxxVector<$ty>;
                     }
                 }
@@ -187,7 +206,7 @@
             unsafe fn __unique_ptr_release(mut repr: *mut c_void) -> *mut CxxVector<Self> {
                 extern "C" {
                     attr! {
-                        #[link_name = concat!("cxxbridge03$unique_ptr$std$vector$", stringify!($ty), "$release")]
+                        #[link_name = concat!("cxxbridge05$unique_ptr$std$vector$", $segment, "$release")]
                         fn __unique_ptr_release(this: *mut *mut c_void) -> *mut CxxVector<$ty>;
                     }
                 }
@@ -196,7 +215,7 @@
             unsafe fn __unique_ptr_drop(mut repr: *mut c_void) {
                 extern "C" {
                     attr! {
-                        #[link_name = concat!("cxxbridge03$unique_ptr$std$vector$", stringify!($ty), "$drop")]
+                        #[link_name = concat!("cxxbridge05$unique_ptr$std$vector$", $segment, "$drop")]
                         fn __unique_ptr_drop(this: *mut *mut c_void);
                     }
                 }
@@ -206,6 +225,12 @@
     };
 }
 
+macro_rules! impl_vector_element_for_primitive {
+    ($ty:ident) => {
+        impl_vector_element!(stringify!($ty), stringify!($ty), $ty);
+    };
+}
+
 impl_vector_element_for_primitive!(u8);
 impl_vector_element_for_primitive!(u16);
 impl_vector_element_for_primitive!(u32);
@@ -218,3 +243,5 @@
 impl_vector_element_for_primitive!(isize);
 impl_vector_element_for_primitive!(f32);
 impl_vector_element_for_primitive!(f64);
+
+impl_vector_element!("string", "CxxString", CxxString);
diff --git a/src/exception.rs b/src/exception.rs
index 125e484..0ffca66 100644
--- a/src/exception.rs
+++ b/src/exception.rs
@@ -1,4 +1,5 @@
-use std::fmt::{self, Debug, Display};
+use alloc::boxed::Box;
+use core::fmt::{self, Debug, Display};
 
 /// Exception thrown from an `extern "C"` function.
 #[derive(Debug)]
diff --git a/src/extern_type.rs b/src/extern_type.rs
index 6701ef5..f92ff40 100644
--- a/src/extern_type.rs
+++ b/src/extern_type.rs
@@ -1,3 +1,5 @@
+use self::kind::{Kind, Opaque, Trivial};
+
 /// A type for which the layout is determined by its C++ definition.
 ///
 /// This trait serves the following two related purposes.
@@ -26,7 +28,7 @@
 /// ```no_run
 /// // file1.rs
 /// # mod file1 {
-/// #[cxx::bridge(namespace = example)]
+/// #[cxx::bridge(namespace = "example")]
 /// pub mod ffi {
 ///     extern "C" {
 ///         type Demo;
@@ -37,7 +39,7 @@
 /// # }
 ///
 /// // file2.rs
-/// #[cxx::bridge(namespace = example)]
+/// #[cxx::bridge(namespace = "example")]
 /// pub mod ffi {
 ///     extern "C" {
 ///         type Demo = crate::file1::ffi::Demo;
@@ -54,7 +56,7 @@
 /// ## Integrating with bindgen-generated types
 ///
 /// Handwritten `ExternType` impls make it possible to plug in a data structure
-/// emitted by bindgen as the definition of an opaque C++ type emitted by CXX.
+/// 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
@@ -73,9 +75,10 @@
 ///
 /// unsafe impl ExternType for folly_sys::StringPiece {
 ///     type Id = type_id!("folly::StringPiece");
+///     type Kind = cxx::kind::Opaque;
 /// }
 ///
-/// #[cxx::bridge(namespace = folly)]
+/// #[cxx::bridge(namespace = "folly")]
 /// pub mod ffi {
 ///     extern "C" {
 ///         include!("rust_cxx_bindings.h");
@@ -101,10 +104,80 @@
     /// # struct TypeName;
     /// # unsafe impl cxx::ExternType for TypeName {
     /// type Id = cxx::type_id!("name::space::of::TypeName");
+    /// #     type Kind = cxx::kind::Opaque;
     /// # }
     /// ```
     type Id;
+
+    /// Either [`cxx::kind::Opaque`] or [`cxx::kind::Trivial`].
+    ///
+    /// [`cxx::kind::Opaque`]: kind::Opaque
+    /// [`cxx::kind::Trivial`]: kind::Trivial
+    ///
+    /// A C++ type is only okay to hold and pass around by value in Rust if its
+    /// [move constructor is trivial] and it has no destructor. In CXX, these
+    /// are called Trivial extern C++ types, while types with nontrivial move
+    /// behavior or a destructor must be considered Opaque and handled by Rust
+    /// only behind an indirection, such as a reference or UniquePtr.
+    ///
+    /// [move constructor is trivial]: https://en.cppreference.com/w/cpp/types/is_move_constructible
+    ///
+    /// If you believe your C++ type reflected by this ExternType impl is indeed
+    /// fine to hold by value and move in Rust, you can specify:
+    ///
+    /// ```
+    /// # struct TypeName;
+    /// # unsafe impl cxx::ExternType for TypeName {
+    /// #     type Id = cxx::type_id!("name::space::of::TypeName");
+    /// type Kind = cxx::kind::Trivial;
+    /// # }
+    /// ```
+    ///
+    /// which will enable you to pass it into C++ functions by value, return it
+    /// by value, and include it in `struct`s that you have declared to
+    /// `cxx::bridge`. Your claim about the triviality of the C++ type will be
+    /// checked by a `static_assert` in the generated C++ side of the binding.
+    type Kind: Kind;
+}
+
+/// Marker types identifying Rust's knowledge about an extern C++ type.
+///
+/// These markers are used in the [`Kind`][ExternType::Kind] associated type in
+/// impls of the `ExternType` trait. Refer to the documentation of `Kind` for an
+/// overview of their purpose.
+pub mod kind {
+    use super::private;
+
+    /// An opaque type which cannot be passed or held by value within Rust.
+    ///
+    /// Rust's move semantics are such that every move is equivalent to a
+    /// memcpy. This is incompatible in general with C++'s constructor-based
+    /// move semantics, so a C++ type which has a destructor or nontrivial move
+    /// constructor must never exist by value in Rust. In CXX, such types are
+    /// called opaque C++ types.
+    ///
+    /// When passed across an FFI boundary, an opaque C++ type must be behind an
+    /// indirection such as a reference or UniquePtr.
+    pub enum Opaque {}
+
+    /// A type with trivial move constructor and no destructor, which can
+    /// therefore be owned and moved around in Rust code without requiring
+    /// indirection.
+    pub enum Trivial {}
+
+    pub trait Kind: private::Sealed {}
+    impl Kind for Opaque {}
+    impl Kind for Trivial {}
+}
+
+mod private {
+    pub trait Sealed {}
+    impl Sealed for super::Opaque {}
+    impl Sealed for super::Trivial {}
 }
 
 #[doc(hidden)]
 pub fn verify_extern_type<T: ExternType<Id = Id>, Id>() {}
+
+#[doc(hidden)]
+pub fn verify_extern_kind<T: ExternType<Kind = Kind>, Kind: self::Kind>() {}
diff --git a/src/lib.rs b/src/lib.rs
index 30dd41f..17d7548 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -18,7 +18,7 @@
 //!
 //! <br>
 //!
-//! *Compiler support: requires rustc 1.42+ and c++11 or newer*<br>
+//! *Compiler support: requires rustc 1.43+ and c++11 or newer*<br>
 //! *[Release notes](https://github.com/dtolnay/cxx/releases)*
 //!
 //! <br>
@@ -57,66 +57,72 @@
 //!
 //! # Example
 //!
-//! A runnable version of this example is provided under the *demo-rs* directory
-//! of [https://github.com/dtolnay/cxx] (with the C++ side of the implementation
-//! in the *demo-cxx* directory). To try it out, jump into demo-rs and run
-//! `cargo run`.
+//! In this example we are writing a Rust application that wishes to take
+//! advantage of an existing C++ client for a large-file blobstore service. The
+//! blobstore supports a `put` operation for a discontiguous buffer upload. For
+//! example we might be uploading snapshots of a circular buffer which would
+//! tend to consist of 2 chunks, or fragments of a file spread across memory for
+//! some other reason.
+//!
+//! A runnable version of this example is provided under the *demo* directory of
+//! <https://github.com/dtolnay/cxx>. To try it out, run `cargo run` from that
+//! directory.
 //!
 //! ```no_run
 //! #[cxx::bridge]
 //! mod ffi {
 //!     // Any shared structs, whose fields will be visible to both languages.
-//!     struct SharedThing {
-//!         z: i32,
-//!         y: Box<ThingR>,
-//!         x: UniquePtr<ThingC>,
-//!     }
-//!
-//!     extern "C" {
-//!         // One or more headers with the matching C++ declarations. Our code
-//!         // generators don't read it but it gets #include'd and used in static
-//!         // assertions to ensure our picture of the FFI boundary is accurate.
-//!         include!("demo-cxx/demo.h");
-//!
-//!         // Zero or more opaque types which both languages can pass around but
-//!         // only C++ can see the fields.
-//!         type ThingC;
-//!
-//!         // Functions implemented in C++.
-//!         fn make_demo(appname: &str) -> UniquePtr<ThingC>;
-//!         fn get_name(thing: &ThingC) -> &CxxString;
-//!         fn do_thing(state: SharedThing);
+//!     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 ThingR;
+//!         type MultiBuf;
 //!
 //!         // Functions implemented in Rust.
-//!         fn print_r(r: &ThingR);
+//!         fn next_chunk(buf: &mut MultiBuf) -> &[u8];
+//!     }
+//!
+//!     extern "C++" {
+//!         // One or more headers with the matching C++ declarations. Our code
+//!         // generators don't read it but it gets #include'd and used in static
+//!         // assertions to ensure our picture of the FFI boundary is accurate.
+//!         include!("demo/include/blobstore.h");
+//!
+//!         // Zero or more opaque types which both languages can pass around but
+//!         // only C++ can see the fields.
+//!         type BlobstoreClient;
+//!
+//!         // Functions implemented in C++.
+//!         fn new_blobstore_client() -> UniquePtr<BlobstoreClient>;
+//!         fn put(&self, parts: &mut MultiBuf) -> u64;
+//!         fn tag(&self, blobid: u64, tag: &str);
+//!         fn metadata(&self, blobid: u64) -> BlobMetadata;
 //!     }
 //! }
 //! #
-//! # pub struct ThingR(usize);
+//! # pub struct MultiBuf;
 //! #
-//! # fn print_r(r: &ThingR) {
-//! #     println!("called back with r={}", r.0);
+//! # fn next_chunk(_buf: &mut MultiBuf) -> &[u8] {
+//! #     unimplemented!()
 //! # }
 //! #
 //! # fn main() {}
 //! ```
 //!
-//! Now we simply provide C++ definitions of all the things in the `extern "C"`
-//! block and Rust definitions of all the things in the `extern "Rust"` block,
-//! and get to call back and forth safely.
+//! Now we simply provide Rust definitions of all the things in the `extern
+//! "Rust"` block and C++ definitions of all the things in the `extern "C++"`
+//! block, and get to call back and forth safely.
 //!
 //! Here are links to the complete set of source files involved in the demo:
 //!
-//! - [demo-rs/src/main.rs](https://github.com/dtolnay/cxx/blob/master/demo-rs/src/main.rs)
-//! - [demo-rs/build.rs](https://github.com/dtolnay/cxx/blob/master/demo-rs/build.rs)
-//! - [demo-cxx/demo.h](https://github.com/dtolnay/cxx/blob/master/demo-cxx/demo.h)
-//! - [demo-cxx/demo.cc](https://github.com/dtolnay/cxx/blob/master/demo-cxx/demo.cc)
+//! - [demo/src/main.rs](https://github.com/dtolnay/cxx/blob/master/demo/src/main.rs)
+//! - [demo/build.rs](https://github.com/dtolnay/cxx/blob/master/demo/build.rs)
+//! - [demo/include/blobstore.h](https://github.com/dtolnay/cxx/blob/master/demo/include/blobstore.h)
+//! - [demo/src/blobstore.cc](https://github.com/dtolnay/cxx/blob/master/demo/src/blobstore.cc)
 //!
 //! To look at the code generated in both languages for the example by the CXX
 //! code generators:
@@ -124,10 +130,10 @@
 //! ```console
 //!    # run Rust code generator and print to stdout
 //!    # (requires https://github.com/dtolnay/cargo-expand)
-//! $ cargo expand --manifest-path demo-rs/Cargo.toml
+//! $ cargo expand --manifest-path demo/Cargo.toml
 //!
 //!    # run C++ code generator and print to stdout
-//! $ cargo run --manifest-path gen/cmd/Cargo.toml -- demo-rs/src/main.rs
+//! $ cargo run --manifest-path gen/cmd/Cargo.toml -- demo/src/main.rs
 //! ```
 //!
 //! <br>
@@ -229,7 +235,7 @@
 //! # Cargo.toml
 //!
 //! [build-dependencies]
-//! cxx-build = "0.3"
+//! cxx-build = "0.5"
 //! ```
 //!
 //! ```no_run
@@ -237,13 +243,13 @@
 //!
 //! fn main() {
 //!     cxx_build::bridge("src/main.rs")  // returns a cc::Build
-//!         .file("../demo-cxx/demo.cc")
+//!         .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=../demo-cxx/demo.h");
-//!     println!("cargo:rerun-if-changed=../demo-cxx/demo.cc");
+//!     println!("cargo:rerun-if-changed=src/demo.cc");
+//!     println!("cargo:rerun-if-changed=include/demo.h");
 //! }
 //! ```
 //!
@@ -254,7 +260,7 @@
 //! For use in non-Cargo builds like Bazel or Buck, CXX provides an alternate
 //! way of invoking the C++ code generator as a standalone command line tool.
 //! The tool is packaged as the `cxxbridge-cmd` crate on crates.io or can be
-//! built from the *cmd* directory of [https://github.com/dtolnay/cxx].
+//! built from the *gen/cmd* directory of <https://github.com/dtolnay/cxx>.
 //!
 //! ```bash
 //! $ cargo install cxxbridge-cmd
@@ -329,7 +335,7 @@
 //! </table>
 //!
 //! The C++ API of the `rust` namespace is defined by the *include/cxx.h* file
-//! in [https://github.com/dtolnay/cxx]. You will need to include this header in
+//! in <https://github.com/dtolnay/cxx>. You will need to include this header in
 //! your C++ code when working with those types.
 //!
 //! The following types are intended to be supported "soon" but are just not
@@ -341,14 +347,14 @@
 //! <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>
 //! <tr><td><sup><i>tbd</i></sup></td><td>std::shared_ptr&lt;T&gt;</td></tr>
 //! </table>
-//!
-//! [https://github.com/dtolnay/cxx]: https://github.com/dtolnay/cxx
 
-#![doc(html_root_url = "https://docs.rs/cxx/0.3.4")]
+#![no_std]
+#![doc(html_root_url = "https://docs.rs/cxx/0.5.9")]
 #![deny(improper_ctypes)]
 #![allow(non_camel_case_types)]
 #![allow(
@@ -367,6 +373,12 @@
     clippy::useless_let_if_seq
 )]
 
+#[cfg(built_with_cargo)]
+extern crate link_cplusplus;
+
+extern crate alloc;
+extern crate std;
+
 #[macro_use]
 mod macros;
 
@@ -381,27 +393,41 @@
 mod rust_str;
 mod rust_string;
 mod rust_vec;
+mod symbols;
 mod unique_ptr;
 mod unwind;
 
-#[cfg(not(no_export_symbols))]
-mod symbols;
-
 pub use crate::cxx_string::CxxString;
 pub use crate::cxx_vector::CxxVector;
 pub use crate::exception::Exception;
-pub use crate::extern_type::ExternType;
+pub use crate::extern_type::{kind, ExternType};
 pub use crate::unique_ptr::UniquePtr;
 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
+/// shouldn't import this type with `use`. Instead, write `cxx::String`, or
+/// import and use `CxxString`.
+pub type String = CxxString;
+
+/// Synonym for `CxxVector`.
+///
+/// To avoid confusion with Rust's standard library vector you probably
+/// shouldn't import this type with `use`. Instead, write `cxx::Vector<T>`, or
+/// import and use `CxxVector`.
+pub type Vector<T> = CxxVector<T>;
+
 // Not public API.
 #[doc(hidden)]
 pub mod private {
     pub use crate::cxx_vector::VectorElement;
-    pub use crate::extern_type::verify_extern_type;
+    pub use crate::extern_type::{verify_extern_kind, verify_extern_type};
     pub use crate::function::FatFunction;
     pub use crate::opaque::Opaque;
     pub use crate::result::{r#try, Result};
diff --git a/src/macros/assert.rs b/src/macros/assert.rs
index 738e5bb..5d5ea9e 100644
--- a/src/macros/assert.rs
+++ b/src/macros/assert.rs
@@ -1,3 +1,5 @@
+#[macro_export]
+#[doc(hidden)]
 macro_rules! const_assert_eq {
     ($left:expr, $right:expr $(,)?) => {
         const _: [(); $left] = [(); $right];
diff --git a/src/macros/concat.rs b/src/macros/concat.rs
index e67e50d..5ee77c5 100644
--- a/src/macros/concat.rs
+++ b/src/macros/concat.rs
@@ -1,3 +1,5 @@
+#[macro_export]
+#[doc(hidden)]
 macro_rules! attr {
     (#[$name:ident = $value:expr] $($rest:tt)*) => {
         #[$name = $value]
diff --git a/src/opaque.rs b/src/opaque.rs
index 0ff6bb9..bad57e7 100644
--- a/src/opaque.rs
+++ b/src/opaque.rs
@@ -1,4 +1,4 @@
-use std::mem;
+use core::mem;
 
 // . size = 0
 // . align = 1
diff --git a/src/result.rs b/src/result.rs
index 0373bd8..fcced76 100644
--- a/src/result.rs
+++ b/src/result.rs
@@ -1,10 +1,12 @@
 use crate::exception::Exception;
 use crate::rust_str::RustStr;
-use std::fmt::Display;
-use std::ptr;
-use std::result::Result as StdResult;
-use std::slice;
-use std::str;
+use alloc::boxed::Box;
+use alloc::string::{String, ToString};
+use core::fmt::Display;
+use core::ptr;
+use core::result::Result as StdResult;
+use core::slice;
+use core::str;
 
 #[repr(C)]
 pub union Result {
@@ -32,7 +34,7 @@
     let len = msg.len();
 
     extern "C" {
-        #[link_name = "cxxbridge03$error"]
+        #[link_name = "cxxbridge05$error"]
         fn error(ptr: *const u8, len: usize) -> *const u8;
     }
 
diff --git a/src/rust_sliceu8.rs b/src/rust_sliceu8.rs
index f509c7f..32f8798 100644
--- a/src/rust_sliceu8.rs
+++ b/src/rust_sliceu8.rs
@@ -1,6 +1,6 @@
-use std::mem;
-use std::ptr::NonNull;
-use std::slice;
+use core::mem;
+use core::ptr::NonNull;
+use core::slice;
 
 // Not necessarily ABI compatible with &[u8]. Codegen performs the translation.
 #[repr(C)]
diff --git a/src/rust_str.rs b/src/rust_str.rs
index b944ede..38e5cab 100644
--- a/src/rust_str.rs
+++ b/src/rust_str.rs
@@ -1,7 +1,7 @@
-use std::mem;
-use std::ptr::NonNull;
-use std::slice;
-use std::str;
+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)]
diff --git a/src/rust_string.rs b/src/rust_string.rs
index a923ced..a5fa3f4 100644
--- a/src/rust_string.rs
+++ b/src/rust_string.rs
@@ -1,4 +1,5 @@
-use std::mem;
+use alloc::string::String;
+use core::mem;
 
 #[repr(C)]
 pub struct RustString {
@@ -14,6 +15,10 @@
         unsafe { &*(s as *const String as *const RustString) }
     }
 
+    pub fn from_mut(s: &mut String) -> &mut Self {
+        unsafe { &mut *(s as *mut String as *mut RustString) }
+    }
+
     pub fn into_string(self) -> String {
         self.repr
     }
diff --git a/src/rust_vec.rs b/src/rust_vec.rs
index 4c5035d..8ddc4a7 100644
--- a/src/rust_vec.rs
+++ b/src/rust_vec.rs
@@ -1,6 +1,11 @@
+use crate::rust_string::RustString;
+use alloc::string::String;
+use alloc::vec::Vec;
+use core::mem::ManuallyDrop;
+
 #[repr(C)]
 pub struct RustVec<T> {
-    repr: Vec<T>,
+    pub(crate) repr: Vec<T>,
 }
 
 impl<T> RustVec<T> {
@@ -16,6 +21,10 @@
         unsafe { &*(v as *const Vec<T> as *const RustVec<T>) }
     }
 
+    pub fn from_mut(v: &mut Vec<T>) -> &mut Self {
+        unsafe { &mut *(v as *mut Vec<T> as *mut RustVec<T>) }
+    }
+
     pub fn into_vec(self) -> Vec<T> {
         self.repr
     }
@@ -35,4 +44,49 @@
     pub fn as_ptr(&self) -> *const T {
         self.repr.as_ptr()
     }
+
+    pub fn reserve_total(&mut self, cap: usize) {
+        let len = self.repr.len();
+        if cap > len {
+            self.repr.reserve(cap - len);
+        }
+    }
+
+    pub unsafe fn set_len(&mut self, len: usize) {
+        self.repr.set_len(len);
+    }
+}
+
+impl RustVec<RustString> {
+    pub fn from_vec_string(v: Vec<String>) -> Self {
+        let mut v = ManuallyDrop::new(v);
+        let ptr = v.as_mut_ptr().cast::<RustString>();
+        let len = v.len();
+        let cap = v.capacity();
+        Self::from(unsafe { Vec::from_raw_parts(ptr, len, cap) })
+    }
+
+    pub fn from_ref_vec_string(v: &Vec<String>) -> &Self {
+        Self::from_ref(unsafe { &*(v as *const Vec<String> as *const Vec<RustString>) })
+    }
+
+    pub fn from_mut_vec_string(v: &mut Vec<String>) -> &mut Self {
+        Self::from_mut(unsafe { &mut *(v as *mut Vec<String> as *mut Vec<RustString>) })
+    }
+
+    pub fn into_vec_string(self) -> Vec<String> {
+        let mut v = ManuallyDrop::new(self.repr);
+        let ptr = v.as_mut_ptr().cast::<String>();
+        let len = v.len();
+        let cap = v.capacity();
+        unsafe { Vec::from_raw_parts(ptr, len, cap) }
+    }
+
+    pub fn as_vec_string(&self) -> &Vec<String> {
+        unsafe { &*(&self.repr as *const Vec<RustString> as *const Vec<String>) }
+    }
+
+    pub fn as_mut_vec_string(&mut self) -> &mut Vec<String> {
+        unsafe { &mut *(&mut self.repr as *mut Vec<RustString> as *mut Vec<String>) }
+    }
 }
diff --git a/src/symbols/exception.rs b/src/symbols/exception.rs
index 849db3b..b408f25 100644
--- a/src/symbols/exception.rs
+++ b/src/symbols/exception.rs
@@ -1,6 +1,8 @@
-use std::slice;
+use alloc::boxed::Box;
+use alloc::string::String;
+use core::slice;
 
-#[export_name = "cxxbridge03$exception"]
+#[export_name = "cxxbridge05$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/rust_str.rs b/src/symbols/rust_str.rs
index 6dc04ac..b655381 100644
--- a/src/symbols/rust_str.rs
+++ b/src/symbols/rust_str.rs
@@ -1,7 +1,7 @@
-use std::slice;
-use std::str;
+use core::slice;
+use core::str;
 
-#[export_name = "cxxbridge03$str$valid"]
+#[export_name = "cxxbridge05$str$valid"]
 unsafe extern "C" fn str_valid(ptr: *const u8, len: usize) -> bool {
     let slice = slice::from_raw_parts(ptr, len);
     str::from_utf8(slice).is_ok()
diff --git a/src/symbols/rust_string.rs b/src/symbols/rust_string.rs
index 63d4ba7..e5ab9ea 100644
--- a/src/symbols/rust_string.rs
+++ b/src/symbols/rust_string.rs
@@ -1,19 +1,21 @@
-use std::mem::{ManuallyDrop, MaybeUninit};
-use std::ptr;
-use std::slice;
-use std::str;
+use alloc::borrow::ToOwned;
+use alloc::string::String;
+use core::mem::{ManuallyDrop, MaybeUninit};
+use core::ptr;
+use core::slice;
+use core::str;
 
-#[export_name = "cxxbridge03$string$new"]
+#[export_name = "cxxbridge05$string$new"]
 unsafe extern "C" fn string_new(this: &mut MaybeUninit<String>) {
     ptr::write(this.as_mut_ptr(), String::new());
 }
 
-#[export_name = "cxxbridge03$string$clone"]
+#[export_name = "cxxbridge05$string$clone"]
 unsafe extern "C" fn string_clone(this: &mut MaybeUninit<String>, other: &String) {
     ptr::write(this.as_mut_ptr(), other.clone());
 }
 
-#[export_name = "cxxbridge03$string$from"]
+#[export_name = "cxxbridge05$string$from"]
 unsafe extern "C" fn string_from(
     this: &mut MaybeUninit<String>,
     ptr: *const u8,
@@ -29,17 +31,17 @@
     }
 }
 
-#[export_name = "cxxbridge03$string$drop"]
+#[export_name = "cxxbridge05$string$drop"]
 unsafe extern "C" fn string_drop(this: &mut ManuallyDrop<String>) {
     ManuallyDrop::drop(this);
 }
 
-#[export_name = "cxxbridge03$string$ptr"]
+#[export_name = "cxxbridge05$string$ptr"]
 unsafe extern "C" fn string_ptr(this: &String) -> *const u8 {
     this.as_ptr()
 }
 
-#[export_name = "cxxbridge03$string$len"]
+#[export_name = "cxxbridge05$string$len"]
 unsafe extern "C" fn string_len(this: &String) -> usize {
     this.len()
 }
diff --git a/src/symbols/rust_vec.rs b/src/symbols/rust_vec.rs
index 9ce87ab..90fbc54 100644
--- a/src/symbols/rust_vec.rs
+++ b/src/symbols/rust_vec.rs
@@ -1,50 +1,53 @@
-use std::mem;
-use std::ptr;
+use crate::rust_string::RustString;
+use crate::rust_vec::RustVec;
+use alloc::vec::Vec;
+use core::mem;
+use core::ptr;
 
-#[repr(C)]
-pub struct RustVec<T> {
-    repr: Vec<T>,
-}
-
-macro_rules! attr {
-    (#[$name:ident = $value:expr] $($rest:tt)*) => {
-        #[$name = $value]
-        $($rest)*
-    };
-}
-
-macro_rules! rust_vec_shims_for_primitive {
-    ($ty:ident) => {
+macro_rules! rust_vec_shims {
+    ($segment:expr, $ty:ty) => {
         const_assert_eq!(mem::size_of::<[usize; 3]>(), mem::size_of::<Vec<$ty>>());
         const_assert_eq!(mem::align_of::<usize>(), mem::align_of::<Vec<$ty>>());
 
         const _: () = {
             attr! {
-                #[export_name = concat!("cxxbridge03$rust_vec$", stringify!($ty), "$new")]
+                #[export_name = concat!("cxxbridge05$rust_vec$", $segment, "$new")]
                 unsafe extern "C" fn __new(this: *mut RustVec<$ty>) {
                     ptr::write(this, RustVec { repr: Vec::new() });
                 }
             }
             attr! {
-                #[export_name = concat!("cxxbridge03$rust_vec$", stringify!($ty), "$drop")]
+                #[export_name = concat!("cxxbridge05$rust_vec$", $segment, "$drop")]
                 unsafe extern "C" fn __drop(this: *mut RustVec<$ty>) {
                     ptr::drop_in_place(this);
                 }
             }
             attr! {
-                #[export_name = concat!("cxxbridge03$rust_vec$", stringify!($ty), "$len")]
+                #[export_name = concat!("cxxbridge05$rust_vec$", $segment, "$len")]
                 unsafe extern "C" fn __len(this: *const RustVec<$ty>) -> usize {
                     (*this).repr.len()
                 }
             }
             attr! {
-                #[export_name = concat!("cxxbridge03$rust_vec$", stringify!($ty), "$data")]
+                #[export_name = concat!("cxxbridge05$rust_vec$", $segment, "$data")]
                 unsafe extern "C" fn __data(this: *const RustVec<$ty>) -> *const $ty {
                     (*this).repr.as_ptr()
                 }
             }
             attr! {
-                #[export_name = concat!("cxxbridge03$rust_vec$", stringify!($ty), "$stride")]
+                #[export_name = concat!("cxxbridge05$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")]
+                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>()
                 }
@@ -53,6 +56,12 @@
     };
 }
 
+macro_rules! rust_vec_shims_for_primitive {
+    ($ty:ident) => {
+        rust_vec_shims!(stringify!($ty), $ty);
+    };
+}
+
 rust_vec_shims_for_primitive!(bool);
 rust_vec_shims_for_primitive!(u8);
 rust_vec_shims_for_primitive!(u16);
@@ -64,3 +73,5 @@
 rust_vec_shims_for_primitive!(i64);
 rust_vec_shims_for_primitive!(f32);
 rust_vec_shims_for_primitive!(f64);
+
+rust_vec_shims!("string", RustString);
diff --git a/src/symbols/symbols.rs b/src/symbols/symbols.rs
deleted file mode 100644
index 2c052ec..0000000
--- a/src/symbols/symbols.rs
+++ /dev/null
@@ -1,5 +0,0 @@
-#[path = "../macros/mod.rs"]
-#[macro_use]
-mod macros;
-
-include!("mod.rs");
diff --git a/src/unique_ptr.rs b/src/unique_ptr.rs
index 34a1f0e..3a5fc66 100644
--- a/src/unique_ptr.rs
+++ b/src/unique_ptr.rs
@@ -1,11 +1,13 @@
 use crate::cxx_string::CxxString;
 use crate::cxx_vector::{self, CxxVector, VectorElement};
-use std::ffi::c_void;
-use std::fmt::{self, Debug, Display};
-use std::marker::PhantomData;
-use std::mem;
-use std::ops::{Deref, DerefMut};
-use std::ptr;
+use crate::kind::Trivial;
+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::ptr;
 
 /// Binding to C++ `std::unique_ptr<T, std::default_delete<T>>`.
 #[repr(C)]
@@ -32,7 +34,10 @@
     }
 
     /// Allocates memory on the heap and makes a UniquePtr pointing to it.
-    pub fn new(value: T) -> Self {
+    pub fn new(value: T) -> Self
+    where
+        T: ExternType<Kind = Trivial>,
+    {
         UniquePtr {
             repr: T::__new(value),
             ty: PhantomData,
@@ -174,15 +179,15 @@
 }
 
 extern "C" {
-    #[link_name = "cxxbridge03$unique_ptr$std$string$null"]
+    #[link_name = "cxxbridge05$unique_ptr$std$string$null"]
     fn unique_ptr_std_string_null(this: *mut *mut c_void);
-    #[link_name = "cxxbridge03$unique_ptr$std$string$raw"]
+    #[link_name = "cxxbridge05$unique_ptr$std$string$raw"]
     fn unique_ptr_std_string_raw(this: *mut *mut c_void, raw: *mut CxxString);
-    #[link_name = "cxxbridge03$unique_ptr$std$string$get"]
+    #[link_name = "cxxbridge05$unique_ptr$std$string$get"]
     fn unique_ptr_std_string_get(this: *const *mut c_void) -> *const CxxString;
-    #[link_name = "cxxbridge03$unique_ptr$std$string$release"]
+    #[link_name = "cxxbridge05$unique_ptr$std$string$release"]
     fn unique_ptr_std_string_release(this: *mut *mut c_void) -> *mut CxxString;
-    #[link_name = "cxxbridge03$unique_ptr$std$string$drop"]
+    #[link_name = "cxxbridge05$unique_ptr$std$string$drop"]
     fn unique_ptr_std_string_drop(this: *mut *mut c_void);
 }
 
@@ -190,7 +195,9 @@
     const __NAME: &'static dyn Display = &"CxxString";
     fn __null() -> *mut c_void {
         let mut repr = ptr::null_mut::<c_void>();
-        unsafe { unique_ptr_std_string_null(&mut repr) }
+        unsafe {
+            unique_ptr_std_string_null(&mut repr);
+        }
         repr
     }
     unsafe fn __raw(raw: *mut Self) -> *mut c_void {
diff --git a/syntax/atom.rs b/syntax/atom.rs
index 6e5fa88..7d0ef6b 100644
--- a/syntax/atom.rs
+++ b/syntax/atom.rs
@@ -81,7 +81,7 @@
 impl PartialEq<Atom> for Type {
     fn eq(&self, atom: &Atom) -> bool {
         match self {
-            Type::Ident(ident) => ident == atom,
+            Type::Ident(ident) => ident.rust == atom,
             _ => false,
         }
     }
diff --git a/syntax/attrs.rs b/syntax/attrs.rs
index 4c76641..af73b7a 100644
--- a/syntax/attrs.rs
+++ b/syntax/attrs.rs
@@ -1,3 +1,4 @@
+use crate::syntax::namespace::Namespace;
 use crate::syntax::report::Errors;
 use crate::syntax::Atom::{self, *};
 use crate::syntax::{Derive, Doc};
@@ -10,19 +11,9 @@
     pub doc: Option<&'a mut Doc>,
     pub derives: Option<&'a mut Vec<Derive>>,
     pub repr: Option<&'a mut Option<Atom>>,
-}
-
-pub(super) fn parse_doc(cx: &mut Errors, attrs: &[Attribute]) -> Doc {
-    let mut doc = Doc::new();
-    parse(
-        cx,
-        attrs,
-        Parser {
-            doc: Some(&mut doc),
-            ..Parser::default()
-        },
-    );
-    doc
+    pub cxx_name: Option<&'a mut Option<Ident>>,
+    pub rust_name: Option<&'a mut Option<Ident>>,
+    pub namespace: Option<&'a mut Namespace>,
 }
 
 pub(super) fn parse(cx: &mut Errors, attrs: &[Attribute], mut parser: Parser) {
@@ -57,6 +48,36 @@
                 }
                 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) => 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()) {
+                Ok(attr) => {
+                    if let Some(namespace) = &mut parser.namespace {
+                        **namespace = attr;
+                        continue;
+                    }
+                }
+                Err(err) => return cx.push(err),
+            }
         }
         return cx.error(attr, "unsupported attribute");
     }
@@ -99,3 +120,19 @@
         "unrecognized repr",
     ))
 }
+
+fn parse_function_alias_attribute(input: ParseStream) -> Result<Ident> {
+    input.parse::<Token![=]>()?;
+    if input.peek(LitStr) {
+        let lit: LitStr = input.parse()?;
+        lit.parse()
+    } else {
+        input.parse()
+    }
+}
+
+fn parse_namespace_attribute(input: ParseStream) -> Result<Namespace> {
+    input.parse::<Token![=]>()?;
+    let namespace = input.parse::<Namespace>()?;
+    Ok(namespace)
+}
diff --git a/syntax/check.rs b/syntax/check.rs
index c54430c..e8a6448 100644
--- a/syntax/check.rs
+++ b/syntax/check.rs
@@ -1,24 +1,22 @@
 use crate::syntax::atom::Atom::{self, *};
-use crate::syntax::namespace::Namespace;
 use crate::syntax::report::Errors;
+use crate::syntax::types::TrivialReason;
 use crate::syntax::{
-    error, ident, Api, Enum, ExternFn, ExternType, Lang, Receiver, Ref, Slice, Struct, Ty1, Type,
-    Types,
+    error, ident, Api, Enum, ExternFn, ExternType, Impl, Lang, Receiver, Ref, Slice, Struct, Ty1,
+    Type, Types,
 };
 use proc_macro2::{Delimiter, Group, Ident, TokenStream};
 use quote::{quote, ToTokens};
 use std::fmt::Display;
 
 pub(crate) struct Check<'a> {
-    namespace: &'a Namespace,
     apis: &'a [Api],
     types: &'a Types<'a>,
     errors: &'a mut Errors,
 }
 
-pub(crate) fn typecheck(cx: &mut Errors, namespace: &Namespace, apis: &[Api], types: &Types) {
+pub(crate) fn typecheck(cx: &mut Errors, apis: &[Api], types: &Types) {
     do_typecheck(&mut Check {
-        namespace,
         apis,
         types,
         errors: cx,
@@ -26,11 +24,11 @@
 }
 
 fn do_typecheck(cx: &mut Check) {
-    ident::check_all(cx, cx.namespace, cx.apis);
+    ident::check_all(cx, cx.apis);
 
     for ty in cx.types {
         match ty {
-            Type::Ident(ident) => check_type_ident(cx, ident),
+            Type::Ident(ident) => check_type_ident(cx, &ident.rust),
             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),
@@ -45,8 +43,9 @@
         match api {
             Api::Struct(strct) => check_api_struct(cx, strct),
             Api::Enum(enm) => check_api_enum(cx, enm),
-            Api::CxxType(ty) | Api::RustType(ty) => check_api_type(cx, ty),
+            Api::CxxType(ety) | Api::RustType(ety) => check_api_type(cx, ety),
             Api::CxxFunction(efn) | Api::RustFunction(efn) => check_api_fn(cx, efn),
+            Api::Impl(imp) => check_api_impl(cx, imp),
             _ => {}
         }
     }
@@ -65,17 +64,21 @@
         && !cx.types.cxx.contains(ident)
         && !cx.types.rust.contains(ident)
     {
-        cx.error(ident, "unsupported type");
+        let msg = format!("unsupported type: {}", ident);
+        cx.error(ident, &msg);
     }
 }
 
 fn check_type_box(cx: &mut Check, ptr: &Ty1) {
     if let Type::Ident(ident) = &ptr.inner {
-        if cx.types.cxx.contains(ident) {
+        if cx.types.cxx.contains(&ident.rust)
+            && !cx.types.structs.contains_key(&ident.rust)
+            && !cx.types.enums.contains_key(&ident.rust)
+        {
             cx.error(ptr, error::BOX_CXX_TYPE.msg);
         }
 
-        if Atom::from(ident).is_none() {
+        if Atom::from(&ident.rust).is_none() {
             return;
         }
     }
@@ -85,15 +88,19 @@
 
 fn check_type_rust_vec(cx: &mut Check, ty: &Ty1) {
     if let Type::Ident(ident) = &ty.inner {
-        if cx.types.cxx.contains(ident) {
+        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 Atom::from(ident) {
+        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) => return,
-            Some(Bool) | Some(RustString) => { /* todo */ }
+            | Some(I16) | Some(I32) | Some(I64) | Some(Isize) | Some(F32) | Some(F64)
+            | Some(RustString) => return,
+            Some(Bool) => { /* todo */ }
             Some(CxxString) => {}
         }
     }
@@ -103,11 +110,11 @@
 
 fn check_type_unique_ptr(cx: &mut Check, ptr: &Ty1) {
     if let Type::Ident(ident) = &ptr.inner {
-        if cx.types.rust.contains(ident) {
+        if cx.types.rust.contains(&ident.rust) {
             cx.error(ptr, "unique_ptr of a Rust type is not supported yet");
         }
 
-        match Atom::from(ident) {
+        match Atom::from(&ident.rust) {
             None | Some(CxxString) => return,
             _ => {}
         }
@@ -120,17 +127,17 @@
 
 fn check_type_cxx_vector(cx: &mut Check, ptr: &Ty1) {
     if let Type::Ident(ident) = &ptr.inner {
-        if cx.types.rust.contains(ident) {
+        if cx.types.rust.contains(&ident.rust) {
             cx.error(
                 ptr,
                 "C++ vector containing a Rust type is not supported yet",
             );
         }
 
-        match Atom::from(ident) {
+        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) => return,
-            Some(CxxString) => { /* todo */ }
+            | Some(I16) | Some(I32) | Some(I64) | Some(Isize) | Some(F32) | Some(F64)
+            | Some(CxxString) => return,
             Some(Bool) | Some(RustString) => {}
         }
     }
@@ -160,13 +167,21 @@
 }
 
 fn check_api_struct(cx: &mut Check, strct: &Struct) {
-    check_reserved_name(cx, &strct.ident);
+    let name = &strct.name;
+    check_reserved_name(cx, &name.rust);
 
     if strct.fields.is_empty() {
         let span = span_for_struct_error(strct);
         cx.error(span, "structs without any fields are not supported");
     }
 
+    if cx.types.cxx.contains(&name.rust) {
+        if let Some(ety) = cx.types.untrusted.get(&name.rust) {
+            let msg = "extern shared struct must be declared in an `unsafe extern` block";
+            cx.error(ety, msg);
+        }
+    }
+
     for field in &strct.fields {
         if is_unsized(cx, &field.ty) {
             let desc = describe(cx, &field.ty);
@@ -183,7 +198,7 @@
 }
 
 fn check_api_enum(cx: &mut Check, enm: &Enum) {
-    check_reserved_name(cx, &enm.ident);
+    check_reserved_name(cx, &enm.name.rust);
 
     if enm.variants.is_empty() {
         let span = span_for_enum_error(enm);
@@ -191,15 +206,28 @@
     }
 }
 
-fn check_api_type(cx: &mut Check, ty: &ExternType) {
-    check_reserved_name(cx, &ty.ident);
+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),
+        };
+        let msg = format!(
+            "needs a cxx::ExternType impl in order to be used as {}",
+            what,
+        );
+        cx.error(ety, msg);
+    }
 }
 
 fn check_api_fn(cx: &mut Check, efn: &ExternFn) {
     if let Some(receiver) = &efn.receiver {
         let ref span = span_for_receiver_error(receiver);
 
-        if receiver.ty == "Self" {
+        if receiver.ty.is_self() {
             let mutability = match receiver.mutability {
                 Some(_) => "mut ",
                 None => "",
@@ -211,9 +239,9 @@
                 mutability = mutability,
             );
             cx.error(span, msg);
-        } else if !cx.types.structs.contains_key(&receiver.ty)
-            && !cx.types.cxx.contains(&receiver.ty)
-            && !cx.types.rust.contains(&receiver.ty)
+        } 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");
         }
@@ -257,6 +285,18 @@
     check_multiple_arg_lifetimes(cx, efn);
 }
 
+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;
+            }
+        }
+    }
+
+    cx.error(imp, "unsupported Self type of explicit impl");
+}
+
 fn check_mut_return_restriction(cx: &mut Check, efn: &ExternFn) {
     match &efn.ret {
         Some(Type::Ref(ty)) if ty.mutability.is_some() => {}
@@ -315,11 +355,17 @@
 
 fn is_unsized(cx: &mut Check, ty: &Type) -> bool {
     let ident = match ty {
-        Type::Ident(ident) => ident,
+        Type::Ident(ident) => &ident.rust,
         Type::CxxVector(_) | Type::Slice(_) | Type::Void(_) => return true,
         _ => return false,
     };
-    ident == CxxString || cx.types.cxx.contains(ident) || cx.types.rust.contains(ident)
+    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)
 }
 
 fn span_for_struct_error(strct: &Struct) -> TokenStream {
@@ -352,16 +398,20 @@
 fn describe(cx: &mut Check, ty: &Type) -> String {
     match ty {
         Type::Ident(ident) => {
-            if cx.types.structs.contains_key(ident) {
+            if cx.types.structs.contains_key(&ident.rust) {
                 "struct".to_owned()
-            } else if cx.types.cxx.contains(ident) {
+            } else if cx.types.enums.contains_key(&ident.rust) {
+                "enum".to_owned()
+            } else if cx.types.aliases.contains_key(&ident.rust) {
                 "C++ type".to_owned()
-            } else if cx.types.rust.contains(ident) {
+            } else if cx.types.cxx.contains(&ident.rust) {
+                "opaque C++ type".to_owned()
+            } else if cx.types.rust.contains(&ident.rust) {
                 "opaque Rust type".to_owned()
-            } else if Atom::from(ident) == Some(CxxString) {
+            } else if Atom::from(&ident.rust) == Some(CxxString) {
                 "C++ string".to_owned()
             } else {
-                ident.to_string()
+                ident.rust.to_string()
             }
         }
         Type::RustBox(_) => "Box".to_owned(),
diff --git a/syntax/error.rs b/syntax/error.rs
index a60b7da..d0ae021 100644
--- a/syntax/error.rs
+++ b/syntax/error.rs
@@ -19,8 +19,10 @@
     CXX_STRING_BY_VALUE,
     CXX_TYPE_BY_VALUE,
     DISCRIMINANT_OVERFLOW,
+    DOT_INCLUDE,
     DOUBLE_UNDERSCORE,
     RUST_TYPE_BY_VALUE,
+    UNSUPPORTED_TYPE,
     USE_NOT_ALLOWED,
 ];
 
@@ -54,6 +56,12 @@
     note: Some("note: explicitly set `= 0` if that is desired outcome"),
 };
 
+pub static DOT_INCLUDE: Error = Error {
+    msg: "#include relative to `.` or `..` is not supported in Cargo builds",
+    label: Some("#include relative to `.` or `..` is not supported in Cargo builds"),
+    note: Some("note: use a path starting with the crate name"),
+};
+
 pub static DOUBLE_UNDERSCORE: Error = Error {
     msg: "identifiers containing double underscore are reserved in C++",
     label: Some("reserved identifier"),
@@ -66,6 +74,12 @@
     note: Some("hint: wrap it in a Box<>"),
 };
 
+pub static UNSUPPORTED_TYPE: Error = Error {
+    msg: "unsupported type: ",
+    label: Some("unsupported type"),
+    note: None,
+};
+
 pub static USE_NOT_ALLOWED: Error = Error {
     msg: "`use` items are not allowed within cxx bridge",
     label: Some("not allowed"),
diff --git a/syntax/file.rs b/syntax/file.rs
new file mode 100644
index 0000000..fd6dd98
--- /dev/null
+++ b/syntax/file.rs
@@ -0,0 +1,108 @@
+use crate::syntax::namespace::Namespace;
+use quote::quote;
+use syn::parse::{Error, Parse, ParseStream, Result};
+use syn::{
+    braced, token, Abi, Attribute, ForeignItem, Ident, Item as RustItem, ItemEnum, ItemImpl,
+    ItemStruct, ItemUse, LitStr, Token, Visibility,
+};
+
+pub struct Module {
+    pub namespace: Namespace,
+    pub attrs: Vec<Attribute>,
+    pub vis: Visibility,
+    pub unsafety: Option<Token![unsafe]>,
+    pub mod_token: Token![mod],
+    pub ident: Ident,
+    pub brace_token: token::Brace,
+    pub content: Vec<Item>,
+}
+
+pub enum Item {
+    Struct(ItemStruct),
+    Enum(ItemEnum),
+    ForeignMod(ItemForeignMod),
+    Use(ItemUse),
+    Impl(ItemImpl),
+    Other(RustItem),
+}
+
+pub struct ItemForeignMod {
+    pub attrs: Vec<Attribute>,
+    pub unsafety: Option<Token![unsafe]>,
+    pub abi: Abi,
+    pub brace_token: token::Brace,
+    pub items: Vec<ForeignItem>,
+}
+
+impl Parse for Module {
+    fn parse(input: ParseStream) -> Result<Self> {
+        let namespace = Namespace::ROOT;
+        let mut attrs = input.call(Attribute::parse_outer)?;
+        let vis: Visibility = input.parse()?;
+        let unsafety: Option<Token![unsafe]> = input.parse()?;
+        let mod_token: Token![mod] = input.parse()?;
+        let ident: Ident = input.parse()?;
+
+        let semi: Option<Token![;]> = input.parse()?;
+        if let Some(semi) = semi {
+            let span = quote!(#vis #mod_token #semi);
+            return Err(Error::new_spanned(
+                span,
+                "#[cxx::bridge] module must have inline contents",
+            ));
+        }
+
+        let content;
+        let brace_token = braced!(content in input);
+        attrs.extend(content.call(Attribute::parse_inner)?);
+
+        let mut items = Vec::new();
+        while !content.is_empty() {
+            items.push(content.parse()?);
+        }
+
+        Ok(Module {
+            namespace,
+            attrs,
+            vis,
+            unsafety,
+            mod_token,
+            ident,
+            brace_token,
+            content: items,
+        })
+    }
+}
+
+impl Parse for Item {
+    fn parse(input: ParseStream) -> Result<Self> {
+        let attrs = input.call(Attribute::parse_outer)?;
+
+        let ahead = input.fork();
+        let unsafety = if ahead.parse::<Option<Token![unsafe]>>()?.is_some()
+            && ahead.parse::<Option<Token![extern]>>()?.is_some()
+            && ahead.parse::<Option<LitStr>>().is_ok()
+            && ahead.peek(token::Brace)
+        {
+            Some(input.parse()?)
+        } else {
+            None
+        };
+
+        let item = input.parse()?;
+        match item {
+            RustItem::Struct(item) => Ok(Item::Struct(ItemStruct { attrs, ..item })),
+            RustItem::Enum(item) => Ok(Item::Enum(ItemEnum { attrs, ..item })),
+            RustItem::ForeignMod(item) => Ok(Item::ForeignMod(ItemForeignMod {
+                attrs,
+                unsafety,
+                abi: item.abi,
+                brace_token: item.brace_token,
+                items: item.items,
+            })),
+            RustItem::Impl(item) => Ok(Item::Impl(ItemImpl { attrs, ..item })),
+            RustItem::Use(item) => Ok(Item::Use(ItemUse { attrs, ..item })),
+            other => Ok(Item::Other(other)),
+        }
+    }
+}
diff --git a/syntax/ident.rs b/syntax/ident.rs
index 7545e92..aaf7832 100644
--- a/syntax/ident.rs
+++ b/syntax/ident.rs
@@ -1,6 +1,5 @@
 use crate::syntax::check::Check;
-use crate::syntax::namespace::Namespace;
-use crate::syntax::{error, Api};
+use crate::syntax::{error, Api, Pair};
 use proc_macro2::Ident;
 
 fn check(cx: &mut Check, ident: &Ident) {
@@ -13,37 +12,40 @@
     }
 }
 
-pub(crate) fn check_all(cx: &mut Check, namespace: &Namespace, apis: &[Api]) {
-    for segment in namespace {
+fn check_ident(cx: &mut Check, name: &Pair) {
+    for segment in &name.namespace {
         check(cx, segment);
     }
+    check(cx, &name.cxx);
+}
 
+pub(crate) fn check_all(cx: &mut Check, apis: &[Api]) {
     for api in apis {
         match api {
-            Api::Include(_) => {}
+            Api::Include(_) | Api::Impl(_) => {}
             Api::Struct(strct) => {
-                check(cx, &strct.ident);
+                check_ident(cx, &strct.name);
                 for field in &strct.fields {
                     check(cx, &field.ident);
                 }
             }
             Api::Enum(enm) => {
-                check(cx, &enm.ident);
+                check_ident(cx, &enm.name);
                 for variant in &enm.variants {
                     check(cx, &variant.ident);
                 }
             }
             Api::CxxType(ety) | Api::RustType(ety) => {
-                check(cx, &ety.ident);
+                check_ident(cx, &ety.name);
             }
             Api::CxxFunction(efn) | Api::RustFunction(efn) => {
-                check(cx, &efn.ident);
+                check(cx, &efn.name.rust);
                 for arg in &efn.args {
                     check(cx, &arg.ident);
                 }
             }
             Api::TypeAlias(alias) => {
-                check(cx, &alias.ident);
+                check_ident(cx, &alias.name);
             }
         }
     }
diff --git a/syntax/impls.rs b/syntax/impls.rs
index c34e3e5..a4b393a 100644
--- a/syntax/impls.rs
+++ b/syntax/impls.rs
@@ -1,8 +1,27 @@
-use crate::syntax::{ExternFn, Receiver, Ref, Signature, Slice, Ty1, Type};
+use crate::syntax::{ExternFn, Impl, Include, Receiver, Ref, Signature, Slice, Ty1, Type};
+use std::borrow::Borrow;
 use std::hash::{Hash, Hasher};
 use std::mem;
 use std::ops::{Deref, DerefMut};
 
+impl PartialEq for Include {
+    fn eq(&self, other: &Include) -> bool {
+        let Include {
+            path,
+            kind,
+            begin_span: _,
+            end_span: _,
+        } = self;
+        let Include {
+            path: path2,
+            kind: kind2,
+            begin_span: _,
+            end_span: _,
+        } = other;
+        path == path2 && kind == kind2
+    }
+}
+
 impl Deref for ExternFn {
     type Target = Signature;
 
@@ -149,6 +168,7 @@
 impl PartialEq for Signature {
     fn eq(&self, other: &Signature) -> bool {
         let Signature {
+            unsafety,
             fn_token: _,
             receiver,
             args,
@@ -158,6 +178,7 @@
             throws_tokens: _,
         } = self;
         let Signature {
+            unsafety: unsafety2,
             fn_token: _,
             receiver: receiver2,
             args: args2,
@@ -166,7 +187,8 @@
             paren_token: _,
             throws_tokens: _,
         } = other;
-        receiver == receiver2
+        unsafety.is_some() == unsafety2.is_some()
+            && receiver == receiver2
             && ret == ret2
             && throws == throws2
             && args.len() == args2.len()
@@ -177,6 +199,7 @@
 impl Hash for Signature {
     fn hash<H: Hasher>(&self, state: &mut H) {
         let Signature {
+            unsafety,
             fn_token: _,
             receiver,
             args,
@@ -185,6 +208,7 @@
             paren_token: _,
             throws_tokens: _,
         } = self;
+        unsafety.is_some().hash(state);
         receiver.hash(state);
         for arg in args {
             arg.hash(state);
@@ -233,3 +257,38 @@
         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
new file mode 100644
index 0000000..6fd3162
--- /dev/null
+++ b/syntax/improper.rs
@@ -0,0 +1,36 @@
+use self::ImproperCtype::*;
+use crate::syntax::atom::Atom::{self, *};
+use crate::syntax::{Type, Types};
+use proc_macro2::Ident;
+
+pub enum ImproperCtype<'a> {
+    Definite(bool),
+    Depends(&'a Ident),
+}
+
+impl<'a> Types<'a> {
+    // yes, no, maybe
+    pub fn determine_improper_ctype(&self, ty: &Type) -> ImproperCtype<'a> {
+        match ty {
+            Type::Ident(ident) => {
+                let ident = &ident.rust;
+                if let Some(atom) = Atom::from(ident) {
+                    Definite(atom == RustString)
+                } else if let Some(strct) = self.structs.get(ident) {
+                    Depends(&strct.name.rust) // iterate to fixed-point
+                } else {
+                    Definite(self.rust.contains(ident))
+                }
+            }
+            Type::RustBox(_)
+            | Type::RustVec(_)
+            | Type::Str(_)
+            | Type::Fn(_)
+            | Type::Void(_)
+            | Type::Slice(_)
+            | Type::SliceRefU8(_) => Definite(true),
+            Type::UniquePtr(_) | Type::CxxVector(_) => Definite(false),
+            Type::Ref(ty) => self.determine_improper_ctype(&ty.inner),
+        }
+    }
+}
diff --git a/syntax/mangle.rs b/syntax/mangle.rs
index c9392db..cc5115a 100644
--- a/syntax/mangle.rs
+++ b/syntax/mangle.rs
@@ -1,9 +1,8 @@
-use crate::syntax::namespace::Namespace;
 use crate::syntax::symbol::{self, Symbol};
-use crate::syntax::ExternFn;
+use crate::syntax::{ExternFn, Types};
 use proc_macro2::Ident;
 
-const CXXBRIDGE: &str = "cxxbridge03";
+const CXXBRIDGE: &str = "cxxbridge05";
 
 macro_rules! join {
     ($($segment:expr),*) => {
@@ -11,19 +10,27 @@
     };
 }
 
-pub fn extern_fn(namespace: &Namespace, efn: &ExternFn) -> Symbol {
+pub fn extern_fn(efn: &ExternFn, types: &Types) -> Symbol {
     match &efn.receiver {
-        Some(receiver) => join!(namespace, CXXBRIDGE, receiver.ty, efn.ident),
-        None => join!(namespace, CXXBRIDGE, efn.ident),
+        Some(receiver) => {
+            let receiver_ident = types.resolve(&receiver.ty);
+            join!(
+                efn.name.namespace,
+                CXXBRIDGE,
+                receiver_ident.cxx,
+                efn.name.rust
+            )
+        }
+        None => join!(efn.name.namespace, CXXBRIDGE, efn.name.rust),
     }
 }
 
 // The C half of a function pointer trampoline.
-pub fn c_trampoline(namespace: &Namespace, efn: &ExternFn, var: &Ident) -> Symbol {
-    join!(extern_fn(namespace, efn), var, 0)
+pub fn c_trampoline(efn: &ExternFn, var: &Ident, types: &Types) -> Symbol {
+    join!(extern_fn(efn, types), var, 0)
 }
 
 // The Rust half of a function pointer trampoline.
-pub fn r_trampoline(namespace: &Namespace, efn: &ExternFn, var: &Ident) -> Symbol {
-    join!(extern_fn(namespace, efn), var, 1)
+pub fn r_trampoline(efn: &ExternFn, var: &Ident, types: &Types) -> Symbol {
+    join!(extern_fn(efn, types), var, 1)
 }
diff --git a/syntax/mod.rs b/syntax/mod.rs
index 88c96e8..33ed31c 100644
--- a/syntax/mod.rs
+++ b/syntax/mod.rs
@@ -7,19 +7,26 @@
 mod discriminant;
 mod doc;
 pub mod error;
+pub mod file;
 pub mod ident;
 mod impls;
+mod improper;
 pub mod mangle;
+mod names;
 pub mod namespace;
 mod parse;
+pub mod qualified;
 pub mod report;
 pub mod set;
 pub mod symbol;
 mod tokens;
+mod toposort;
 pub mod types;
 
 use self::discriminant::Discriminant;
+use self::namespace::Namespace;
 use self::parse::kw;
+use self::symbol::Symbol;
 use proc_macro2::{Ident, Span};
 use syn::punctuated::Punctuated;
 use syn::token::{Brace, Bracket, Paren};
@@ -32,7 +39,7 @@
 pub use self::types::Types;
 
 pub enum Api {
-    Include(String),
+    Include(Include),
     Struct(Struct),
     Enum(Enum),
     CxxType(ExternType),
@@ -40,19 +47,38 @@
     RustType(ExternType),
     RustFunction(ExternFn),
     TypeAlias(TypeAlias),
+    Impl(Impl),
+}
+
+pub struct Include {
+    pub path: String,
+    pub kind: IncludeKind,
+    pub begin_span: Span,
+    pub end_span: Span,
+}
+
+/// Whether to emit `#include "path"` or `#include <path>`.
+#[derive(Copy, Clone, PartialEq, Debug)]
+pub enum IncludeKind {
+    /// `#include "quoted/path/to"`
+    Quoted,
+    /// `#include <bracketed/path/to>`
+    Bracketed,
 }
 
 pub struct ExternType {
     pub doc: Doc,
     pub type_token: Token![type],
-    pub ident: Ident,
+    pub name: Pair,
+    pub semi_token: Token![;],
+    pub trusted: bool,
 }
 
 pub struct Struct {
     pub doc: Doc,
     pub derives: Vec<Derive>,
     pub struct_token: Token![struct],
-    pub ident: Ident,
+    pub name: Pair,
     pub brace_token: Brace,
     pub fields: Vec<Var>,
 }
@@ -60,7 +86,7 @@
 pub struct Enum {
     pub doc: Doc,
     pub enum_token: Token![enum],
-    pub ident: Ident,
+    pub name: Pair,
     pub brace_token: Brace,
     pub variants: Vec<Variant>,
     pub repr: Atom,
@@ -69,20 +95,28 @@
 pub struct ExternFn {
     pub lang: Lang,
     pub doc: Doc,
-    pub ident: Ident,
+    pub name: Pair,
     pub sig: Signature,
     pub semi_token: Token![;],
 }
 
 pub struct TypeAlias {
+    pub doc: Doc,
     pub type_token: Token![type],
-    pub ident: Ident,
+    pub name: Pair,
     pub eq_token: Token![=],
     pub ty: RustType,
     pub semi_token: Token![;],
 }
 
+pub struct Impl {
+    pub impl_token: Token![impl],
+    pub ty: Type,
+    pub brace_token: Brace,
+}
+
 pub struct Signature {
+    pub unsafety: Option<Token![unsafe]>,
     pub fn_token: Token![fn],
     pub receiver: Option<Receiver>,
     pub args: Punctuated<Var, Token![,]>,
@@ -103,7 +137,7 @@
     pub lifetime: Option<Lifetime>,
     pub mutability: Option<Token![mut]>,
     pub var: Token![self],
-    pub ty: Ident,
+    pub ty: ResolvableName,
     pub shorthand: bool,
 }
 
@@ -114,7 +148,7 @@
 }
 
 pub enum Type {
-    Ident(Ident),
+    Ident(ResolvableName),
     RustBox(Box<Ty1>),
     RustVec(Box<Ty1>),
     UniquePtr(Box<Ty1>),
@@ -151,3 +185,19 @@
     Cxx,
     Rust,
 }
+
+// An association of a defined Rust name with a fully resolved, namespace
+// qualified C++ name.
+#[derive(Clone)]
+pub struct Pair {
+    pub namespace: Namespace,
+    pub cxx: Ident,
+    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 {
+    pub rust: Ident,
+}
diff --git a/syntax/names.rs b/syntax/names.rs
new file mode 100644
index 0000000..25ae9f4
--- /dev/null
+++ b/syntax/names.rs
@@ -0,0 +1,73 @@
+use crate::syntax::{Namespace, Pair, ResolvableName, Symbol, Types};
+use proc_macro2::{Ident, Span};
+use std::iter;
+use syn::Token;
+
+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())
+    }
+
+    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)
+    }
+}
+
+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"
+    }
+
+    pub fn span(&self) -> Span {
+        self.rust.span()
+    }
+
+    pub fn to_symbol(&self, types: &Types) -> Symbol {
+        types.resolve(self).to_symbol()
+    }
+}
diff --git a/syntax/namespace.rs b/syntax/namespace.rs
index e2dce18..07185e1 100644
--- a/syntax/namespace.rs
+++ b/syntax/namespace.rs
@@ -1,42 +1,52 @@
+use crate::syntax::qualified::QualifiedName;
 use quote::IdentFragment;
 use std::fmt::{self, Display};
+use std::iter::FromIterator;
 use std::slice::Iter;
 use syn::parse::{Parse, ParseStream, Result};
-use syn::{Ident, Path, Token};
+use syn::{Ident, Token};
 
 mod kw {
     syn::custom_keyword!(namespace);
 }
 
-#[derive(Clone)]
+#[derive(Clone, Default)]
 pub struct Namespace {
     segments: Vec<Ident>,
 }
 
 impl Namespace {
-    pub fn none() -> Self {
-        Namespace {
-            segments: Vec::new(),
-        }
-    }
+    pub const ROOT: Self = Namespace {
+        segments: Vec::new(),
+    };
 
     pub fn iter(&self) -> Iter<Ident> {
         self.segments.iter()
     }
+
+    pub fn parse_bridge_attr_namespace(input: ParseStream) -> Result<Namespace> {
+        if input.is_empty() {
+            return Ok(Namespace::ROOT);
+        }
+
+        input.parse::<kw::namespace>()?;
+        input.parse::<Token![=]>()?;
+        let namespace = input.parse::<Namespace>()?;
+        input.parse::<Option<Token![,]>>()?;
+        Ok(namespace)
+    }
+}
+
+impl Default for &Namespace {
+    fn default() -> Self {
+        const ROOT: &Namespace = &Namespace::ROOT;
+        ROOT
+    }
 }
 
 impl Parse for Namespace {
     fn parse(input: ParseStream) -> Result<Self> {
-        let mut segments = Vec::new();
-        if !input.is_empty() {
-            input.parse::<kw::namespace>()?;
-            input.parse::<Token![=]>()?;
-            let path = input.call(Path::parse_mod_style)?;
-            for segment in path.segments {
-                segments.push(segment.ident);
-            }
-            input.parse::<Option<Token![,]>>()?;
-        }
+        let segments = QualifiedName::parse_quoted_or_unquoted(input)?.segments;
         Ok(Namespace { segments })
     }
 }
@@ -63,3 +73,13 @@
         self.iter()
     }
 }
+
+impl<'a> FromIterator<&'a Ident> for Namespace {
+    fn from_iter<I>(idents: I) -> Self
+    where
+        I: IntoIterator<Item = &'a Ident>,
+    {
+        let segments = idents.into_iter().cloned().collect();
+        Namespace { segments }
+    }
+}
diff --git a/syntax/parse.rs b/syntax/parse.rs
index a1d31cf..a5f8bd1 100644
--- a/syntax/parse.rs
+++ b/syntax/parse.rs
@@ -1,45 +1,58 @@
 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, Lang, Receiver, Ref, Signature, Slice,
-    Struct, Ty1, Type, TypeAlias, Var, Variant,
+    attrs, error, Api, Doc, Enum, ExternFn, ExternType, Impl, Include, IncludeKind, Lang,
+    Namespace, Pair, Receiver, Ref, ResolvableName, Signature, Slice, Struct, Ty1, Type, TypeAlias,
+    Var, Variant,
 };
-use proc_macro2::{TokenStream, TokenTree};
+use proc_macro2::{Delimiter, Group, TokenStream, TokenTree};
 use quote::{format_ident, quote, quote_spanned};
 use syn::parse::{ParseStream, Parser};
 use syn::punctuated::Punctuated;
 use syn::{
     Abi, Attribute, Error, Fields, FnArg, ForeignItem, ForeignItemFn, ForeignItemType,
-    GenericArgument, Ident, Item, ItemEnum, ItemForeignMod, ItemStruct, LitStr, Pat, PathArguments,
-    Result, ReturnType, Token, Type as RustType, TypeBareFn, TypePath, TypeReference, TypeSlice,
+    GenericArgument, Ident, ItemEnum, ItemImpl, ItemStruct, LitStr, Pat, PathArguments, Result,
+    ReturnType, Token, Type as RustType, TypeBareFn, TypePath, TypeReference, TypeSlice,
 };
 
 pub mod kw {
     syn::custom_keyword!(Result);
 }
 
-pub fn parse_items(cx: &mut Errors, items: Vec<Item>) -> Vec<Api> {
+pub fn parse_items(
+    cx: &mut Errors,
+    items: Vec<Item>,
+    trusted: bool,
+    namespace: &Namespace,
+) -> Vec<Api> {
     let mut apis = Vec::new();
     for item in items {
         match item {
-            Item::Struct(item) => match parse_struct(cx, item) {
+            Item::Struct(item) => match parse_struct(cx, item, namespace) {
                 Ok(strct) => apis.push(strct),
                 Err(err) => cx.push(err),
             },
-            Item::Enum(item) => match parse_enum(cx, item) {
+            Item::Enum(item) => match parse_enum(cx, item, namespace) {
                 Ok(enm) => apis.push(enm),
                 Err(err) => cx.push(err),
             },
-            Item::ForeignMod(foreign_mod) => parse_foreign_mod(cx, foreign_mod, &mut apis),
+            Item::ForeignMod(foreign_mod) => {
+                parse_foreign_mod(cx, foreign_mod, &mut apis, trusted, namespace)
+            }
+            Item::Impl(item) => match parse_impl(item, namespace) {
+                Ok(imp) => apis.push(imp),
+                Err(err) => cx.push(err),
+            },
             Item::Use(item) => cx.error(item, error::USE_NOT_ALLOWED),
-            _ => cx.error(item, "unsupported item"),
+            Item::Other(item) => cx.error(item, "unsupported item"),
         }
     }
     apis
 }
 
-fn parse_struct(cx: &mut Errors, item: ItemStruct) -> Result<Api> {
+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;
@@ -54,44 +67,48 @@
 
     let mut doc = Doc::new();
     let mut derives = Vec::new();
+    let mut namespace = namespace.clone();
     attrs::parse(
         cx,
         &item.attrs,
         attrs::Parser {
             doc: Some(&mut doc),
             derives: Some(&mut derives),
+            namespace: Some(&mut namespace),
             ..Default::default()
         },
     );
 
-    let fields = match item.fields {
+    let named_fields = match item.fields {
         Fields::Named(fields) => fields,
         Fields::Unit => return Err(Error::new_spanned(item, "unit structs are not supported")),
         Fields::Unnamed(_) => {
-            return Err(Error::new_spanned(item, "tuple structs are not supported"))
+            return Err(Error::new_spanned(item, "tuple structs are not supported"));
         }
     };
 
+    let fields = named_fields
+        .named
+        .into_iter()
+        .map(|field| {
+            Ok(Var {
+                ident: field.ident.unwrap(),
+                ty: parse_type(&field.ty, &namespace)?,
+            })
+        })
+        .collect::<Result<_>>()?;
+
     Ok(Api::Struct(Struct {
         doc,
         derives,
         struct_token: item.struct_token,
-        ident: item.ident,
-        brace_token: fields.brace_token,
-        fields: fields
-            .named
-            .into_iter()
-            .map(|field| {
-                Ok(Var {
-                    ident: field.ident.unwrap(),
-                    ty: parse_type(&field.ty)?,
-                })
-            })
-            .collect::<Result<_>>()?,
+        name: Pair::new(namespace, item.ident),
+        brace_token: named_fields.brace_token,
+        fields,
     }))
 }
 
-fn parse_enum(cx: &mut Errors, item: ItemEnum) -> Result<Api> {
+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;
@@ -106,12 +123,14 @@
 
     let mut doc = Doc::new();
     let mut repr = None;
+    let mut namespace = namespace.clone();
     attrs::parse(
         cx,
         &item.attrs,
         attrs::Parser {
             doc: Some(&mut doc),
             repr: Some(&mut repr),
+            namespace: Some(&mut namespace),
             ..Default::default()
         },
     );
@@ -162,27 +181,59 @@
     Ok(Api::Enum(Enum {
         doc,
         enum_token,
-        ident: item.ident,
+        name: Pair::new(namespace, item.ident),
         brace_token,
         variants,
         repr,
     }))
 }
 
-fn parse_foreign_mod(cx: &mut Errors, foreign_mod: ItemForeignMod, out: &mut Vec<Api>) {
-    let lang = match parse_lang(foreign_mod.abi) {
+fn parse_foreign_mod(
+    cx: &mut Errors,
+    foreign_mod: ItemForeignMod,
+    out: &mut Vec<Api>,
+    trusted: bool,
+    namespace: &Namespace,
+) {
+    let lang = match parse_lang(&foreign_mod.abi) {
         Ok(lang) => lang,
         Err(err) => return cx.push(err),
     };
 
+    match lang {
+        Lang::Rust => {
+            if foreign_mod.unsafety.is_some() {
+                let unsafety = foreign_mod.unsafety;
+                let abi = foreign_mod.abi;
+                let span = quote!(#unsafety #abi);
+                cx.error(span, "extern \"Rust\" block does not need to be unsafe");
+            }
+        }
+        Lang::Cxx => {}
+    }
+
+    let trusted = trusted || foreign_mod.unsafety.is_some();
+
+    let mut namespace = namespace.clone();
+    attrs::parse(
+        cx,
+        &foreign_mod.attrs,
+        attrs::Parser {
+            namespace: Some(&mut namespace),
+            ..Default::default()
+        },
+    );
+
     let mut items = Vec::new();
     for foreign in &foreign_mod.items {
         match foreign {
-            ForeignItem::Type(foreign) => match parse_extern_type(cx, foreign, lang) {
-                Ok(ety) => items.push(ety),
-                Err(err) => cx.push(err),
-            },
-            ForeignItem::Fn(foreign) => match parse_extern_fn(cx, foreign, lang) {
+            ForeignItem::Type(foreign) => {
+                match parse_extern_type(cx, foreign, lang, trusted, &namespace) {
+                    Ok(ety) => items.push(ety),
+                    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),
             },
@@ -192,17 +243,19 @@
                     Err(err) => cx.push(err),
                 }
             }
-            ForeignItem::Verbatim(tokens) => match parse_extern_verbatim(cx, tokens, lang) {
-                Ok(api) => items.push(api),
-                Err(err) => cx.push(err),
-            },
+            ForeignItem::Verbatim(tokens) => {
+                match parse_extern_verbatim(cx, tokens, lang, &namespace) {
+                    Ok(api) => items.push(api),
+                    Err(err) => cx.push(err),
+                }
+            }
             _ => cx.error(foreign, "unsupported foreign item"),
         }
     }
 
     let mut types = items.iter().filter_map(|item| match item {
-        Api::CxxType(ty) | Api::RustType(ty) => Some(&ty.ident),
-        Api::TypeAlias(alias) => Some(&alias.ident),
+        Api::CxxType(ety) | Api::RustType(ety) => Some(&ety.name),
+        Api::TypeAlias(alias) => Some(&alias.name),
         _ => None,
     });
     if let (Some(single_type), None) = (types.next(), types.next()) {
@@ -210,8 +263,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 == "Self" {
-                        receiver.ty = single_type.clone();
+                    if receiver.ty.is_self() {
+                        receiver.ty = ResolvableName::new(single_type.rust.clone());
                     }
                 }
             }
@@ -221,7 +274,7 @@
     out.extend(items);
 }
 
-fn parse_lang(abi: Abi) -> Result<Lang> {
+fn parse_lang(abi: &Abi) -> Result<Lang> {
     let name = match &abi.name {
         Some(name) => name,
         None => {
@@ -238,10 +291,27 @@
     }
 }
 
-fn parse_extern_type(cx: &mut Errors, foreign_type: &ForeignItemType, lang: Lang) -> Result<Api> {
-    let doc = attrs::parse_doc(cx, &foreign_type.attrs);
+fn parse_extern_type(
+    cx: &mut Errors,
+    foreign_type: &ForeignItemType,
+    lang: Lang,
+    trusted: bool,
+    namespace: &Namespace,
+) -> Result<Api> {
+    let mut doc = Doc::new();
+    let mut namespace = namespace.clone();
+    attrs::parse(
+        cx,
+        &foreign_type.attrs,
+        attrs::Parser {
+            doc: Some(&mut doc),
+            namespace: Some(&mut namespace),
+            ..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,
@@ -249,11 +319,18 @@
     Ok(api_type(ExternType {
         doc,
         type_token,
-        ident,
+        name: Pair::new(namespace, ident),
+        semi_token,
+        trusted,
     }))
 }
 
-fn parse_extern_fn(cx: &mut Errors, foreign_fn: &ForeignItemFn, lang: Lang) -> Result<Api> {
+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() {
         return Err(Error::new_spanned(
@@ -268,6 +345,22 @@
         ));
     }
 
+    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()
+        },
+    );
+
     let mut receiver = None;
     let mut args = Punctuated::new();
     for arg in foreign_fn.sig.inputs.pairs() {
@@ -280,7 +373,7 @@
                         lifetime: lifetime.clone(),
                         mutability: arg.mutability,
                         var: arg.self_token,
-                        ty: Token![Self](arg.self_token.span).into(),
+                        ty: ResolvableName::make_self(arg.self_token.span),
                         shorthand: true,
                     });
                     continue;
@@ -295,7 +388,7 @@
                     }
                     _ => return Err(Error::new_spanned(arg, "unsupported signature")),
                 };
-                let ty = parse_type(&arg.ty)?;
+                let ty = parse_type(&arg.ty, &namespace)?;
                 if ident != "self" {
                     args.push_value(Var { ident, ty });
                     if let Some(comma) = comma {
@@ -309,7 +402,7 @@
                             ampersand: reference.ampersand,
                             lifetime: reference.lifetime,
                             mutability: reference.mutability,
-                            var: Token![self](ident.span()),
+                            var: Token![self](ident.rust.span()),
                             ty: ident,
                             shorthand: false,
                         });
@@ -322,11 +415,15 @@
     }
 
     let mut throws_tokens = None;
-    let ret = parse_return_type(&foreign_fn.sig.output, &mut throws_tokens)?;
+    let ret = parse_return_type(&foreign_fn.sig.output, &mut throws_tokens, &namespace)?;
     let throws = throws_tokens.is_some();
-    let doc = attrs::parse_doc(cx, &foreign_fn.attrs);
+    let unsafety = foreign_fn.sig.unsafety;
     let fn_token = foreign_fn.sig.fn_token;
-    let ident = foreign_fn.sig.ident.clone();
+    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 paren_token = foreign_fn.sig.paren_token;
     let semi_token = foreign_fn.semi_token;
     let api_function = match lang {
@@ -337,8 +434,9 @@
     Ok(api_function(ExternFn {
         lang,
         doc,
-        ident,
+        name,
         sig: Signature {
+            unsafety,
             fn_token,
             receiver,
             args,
@@ -351,7 +449,12 @@
     }))
 }
 
-fn parse_extern_verbatim(cx: &mut Errors, tokens: &TokenStream, lang: Lang) -> Result<Api> {
+fn parse_extern_verbatim(
+    cx: &mut Errors,
+    tokens: &TokenStream,
+    lang: Lang,
+    namespace: &Namespace,
+) -> Result<Api> {
     // type Alias = crate::path::to::Type;
     let parse = |input: ParseStream| -> Result<TypeAlias> {
         let attrs = input.call(Attribute::parse_outer)?;
@@ -366,11 +469,22 @@
         let eq_token: Token![=] = input.parse()?;
         let ty: RustType = input.parse()?;
         let semi_token: Token![;] = input.parse()?;
-        attrs::parse_doc(cx, &attrs);
+        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,
-            ident,
+            name: Pair::new(namespace, ident),
             eq_token,
             ty,
             semi_token,
@@ -389,15 +503,53 @@
     }
 }
 
-fn parse_include(input: ParseStream) -> Result<String> {
+fn parse_impl(imp: ItemImpl, namespace: &Namespace) -> Result<Api> {
+    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 span = quote!(#bang #path #for_token #self_ty);
+        return Err(Error::new_spanned(
+            span,
+            "unexpected impl, expected something like `impl UniquePtr<T> {}`",
+        ));
+    }
+
+    let generics = &imp.generics;
+    if !generics.params.is_empty() || generics.where_clause.is_some() {
+        return Err(Error::new_spanned(
+            imp,
+            "generic parameters on an impl is not supported",
+        ));
+    }
+
+    Ok(Api::Impl(Impl {
+        impl_token: imp.impl_token,
+        ty: parse_type(&self_ty, namespace)?,
+        brace_token: imp.brace_token,
+    }))
+}
+
+fn parse_include(input: ParseStream) -> Result<Include> {
     if input.peek(LitStr) {
-        return Ok(input.parse::<LitStr>()?.value());
+        let lit: LitStr = input.parse()?;
+        let span = lit.span();
+        return Ok(Include {
+            path: lit.value(),
+            kind: IncludeKind::Quoted,
+            begin_span: span,
+            end_span: span,
+        });
     }
 
     if input.peek(Token![<]) {
         let mut path = String::new();
-        input.parse::<Token![<]>()?;
-        path.push('<');
+
+        let langle: Token![<] = input.parse()?;
         while !input.is_empty() && !input.peek(Token![>]) {
             let token: TokenTree = input.parse()?;
             match token {
@@ -413,29 +565,34 @@
                 _ => return Err(Error::new(token.span(), "unexpected token in include path")),
             }
         }
-        input.parse::<Token![>]>()?;
-        path.push('>');
-        return Ok(path);
+        let rangle: Token![>] = input.parse()?;
+
+        return Ok(Include {
+            path,
+            kind: IncludeKind::Bracketed,
+            begin_span: langle.span,
+            end_span: rangle.span,
+        });
     }
 
     Err(input.error("expected \"quoted/path/to\" or <bracketed/path/to>"))
 }
 
-fn parse_type(ty: &RustType) -> Result<Type> {
+fn parse_type(ty: &RustType, namespace: &Namespace) -> Result<Type> {
     match ty {
-        RustType::Reference(ty) => parse_type_reference(ty),
-        RustType::Path(ty) => parse_type_path(ty),
-        RustType::Slice(ty) => parse_type_slice(ty),
-        RustType::BareFn(ty) => parse_type_fn(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::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) -> Result<Type> {
-    let inner = parse_type(&ty.elem)?;
+fn parse_type_reference(ty: &TypeReference, namespace: &Namespace) -> Result<Type> {
+    let inner = parse_type(&ty.elem, namespace)?;
     let which = match &inner {
-        Type::Ident(ident) if ident == "str" => {
+        Type::Ident(ident) if ident.rust == "str" => {
             if ty.mutability.is_some() {
                 return Err(Error::new_spanned(ty, "unsupported type"));
             } else {
@@ -443,7 +600,7 @@
             }
         }
         Type::Slice(slice) => match &slice.inner {
-            Type::Ident(ident) if ident == U8 && ty.mutability.is_none() => Type::SliceRefU8,
+            Type::Ident(ident) if ident.rust == U8 && ty.mutability.is_none() => Type::SliceRefU8,
             _ => Type::Ref,
         },
         _ => Type::Ref,
@@ -456,17 +613,17 @@
     })))
 }
 
-fn parse_type_path(ty: &TypePath) -> Result<Type> {
+fn parse_type_path(ty: &TypePath, namespace: &Namespace) -> 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(ident)),
+            PathArguments::None => return Ok(Type::Ident(ResolvableName::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)?;
+                        let inner = parse_type(arg, namespace)?;
                         return Ok(Type::UniquePtr(Box::new(Ty1 {
                             name: ident,
                             langle: generic.lt_token,
@@ -476,7 +633,7 @@
                     }
                 } else if ident == "CxxVector" && generic.args.len() == 1 {
                     if let GenericArgument::Type(arg) = &generic.args[0] {
-                        let inner = parse_type(arg)?;
+                        let inner = parse_type(arg, namespace)?;
                         return Ok(Type::CxxVector(Box::new(Ty1 {
                             name: ident,
                             langle: generic.lt_token,
@@ -486,7 +643,7 @@
                     }
                 } else if ident == "Box" && generic.args.len() == 1 {
                     if let GenericArgument::Type(arg) = &generic.args[0] {
-                        let inner = parse_type(arg)?;
+                        let inner = parse_type(arg, namespace)?;
                         return Ok(Type::RustBox(Box::new(Ty1 {
                             name: ident,
                             langle: generic.lt_token,
@@ -496,7 +653,7 @@
                     }
                 } else if ident == "Vec" && generic.args.len() == 1 {
                     if let GenericArgument::Type(arg) = &generic.args[0] {
-                        let inner = parse_type(arg)?;
+                        let inner = parse_type(arg, namespace)?;
                         return Ok(Type::RustVec(Box::new(Ty1 {
                             name: ident,
                             langle: generic.lt_token,
@@ -512,15 +669,15 @@
     Err(Error::new_spanned(ty, "unsupported type"))
 }
 
-fn parse_type_slice(ty: &TypeSlice) -> Result<Type> {
-    let inner = parse_type(&ty.elem)?;
+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,
         inner,
     })))
 }
 
-fn parse_type_fn(ty: &TypeBareFn) -> Result<Type> {
+fn parse_type_fn(ty: &TypeBareFn, namespace: &Namespace) -> Result<Type> {
     if ty.lifetimes.is_some() {
         return Err(Error::new_spanned(
             ty,
@@ -538,7 +695,7 @@
         .iter()
         .enumerate()
         .map(|(i, arg)| {
-            let ty = parse_type(&arg.ty)?;
+            let ty = parse_type(&arg.ty, namespace)?;
             let ident = match &arg.name {
                 Some(ident) => ident.0.clone(),
                 None => format_ident!("_{}", i),
@@ -547,9 +704,10 @@
         })
         .collect::<Result<_>>()?;
     let mut throws_tokens = None;
-    let ret = parse_return_type(&ty.output, &mut throws_tokens)?;
+    let ret = parse_return_type(&ty.output, &mut throws_tokens, namespace)?;
     let throws = throws_tokens.is_some();
     Ok(Type::Fn(Box::new(Signature {
+        unsafety: ty.unsafety,
         fn_token: ty.fn_token,
         receiver: None,
         args,
@@ -563,6 +721,7 @@
 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),
@@ -584,7 +743,7 @@
             }
         }
     }
-    match parse_type(ret)? {
+    match parse_type(ret, namespace)? {
         Type::Void(_) => Ok(None),
         ty => Ok(Some(ty)),
     }
diff --git a/syntax/qualified.rs b/syntax/qualified.rs
new file mode 100644
index 0000000..5eefb8d
--- /dev/null
+++ b/syntax/qualified.rs
@@ -0,0 +1,36 @@
+use syn::ext::IdentExt;
+use syn::parse::{ParseStream, Result};
+use syn::{Ident, LitStr, Token};
+
+pub struct QualifiedName {
+    pub segments: Vec<Ident>,
+}
+
+impl QualifiedName {
+    pub fn parse_unquoted(input: ParseStream) -> Result<Self> {
+        let mut segments = Vec::new();
+        let mut trailing_punct = true;
+        input.parse::<Option<Token![::]>>()?;
+        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() {
+            return Err(input.error("expected path"));
+        } else if trailing_punct {
+            return Err(input.error("expected path segment"));
+        }
+        Ok(QualifiedName { segments })
+    }
+
+    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)
+        } else {
+            Self::parse_unquoted(input)
+        }
+    }
+}
diff --git a/syntax/set.rs b/syntax/set.rs
index 688d1c0..6508f55 100644
--- a/syntax/set.rs
+++ b/syntax/set.rs
@@ -1,41 +1,100 @@
-use std::collections::HashSet;
-use std::hash::Hash;
+use std::fmt::{self, Debug};
 use std::slice;
 
-pub struct OrderedSet<'a, T> {
-    set: HashSet<&'a T>,
-    vec: Vec<&'a T>,
-}
+pub use self::ordered::OrderedSet;
+pub use self::unordered::UnorderedSet;
 
-impl<'a, T> OrderedSet<'a, T>
-where
-    T: Hash + Eq,
-{
-    pub fn new() -> Self {
-        OrderedSet {
-            set: HashSet::new(),
-            vec: Vec::new(),
+mod ordered {
+    use super::{Iter, UnorderedSet};
+    use std::borrow::Borrow;
+    use std::hash::Hash;
+
+    pub struct OrderedSet<T> {
+        set: UnorderedSet<T>,
+        vec: Vec<T>,
+    }
+
+    impl<'a, T> OrderedSet<&'a T>
+    where
+        T: Hash + Eq,
+    {
+        pub fn new() -> Self {
+            OrderedSet {
+                set: UnorderedSet::new(),
+                vec: Vec::new(),
+            }
+        }
+
+        pub fn insert(&mut self, value: &'a T) -> bool {
+            let new = self.set.insert(value);
+            if new {
+                self.vec.push(value);
+            }
+            new
+        }
+
+        pub fn contains<Q>(&self, value: &Q) -> bool
+        where
+            &'a T: Borrow<Q>,
+            Q: ?Sized + Hash + Eq,
+        {
+            self.set.contains(value)
+        }
+
+        pub fn get<Q>(&self, value: &Q) -> Option<&'a T>
+        where
+            &'a T: Borrow<Q>,
+            Q: ?Sized + Hash + Eq,
+        {
+            self.set.get(value).copied()
         }
     }
 
-    pub fn insert(&mut self, value: &'a T) -> bool {
-        let new = self.set.insert(value);
-        if new {
-            self.vec.push(value);
+    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())
         }
-        new
-    }
-
-    pub fn contains(&self, value: &T) -> bool {
-        self.set.contains(value)
     }
 }
 
-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())
+mod unordered {
+    use std::borrow::Borrow;
+    use std::collections::HashSet;
+    use std::hash::Hash;
+
+    // Wrapper prohibits accidentally introducing iteration over the set, which
+    // could lead to nondeterministic generated code.
+    pub struct UnorderedSet<T>(HashSet<T>);
+
+    impl<T> UnorderedSet<T>
+    where
+        T: Hash + Eq,
+    {
+        pub fn new() -> Self {
+            UnorderedSet(HashSet::new())
+        }
+
+        pub fn insert(&mut self, value: T) -> bool {
+            self.0.insert(value)
+        }
+
+        pub fn contains<Q>(&self, value: &Q) -> bool
+        where
+            T: Borrow<Q>,
+            Q: ?Sized + Hash + Eq,
+        {
+            self.0.contains(value)
+        }
+
+        pub fn get<Q>(&self, value: &Q) -> Option<&T>
+        where
+            T: Borrow<Q>,
+            Q: ?Sized + Hash + Eq,
+        {
+            self.0.get(value)
+        }
     }
 }
 
@@ -47,3 +106,12 @@
         self.0.next().copied()
     }
 }
+
+impl<'a, T> Debug for OrderedSet<&'a T>
+where
+    T: Debug,
+{
+    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+        formatter.debug_set().entries(self).finish()
+    }
+}
diff --git a/syntax/symbol.rs b/syntax/symbol.rs
index 066d238..253f57d 100644
--- a/syntax/symbol.rs
+++ b/syntax/symbol.rs
@@ -1,10 +1,11 @@
 use crate::syntax::namespace::Namespace;
+use crate::syntax::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: cxxbridge03$string$new
+// For example: cxxbridge05$string$new
 pub struct Symbol(String);
 
 impl Display for Symbol {
@@ -19,12 +20,6 @@
     }
 }
 
-impl From<&Ident> for Symbol {
-    fn from(ident: &Ident) -> Self {
-        Symbol(ident.to_string())
-    }
-}
-
 impl Symbol {
     fn push(&mut self, segment: &dyn Display) {
         let len_before = self.0.len();
@@ -34,18 +29,47 @@
         self.0.write_fmt(format_args!("{}", segment)).unwrap();
         assert!(self.0.len() > len_before);
     }
+
+    pub fn from_idents<'a, T: Iterator<Item = &'a Ident>>(it: T) -> Self {
+        let mut symbol = Symbol(String::new());
+        for segment in it {
+            segment.write(&mut symbol);
+        }
+        assert!(!symbol.0.is_empty());
+        symbol
+    }
+
+    /// For example, for taking a symbol and then making a new symbol
+    /// for a vec of that symbol.
+    pub fn prefix_with(&self, prefix: &str) -> Symbol {
+        Symbol(format!("{}{}", prefix, self.to_string()))
+    }
 }
 
-pub trait Segment: Display {
+pub trait Segment {
+    fn write(&self, symbol: &mut Symbol);
+}
+
+impl Segment for str {
     fn write(&self, symbol: &mut Symbol) {
         symbol.push(&self);
     }
 }
-
-impl Segment for str {}
-impl Segment for usize {}
-impl Segment for Ident {}
-impl Segment for Symbol {}
+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);
+    }
+}
 
 impl Segment for Namespace {
     fn write(&self, symbol: &mut Symbol) {
@@ -55,9 +79,16 @@
     }
 }
 
+impl Segment for Pair {
+    fn write(&self, symbol: &mut Symbol) {
+        self.namespace.write(symbol);
+        self.cxx.write(symbol);
+    }
+}
+
 impl<T> Segment for &'_ T
 where
-    T: ?Sized + Segment,
+    T: ?Sized + Segment + Display,
 {
     fn write(&self, symbol: &mut Symbol) {
         (**self).write(symbol);
diff --git a/syntax/tokens.rs b/syntax/tokens.rs
index 4ed264a..500ea0b 100644
--- a/syntax/tokens.rs
+++ b/syntax/tokens.rs
@@ -1,7 +1,7 @@
 use crate::syntax::atom::Atom::*;
 use crate::syntax::{
-    Atom, Derive, Enum, ExternFn, ExternType, Receiver, Ref, Signature, Slice, Struct, Ty1, Type,
-    TypeAlias, Var,
+    Atom, Derive, Enum, ExternFn, ExternType, Impl, Receiver, Ref, ResolvableName, Signature,
+    Slice, Struct, Ty1, Type, TypeAlias, Var,
 };
 use proc_macro2::{Ident, Span, TokenStream};
 use quote::{quote_spanned, ToTokens};
@@ -11,11 +11,11 @@
     fn to_tokens(&self, tokens: &mut TokenStream) {
         match self {
             Type::Ident(ident) => {
-                if ident == CxxString {
-                    let span = ident.span();
+                if ident.rust == CxxString {
+                    let span = ident.rust.span();
                     tokens.extend(quote_spanned!(span=> ::cxx::));
                 }
-                ident.to_tokens(tokens);
+                ident.rust.to_tokens(tokens);
             }
             Type::RustBox(ty) | Type::UniquePtr(ty) | Type::CxxVector(ty) | Type::RustVec(ty) => {
                 ty.to_tokens(tokens)
@@ -85,7 +85,7 @@
     fn to_tokens(&self, tokens: &mut TokenStream) {
         // Notional token range for error reporting purposes.
         self.type_token.to_tokens(tokens);
-        self.ident.to_tokens(tokens);
+        self.name.rust.to_tokens(tokens);
     }
 }
 
@@ -93,7 +93,7 @@
     fn to_tokens(&self, tokens: &mut TokenStream) {
         // Notional token range for error reporting purposes.
         self.type_token.to_tokens(tokens);
-        self.ident.to_tokens(tokens);
+        self.name.rust.to_tokens(tokens);
     }
 }
 
@@ -101,7 +101,7 @@
     fn to_tokens(&self, tokens: &mut TokenStream) {
         // Notional token range for error reporting purposes.
         self.struct_token.to_tokens(tokens);
-        self.ident.to_tokens(tokens);
+        self.name.rust.to_tokens(tokens);
     }
 }
 
@@ -109,7 +109,7 @@
     fn to_tokens(&self, tokens: &mut TokenStream) {
         // Notional token range for error reporting purposes.
         self.enum_token.to_tokens(tokens);
-        self.ident.to_tokens(tokens);
+        self.name.rust.to_tokens(tokens);
     }
 }
 
@@ -121,6 +121,14 @@
     }
 }
 
+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| {});
+    }
+}
+
 impl ToTokens for Signature {
     fn to_tokens(&self, tokens: &mut TokenStream) {
         self.fn_token.to_tokens(tokens);
@@ -141,6 +149,12 @@
     }
 }
 
+impl ToTokens for ResolvableName {
+    fn to_tokens(&self, tokens: &mut TokenStream) {
+        self.rust.to_tokens(tokens);
+    }
+}
+
 pub struct ReceiverType<'a>(&'a Receiver);
 
 impl Receiver {
diff --git a/syntax/toposort.rs b/syntax/toposort.rs
new file mode 100644
index 0000000..876f9ad
--- /dev/null
+++ b/syntax/toposort.rs
@@ -0,0 +1,51 @@
+use crate::syntax::report::Errors;
+use crate::syntax::{Api, Struct, Type, Types};
+use std::collections::btree_map::{BTreeMap as Map, Entry};
+
+enum Mark {
+    Visiting,
+    Visited,
+}
+
+pub fn sort<'a>(cx: &mut Errors, apis: &'a [Api], types: &Types<'a>) -> Vec<&'a Struct> {
+    let mut sorted = Vec::new();
+    let ref mut marks = Map::new();
+    for api in apis {
+        if let Api::Struct(strct) = api {
+            let _ = visit(cx, strct, &mut sorted, marks, types);
+        }
+    }
+    sorted
+}
+
+fn visit<'a>(
+    cx: &mut Errors,
+    strct: &'a Struct,
+    sorted: &mut Vec<&'a Struct>,
+    marks: &mut Map<*const Struct, Mark>,
+    types: &Types<'a>,
+) -> Result<(), ()> {
+    match marks.entry(strct) {
+        Entry::Occupied(entry) => match entry.get() {
+            Mark::Visiting => return Err(()), // not a DAG
+            Mark::Visited => return Ok(()),
+        },
+        Entry::Vacant(entry) => {
+            entry.insert(Mark::Visiting);
+        }
+    }
+    let mut result = Ok(());
+    for field in &strct.fields {
+        if let Type::Ident(ident) = &field.ty {
+            if let Some(inner) = types.structs.get(&ident.rust) {
+                if visit(cx, inner, sorted, marks, types).is_err() {
+                    cx.error(field, "unsupported cyclic data structure");
+                    result = Err(());
+                }
+            }
+        }
+    }
+    marks.insert(strct, Mark::Visited);
+    sorted.push(strct);
+    result
+}
diff --git a/syntax/types.rs b/syntax/types.rs
index ba9bc78..90a8221 100644
--- a/syntax/types.rs
+++ b/syntax/types.rs
@@ -1,18 +1,28 @@
 use crate::syntax::atom::Atom::{self, *};
+use crate::syntax::improper::ImproperCtype;
 use crate::syntax::report::Errors;
-use crate::syntax::set::OrderedSet as Set;
-use crate::syntax::{Api, Derive, Enum, Struct, Type, TypeAlias};
+use crate::syntax::set::{OrderedSet as Set, UnorderedSet};
+use crate::syntax::{
+    toposort, Api, Derive, Enum, ExternFn, ExternType, Impl, Pair, ResolvableName, Struct, Type,
+    TypeAlias,
+};
 use proc_macro2::Ident;
 use quote::ToTokens;
-use std::collections::{BTreeMap as Map, HashSet as UnorderedSet};
+use std::collections::BTreeMap as Map;
 
 pub struct Types<'a> {
-    pub all: Set<'a, Type>,
-    pub structs: Map<Ident, &'a Struct>,
-    pub enums: Map<Ident, &'a Enum>,
-    pub cxx: Set<'a, Ident>,
-    pub rust: Set<'a, Ident>,
-    pub aliases: Map<Ident, &'a TypeAlias>,
+    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 struct_improper_ctypes: UnorderedSet<&'a Ident>,
+    pub toposorted_structs: Vec<&'a Struct>,
 }
 
 impl<'a> Types<'a> {
@@ -23,8 +33,13 @@
         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 struct_improper_ctypes = UnorderedSet::new();
+        let toposorted_structs = Vec::new();
 
-        fn visit<'a>(all: &mut Set<'a, Type>, ty: &'a Type) {
+        fn visit<'a>(all: &mut Set<&'a Type>, ty: &'a Type) {
             all.insert(ty);
             match ty {
                 Type::Ident(_) | Type::Str(_) | Type::Void(_) | Type::SliceRefU8(_) => {}
@@ -45,51 +60,84 @@
             }
         }
 
+        let mut add_resolution = |pair: &'a Pair| {
+            resolutions.insert(&pair.rust, pair);
+        };
+
         let mut type_names = UnorderedSet::new();
         let mut function_names = UnorderedSet::new();
         for api in apis {
+            // The same identifier is permitted to be declared as both a shared
+            // enum and extern C++ type, or shared struct and extern C++ type.
+            // That indicates to not emit the C++ enum/struct definition because
+            // it's defined by the included headers already.
+            //
+            // All other cases of duplicate identifiers are reported as an error.
             match api {
                 Api::Include(_) => {}
                 Api::Struct(strct) => {
-                    let ident = &strct.ident;
-                    if type_names.insert(ident) {
-                        structs.insert(ident.clone(), strct);
-                    } else {
+                    let ident = &strct.name.rust;
+                    if !type_names.insert(ident)
+                        && (!cxx.contains(ident)
+                            || structs.contains_key(ident)
+                            || enums.contains_key(ident))
+                    {
+                        // If already declared as a struct or enum, or if
+                        // colliding with something other than an extern C++
+                        // type, then error.
                         duplicate_name(cx, strct, ident);
                     }
+                    structs.insert(&strct.name.rust, strct);
                     for field in &strct.fields {
                         visit(&mut all, &field.ty);
                     }
+                    add_resolution(&strct.name);
                 }
                 Api::Enum(enm) => {
-                    let ident = &enm.ident;
-                    // We allow declaring the same type as a shared enum and as a Cxxtype, as this
-                    // means not to emit the C++ enum definition.
-                    if !type_names.insert(ident) && !cxx.contains(ident) {
+                    let ident = &enm.name.rust;
+                    if !type_names.insert(ident)
+                        && (!cxx.contains(ident)
+                            || structs.contains_key(ident)
+                            || enums.contains_key(ident))
+                    {
+                        // If already declared as a struct or enum, or if
+                        // colliding with something other than an extern C++
+                        // type, then error.
                         duplicate_name(cx, enm, ident);
                     }
-                    enums.insert(ident.clone(), enm);
+                    enums.insert(ident, enm);
+                    add_resolution(&enm.name);
                 }
                 Api::CxxType(ety) => {
-                    let ident = &ety.ident;
-                    // We allow declaring the same type as a shared enum and as a Cxxtype, as this
-                    // means not to emit the C++ enum definition.
-                    if !type_names.insert(ident) && !enums.contains_key(ident) {
+                    let ident = &ety.name.rust;
+                    if !type_names.insert(ident)
+                        && (cxx.contains(ident)
+                            || !structs.contains_key(ident) && !enums.contains_key(ident))
+                    {
+                        // If already declared as an extern C++ type, or if
+                        // colliding with something which is neither struct nor
+                        // enum, then error.
                         duplicate_name(cx, ety, ident);
                     }
                     cxx.insert(ident);
+                    if !ety.trusted {
+                        untrusted.insert(ident, ety);
+                    }
+                    add_resolution(&ety.name);
                 }
                 Api::RustType(ety) => {
-                    let ident = &ety.ident;
+                    let ident = &ety.name.rust;
                     if !type_names.insert(ident) {
                         duplicate_name(cx, ety, ident);
                     }
                     rust.insert(ident);
+                    add_resolution(&ety.name);
                 }
                 Api::CxxFunction(efn) | Api::RustFunction(efn) => {
-                    let ident = &efn.ident;
-                    if !function_names.insert((&efn.receiver, ident)) {
-                        duplicate_name(cx, efn, ident);
+                    // Note: duplication of the C++ name is fine because C++ has
+                    // function overloading.
+                    if !function_names.insert((&efn.receiver, &efn.name.rust)) {
+                        duplicate_name(cx, efn, &efn.name.rust);
                     }
                     for arg in &efn.args {
                         visit(&mut all, &arg.ty);
@@ -99,33 +147,106 @@
                     }
                 }
                 Api::TypeAlias(alias) => {
-                    let ident = &alias.ident;
+                    let ident = &alias.name.rust;
                     if !type_names.insert(ident) {
                         duplicate_name(cx, alias, ident);
                     }
                     cxx.insert(ident);
-                    aliases.insert(ident.clone(), alias);
+                    aliases.insert(ident, alias);
+                    add_resolution(&alias.name);
+                }
+                Api::Impl(imp) => {
+                    visit(&mut all, &imp.ty);
+                    explicit_impls.insert(imp);
                 }
             }
         }
 
-        Types {
+        // 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 mut types = Types {
             all,
             structs,
             enums,
             cxx,
             rust,
             aliases,
+            untrusted,
+            required_trivial,
+            explicit_impls,
+            resolutions,
+            struct_improper_ctypes,
+            toposorted_structs,
+        };
+
+        types.toposorted_structs = toposort::sort(cx, apis, &types);
+
+        let mut unresolved_structs: Vec<&Ident> = types.structs.keys().copied().collect();
+        let mut new_information = true;
+        while new_information {
+            new_information = false;
+            unresolved_structs.retain(|ident| {
+                let mut retain = false;
+                for var in &types.structs[ident].fields {
+                    if match types.determine_improper_ctype(&var.ty) {
+                        ImproperCtype::Depends(inner) => {
+                            retain = true;
+                            types.struct_improper_ctypes.contains(inner)
+                        }
+                        ImproperCtype::Definite(improper) => improper,
+                    } {
+                        types.struct_improper_ctypes.insert(ident);
+                        new_information = true;
+                        return false;
+                    }
+                }
+                // If all fields definite false, remove from unresolved_structs.
+                retain
+            });
         }
+
+        types
     }
 
     pub fn needs_indirect_abi(&self, ty: &Type) -> bool {
         match ty {
             Type::Ident(ident) => {
-                if let Some(strct) = self.structs.get(ident) {
+                if let Some(strct) = self.structs.get(&ident.rust) {
                     !self.is_pod(strct)
                 } else {
-                    Atom::from(ident) == Some(RustString)
+                    Atom::from(&ident.rust) == Some(RustString)
                 }
             }
             Type::RustVec(_) => true,
@@ -141,6 +262,25 @@
         }
         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
+    // refuses to believe that C could know how to supply us with a pointer to a
+    // Rust String, even though C could easily have obtained that pointer
+    // legitimately from a Rust call.
+    pub fn is_considered_improper_ctype(&self, ty: &Type) -> bool {
+        match self.determine_improper_ctype(ty) {
+            ImproperCtype::Definite(improper) => improper,
+            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> {
@@ -151,6 +291,13 @@
     }
 }
 
+#[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/tests/BUCK b/tests/BUCK
index 41781c8..5bbe500 100644
--- a/tests/BUCK
+++ b/tests/BUCK
@@ -1,12 +1,18 @@
+load("//tools/buck:rust_cxx_bridge.bzl", "rust_cxx_bridge")
+
 rust_test(
     name = "test",
     srcs = ["test.rs"],
-    deps = [":ffi"],
+    deps = [
+        ":ffi",
+        "//:cxx",
+    ],
 )
 
 rust_library(
     name = "ffi",
     srcs = [
+        "ffi/extra.rs",
         "ffi/lib.rs",
         "ffi/module.rs",
     ],
@@ -21,33 +27,28 @@
     name = "impl",
     srcs = [
         "ffi/tests.cc",
-        ":gen-lib-source",
-        ":gen-module-source",
+        ":bridge/source",
+        ":extra/source",
+        ":module/source",
     ],
     headers = {
-        "ffi/lib.rs.h": ":gen-lib-header",
+        "ffi/lib.rs.h": ":bridge/header",
         "ffi/tests.h": "ffi/tests.h",
     },
     deps = ["//:core"],
 )
 
-genrule(
-    name = "gen-lib-header",
-    srcs = ["ffi/lib.rs"],
-    cmd = "$(exe //:codegen) --header ${SRCS} > ${OUT}",
-    out = "lib.rs.h",
+rust_cxx_bridge(
+    name = "bridge",
+    src = "ffi/lib.rs",
 )
 
-genrule(
-    name = "gen-lib-source",
-    srcs = ["ffi/lib.rs"],
-    cmd = "$(exe //:codegen) ${SRCS} > ${OUT}",
-    out = "lib.rs.cc",
+rust_cxx_bridge(
+    name = "extra",
+    src = "ffi/extra.rs",
 )
 
-genrule(
-    name = "gen-module-source",
-    srcs = ["ffi/module.rs"],
-    cmd = "$(exe //:codegen) ${SRCS} > ${OUT}",
-    out = "module.rs.cc",
+rust_cxx_bridge(
+    name = "module",
+    src = "ffi/module.rs",
 )
diff --git a/tests/BUILD b/tests/BUILD
index e1f1637..57ffab9 100644
--- a/tests/BUILD
+++ b/tests/BUILD
@@ -1,14 +1,21 @@
+load("@rules_cc//cc:defs.bzl", "cc_library")
 load("//tools/bazel:rust.bzl", "rust_library", "rust_test")
+load("//tools/bazel:rust_cxx_bridge.bzl", "rust_cxx_bridge")
 
 rust_test(
     name = "test",
+    size = "small",
     srcs = ["test.rs"],
-    deps = [":cxx_test_suite"],
+    deps = [
+        ":cxx_test_suite",
+        "//:cxx",
+    ],
 )
 
 rust_library(
     name = "cxx_test_suite",
     srcs = [
+        "ffi/extra.rs",
         "ffi/lib.rs",
         "ffi/module.rs",
     ],
@@ -22,42 +29,31 @@
     name = "impl",
     srcs = [
         "ffi/tests.cc",
-        ":gen-lib-source",
-        ":gen-module-source",
+        ":bridge/source",
+        ":extra/source",
+        ":module/source",
     ],
     hdrs = ["ffi/tests.h"],
     deps = [
-        ":lib-include",
+        ":bridge/include",
         "//:core",
     ],
 )
 
-genrule(
-    name = "gen-lib-header",
-    srcs = ["ffi/lib.rs"],
-    outs = ["lib.rs.h"],
-    cmd = "$(location //:codegen) --header $< > $@",
-    tools = ["//:codegen"],
+rust_cxx_bridge(
+    name = "bridge",
+    src = "ffi/lib.rs",
+    deps = [":impl"],
 )
 
-genrule(
-    name = "gen-lib-source",
-    srcs = ["ffi/lib.rs"],
-    outs = ["lib.rs.cc"],
-    cmd = "$(location //:codegen) $< > $@",
-    tools = ["//:codegen"],
+rust_cxx_bridge(
+    name = "extra",
+    src = "ffi/extra.rs",
+    deps = [":impl"],
 )
 
-cc_library(
-    name = "lib-include",
-    hdrs = [":gen-lib-header"],
-    include_prefix = "tests/ffi",
-)
-
-genrule(
-    name = "gen-module-source",
-    srcs = ["ffi/module.rs"],
-    outs = ["module.rs.cc"],
-    cmd = "$(location //:codegen) $< > $@",
-    tools = ["//:codegen"],
+rust_cxx_bridge(
+    name = "module",
+    src = "ffi/module.rs",
+    deps = [":impl"],
 )
diff --git a/tests/compiletest.rs b/tests/compiletest.rs
index f9aea23..d2b516f 100644
--- a/tests/compiletest.rs
+++ b/tests/compiletest.rs
@@ -1,4 +1,5 @@
 #[rustversion::attr(not(nightly), ignore)]
+#[cfg_attr(skip_ui_tests, ignore)]
 #[test]
 fn ui() {
     let t = trybuild::TestCases::new();
diff --git a/tests/cxx_gen.rs b/tests/cxx_gen.rs
new file mode 100644
index 0000000..06d7bc7
--- /dev/null
+++ b/tests/cxx_gen.rs
@@ -0,0 +1,34 @@
+#![allow(clippy::field_reassign_with_default)]
+
+use cxx_gen::{generate_header_and_cc, Opt};
+use std::str;
+
+const BRIDGE0: &str = r#"
+    #[cxx::bridge]
+    mod ffi {
+        extern "C" {
+            pub fn do_cpp_thing(foo: &str);
+        }
+    }
+"#;
+
+#[test]
+fn test_extern_c_function() {
+    let opt = Opt::default();
+    let source = BRIDGE0.parse().unwrap();
+    let generated = generate_header_and_cc(source, &opt).unwrap();
+    let output = str::from_utf8(&generated.implementation).unwrap();
+    // To avoid continual breakage we won't test every byte.
+    // Let's look for the major features.
+    assert!(output.contains("void cxxbridge05$do_cpp_thing(::rust::repr::PtrLen foo)"));
+}
+
+#[test]
+fn test_impl_annotation() {
+    let mut opt = Opt::default();
+    opt.cxx_impl_annotations = Some("ANNOTATION".to_owned());
+    let source = BRIDGE0.parse().unwrap();
+    let generated = generate_header_and_cc(source, &opt).unwrap();
+    let output = str::from_utf8(&generated.implementation).unwrap();
+    assert!(output.contains("ANNOTATION void cxxbridge05$do_cpp_thing(::rust::repr::PtrLen foo)"));
+}
diff --git a/tests/ffi/Cargo.toml b/tests/ffi/Cargo.toml
index c2d8227..e62d090 100644
--- a/tests/ffi/Cargo.toml
+++ b/tests/ffi/Cargo.toml
@@ -12,3 +12,4 @@
 
 [build-dependencies]
 cxx-build = { path = "../../gen/build" }
+cxxbridge-flags = { path = "../../flags" }
diff --git a/tests/ffi/build.rs b/tests/ffi/build.rs
index f6fa59e..4b2cbdf 100644
--- a/tests/ffi/build.rs
+++ b/tests/ffi/build.rs
@@ -1,11 +1,14 @@
+use cxx_build::CFG;
+
 fn main() {
     if cfg!(trybuild) {
         return;
     }
 
-    let sources = vec!["lib.rs", "module.rs"];
+    CFG.include_prefix = "tests/ffi";
+    let sources = vec!["lib.rs", "extra.rs", "module.rs"];
     cxx_build::bridges(sources)
         .file("tests.cc")
-        .flag_if_supported("-std=c++11")
+        .flag_if_supported(cxxbridge_flags::STD)
         .compile("cxx-test-suite");
 }
diff --git a/tests/ffi/extra.rs b/tests/ffi/extra.rs
new file mode 100644
index 0000000..cd76a7d
--- /dev/null
+++ b/tests/ffi/extra.rs
@@ -0,0 +1,63 @@
+// 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 84a746b..4c7cbe4 100644
--- a/tests/ffi/lib.rs
+++ b/tests/ffi/lib.rs
@@ -1,26 +1,121 @@
 #![allow(
     clippy::boxed_local,
+    clippy::just_underscores_and_digits,
     clippy::ptr_arg,
     clippy::trivially_copy_pass_by_ref
 )]
 
+pub mod extra;
 pub mod module;
 
-use cxx::{CxxString, UniquePtr};
+use cxx::{CxxString, CxxVector, UniquePtr};
 use std::fmt::{self, Display};
 
-#[cxx::bridge(namespace = tests)]
+mod other {
+    use cxx::kind::{Opaque, Trivial};
+    use cxx::{type_id, CxxString, ExternType};
+
+    #[repr(C)]
+    pub struct D {
+        pub d: u64,
+    }
+
+    #[repr(C)]
+    pub struct E {
+        e: u64,
+        e_str: CxxString,
+    }
+
+    pub mod f {
+        use cxx::kind::Opaque;
+        use cxx::{type_id, CxxString, ExternType};
+
+        #[repr(C)]
+        pub struct F {
+            e: u64,
+            e_str: CxxString,
+        }
+
+        unsafe impl ExternType for F {
+            type Id = type_id!("F::F");
+            type Kind = Opaque;
+        }
+    }
+
+    #[repr(C)]
+    pub struct G {
+        pub g: u64,
+    }
+
+    unsafe impl ExternType for G {
+        type Id = type_id!("G::G");
+        type Kind = Trivial;
+    }
+
+    unsafe impl ExternType for D {
+        type Id = type_id!("tests::D");
+        type Kind = Trivial;
+    }
+
+    unsafe impl ExternType for E {
+        type Id = type_id!("tests::E");
+        type Kind = Opaque;
+    }
+}
+
+#[cxx::bridge(namespace = "tests")]
 pub mod ffi {
+    #[derive(Clone)]
     struct Shared {
         z: 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");
 
@@ -31,20 +126,29 @@
         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);
@@ -57,18 +161,29 @@
         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>);
-        /*
-        // https://github.com/dtolnay/cxx/issues/232
+        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>;
@@ -80,6 +195,7 @@
         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;
@@ -88,6 +204,19 @@
         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" {
@@ -109,11 +238,14 @@
         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;
@@ -128,17 +260,47 @@
         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,
     }
 }
 
@@ -157,6 +319,12 @@
     }
 }
 
+impl ffi::Shared {
+    fn r_method_on_shared(&self) -> String {
+        "2020".to_owned()
+    }
+}
+
 #[derive(Debug)]
 struct Error;
 
@@ -191,6 +359,10 @@
     &shared.z
 }
 
+fn r_return_mut(shared: &mut ffi::Shared) -> &mut usize {
+    &mut shared.z
+}
+
 fn r_return_str(shared: &ffi::Shared) -> &str {
     let _ = shared;
     "2020"
@@ -211,11 +383,20 @@
     Vec::new()
 }
 
+fn r_return_rust_vec_string() -> Vec<String> {
+    Vec::new()
+}
+
 fn r_return_ref_rust_vec(shared: &ffi::Shared) -> &Vec<u8> {
     let _ = shared;
     unimplemented!()
 }
 
+fn r_return_mut_rust_vec(shared: &mut ffi::Shared) -> &mut Vec<u8> {
+    let _ = shared;
+    unimplemented!()
+}
+
 fn r_return_identity(n: usize) -> usize {
     n
 }
@@ -275,14 +456,32 @@
     assert_eq!(s.as_ref().unwrap().to_str().unwrap(), "2020");
 }
 
+fn r_take_ref_vector(v: &CxxVector<u8>) {
+    let slice = v.as_slice();
+    assert_eq!(slice, [20, 2, 0]);
+}
+
+fn r_take_ref_empty_vector(v: &CxxVector<u64>) {
+    assert!(v.as_slice().is_empty());
+    assert!(v.is_empty());
+}
+
 fn r_take_rust_vec(v: Vec<u8>) {
     let _ = v;
 }
 
+fn r_take_rust_vec_string(v: Vec<String>) {
+    let _ = v;
+}
+
 fn r_take_ref_rust_vec(v: &Vec<u8>) {
     let _ = v;
 }
 
+fn r_take_ref_rust_vec_string(v: &Vec<String>) {
+    let _ = v;
+}
+
 fn r_take_enum(e: ffi::Enum) {
     let _ = e;
 }
@@ -295,6 +494,10 @@
     Ok(2020)
 }
 
+fn r_try_return_box() -> Result<Box<R>, Error> {
+    Ok(Box::new(2020))
+}
+
 fn r_fail_return_primitive() -> Result<usize, Error> {
     Err(Error)
 }
@@ -302,3 +505,7 @@
 fn r_return_r2(n: usize) -> Box<R2> {
     Box::new(R2(n))
 }
+
+fn r_aliased_function(x: i32) -> String {
+    x.to_string()
+}
diff --git a/tests/ffi/module.rs b/tests/ffi/module.rs
index 77bae06..899d45b 100644
--- a/tests/ffi/module.rs
+++ b/tests/ffi/module.rs
@@ -1,7 +1,7 @@
 // Rustfmt mangles the extern type alias.
 // https://github.com/rust-lang/rustfmt/issues/4159
 #[rustfmt::skip]
-#[cxx::bridge(namespace = tests)]
+#[cxx::bridge(namespace = "tests")]
 pub mod ffi {
     extern "C" {
         include!("tests/ffi/tests.h");
diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc
index d70d5a0..2305766 100644
--- a/tests/ffi/tests.cc
+++ b/tests/ffi/tests.cc
@@ -1,8 +1,11 @@
 #include "tests/ffi/tests.h"
 #include "tests/ffi/lib.rs.h"
 #include <cstring>
+#include <iterator>
+#include <memory>
 #include <numeric>
 #include <stdexcept>
+#include <string>
 
 extern "C" void cxx_test_suite_set_correct() noexcept;
 extern "C" tests::R *cxx_test_suite_get_box() noexcept;
@@ -32,12 +35,20 @@
 
 size_t C::get_fail() { throw std::runtime_error("unimplemented"); }
 
+size_t Shared::c_method_on_shared() const noexcept { return 2021; }
+
 const std::vector<uint8_t> &C::get_v() const { return this->v; }
 
+std::vector<uint8_t> &C::get_v() { return this->v; }
+
 size_t c_return_primitive() { return 2020; }
 
 Shared c_return_shared() { return Shared{2020}; }
 
+::A::AShared c_return_ns_shared() { return ::A::AShared{2020}; }
+
+::A::B::ABShared c_return_nested_ns_shared() { return ::A::B::ABShared{2020}; }
+
 rust::Box<R> c_return_box() {
   return rust::Box<R>::from_raw(cxx_test_suite_get_box());
 }
@@ -46,8 +57,20 @@
   return std::unique_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"});
+}
+
 const size_t &c_return_ref(const Shared &shared) { return shared.z; }
 
+const size_t &c_return_ns_ref(const ::A::AShared &shared) { return shared.z; }
+
+const size_t &c_return_nested_ns_ref(const ::A::B::ABShared &shared) {
+  return shared.z;
+}
+
+size_t &c_return_mut(Shared &shared) { return shared.z; }
+
 rust::Str c_return_str(const Shared &shared) {
   (void)shared;
   return "2020";
@@ -83,6 +106,11 @@
   return vec;
 }
 
+std::unique_ptr<std::vector<std::string>> c_return_unique_ptr_vector_string() {
+  return std::unique_ptr<std::vector<std::string>>(
+      new std::vector<std::string>());
+}
+
 std::unique_ptr<std::vector<Shared>> c_return_unique_ptr_vector_shared() {
   auto vec = std::unique_ptr<std::vector<Shared>>(new std::vector<Shared>());
   vec->push_back(Shared{1010});
@@ -98,6 +126,8 @@
   return c.get_v();
 }
 
+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");
 }
@@ -107,6 +137,15 @@
   throw std::runtime_error("unimplemented");
 }
 
+rust::Vec<uint8_t> &c_return_mut_rust_vec(C &c) {
+  (void)c;
+  throw std::runtime_error("unimplemented");
+}
+
+rust::Vec<rust::String> c_return_rust_vec_string() {
+  throw std::runtime_error("unimplemented");
+}
+
 size_t c_return_identity(size_t n) { return n; }
 
 size_t c_return_sum(size_t n1, size_t n2) { return n1 + n2; }
@@ -121,6 +160,26 @@
   }
 }
 
+::A::AEnum c_return_ns_enum(uint16_t n) {
+  if (n <= static_cast<uint16_t>(::A::AEnum::AAVal)) {
+    return ::A::AEnum::AAVal;
+  } else if (n <= static_cast<uint16_t>(::A::AEnum::ABVal)) {
+    return ::A::AEnum::ABVal;
+  } else {
+    return ::A::AEnum::ACVal;
+  }
+}
+
+::A::B::ABEnum c_return_nested_ns_enum(uint16_t n) {
+  if (n <= static_cast<uint16_t>(::A::B::ABEnum::ABAVal)) {
+    return ::A::B::ABEnum::ABAVal;
+  } else if (n <= static_cast<uint16_t>(::A::B::ABEnum::ABBVal)) {
+    return ::A::B::ABEnum::ABBVal;
+  } else {
+    return ::A::B::ABEnum::ABCVal;
+  }
+}
+
 void c_take_primitive(size_t n) {
   if (n == 2020) {
     cxx_test_suite_set_correct();
@@ -133,6 +192,18 @@
   }
 }
 
+void c_take_ns_shared(::A::AShared shared) {
+  if (shared.z == 2020) {
+    cxx_test_suite_set_correct();
+  }
+}
+
+void c_take_nested_ns_shared(::A::B::ABShared shared) {
+  if (shared.z == 2020) {
+    cxx_test_suite_set_correct();
+  }
+}
+
 void c_take_box(rust::Box<R> r) {
   if (cxx_test_suite_r_is_correct(&*r)) {
     cxx_test_suite_set_correct();
@@ -157,6 +228,12 @@
   }
 }
 
+void c_take_ref_ns_c(const ::H::H &h) {
+  if (h.h == "hello") {
+    cxx_test_suite_set_correct();
+  }
+}
+
 void c_take_str(rust::Str s) {
   if (std::string(s) == "2020") {
     cxx_test_suite_set_correct();
@@ -194,6 +271,12 @@
   }
 }
 
+void c_take_unique_ptr_vector_string(
+    std::unique_ptr<std::vector<std::string>> v) {
+  (void)v;
+  cxx_test_suite_set_correct();
+}
+
 void c_take_unique_ptr_vector_shared(std::unique_ptr<std::vector<Shared>> v) {
   if (v->size() == 2) {
     cxx_test_suite_set_correct();
@@ -208,6 +291,17 @@
 
 void c_take_rust_vec(rust::Vec<uint8_t> v) { c_take_ref_rust_vec(v); }
 
+void c_take_rust_vec_index(rust::Vec<uint8_t> v) {
+  try {
+    v.at(100);
+  } catch (const std::out_of_range &ex) {
+    std::string expected = "rust::Vec index out of range";
+    if (ex.what() == expected) {
+      cxx_test_suite_set_correct();
+    }
+  }
+}
+
 void c_take_rust_vec_shared(rust::Vec<Shared> v) {
   uint32_t sum = 0;
   for (auto i : v) {
@@ -218,6 +312,31 @@
   }
 }
 
+void c_take_rust_vec_ns_shared(rust::Vec<::A::AShared> v) {
+  uint32_t sum = 0;
+  for (auto i : v) {
+    sum += i.z;
+  }
+  if (sum == 2021) {
+    cxx_test_suite_set_correct();
+  }
+}
+
+void c_take_rust_vec_nested_ns_shared(rust::Vec<::A::B::ABShared> v) {
+  uint32_t sum = 0;
+  for (auto i : v) {
+    sum += i.z;
+  }
+  if (sum == 2021) {
+    cxx_test_suite_set_correct();
+  }
+}
+
+void c_take_rust_vec_string(rust::Vec<rust::String> v) {
+  (void)v;
+  cxx_test_suite_set_correct();
+}
+
 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
@@ -230,6 +349,21 @@
   }
 }
 
+void c_take_rust_vec_shared_index(rust::Vec<Shared> v) {
+  if (v[0].z == 1010 && v.at(0).z == 1010 && v.front().z == 1010 &&
+      v[1].z == 1011 && v.at(1).z == 1011 && v.back().z == 1011) {
+    cxx_test_suite_set_correct();
+  }
+}
+
+void c_take_rust_vec_shared_push(rust::Vec<Shared> v) {
+  v.push_back(Shared{3});
+  v.emplace_back(Shared{2});
+  if (v[v.size() - 2].z == 3 && v.back().z == 2) {
+    cxx_test_suite_set_correct();
+  }
+}
+
 void c_take_ref_rust_vec(const rust::Vec<uint8_t> &v) {
   uint8_t sum = std::accumulate(v.begin(), v.end(), 0);
   if (sum == 200) {
@@ -237,6 +371,18 @@
   }
 }
 
+void c_take_ref_rust_vec_string(const rust::Vec<rust::String> &v) {
+  (void)v;
+  cxx_test_suite_set_correct();
+}
+
+void c_take_ref_rust_vec_index(const rust::Vec<uint8_t> &v) {
+  if (v[0] == 86 && v.at(0) == 86 && v.front() == 86 && v[1] == 75 &&
+      v.at(1) == 75 && v[3] == 9 && v.at(3) == 9 && v.back() == 9) {
+    cxx_test_suite_set_correct();
+  }
+}
+
 void c_take_ref_rust_vec_copy(const rust::Vec<uint8_t> &v) {
   // The std::copy() will make sure rust::Vec<>::const_iterator satisfies the
   // requirements for std::iterator_traits.
@@ -249,12 +395,16 @@
   }
 }
 
-/*
-// https://github.com/dtolnay/cxx/issues/232
+const SharedString &c_take_ref_shared_string(const SharedString &s) {
+  if (std::string(s.msg) == "2020") {
+    cxx_test_suite_set_correct();
+  }
+  return s;
+}
+
 void c_take_callback(rust::Fn<size_t(rust::String)> callback) {
   callback("2020");
 }
-*/
 
 void c_take_enum(Enum e) {
   if (e == Enum::AVal) {
@@ -262,6 +412,18 @@
   }
 }
 
+void c_take_ns_enum(::A::AEnum e) {
+  if (e == ::A::AEnum::AAVal) {
+    cxx_test_suite_set_correct();
+  }
+}
+
+void c_take_nested_ns_enum(::A::B::ABEnum e) {
+  if (e == ::A::B::ABEnum::ABAVal) {
+    cxx_test_suite_set_correct();
+  }
+}
+
 void c_try_return_void() {}
 
 size_t c_try_return_primitive() { return 2020; }
@@ -286,6 +448,10 @@
   throw std::runtime_error("unimplemented");
 }
 
+rust::Vec<rust::String> c_try_return_rust_vec_string() {
+  throw std::runtime_error("unimplemented");
+}
+
 const rust::Vec<uint8_t> &c_try_return_ref_rust_vec(const C &c) {
   (void)c;
   throw std::runtime_error("unimplemented");
@@ -299,6 +465,120 @@
   return std::unique_ptr<std::string>(new std::string("2020")).release();
 }
 
+rust::String C::cOverloadedMethod(int32_t x) const {
+  return rust::String(std::to_string(x));
+}
+
+rust::String C::cOverloadedMethod(rust::Str x) const {
+  return rust::String(std::string(x));
+}
+
+rust::String cOverloadedFunction(int x) {
+  return rust::String(std::to_string(x));
+}
+
+rust::String cOverloadedFunction(rust::Str x) {
+  return rust::String(std::string(x));
+}
+
+void c_take_trivial_ptr(std::unique_ptr<D> d) {
+  if (d->d == 30) {
+    cxx_test_suite_set_correct();
+  }
+}
+
+void c_take_trivial_ref(const D &d) {
+  if (d.d == 30) {
+    cxx_test_suite_set_correct();
+  }
+}
+
+void c_take_trivial(D d) {
+  if (d.d == 30) {
+    cxx_test_suite_set_correct();
+  }
+}
+
+void c_take_trivial_ns_ptr(std::unique_ptr<::G::G> g) {
+  if (g->g == 30) {
+    cxx_test_suite_set_correct();
+  }
+}
+
+void c_take_trivial_ns_ref(const ::G::G &g) {
+  if (g.g == 30) {
+    cxx_test_suite_set_correct();
+  }
+}
+
+void c_take_trivial_ns(::G::G g) {
+  if (g.g == 30) {
+    cxx_test_suite_set_correct();
+  }
+}
+
+void c_take_opaque_ptr(std::unique_ptr<E> e) {
+  if (e->e == 40) {
+    cxx_test_suite_set_correct();
+  }
+}
+
+void c_take_opaque_ns_ptr(std::unique_ptr<::F::F> f) {
+  if (f->f == 40) {
+    cxx_test_suite_set_correct();
+  }
+}
+
+void c_take_opaque_ref(const E &e) {
+  if (e.e == 40 && e.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();
+  }
+}
+
+std::unique_ptr<D> c_return_trivial_ptr() {
+  auto d = std::unique_ptr<D>(new D());
+  d->d = 30;
+  return d;
+}
+
+D c_return_trivial() {
+  D d;
+  d.d = 30;
+  return d;
+}
+
+std::unique_ptr<::G::G> c_return_trivial_ns_ptr() {
+  auto g = std::unique_ptr<::G::G>(new ::G::G());
+  g->g = 30;
+  return g;
+}
+
+::G::G c_return_trivial_ns() {
+  ::G::G g;
+  g.g = 30;
+  return g;
+}
+
+std::unique_ptr<E> c_return_opaque_ptr() {
+  auto e = std::unique_ptr<E>(new E());
+  e->e = 40;
+  e->e_str = std::string("hello");
+  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;
+  f->f_str = std::string("hello");
+  return f;
+}
+
 extern "C" const char *cxx_run_test() noexcept {
 #define STRINGIFY(x) #x
 #define TOSTRING(x) STRINGIFY(x)
@@ -333,6 +613,11 @@
   r_take_rust_string(rust::String("2020"));
   r_take_unique_ptr_string(
       std::unique_ptr<std::string>(new std::string("2020")));
+  r_take_ref_vector(std::vector<uint8_t>{20, 2, 0});
+  std::vector<uint64_t> empty_vector;
+  r_take_ref_empty_vector(empty_vector);
+  empty_vector.reserve(10);
+  r_take_ref_empty_vector(empty_vector);
   r_take_enum(Enum::AVal);
 
   ASSERT(r_try_return_primitive() == 2020);
@@ -349,9 +634,40 @@
   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");
 
   cxx_test_suite_set_correct();
   return nullptr;
 }
 
 } // namespace tests
+
+namespace other {
+void ns_c_take_trivial(::tests::D d) {
+  if (d.d == 30) {
+    cxx_test_suite_set_correct();
+  }
+}
+
+::tests::D ns_c_return_trivial() {
+  ::tests::D d;
+  d.d = 30;
+  return d;
+}
+
+void ns_c_take_ns_shared(::A::AShared shared) {
+  if (shared.z == 2020) {
+    cxx_test_suite_set_correct();
+  }
+}
+} // namespace other
+
+namespace I {
+uint32_t I::get() const { return a; }
+
+std::unique_ptr<I> ns_c_return_unique_ptr_ns() {
+  return std::unique_ptr<I>(new I());
+}
+} // namespace I
diff --git a/tests/ffi/tests.h b/tests/ffi/tests.h
index 7121cb2..2217c36 100644
--- a/tests/ffi/tests.h
+++ b/tests/ffi/tests.h
@@ -3,10 +3,40 @@
 #include <memory>
 #include <string>
 
+namespace A {
+struct AShared;
+enum class AEnum : uint16_t;
+namespace B {
+struct ABShared;
+enum class ABEnum : uint16_t;
+} // namespace B
+} // namespace A
+
+namespace F {
+struct F {
+  uint64_t f;
+  std::string f_str;
+};
+} // namespace F
+
+namespace G {
+struct G {
+  uint64_t g;
+};
+} // namespace G
+
+namespace H {
+class H {
+public:
+  std::string h;
+};
+} // namespace H
+
 namespace tests {
 
 struct R;
 struct Shared;
+struct SharedString;
 enum class Enum : uint16_t;
 
 class C {
@@ -19,12 +49,24 @@
   size_t set_succeed(size_t n);
   size_t get_fail();
   const std::vector<uint8_t> &get_v() const;
+  std::vector<uint8_t> &get_v();
+  rust::String cOverloadedMethod(int32_t x) const;
+  rust::String cOverloadedMethod(rust::Str x) const;
 
 private:
   size_t n;
   std::vector<uint8_t> v;
 };
 
+struct D {
+  uint64_t d;
+};
+
+struct E {
+  uint64_t e;
+  std::string e_str;
+};
+
 enum COwnedEnum {
   CVal1,
   CVal2,
@@ -32,48 +74,73 @@
 
 size_t c_return_primitive();
 Shared c_return_shared();
+::A::AShared c_return_ns_shared();
+::A::B::ABShared c_return_nested_ns_shared();
 rust::Box<R> c_return_box();
 std::unique_ptr<C> c_return_unique_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::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();
 std::unique_ptr<std::vector<double>> c_return_unique_ptr_vector_f64();
+std::unique_ptr<std::vector<std::string>> c_return_unique_ptr_vector_string();
 std::unique_ptr<std::vector<Shared>> c_return_unique_ptr_vector_shared();
 std::unique_ptr<std::vector<C>> c_return_unique_ptr_vector_opaque();
 const std::vector<uint8_t> &c_return_ref_vector(const C &c);
+std::vector<uint8_t> &c_return_mut_vector(C &c);
 rust::Vec<uint8_t> c_return_rust_vec();
 const rust::Vec<uint8_t> &c_return_ref_rust_vec(const C &c);
+rust::Vec<uint8_t> &c_return_mut_rust_vec(C &c);
+rust::Vec<rust::String> c_return_rust_vec_string();
 size_t c_return_identity(size_t n);
 size_t c_return_sum(size_t n1, size_t n2);
 Enum c_return_enum(uint16_t n);
+::A::AEnum c_return_ns_enum(uint16_t n);
+::A::B::ABEnum c_return_nested_ns_enum(uint16_t n);
 
 void c_take_primitive(size_t n);
 void c_take_shared(Shared shared);
+void c_take_ns_shared(::A::AShared shared);
+void c_take_nested_ns_shared(::A::B::ABShared shared);
 void c_take_box(rust::Box<R> r);
 void c_take_unique_ptr(std::unique_ptr<C> c);
 void c_take_ref_r(const R &r);
 void c_take_ref_c(const C &c);
+void c_take_ref_ns_c(const ::H::H &h);
 void c_take_str(rust::Str s);
 void c_take_sliceu8(rust::Slice<uint8_t> s);
 void c_take_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);
 void c_take_unique_ptr_vector_f64(std::unique_ptr<std::vector<double>> v);
+void c_take_unique_ptr_vector_string(
+    std::unique_ptr<std::vector<std::string>> v);
 void c_take_unique_ptr_vector_shared(std::unique_ptr<std::vector<Shared>> v);
 void c_take_ref_vector(const std::vector<uint8_t> &v);
 void c_take_rust_vec(rust::Vec<uint8_t> v);
+void c_take_rust_vec_index(rust::Vec<uint8_t> v);
 void c_take_rust_vec_shared(rust::Vec<Shared> v);
+void c_take_rust_vec_ns_shared(rust::Vec<::A::AShared> v);
+void c_take_rust_vec_nested_ns_shared(rust::Vec<::A::B::ABShared> v);
+void c_take_rust_vec_string(rust::Vec<rust::String> v);
+void c_take_rust_vec_shared_index(rust::Vec<Shared> v);
+void c_take_rust_vec_shared_push(rust::Vec<Shared> v);
 void c_take_rust_vec_shared_forward_iterator(rust::Vec<Shared> v);
 void c_take_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);
 void c_take_ref_rust_vec_copy(const rust::Vec<uint8_t> &v);
-/*
-// https://github.com/dtolnay/cxx/issues/232
+const SharedString &c_take_ref_shared_string(const SharedString &s);
 void c_take_callback(rust::Fn<size_t(rust::String)> callback);
-*/
 void c_take_enum(Enum e);
+void c_take_ns_enum(::A::AEnum e);
+void c_take_nested_ns_enum(::A::B::ABEnum e);
 
 void c_try_return_void();
 size_t c_try_return_primitive();
@@ -85,6 +152,47 @@
 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);
 
+void c_take_trivial_ptr(std::unique_ptr<D> d);
+void c_take_trivial_ref(const D &d);
+void c_take_trivial(D d);
+
+void c_take_trivial_ns_ptr(std::unique_ptr<::G::G> g);
+void c_take_trivial_ns_ref(const ::G::G &g);
+void c_take_trivial_ns(::G::G g);
+void c_take_opaque_ptr(std::unique_ptr<E> e);
+void c_take_opaque_ns_ptr(std::unique_ptr<::F::F> f);
+void c_take_opaque_ref(const E &e);
+void c_take_opaque_ns_ref(const ::F::F &f);
+std::unique_ptr<D> c_return_trivial_ptr();
+D c_return_trivial();
+std::unique_ptr<::G::G> c_return_trivial_ns_ptr();
+::G::G c_return_trivial_ns();
+std::unique_ptr<E> c_return_opaque_ptr();
+std::unique_ptr<::F::F> c_return_ns_opaque_ptr();
+
+rust::String cOverloadedFunction(int32_t x);
+rust::String cOverloadedFunction(rust::Str x);
+
 } // namespace tests
+
+namespace other {
+void ns_c_take_trivial(::tests::D d);
+::tests::D ns_c_return_trivial();
+void ns_c_take_ns_shared(::A::AShared shared);
+} // namespace other
+
+namespace I {
+class I {
+private:
+  uint32_t a;
+
+public:
+  I() : a(1000) {}
+  uint32_t get() const;
+};
+
+std::unique_ptr<I> ns_c_return_unique_ptr_ns();
+} // namespace I
diff --git a/tests/test.rs b/tests/test.rs
index f2f670c..2bae4d8 100644
--- a/tests/test.rs
+++ b/tests/test.rs
@@ -1,3 +1,6 @@
+#![allow(clippy::assertions_on_constants, clippy::float_cmp, clippy::unit_cmp)]
+
+use cxx_test_suite::extra::ffi2;
 use cxx_test_suite::ffi;
 use std::cell::Cell;
 use std::ffi::CStr;
@@ -22,12 +25,17 @@
 #[test]
 fn test_c_return() {
     let shared = ffi::Shared { z: 2020 };
+    let ns_shared = ffi::AShared { z: 2020 };
+    let nested_ns_shared = ffi::ABShared { z: 2020 };
 
     assert_eq!(2020, ffi::c_return_primitive());
     assert_eq!(2020, ffi::c_return_shared().z);
     assert_eq!(2020, *ffi::c_return_box());
     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!("2020", ffi::c_return_rust_string());
@@ -63,6 +71,14 @@
         enm @ ffi::Enum::CVal => assert_eq!(2021, enm.repr),
         _ => assert!(false),
     }
+    match ffi::c_return_ns_enum(0) {
+        enm @ ffi::AEnum::AAVal => assert_eq!(0, enm.repr),
+        _ => assert!(false),
+    }
+    match ffi::c_return_nested_ns_enum(0) {
+        enm @ ffi::ABEnum::ABAVal => assert_eq!(0, enm.repr),
+        _ => assert!(false),
+    }
 }
 
 #[test]
@@ -84,11 +100,16 @@
 #[test]
 fn test_c_take() {
     let unique_ptr = ffi::c_return_unique_ptr();
+    let unique_ptr_ns = ffi2::c_return_ns_unique_ptr();
 
     check!(ffi::c_take_primitive(2020));
     check!(ffi::c_take_shared(ffi::Shared { z: 2020 }));
+    check!(ffi::c_take_ns_shared(ffi::AShared { z: 2020 }));
+    check!(ffi::ns_c_take_ns_shared(ffi::AShared { z: 2020 }));
+    check!(ffi::c_take_nested_ns_shared(ffi::ABShared { z: 2020 }));
     check!(ffi::c_take_box(Box::new(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"));
@@ -108,21 +129,32 @@
     check!(ffi::c_take_ref_vector(&ffi::c_return_unique_ptr_vector_u8()));
     let test_vec = [86_u8, 75_u8, 30_u8, 9_u8].to_vec();
     check!(ffi::c_take_rust_vec(test_vec.clone()));
-    check!(ffi::c_take_rust_vec_shared(vec![
-        ffi::Shared { z: 1010 },
-        ffi::Shared { z: 1011 }
-    ]));
-    check!(ffi::c_take_rust_vec_shared_forward_iterator(vec![
-        ffi::Shared { z: 1010 },
-        ffi::Shared { z: 1011 }
-    ]));
+    check!(ffi::c_take_rust_vec_index(test_vec.clone()));
+    let shared_test_vec = vec![ffi::Shared { z: 1010 }, ffi::Shared { z: 1011 }];
+    check!(ffi::c_take_rust_vec_shared(shared_test_vec.clone()));
+    check!(ffi::c_take_rust_vec_shared_index(shared_test_vec.clone()));
+    check!(ffi::c_take_rust_vec_shared_push(shared_test_vec.clone()));
+    check!(ffi::c_take_rust_vec_shared_forward_iterator(
+        shared_test_vec,
+    ));
     check!(ffi::c_take_ref_rust_vec(&test_vec));
+    check!(ffi::c_take_ref_rust_vec_index(&test_vec));
     check!(ffi::c_take_ref_rust_vec_copy(&test_vec));
+    check!(ffi::c_take_ref_shared_string(&ffi::SharedString {
+        msg: "2020".to_owned()
+    }));
+    let ns_shared_test_vec = vec![ffi::AShared { z: 1010 }, ffi::AShared { z: 1011 }];
+    check!(ffi::c_take_rust_vec_ns_shared(ns_shared_test_vec));
+    let nested_ns_shared_test_vec = vec![ffi::ABShared { z: 1010 }, ffi::ABShared { z: 1011 }];
+    check!(ffi::c_take_rust_vec_nested_ns_shared(
+        nested_ns_shared_test_vec
+    ));
+
     check!(ffi::c_take_enum(ffi::Enum::AVal));
+    check!(ffi::c_take_ns_enum(ffi::AEnum::AAVal));
+    check!(ffi::c_take_nested_ns_enum(ffi::ABEnum::ABAVal));
 }
 
-/*
-// https://github.com/dtolnay/cxx/issues/232
 #[test]
 fn test_c_callback() {
     fn callback(s: String) -> usize {
@@ -134,7 +166,6 @@
 
     check!(ffi::c_take_callback(callback));
 }
-*/
 
 #[test]
 fn test_c_call_r() {
@@ -163,6 +194,15 @@
     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, ffi::Shared { z: 0 }.c_method_on_shared());
+}
+
+#[test]
+fn test_c_ns_method_calls() {
+    let unique_ptr = ffi2::ns_c_return_unique_ptr_ns();
+
+    let old_value = unique_ptr.get();
+    assert_eq!(1000, old_value);
 }
 
 #[test]
@@ -181,3 +221,42 @@
 unsafe extern "C" fn cxx_test_suite_r_is_correct(r: *const cxx_test_suite::R) -> bool {
     *r == 2020
 }
+
+#[test]
+fn test_rust_name_attribute() {
+    assert_eq!("2020", ffi::i32_overloaded_function(2020));
+    assert_eq!("2020", ffi::str_overloaded_function("2020"));
+    let unique_ptr = ffi::c_return_unique_ptr();
+    assert_eq!("2020", unique_ptr.i32_overloaded_method(2020));
+    assert_eq!("2020", unique_ptr.str_overloaded_method("2020"));
+}
+
+#[test]
+fn test_extern_trivial() {
+    let d = ffi2::c_return_trivial();
+    check!(ffi2::c_take_trivial_ref(&d));
+    check!(ffi2::c_take_trivial(d));
+    let d = ffi2::c_return_trivial_ptr();
+    check!(ffi2::c_take_trivial_ptr(d));
+    cxx::UniquePtr::new(ffi2::D { d: 42 });
+    let d = ffi2::ns_c_return_trivial();
+    check!(ffi2::ns_c_take_trivial(d));
+
+    let g = ffi2::c_return_trivial_ns();
+    check!(ffi2::c_take_trivial_ns_ref(&g));
+    check!(ffi2::c_take_trivial_ns(g));
+    let g = ffi2::c_return_trivial_ns_ptr();
+    check!(ffi2::c_take_trivial_ns_ptr(g));
+    cxx::UniquePtr::new(ffi2::G { g: 42 });
+}
+
+#[test]
+fn test_extern_opaque() {
+    let e = ffi2::c_return_opaque_ptr();
+    check!(ffi2::c_take_opaque_ref(e.as_ref().unwrap()));
+    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));
+}
diff --git a/tests/ui/bad_explicit_impl.rs b/tests/ui/bad_explicit_impl.rs
new file mode 100644
index 0000000..2106446
--- /dev/null
+++ b/tests/ui/bad_explicit_impl.rs
@@ -0,0 +1,10 @@
+#[cxx::bridge]
+mod ffi {
+    struct S {
+        x: u8,
+    }
+
+    impl fn() -> &S {}
+}
+
+fn main() {}
diff --git a/tests/ui/bad_explicit_impl.stderr b/tests/ui/bad_explicit_impl.stderr
new file mode 100644
index 0000000..cd0a317
--- /dev/null
+++ b/tests/ui/bad_explicit_impl.stderr
@@ -0,0 +1,5 @@
+error: unsupported Self type of explicit impl
+ --> $DIR/bad_explicit_impl.rs:7:5
+  |
+7 |     impl fn() -> &S {}
+  |     ^^^^^^^^^^^^^^^^^^
diff --git a/tests/ui/by_value_not_supported.stderr b/tests/ui/by_value_not_supported.stderr
index 1ff8dbf..0a56dd4 100644
--- a/tests/ui/by_value_not_supported.stderr
+++ b/tests/ui/by_value_not_supported.stderr
@@ -1,4 +1,4 @@
-error: using C++ type by value is not supported
+error: using opaque C++ type by value is not supported
  --> $DIR/by_value_not_supported.rs:4:9
   |
 4 |         c: C,
@@ -16,13 +16,19 @@
 6 |         s: CxxString,
   |         ^^^^^^^^^^^^
 
-error: passing C++ type by value is not supported
+error: needs a cxx::ExternType impl in order to be used as a field of `S`
+  --> $DIR/by_value_not_supported.rs:10:9
+   |
+10 |         type C;
+   |         ^^^^^^
+
+error: passing opaque C++ type by value is not supported
   --> $DIR/by_value_not_supported.rs:16:14
    |
 16 |         fn f(c: C) -> C;
    |              ^^^^
 
-error: returning C++ type by value is not supported
+error: returning opaque C++ type by value is not supported
   --> $DIR/by_value_not_supported.rs:16:23
    |
 16 |         fn f(c: C) -> C;
diff --git a/tests/ui/empty_enum.rs b/tests/ui/empty_enum.rs
index a9ad533..987004b 100644
--- a/tests/ui/empty_enum.rs
+++ b/tests/ui/empty_enum.rs
@@ -1,8 +1,6 @@
 #[cxx::bridge]
 mod ffi {
-    enum A {
-
-    }
+    enum A {}
 }
 
 fn main() {}
diff --git a/tests/ui/empty_enum.stderr b/tests/ui/empty_enum.stderr
index c73578a..7f35019 100644
--- a/tests/ui/empty_enum.stderr
+++ b/tests/ui/empty_enum.stderr
@@ -1,7 +1,5 @@
 error: enums without any variants are not supported
  --> $DIR/empty_enum.rs:3:5
   |
-3 | /     enum A {
-4 | |
-5 | |     }
-  | |_____^
+3 |     enum A {}
+  |     ^^^^^^^^^
diff --git a/tests/ui/impl_trait_for_type.rs b/tests/ui/impl_trait_for_type.rs
new file mode 100644
index 0000000..9284f73
--- /dev/null
+++ b/tests/ui/impl_trait_for_type.rs
@@ -0,0 +1,10 @@
+#[cxx::bridge]
+mod ffi {
+    struct S {
+        x: u8,
+    }
+
+    impl UniquePtrTarget for S {}
+}
+
+fn main() {}
diff --git a/tests/ui/impl_trait_for_type.stderr b/tests/ui/impl_trait_for_type.stderr
new file mode 100644
index 0000000..e05a461
--- /dev/null
+++ b/tests/ui/impl_trait_for_type.stderr
@@ -0,0 +1,5 @@
+error: unexpected impl, expected something like `impl UniquePtr<T> {}`
+ --> $DIR/impl_trait_for_type.rs:7:10
+  |
+7 |     impl UniquePtrTarget for S {}
+  |          ^^^^^^^^^^^^^^^^^^^^^
diff --git a/tests/ui/missing_unsafe.rs b/tests/ui/missing_unsafe.rs
new file mode 100644
index 0000000..d8c0a23
--- /dev/null
+++ b/tests/ui/missing_unsafe.rs
@@ -0,0 +1,10 @@
+#[cxx::bridge]
+mod ffi {
+    extern "Rust" {
+        fn f(x: i32);
+    }
+}
+
+unsafe fn f(_x: i32) {}
+
+fn main() {}
diff --git a/tests/ui/missing_unsafe.stderr b/tests/ui/missing_unsafe.stderr
new file mode 100644
index 0000000..df1bce2
--- /dev/null
+++ b/tests/ui/missing_unsafe.stderr
@@ -0,0 +1,7 @@
+error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
+ --> $DIR/missing_unsafe.rs:4:12
+  |
+4 |         fn f(x: i32);
+  |            ^ call to unsafe function
+  |
+  = note: consult the function's documentation for information on how to avoid undefined behavior
diff --git a/tests/ui/multiple_parse_error.rs b/tests/ui/multiple_parse_error.rs
index 061eab6..138d6d6 100644
--- a/tests/ui/multiple_parse_error.rs
+++ b/tests/ui/multiple_parse_error.rs
@@ -2,8 +2,7 @@
 mod ffi {
     struct Monad<T>;
 
-    extern "Haskell" {
-    }
+    extern "Haskell" {}
 }
 
 fn main() {}
diff --git a/tests/ui/multiple_parse_error.stderr b/tests/ui/multiple_parse_error.stderr
index 854aa90..4189507 100644
--- a/tests/ui/multiple_parse_error.stderr
+++ b/tests/ui/multiple_parse_error.stderr
@@ -7,5 +7,5 @@
 error: unrecognized ABI
  --> $DIR/multiple_parse_error.rs:5:5
   |
-5 |     extern "Haskell" {
+5 |     extern "Haskell" {}
   |     ^^^^^^^^^^^^^^^^
diff --git a/tests/ui/nonempty_impl_block.rs b/tests/ui/nonempty_impl_block.rs
new file mode 100644
index 0000000..239d1ec
--- /dev/null
+++ b/tests/ui/nonempty_impl_block.rs
@@ -0,0 +1,12 @@
+#[cxx::bridge]
+mod ffi {
+    struct S {
+        x: u8,
+    }
+
+    impl UniquePtr<S> {
+        fn new() -> Self;
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/nonempty_impl_block.stderr b/tests/ui/nonempty_impl_block.stderr
new file mode 100644
index 0000000..e7881bb
--- /dev/null
+++ b/tests/ui/nonempty_impl_block.stderr
@@ -0,0 +1,8 @@
+error: expected an empty impl block
+ --> $DIR/nonempty_impl_block.rs:7:23
+  |
+7 |       impl UniquePtr<S> {
+  |  _______________________^
+8 | |         fn new() -> Self;
+9 | |     }
+  | |_____^
diff --git a/tests/ui/opaque_not_sized.stderr b/tests/ui/opaque_not_sized.stderr
index efd1144..9818e44 100644
--- a/tests/ui/opaque_not_sized.stderr
+++ b/tests/ui/opaque_not_sized.stderr
@@ -1,8 +1,11 @@
 error[E0277]: the size for values of type `str` cannot be known at compilation time
-   --> $DIR/opaque_not_sized.rs:4:14
-    |
-4   |         type TypeR;
-    |              ^^^^^ doesn't have a size known at compile-time
-    |
-    = help: within `TypeR`, the trait `std::marker::Sized` is not implemented for `str`
-    = note: required because it appears within the type `TypeR`
+ --> $DIR/opaque_not_sized.rs:4:14
+  |
+4 |         type TypeR;
+  |         -----^^^^^-
+  |         |    |
+  |         |    doesn't have a size known at compile-time
+  |         required by this bound in `__AssertSized`
+  |
+  = help: within `TypeR`, the trait `Sized` is not implemented for `str`
+  = note: required because it appears within the type `TypeR`
diff --git a/tests/ui/result_no_display.rs b/tests/ui/result_no_display.rs
new file mode 100644
index 0000000..b535677
--- /dev/null
+++ b/tests/ui/result_no_display.rs
@@ -0,0 +1,14 @@
+#[cxx::bridge]
+mod ffi {
+    extern "Rust" {
+        fn f() -> Result<()>;
+    }
+}
+
+pub struct NonError;
+
+fn f() -> Result<(), NonError> {
+    Ok(())
+}
+
+fn main() {}
diff --git a/tests/ui/result_no_display.stderr b/tests/ui/result_no_display.stderr
new file mode 100644
index 0000000..d0502f9
--- /dev/null
+++ b/tests/ui/result_no_display.stderr
@@ -0,0 +1,8 @@
+error[E0277]: `NonError` doesn't implement `std::fmt::Display`
+ --> $DIR/result_no_display.rs:4:19
+  |
+4 |         fn f() -> Result<()>;
+  |                   ^^^^^^^^^^ `NonError` cannot be formatted with the default formatter
+  |
+  = help: the trait `std::fmt::Display` is not implemented for `NonError`
+  = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
diff --git a/tests/ui/struct_cycle.rs b/tests/ui/struct_cycle.rs
new file mode 100644
index 0000000..e74c2b1
--- /dev/null
+++ b/tests/ui/struct_cycle.rs
@@ -0,0 +1,34 @@
+#[cxx::bridge]
+mod ffi {
+    struct Node0 {
+        i: i32,
+    }
+
+    struct Node1 {
+        node2: Node2,
+        vec: Vec<Node3>,
+    }
+
+    struct Node2 {
+        node4: Node4,
+    }
+
+    struct Node3 {
+        node1: Node1,
+    }
+
+    struct Node4 {
+        node0: Node0,
+        node5: Node5,
+    }
+
+    struct Node5 {
+        node2: Node2,
+    }
+
+    struct Node6 {
+        node2: Node2,
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/struct_cycle.stderr b/tests/ui/struct_cycle.stderr
new file mode 100644
index 0000000..49bcb5f
--- /dev/null
+++ b/tests/ui/struct_cycle.stderr
@@ -0,0 +1,23 @@
+error: unsupported cyclic data structure
+  --> $DIR/struct_cycle.rs:26:9
+   |
+26 |         node2: Node2,
+   |         ^^^^^^^^^^^^
+
+error: unsupported cyclic data structure
+  --> $DIR/struct_cycle.rs:22:9
+   |
+22 |         node5: Node5,
+   |         ^^^^^^^^^^^^
+
+error: unsupported cyclic data structure
+  --> $DIR/struct_cycle.rs:13:9
+   |
+13 |         node4: Node4,
+   |         ^^^^^^^^^^^^
+
+error: unsupported cyclic data structure
+ --> $DIR/struct_cycle.rs:8:9
+  |
+8 |         node2: Node2,
+  |         ^^^^^^^^^^^^
diff --git a/tests/ui/unique_ptr_to_opaque.rs b/tests/ui/unique_ptr_to_opaque.rs
new file mode 100644
index 0000000..720ca69
--- /dev/null
+++ b/tests/ui/unique_ptr_to_opaque.rs
@@ -0,0 +1,23 @@
+mod outside {
+    #[repr(C)]
+    pub struct C {
+        pub a: u8,
+    }
+    unsafe impl cxx::ExternType for C {
+        type Id = cxx::type_id!("C");
+        type Kind = cxx::kind::Opaque;
+    }
+}
+
+#[cxx::bridge]
+mod ffi {
+    extern "C" {
+        type C = crate::outside::C;
+    }
+
+    impl UniquePtr<C> {}
+}
+
+fn main() {
+    cxx::UniquePtr::new(outside::C { a: 4 });
+}
diff --git a/tests/ui/unique_ptr_to_opaque.stderr b/tests/ui/unique_ptr_to_opaque.stderr
new file mode 100644
index 0000000..19d76a3
--- /dev/null
+++ b/tests/ui/unique_ptr_to_opaque.stderr
@@ -0,0 +1,7 @@
+error[E0271]: type mismatch resolving `<outside::C as ExternType>::Kind == Trivial`
+  --> $DIR/unique_ptr_to_opaque.rs:22:5
+   |
+22 |     cxx::UniquePtr::new(outside::C { a: 4 });
+   |     ^^^^^^^^^^^^^^^^^^^ expected enum `Trivial`, found enum `cxx::kind::Opaque`
+   |
+   = note: required by `UniquePtr::<T>::new`
diff --git a/tests/ui/unique_ptr_twice.rs b/tests/ui/unique_ptr_twice.rs
new file mode 100644
index 0000000..b6cb4d4
--- /dev/null
+++ b/tests/ui/unique_ptr_twice.rs
@@ -0,0 +1,19 @@
+#[cxx::bridge]
+mod here {
+    extern "C" {
+        type C;
+    }
+
+    impl UniquePtr<C> {}
+}
+
+#[cxx::bridge]
+mod there {
+    extern "C" {
+        type C = crate::here::C;
+    }
+
+    impl UniquePtr<C> {}
+}
+
+fn main() {}
diff --git a/tests/ui/unique_ptr_twice.stderr b/tests/ui/unique_ptr_twice.stderr
new file mode 100644
index 0000000..5686cf1
--- /dev/null
+++ b/tests/ui/unique_ptr_twice.stderr
@@ -0,0 +1,8 @@
+error[E0119]: conflicting implementations of trait `cxx::private::UniquePtrTarget` for type `here::C`:
+  --> $DIR/unique_ptr_twice.rs:16:5
+   |
+7  |     impl UniquePtr<C> {}
+   |     ----------------- first implementation here
+...
+16 |     impl UniquePtr<C> {}
+   |     ^^^^^^^^^^^^^^^^^ conflicting implementation for `here::C`
diff --git a/tests/ui/unsupported_elided.rs b/tests/ui/unsupported_elided.rs
new file mode 100644
index 0000000..4033319
--- /dev/null
+++ b/tests/ui/unsupported_elided.rs
@@ -0,0 +1,20 @@
+use std::marker::PhantomData;
+
+#[cxx::bridge]
+mod ffi {
+    extern "Rust" {
+        type T;
+
+        fn f(t: &T) -> &str;
+    }
+}
+
+pub struct T<'a> {
+    _lifetime: PhantomData<&'a ()>,
+}
+
+fn f<'a>(_t: &T<'a>) -> &'a str {
+    ""
+}
+
+fn main() {}
diff --git a/tests/ui/unsupported_elided.stderr b/tests/ui/unsupported_elided.stderr
new file mode 100644
index 0000000..5136390
--- /dev/null
+++ b/tests/ui/unsupported_elided.stderr
@@ -0,0 +1,11 @@
+error[E0106]: missing lifetime specifier
+ --> $DIR/unsupported_elided.rs:8:24
+  |
+8 |         fn f(t: &T) -> &str;
+  |                 --     ^ expected named lifetime parameter
+  |
+  = help: this function's return type contains a borrowed value, but the signature does not say which one of `t`'s 2 lifetimes it is borrowed from
+help: consider introducing a named lifetime parameter
+  |
+8 |         fn f<'a>(t: &'a T) -> &'a str;
+  |             ^^^^    ^^^^^     ^^^
diff --git a/tests/ui/wrong_type_id.rs b/tests/ui/wrong_type_id.rs
index 81a9b3f..e3d1380 100644
--- a/tests/ui/wrong_type_id.rs
+++ b/tests/ui/wrong_type_id.rs
@@ -1,11 +1,11 @@
-#[cxx::bridge(namespace = folly)]
+#[cxx::bridge(namespace = "folly")]
 mod here {
     extern "C" {
         type StringPiece;
     }
 }
 
-#[cxx::bridge(namespace = folly)]
+#[cxx::bridge(namespace = "folly")]
 mod there {
     extern "C" {
         type ByteRange = crate::here::StringPiece;
diff --git a/tests/ui/wrong_type_id.stderr b/tests/ui/wrong_type_id.stderr
index cb3ae15..2d8e50a 100644
--- a/tests/ui/wrong_type_id.stderr
+++ b/tests/ui/wrong_type_id.stderr
@@ -1,13 +1,13 @@
-error[E0271]: type mismatch resolving `<here::StringPiece as cxx::ExternType>::Id == (cxx::f, cxx::o, cxx::l, cxx::l, cxx::y, (), cxx::B, cxx::y, cxx::t, cxx::e, cxx::R, cxx::a, cxx::n, cxx::g, cxx::e)`
+error[E0271]: type mismatch resolving `<StringPiece as ExternType>::Id == (f, o, l, l, y, (), B, y, t, e, R, a, n, g, e)`
    --> $DIR/wrong_type_id.rs:11:9
     |
 11  |         type ByteRange = crate::here::StringPiece;
     |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected a tuple with 15 elements, found one with 17 elements
     |
-   ::: $WORKSPACE/src/extern_type.rs:110:41
+   ::: $WORKSPACE/src/extern_type.rs
     |
-110 | pub fn verify_extern_type<T: ExternType<Id = Id>, Id>() {}
-    |                                         ------- required by this bound in `cxx::private::verify_extern_type`
+    | pub fn verify_extern_type<T: ExternType<Id = Id>, Id>() {}
+    |                                         ------- required by this bound in `verify_extern_type`
     |
-    = note: expected tuple `(cxx::f, cxx::o, cxx::l, cxx::l, cxx::y, (), cxx::B, cxx::y, cxx::t, cxx::e, cxx::R, cxx::a, cxx::n, cxx::g, cxx::e)`
-               found tuple `(cxx::f, cxx::o, cxx::l, cxx::l, cxx::y, (), cxx::S, cxx::t, cxx::r, cxx::i, cxx::n, cxx::g, cxx::P, cxx::i, cxx::e, cxx::c, cxx::e)`
+    = note: expected tuple `(f, o, l, l, y, (), B, y, t, e, R, a, n, g, e)`
+               found tuple `(f, o, l, l, y, (), S, t, r, i, n, g, P, i, e, c, e)`
diff --git a/third-party/BUCK b/third-party/BUCK
index 9890a71..a9a3913 100644
--- a/third-party/BUCK
+++ b/third-party/BUCK
@@ -1,27 +1,21 @@
 # To be generated by Facebook's `reindeer` tool once that is open source.
 
 rust_library(
-    name = "anyhow",
-    srcs = glob(["vendor/anyhow-1.0.32/src/**"]),
-    visibility = ["PUBLIC"],
-    features = ["std"],
-)
-
-rust_library(
     name = "bitflags",
     srcs = glob(["vendor/bitflags-1.2.1/src/**"]),
 )
 
 rust_library(
     name = "cc",
-    srcs = glob(["vendor/cc-1.0.58/src/**"]),
+    srcs = glob(["vendor/cc-1.0.62/src/**"]),
     visibility = ["PUBLIC"],
 )
 
 rust_library(
     name = "clap",
-    srcs = glob(["vendor/clap-2.33.1/src/**"]),
+    srcs = glob(["vendor/clap-2.33.3/src/**"]),
     edition = "2015",
+    visibility = ["PUBLIC"],
     deps = [
         ":bitflags",
         ":textwrap",
@@ -40,50 +34,14 @@
 )
 
 rust_library(
-    name = "heck",
-    srcs = glob(["vendor/heck-0.3.1/src/**"]),
-    edition = "2015",
-    deps = [":unicode-segmentation"],
-)
-
-rust_library(
     name = "lazy_static",
     srcs = glob(["vendor/lazy_static-1.4.0/src/**"]),
-)
-
-rust_library(
-    name = "link-cplusplus",
-    srcs = glob(["vendor/link-cplusplus-1.0.2/src/**"]),
     visibility = ["PUBLIC"],
 )
 
 rust_library(
-    name = "proc-macro-error",
-    srcs = glob(["vendor/proc-macro-error-1.0.3/src/**"]),
-    rustc_flags = ["--cfg=use_fallback"],
-    deps = [
-        ":proc-macro-error-attr",
-        ":proc-macro2",
-        ":quote",
-        ":syn",
-    ],
-)
-
-rust_library(
-    name = "proc-macro-error-attr",
-    srcs = glob(["vendor/proc-macro-error-attr-1.0.3/src/**"]),
-    proc_macro = True,
-    deps = [
-        ":proc-macro2",
-        ":quote",
-        ":syn",
-        ":syn-mid",
-    ],
-)
-
-rust_library(
     name = "proc-macro2",
-    srcs = glob(["vendor/proc-macro2-1.0.19/src/**"]),
+    srcs = glob(["vendor/proc-macro2-1.0.24/src/**"]),
     visibility = ["PUBLIC"],
     features = [
         "proc-macro",
@@ -106,32 +64,15 @@
 )
 
 rust_library(
-    name = "structopt",
-    srcs = glob(["vendor/structopt-0.3.15/src/**"]),
+    name = "scratch",
+    srcs = glob(["vendor/scratch-1.0.0/src/**"]),
+    env = {"OUT_DIR": ""},
     visibility = ["PUBLIC"],
-    deps = [
-        ":clap",
-        ":lazy_static",
-        ":structopt-derive",
-    ],
-)
-
-rust_library(
-    name = "structopt-derive",
-    srcs = glob(["vendor/structopt-derive-0.4.8/src/**"]),
-    proc_macro = True,
-    deps = [
-        ":heck",
-        ":proc-macro-error",
-        ":proc-macro2",
-        ":quote",
-        ":syn",
-    ],
 )
 
 rust_library(
     name = "syn",
-    srcs = glob(["vendor/syn-1.0.36/src/**"]),
+    srcs = glob(["vendor/syn-1.0.48/src/**"]),
     visibility = ["PUBLIC"],
     features = [
         "clone-impls",
@@ -149,16 +90,6 @@
 )
 
 rust_library(
-    name = "syn-mid",
-    srcs = glob(["vendor/syn-mid-0.5.0/src/**"]),
-    deps = [
-        ":proc-macro2",
-        ":quote",
-        ":syn",
-    ],
-)
-
-rust_library(
     name = "termcolor",
     srcs = glob(["vendor/termcolor-1.1.0/src/**"]),
 )
@@ -170,12 +101,6 @@
 )
 
 rust_library(
-    name = "unicode-segmentation",
-    srcs = glob(["vendor/unicode-segmentation-1.6.0/src/**"]),
-    edition = "2015",
-)
-
-rust_library(
     name = "unicode-width",
     srcs = glob(["vendor/unicode-width-0.1.8/src/**"]),
 )
diff --git a/third-party/BUILD b/third-party/BUILD
index a01d4b7..d72b083 100644
--- a/third-party/BUILD
+++ b/third-party/BUILD
@@ -1,32 +1,27 @@
 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 = "anyhow",
-    srcs = glob(["vendor/anyhow-1.0.32/src/**"]),
-    crate_features = ["std"],
-    visibility = ["//visibility:public"],
-)
-
-rust_library(
     name = "bitflags",
     srcs = glob(["vendor/bitflags-1.2.1/src/**"]),
 )
 
 rust_library(
     name = "cc",
-    srcs = glob(["vendor/cc-1.0.58/src/**"]),
+    srcs = glob(["vendor/cc-1.0.62/src/**"]),
     visibility = ["//visibility:public"],
 )
 
 rust_library(
     name = "clap",
-    srcs = glob(["vendor/clap-2.33.1/src/**"]),
+    srcs = glob(["vendor/clap-2.33.3/src/**"]),
     edition = "2015",
+    visibility = ["//visibility:public"],
     deps = [
         ":bitflags",
         ":textwrap",
@@ -45,52 +40,14 @@
 )
 
 rust_library(
-    name = "heck",
-    srcs = glob(["vendor/heck-0.3.1/src/**"]),
-    edition = "2015",
-    deps = [":unicode-segmentation"],
-)
-
-rust_library(
     name = "lazy_static",
     srcs = glob(["vendor/lazy_static-1.4.0/src/**"]),
-)
-
-rust_library(
-    name = "link-cplusplus",
-    srcs = glob(["vendor/link-cplusplus-1.0.2/src/**"]),
     visibility = ["//visibility:public"],
 )
 
 rust_library(
-    name = "proc-macro-error",
-    srcs = glob(["vendor/proc-macro-error-1.0.3/src/**"]),
-    proc_macro_deps = [
-        ":proc-macro-error-attr",
-    ],
-    rustc_flags = ["--cfg=use_fallback"],
-    deps = [
-        ":proc-macro2",
-        ":quote",
-        ":syn",
-    ],
-)
-
-rust_library(
-    name = "proc-macro-error-attr",
-    srcs = glob(["vendor/proc-macro-error-attr-1.0.3/src/**"]),
-    crate_type = "proc-macro",
-    deps = [
-        ":proc-macro2",
-        ":quote",
-        ":syn",
-        ":syn-mid",
-    ],
-)
-
-rust_library(
     name = "proc-macro2",
-    srcs = glob(["vendor/proc-macro2-1.0.19/src/**"]),
+    srcs = glob(["vendor/proc-macro2-1.0.24/src/**"]),
     crate_features = [
         "proc-macro",
         "span-locations",
@@ -113,34 +70,15 @@
 )
 
 rust_library(
-    name = "structopt",
-    srcs = glob(["vendor/structopt-0.3.15/src/**"]),
-    proc_macro_deps = [
-        ":structopt-derive",
-    ],
+    name = "scratch",
+    srcs = glob(["vendor/scratch-1.0.0/src/**"]),
+    rustc_env = {"OUT_DIR": ""},
     visibility = ["//visibility:public"],
-    deps = [
-        ":clap",
-        ":lazy_static",
-    ],
-)
-
-rust_library(
-    name = "structopt-derive",
-    srcs = glob(["vendor/structopt-derive-0.4.8/src/**"]),
-    crate_type = "proc-macro",
-    deps = [
-        ":heck",
-        ":proc-macro-error",
-        ":proc-macro2",
-        ":quote",
-        ":syn",
-    ],
 )
 
 rust_library(
     name = "syn",
-    srcs = glob(["vendor/syn-1.0.36/src/**"]),
+    srcs = glob(["vendor/syn-1.0.48/src/**"]),
     crate_features = [
         "clone-impls",
         "derive",
@@ -158,16 +96,6 @@
 )
 
 rust_library(
-    name = "syn-mid",
-    srcs = glob(["vendor/syn-mid-0.5.0/src/**"]),
-    deps = [
-        ":proc-macro2",
-        ":quote",
-        ":syn",
-    ],
-)
-
-rust_library(
     name = "termcolor",
     srcs = glob(["vendor/termcolor-1.1.0/src/**"]),
 )
@@ -179,12 +107,6 @@
 )
 
 rust_library(
-    name = "unicode-segmentation",
-    srcs = glob(["vendor/unicode-segmentation-1.6.0/src/**"]),
-    edition = "2015",
-)
-
-rust_library(
     name = "unicode-width",
     srcs = glob(["vendor/unicode-width-0.1.8/src/**"]),
 )
diff --git a/third-party/Cargo.lock b/third-party/Cargo.lock
index 8f40664..76b95f0 100644
--- a/third-party/Cargo.lock
+++ b/third-party/Cargo.lock
@@ -10,12 +10,6 @@
 ]
 
 [[package]]
-name = "anyhow"
-version = "1.0.32"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6b602bfe940d21c130f3895acd65221e8a61270debe89d628b9cb4e3ccb8569b"
-
-[[package]]
 name = "atty"
 version = "0.2.14"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -34,15 +28,15 @@
 
 [[package]]
 name = "cc"
-version = "1.0.58"
+version = "1.0.62"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f9a06fb2e53271d7c279ec1efea6ab691c35a2ae67ec0d91d7acec0caf13b518"
+checksum = "f1770ced377336a88a67c473594ccc14eca6f4559217c34f64aac8f83d641b40"
 
 [[package]]
 name = "clap"
-version = "2.33.1"
+version = "2.33.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bdfa80d47f954d53a35a64987ca1422f495b8d6483c0fe9f7117b36c2a792129"
+checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002"
 dependencies = [
  "ansi_term",
  "atty",
@@ -65,11 +59,13 @@
 
 [[package]]
 name = "cxx"
-version = "0.3.4"
+version = "0.5.9"
 dependencies = [
  "cc",
  "cxx-build",
+ "cxx-gen",
  "cxx-test-suite",
+ "cxxbridge-flags",
  "cxxbridge-macro",
  "link-cplusplus",
  "rustversion",
@@ -78,9 +74,23 @@
 
 [[package]]
 name = "cxx-build"
-version = "0.3.4"
+version = "0.5.9"
 dependencies = [
- "anyhow",
+ "cc",
+ "codespan-reporting",
+ "cxx-gen",
+ "lazy_static",
+ "pkg-config",
+ "proc-macro2",
+ "quote",
+ "scratch",
+ "syn",
+]
+
+[[package]]
+name = "cxx-gen"
+version = "0.6.6"
+dependencies = [
  "cc",
  "codespan-reporting",
  "proc-macro2",
@@ -94,22 +104,36 @@
 dependencies = [
  "cxx",
  "cxx-build",
+ "cxxbridge-flags",
 ]
 
 [[package]]
 name = "cxxbridge-cmd"
-version = "0.3.4"
+version = "0.5.9"
 dependencies = [
- "anyhow",
+ "clap",
  "codespan-reporting",
  "proc-macro2",
  "quote",
- "structopt",
  "syn",
 ]
 
 [[package]]
-name = "cxxbridge-demo"
+name = "cxxbridge-flags"
+version = "0.5.9"
+
+[[package]]
+name = "cxxbridge-macro"
+version = "0.5.9"
+dependencies = [
+ "cxx",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "demo"
 version = "0.0.0"
 dependencies = [
  "cxx",
@@ -117,16 +141,6 @@
 ]
 
 [[package]]
-name = "cxxbridge-macro"
-version = "0.3.4"
-dependencies = [
- "cxx",
- "proc-macro2",
- "quote",
- "syn",
-]
-
-[[package]]
 name = "dissimilar"
 version = "1.0.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -139,19 +153,10 @@
 checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
 
 [[package]]
-name = "heck"
-version = "0.3.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205"
-dependencies = [
- "unicode-segmentation",
-]
-
-[[package]]
 name = "hermit-abi"
-version = "0.1.15"
+version = "0.1.17"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3deed196b6e7f9e44a2ae8d94225d80302d81208b1bb673fd21fe634645c85a9"
+checksum = "5aca5565f760fb5b220e499d72710ed156fdb74e631659e99377d9ebfbd13ae8"
 dependencies = [
  "libc",
 ]
@@ -170,50 +175,30 @@
 
 [[package]]
 name = "libc"
-version = "0.2.74"
+version = "0.2.80"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a2f02823cf78b754822df5f7f268fb59822e7296276d3e069d8e8cb26a14bd10"
+checksum = "4d58d1b70b004888f764dfbf6a26a3b0342a1632d33968e4a179d8011c760614"
 
 [[package]]
 name = "link-cplusplus"
-version = "1.0.2"
+version = "1.0.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f563b3814ea63e830e3e321206e4e2b5177854586ebe3f595795ed7053b217f5"
+checksum = "f96aa785c87218ec773df6c510af203872b34e2df2cf47d6e908e5f36231e354"
 dependencies = [
  "cc",
 ]
 
 [[package]]
-name = "proc-macro-error"
-version = "1.0.3"
+name = "pkg-config"
+version = "0.3.19"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fc175e9777c3116627248584e8f8b3e2987405cabe1c0adf7d1dd28f09dc7880"
-dependencies = [
- "proc-macro-error-attr",
- "proc-macro2",
- "quote",
- "syn",
- "version_check",
-]
-
-[[package]]
-name = "proc-macro-error-attr"
-version = "1.0.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3cc9795ca17eb581285ec44936da7fc2335a3f34f2ddd13118b6f4d515435c50"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn",
- "syn-mid",
- "version_check",
-]
+checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c"
 
 [[package]]
 name = "proc-macro2"
-version = "1.0.19"
+version = "1.0.24"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "04f5f085b5d71e2188cb8271e5da0161ad52c3f227a661a3c135fdf28e258b12"
+checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71"
 dependencies = [
  "unicode-xid",
 ]
@@ -229,14 +214,9 @@
 
 [[package]]
 name = "rustversion"
-version = "1.0.3"
+version = "1.0.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b9bdc5e856e51e685846fb6c13a1f5e5432946c2c90501bdc76a1319f19e29da"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn",
-]
+checksum = "cb5d2a036dc6d2d8fd16fde3498b04306e29bd193bf306a57427019b823d5acd"
 
 [[package]]
 name = "ryu"
@@ -245,19 +225,25 @@
 checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e"
 
 [[package]]
-name = "serde"
-version = "1.0.114"
+name = "scratch"
+version = "1.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5317f7588f0a5078ee60ef675ef96735a1442132dc645eb1d12c018620ed8cd3"
+checksum = "7e114536316b51a5aa7a0e59fc49661fd263c5507dd08bd28de052e57626ce69"
+
+[[package]]
+name = "serde"
+version = "1.0.117"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b88fa983de7720629c9387e9f517353ed404164b1e482c970a90c1a4aaf7dc1a"
 dependencies = [
  "serde_derive",
 ]
 
 [[package]]
 name = "serde_derive"
-version = "1.0.114"
+version = "1.0.117"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2a0be94b04690fbaed37cddffc5c134bf537c8e3329d53e982fe04c374978f8e"
+checksum = "cbd1ae72adb44aab48f325a02444a5fc079349a8d804c1fc922aed3f7454c74e"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -266,9 +252,9 @@
 
 [[package]]
 name = "serde_json"
-version = "1.0.57"
+version = "1.0.59"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "164eacbdb13512ec2745fb09d51fd5b22b0d65ed294a1dcf7285a360c80a675c"
+checksum = "dcac07dbffa1c65e7f816ab9eba78eb142c6d44410f4eeba1e26e4f5dfa56b95"
 dependencies = [
  "itoa",
  "ryu",
@@ -282,34 +268,10 @@
 checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
 
 [[package]]
-name = "structopt"
-version = "0.3.15"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "de2f5e239ee807089b62adce73e48c625e0ed80df02c7ab3f068f5db5281065c"
-dependencies = [
- "clap",
- "lazy_static",
- "structopt-derive",
-]
-
-[[package]]
-name = "structopt-derive"
-version = "0.4.8"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "510413f9de616762a4fbeab62509bf15c729603b72d7cd71280fbca431b1c118"
-dependencies = [
- "heck",
- "proc-macro-error",
- "proc-macro2",
- "quote",
- "syn",
-]
-
-[[package]]
 name = "syn"
-version = "1.0.36"
+version = "1.0.48"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4cdb98bcb1f9d81d07b536179c269ea15999b5d14ea958196413869445bb5250"
+checksum = "cc371affeffc477f42a221a1e4297aedcea33d47d19b61455588bd9d8f6b19ac"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -317,17 +279,6 @@
 ]
 
 [[package]]
-name = "syn-mid"
-version = "0.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7be3539f6c128a931cf19dcee741c1af532c7fd387baa739c03dd2e96479338a"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn",
-]
-
-[[package]]
 name = "termcolor"
 version = "1.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -347,18 +298,18 @@
 
 [[package]]
 name = "toml"
-version = "0.5.6"
+version = "0.5.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ffc92d160b1eef40665be3a05630d003936a3bc7da7421277846c2613e92c71a"
+checksum = "75cf45bb0bef80604d001caaec0d09da99611b3c0fd39d3080468875cdb65645"
 dependencies = [
  "serde",
 ]
 
 [[package]]
 name = "trybuild"
-version = "1.0.31"
+version = "1.0.35"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7a4d94e6adf00b96b1ab94fcfcd8c3cf916733b39adf90c8f72693629887b9b8"
+checksum = "b7d30fe369fd650072b352b1a9cb9587669de6b89be3b8225544012c1c45292d"
 dependencies = [
  "dissimilar",
  "glob",
@@ -370,12 +321,6 @@
 ]
 
 [[package]]
-name = "unicode-segmentation"
-version = "1.6.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e83e153d1053cbb5a118eeff7fd5be06ed99153f00dbcd8ae310c5fb2b22edc0"
-
-[[package]]
 name = "unicode-width"
 version = "0.1.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -394,12 +339,6 @@
 checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
 
 [[package]]
-name = "version_check"
-version = "0.9.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed"
-
-[[package]]
 name = "winapi"
 version = "0.3.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/tools/bazel/rust.bzl b/tools/bazel/rust.bzl
index 3ef4d91..b7b23e5 100644
--- a/tools/bazel/rust.bzl
+++ b/tools/bazel/rust.bzl
@@ -4,6 +4,10 @@
     _rust_library = "rust_library",
     _rust_test = "rust_test",
 )
+load("@third-party//:vendor.bzl", "vendored")
+
+def third_party_glob(include):
+    return vendored and native.glob(include)
 
 def rust_binary(edition = "2018", **kwargs):
     _rust_binary(edition = edition, **kwargs)
diff --git a/tools/bazel/rust_cxx_bridge.bzl b/tools/bazel/rust_cxx_bridge.bzl
new file mode 100644
index 0000000..534f6b5
--- /dev/null
+++ b/tools/bazel/rust_cxx_bridge.bzl
@@ -0,0 +1,41 @@
+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"],
+    )
diff --git a/tools/bazel/vendor.bzl b/tools/bazel/vendor.bzl
new file mode 100644
index 0000000..e9f10ac
--- /dev/null
+++ b/tools/bazel/vendor.bzl
@@ -0,0 +1,54 @@
+def _impl(repository_ctx):
+    # Link cxx repository into @third-party.
+    lockfile = repository_ctx.path(repository_ctx.attr.lockfile)
+    workspace = lockfile.dirname.dirname
+    repository_ctx.symlink(workspace, "workspace")
+
+    # Copy third-party/Cargo.lock since those are the crate versions that the
+    # BUILD file is written against.
+    vendor_lockfile = repository_ctx.path("workspace/third-party/Cargo.lock")
+    root_lockfile = repository_ctx.path("workspace/Cargo.lock")
+    _copy_file(repository_ctx, src = vendor_lockfile, dst = root_lockfile)
+
+    # Execute cargo vendor.
+    cmd = ["cargo", "vendor", "--versioned-dirs", "third-party/vendor"]
+    result = repository_ctx.execute(
+        cmd,
+        quiet = True,
+        working_directory = "workspace",
+    )
+    _log_cargo_vendor(repository_ctx, result)
+    if result.return_code != 0:
+        fail("failed to execute `{}`".format(" ".join(cmd)))
+
+    # Copy lockfile back to third-party/Cargo.lock to reflect any modification
+    # performed by Cargo.
+    _copy_file(repository_ctx, src = root_lockfile, dst = vendor_lockfile)
+
+    # Produce a token for third_party_glob to depend on so that the necessary
+    # sequencing is visible to Bazel.
+    repository_ctx.file("BUILD", executable = False)
+    repository_ctx.file("vendor.bzl", "vendored = True", executable = False)
+
+def _copy_file(repository_ctx, *, src, dst):
+    content = repository_ctx.read(src)
+    if not dst.exists or content != repository_ctx.read(dst):
+        repository_ctx.file(dst, content = content, executable = False)
+
+def _log_cargo_vendor(repository_ctx, result):
+    relevant = ""
+    for line in result.stderr.splitlines(True):
+        if line.strip() and not line.startswith("To use vendored sources,"):
+            relevant += line
+    if relevant:
+        # Render it as command output.
+        # If we just use print(), Bazel will cache and repeat the output even
+        # when not rerunning the command.
+        print = ["echo", relevant]
+        repository_ctx.execute(print, quiet = False)
+
+vendor = repository_rule(
+    attrs = {"lockfile": attr.label()},
+    local = True,
+    implementation = _impl,
+)
diff --git a/tools/buck/genrule.bzl b/tools/buck/genrule.bzl
new file mode 100644
index 0000000..b5364b7
--- /dev/null
+++ b/tools/buck/genrule.bzl
@@ -0,0 +1,8 @@
+def genrule(cmd, **kwargs):
+    # Resolve a distracting inconsistency between Buck and Bazel.
+    # Bazel creates the directory for your output file, while Buck expects the
+    # cmd to create it.
+    #
+    # TODO: send this as a PR to Buck, because Bazel's behavior here is better.
+    cmd = "mkdir -p `dirname ${OUT}`; " + cmd
+    native.genrule(cmd = cmd, **kwargs)
diff --git a/tools/buck/rust_cxx_bridge.bzl b/tools/buck/rust_cxx_bridge.bzl
new file mode 100644
index 0000000..4acc7c6
--- /dev/null
+++ b/tools/buck/rust_cxx_bridge.bzl
@@ -0,0 +1,34 @@
+load("//tools/buck:genrule.bzl", "genrule")
+
+def rust_cxx_bridge(name, src, deps = []):
+    genrule(
+        name = "%s/header" % name,
+        out = src + ".h",
+        cmd = "cp $(location :%s/generated)/generated.h ${OUT}" % name,
+    )
+
+    genrule(
+        name = "%s/source" % name,
+        out = src + ".cc",
+        cmd = "cp $(location :%s/generated)/generated.cc ${OUT}" % name,
+    )
+
+    genrule(
+        name = "%s/generated" % name,
+        srcs = [src],
+        out = ".",
+        cmd = "$(exe //:codegen) ${SRCS} -o ${OUT}/generated.h -o ${OUT}/generated.cc",
+        type = "cxxbridge",
+    )
+
+    cxx_library(
+        name = name,
+        srcs = [":%s/source" % name],
+        preferred_linkage = "static",
+        deps = deps + [":%s/include" % name],
+    )
+
+    cxx_library(
+        name = "%s/include" % name,
+        exported_headers = [":%s/header" % name],
+    )
diff --git a/tools/cargo/build.rs b/tools/cargo/build.rs
new file mode 100644
index 0000000..6c9d22d
--- /dev/null
+++ b/tools/cargo/build.rs
@@ -0,0 +1,73 @@
+use std::io::{self, Write};
+#[cfg(windows)]
+use std::os::windows::fs as windows;
+use std::path::Path;
+use std::process;
+#[cfg(windows)]
+use std::{env, fs};
+
+const MISSING: &str = "
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+When building `cxx` from a git clone, git's symlink support needs
+to be enabled on platforms that have it off by default (Windows).
+Either use:
+
+   $ git config --global core.symlinks true
+
+prior to cloning, or else use:
+
+   $ git clone -c core.symlinks=true https://github.com/dtolnay/cxx
+
+for the clone.
+
+Symlinks are only required when compiling locally from a clone of
+the git repository---they are NOT required when building `cxx` as
+a Cargo-managed (possibly transitive) build dependency downloaded
+through crates.io.
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+";
+
+#[cfg(windows)]
+const DENIED: &str = "
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+When building `cxx` from a git clone on Windows we need Developer
+Mode enabled for symlink support.
+
+To enable Developer Mode: go under Settings to Update & Security,
+then 'For developers', and turn on the toggle for Developer Mode.
+
+For more explanation of symlinks in Windows, see these resources:
+> https://blogs.windows.com/windowsdeveloper/2016/12/02/symlinks-windows-10/
+> https://docs.microsoft.com/windows/uwp/get-started/enable-your-device-for-development
+
+Symlinks are only required when compiling locally from a clone of
+the git repository---they are NOT required when building `cxx` as
+a Cargo-managed (possibly transitive) build dependency downloaded
+through crates.io.
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+";
+
+fn main() {
+    if Path::new("src/syntax/mod.rs").exists() {
+        return;
+    }
+
+    #[allow(unused_mut)]
+    let mut message = MISSING;
+
+    #[cfg(windows)]
+    if let Some(out_dir) = env::var_os("OUT_DIR") {
+        let parent_dir = Path::new(&out_dir).join("symlink");
+        let 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()
+        {
+            message = DENIED;
+        }
+    }
+
+    let _ = io::stderr().write_all(message.as_bytes());
+    process::exit(1);
+}