Add Rust std::shared_ptr binding
diff --git a/README.md b/README.md
index 746e466..f6a18eb 100644
--- a/README.md
+++ b/README.md
@@ -327,6 +327,7 @@
 <tr><td><a href="https://docs.rs/cxx/1.0/cxx/struct.CxxString.html">CxxString</a></td><td>std::string</td><td><sup><i>cannot be passed by value</i></sup></td></tr>
 <tr><td>Box<T></td><td>rust::Box<T></td><td><sup><i>cannot hold opaque C++ type</i></sup></td></tr>
 <tr><td><a href="https://docs.rs/cxx/1.0/cxx/struct.UniquePtr.html">UniquePtr<T></a></td><td>std::unique_ptr<T></td><td><sup><i>cannot hold opaque Rust type</i></sup></td></tr>
+<tr><td><a href="https://docs.rs/cxx/1.0/cxx/struct.SharedPtr.html">SharedPtr<T></a></td><td>std::shared_ptr<T></td><td><sup><i>cannot hold opaque Rust type</i></sup></td></tr>
 <tr><td>[T; N]</td><td>std::array<T, N></td><td><sup><i>cannot hold opaque Rust or C++ type</i></sup></td></tr>
 <tr><td>Vec<T></td><td>rust::Vec<T></td><td><sup><i>cannot hold opaque C++ type</i></sup></td></tr>
 <tr><td><a href="https://docs.rs/cxx/1.0/cxx/struct.CxxVector.html">CxxVector<T></a></td><td>std::vector<T></td><td><sup><i>cannot be passed by value, cannot hold opaque Rust type</i></sup></td></tr>
@@ -350,7 +351,6 @@
 <tr><td>Option<T></td><td><sup><i>tbd</i></sup></td></tr>
 <tr><td><sup><i>tbd</i></sup></td><td>std::map<K, V></td></tr>
 <tr><td><sup><i>tbd</i></sup></td><td>std::unordered_map<K, V></td></tr>
-<tr><td><sup><i>tbd</i></sup></td><td>std::shared_ptr<T></td></tr>
 </table>
 
 <br>
diff --git a/book/src/SUMMARY.md b/book/src/SUMMARY.md
index e10c2cc..4987694 100644
--- a/book/src/SUMMARY.md
+++ b/book/src/SUMMARY.md
@@ -29,6 +29,7 @@
     - [CxxString — std::string](binding/cxxstring.md)
     - [Box\<T\> — rust::Box\<T\>](binding/box.md)
     - [UniquePtr\<T\> — std::unique\_ptr\<T\>](binding/uniqueptr.md)
+    - [SharedPtr\<T\> — std::shared\_ptr\<T\>](binding/sharedptr.md)
     - [Vec\<T\> — rust::Vec\<T\>](binding/vec.md)
     - [CxxVector\<T\> — std::vector\<T\>](binding/cxxvector.md)
     - [Function pointers](binding/fn.md)
diff --git a/book/src/binding/box.md b/book/src/binding/box.md
index 8bcae58..7456ada 100644
--- a/book/src/binding/box.md
+++ b/book/src/binding/box.md
@@ -49,8 +49,8 @@
 ### Restrictions:
 
 Box\<T\> does not support T being an opaque C++ type. You should use
-[UniquePtr\<T\>](uniqueptr.md) instead for transferring ownership of opaque C++
-types on the language boundary.
+[UniquePtr\<T\>](uniqueptr.md) or [SharedPtr\<T\>](sharedptr.md) instead for
+transferring ownership of opaque C++ types on the language boundary.
 
 If T is an opaque Rust type, the Rust type is required to be [Sized] i.e. size
 known at compile time. In the future we may introduce support for dynamically
diff --git a/book/src/binding/sharedptr.md b/book/src/binding/sharedptr.md
new file mode 100644
index 0000000..8f494ae
--- /dev/null
+++ b/book/src/binding/sharedptr.md
@@ -0,0 +1,80 @@
+{{#title std::shared_ptr<T> — Rust ♡ C++}}
+# std::shared\_ptr\<T\>
+
+The Rust binding of std::shared\_ptr\<T\> is called **[`SharedPtr<T>`]**. See
+the link for documentation of the Rust API.
+
+[`SharedPtr<T>`]: https://docs.rs/cxx/*/cxx/struct.SharedPtr.html
+
+### Restrictions:
+
+SharedPtr\<T\> does not support T being an opaque Rust type. You should use a
+Box\<T\> (C++ [rust::Box\<T\>](box.md)) instead for transferring ownership of
+opaque Rust types on the language boundary.
+
+## Example
+
+```rust,noplayground
+// src/main.rs
+
+use std::ops::Deref;
+use std::ptr;
+
+#[cxx::bridge]
+mod ffi {
+    unsafe extern "C++" {
+        include!("example/include/example.h");
+
+        type Object;
+
+        fn create_shared_ptr() -> SharedPtr<Object>;
+    }
+}
+
+fn main() {
+    let ptr1 = ffi::create_shared_ptr();
+
+    {
+        // Create a second shared_ptr holding shared ownership of the same
+        // object. There is still only one Object but two SharedPtr<Object>.
+        // Both pointers point to the same object on the heap. 
+        let ptr2 = ptr1.clone();
+        assert!(ptr::eq(ptr1.deref(), ptr2.deref()));
+
+        // ptr2 goes out of scope, but Object is not destroyed yet.
+    }
+
+    println!("say goodbye to Object");
+
+    // ptr1 goes out of scope and Object is destroyed.
+}
+```
+
+```cpp
+// include/example.h
+
+#pragma once
+#include <memory>
+
+class Object {
+public:
+  Object();
+  ~Object();
+};
+
+std::shared_ptr<Object> create_shared_ptr();
+```
+
+```cpp
+// src/example.cc
+
+#include "example/include/example.h"
+#include <iostream>
+
+Object::Object() { std::cout << "construct Object" << std::endl; }
+Object::~Object() { std::cout << "~Object" << std::endl; }
+
+std::shared_ptr<Object> create_shared_ptr() {
+  return std::make_shared<Object>();
+}
+```
diff --git a/book/src/bindings.md b/book/src/bindings.md
index c2a525c..b2069b8 100644
--- a/book/src/bindings.md
+++ b/book/src/bindings.md
@@ -16,6 +16,7 @@
 <tr><td style="padding:3px 6px"><b><a href="binding/cxxstring.md">CxxString</a></b></td><td style="padding:3px 6px">std::string</td><td style="padding:3px 6px"><sup><i>cannot be passed by value</i></sup></td></tr>
 <tr><td style="padding:3px 6px">Box<T></td><td style="padding:3px 6px"><b><a href="binding/box.md">rust::Box<T></a></b></td><td style="padding:3px 6px"><sup><i>cannot hold opaque C++ type</i></sup></td></tr>
 <tr><td style="padding:3px 6px"><b><a href="binding/uniqueptr.md">UniquePtr<T></a></b></td><td style="padding:3px 6px">std::unique_ptr<T></td><td style="padding:3px 6px"><sup><i>cannot hold opaque Rust type</i></sup></td></tr>
+<tr><td style="padding:3px 6px"><b><a href="binding/sharedptr.md">SharedPtr<T></a></b></td><td style="padding:3px 6px">std::shared_ptr<T></td><td style="padding:3px 6px"><sup><i>cannot hold opaque Rust type</i></sup></td></tr>
 <tr><td style="padding:3px 6px">[T; N]</td><td style="padding:3px 6px">std::array<T, N></td><td style="padding:3px 6px"><sup><i>cannot hold opaque Rust or C++ type</i></sup></td></tr>
 <tr><td style="padding:3px 6px">Vec<T></td><td style="padding:3px 6px"><b><a href="binding/vec.md">rust::Vec<T></a></b></td><td style="padding:3px 6px"><sup><i>cannot hold opaque C++ type</i></sup></td></tr>
 <tr><td style="padding:3px 6px"><b><a href="binding/cxxvector.md">CxxVector<T></a></b></td><td style="padding:3px 6px">std::vector<T></td><td style="padding:3px 6px"><sup><i>cannot be passed by value, cannot hold opaque Rust type</i></sup></td></tr>
diff --git a/gen/src/write.rs b/gen/src/write.rs
index 5413b68..26da9e4 100644
--- a/gen/src/write.rs
+++ b/gen/src/write.rs
@@ -201,6 +201,7 @@
             Type::RustBox(_) => out.builtin.rust_box = true,
             Type::RustVec(_) => out.builtin.rust_vec = true,
             Type::UniquePtr(_) => out.include.memory = true,
+            Type::SharedPtr(_) => out.include.memory = true,
             Type::Str(_) => out.builtin.rust_str = true,
             Type::CxxVector(_) => out.include.vector = true,
             Type::Fn(_) => out.builtin.rust_fn = true,
@@ -1104,6 +1105,11 @@
             write_type(out, &ptr.inner);
             write!(out, ">");
         }
+        Type::SharedPtr(ptr) => {
+            write!(out, "::std::shared_ptr<");
+            write_type(out, &ptr.inner);
+            write!(out, ">");
+        }
         Type::CxxVector(ty) => {
             write!(out, "::std::vector<");
             write_type(out, &ty.inner);
@@ -1182,6 +1188,7 @@
         Type::Ident(_)
         | Type::RustBox(_)
         | Type::UniquePtr(_)
+        | Type::SharedPtr(_)
         | Type::Str(_)
         | Type::CxxVector(_)
         | Type::RustVec(_)
@@ -1270,6 +1277,16 @@
                     write_unique_ptr(out, inner);
                 }
             }
+        } else if let Type::SharedPtr(ptr) = ty {
+            if let Type::Ident(inner) = &ptr.inner {
+                if Atom::from(&inner.rust).is_none()
+                    && !(out.types.aliases.contains_key(&inner.rust)
+                        || out.types.explicit_impls.contains(ty))
+                {
+                    out.next_section();
+                    write_shared_ptr(out, inner);
+                }
+            }
         } else if let Type::CxxVector(ptr) = ty {
             if let Type::Ident(inner) = &ptr.inner {
                 if Atom::from(&inner.rust).is_none()
@@ -1554,6 +1571,87 @@
     writeln!(out, "}}");
 }
 
+fn write_shared_ptr(out: &mut OutFile, ident: &ResolvableName) {
+    let resolved = out.types.resolve(ident);
+    let inner = resolved.to_fully_qualified();
+    let instance = ident.to_symbol(out.types);
+
+    writeln!(out, "#ifndef CXXBRIDGE1_SHARED_PTR_{}", instance);
+    writeln!(out, "#define CXXBRIDGE1_SHARED_PTR_{}", instance);
+    out.include.new = true;
+    out.include.utility = true;
+
+    // Some aliases are to opaque types; some are to trivial types. We can't
+    // know at code generation time, so we generate both C++ and Rust side
+    // bindings for a "new" method anyway. But the Rust code can't be called for
+    // Opaque types because the 'new' method is not implemented.
+    let can_construct_from_value = out.types.structs.contains_key(&ident.rust)
+        || out.types.enums.contains_key(&ident.rust)
+        || out.types.aliases.contains_key(&ident.rust);
+
+    if !out.types.structs.contains_key(&ident.rust) && !out.types.enums.contains_key(&ident.rust) {
+        out.builtin.is_complete = true;
+        writeln!(
+            out,
+            "static_assert(::rust::is_complete<{}>::value, \"definition of {} is required\");",
+            inner, resolved.cxx,
+        );
+    }
+    writeln!(
+        out,
+        "static_assert(sizeof(::std::shared_ptr<{}>) == 2 * sizeof(void *), \"\");",
+        inner,
+    );
+    writeln!(
+        out,
+        "static_assert(alignof(::std::shared_ptr<{}>) == alignof(void *), \"\");",
+        inner,
+    );
+    writeln!(
+        out,
+        "void cxxbridge1$shared_ptr${}$null(::std::shared_ptr<{}> *ptr) noexcept {{",
+        instance, inner,
+    );
+    writeln!(out, "  new (ptr) ::std::shared_ptr<{}>();", inner);
+    writeln!(out, "}}");
+    if can_construct_from_value {
+        writeln!(
+            out,
+            "void cxxbridge1$shared_ptr${}$new(::std::shared_ptr<{}> *ptr, {} *value) noexcept {{",
+            instance, inner, inner,
+        );
+        writeln!(
+            out,
+            "  new (ptr) ::std::shared_ptr<{}>(new {}(::std::move(*value)));",
+            inner, inner,
+        );
+        writeln!(out, "}}");
+    }
+    writeln!(
+        out,
+        "void cxxbridge1$shared_ptr${}$clone(const ::std::shared_ptr<{}>& self, ::std::shared_ptr<{}> *ptr) noexcept {{",
+        instance, inner, inner,
+    );
+    writeln!(out, "  new (ptr) ::std::shared_ptr<{}>(self);", inner);
+    writeln!(out, "}}");
+    writeln!(
+        out,
+        "const {} *cxxbridge1$shared_ptr${}$get(const ::std::shared_ptr<{}>& self) noexcept {{",
+        inner, instance, inner,
+    );
+    writeln!(out, "  return self.get();");
+    writeln!(out, "}}");
+    writeln!(
+        out,
+        "void cxxbridge1$shared_ptr${}$drop(::std::shared_ptr<{}> *self) noexcept {{",
+        instance, inner,
+    );
+    writeln!(out, "  self->~shared_ptr();");
+    writeln!(out, "}}");
+
+    writeln!(out, "#endif // CXXBRIDGE1_SHARED_PTR_{}", instance);
+}
+
 fn write_cxx_vector(out: &mut OutFile, element: &ResolvableName) {
     let inner = element.to_typename(out.types);
     let instance = element.to_mangled(out.types);
diff --git a/macro/src/expand.rs b/macro/src/expand.rs
index 63fe888..82b79aa 100644
--- a/macro/src/expand.rs
+++ b/macro/src/expand.rs
@@ -86,6 +86,14 @@
                     expanded.extend(expand_unique_ptr(ident, types, explicit_impl));
                 }
             }
+        } else if let Type::SharedPtr(ptr) = ty {
+            if let Type::Ident(ident) = &ptr.inner {
+                if Atom::from(&ident.rust).is_none()
+                    && (explicit_impl.is_some() || !types.aliases.contains_key(&ident.rust))
+                {
+                    expanded.extend(expand_shared_ptr(ident, types, explicit_impl));
+                }
+            }
         } else if let Type::CxxVector(ptr) = ty {
             if let Type::Ident(ident) = &ptr.inner {
                 if Atom::from(&ident.rust).is_none()
@@ -1110,6 +1118,77 @@
     }
 }
 
+fn expand_shared_ptr(
+    ident: &ResolvableName,
+    types: &Types,
+    explicit_impl: Option<&Impl>,
+) -> TokenStream {
+    let name = ident.rust.to_string();
+    let prefix = format!("cxxbridge1$shared_ptr${}$", ident.to_symbol(types));
+    let link_null = format!("{}null", prefix);
+    let link_new = format!("{}new", prefix);
+    let link_clone = format!("{}clone", prefix);
+    let link_get = format!("{}get", prefix);
+    let link_drop = format!("{}drop", prefix);
+
+    let can_construct_from_value = types.structs.contains_key(&ident.rust)
+        || types.enums.contains_key(&ident.rust)
+        || types.aliases.contains_key(&ident.rust);
+    let new_method = if can_construct_from_value {
+        Some(quote! {
+            unsafe fn __new(mut value: Self, new: *mut ::std::ffi::c_void) {
+                extern "C" {
+                    #[link_name = #link_new]
+                    fn __new(new: *mut ::std::ffi::c_void, value: *mut #ident);
+                }
+                __new(new, &mut value);
+            }
+        })
+    } else {
+        None
+    };
+
+    let begin_span =
+        explicit_impl.map_or_else(Span::call_site, |explicit| explicit.impl_token.span);
+    let end_span = explicit_impl.map_or_else(Span::call_site, |explicit| explicit.brace_token.span);
+    let unsafe_token = format_ident!("unsafe", span = begin_span);
+
+    quote_spanned! {end_span=>
+        #unsafe_token impl ::cxx::private::SharedPtrTarget for #ident {
+            const __NAME: &'static dyn ::std::fmt::Display = &#name;
+            unsafe fn __null(new: *mut ::std::ffi::c_void) {
+                extern "C" {
+                    #[link_name = #link_null]
+                    fn __null(new: *mut ::std::ffi::c_void);
+                }
+                __null(new);
+            }
+            #new_method
+            unsafe fn __clone(this: *const ::std::ffi::c_void, new: *mut ::std::ffi::c_void) {
+                extern "C" {
+                    #[link_name = #link_clone]
+                    fn __clone(this: *const ::std::ffi::c_void, new: *mut ::std::ffi::c_void);
+                }
+                __clone(this, new);
+            }
+            unsafe fn __get(this: *const ::std::ffi::c_void) -> *const Self {
+                extern "C" {
+                    #[link_name = #link_get]
+                    fn __get(this: *const ::std::ffi::c_void) -> *const #ident;
+                }
+                __get(this)
+            }
+            unsafe fn __drop(this: *mut ::std::ffi::c_void) {
+                extern "C" {
+                    #[link_name = #link_drop]
+                    fn __drop(this: *mut ::std::ffi::c_void);
+                }
+                __drop(this);
+            }
+        }
+    }
+}
+
 fn expand_cxx_vector(
     elem: &ResolvableName,
     explicit_impl: Option<&Impl>,
diff --git a/src/lib.rs b/src/lib.rs
index 917dd3b..bfd7a51 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -336,6 +336,7 @@
 //! <tr><td><a href="struct.CxxString.html">CxxString</a></td><td>std::string</td><td><sup><i>cannot be passed by value</i></sup></td></tr>
 //! <tr><td>Box<T></td><td>rust::Box<T></td><td><sup><i>cannot hold opaque C++ type</i></sup></td></tr>
 //! <tr><td><a href="struct.UniquePtr.html">UniquePtr<T></a></td><td>std::unique_ptr<T></td><td><sup><i>cannot hold opaque Rust type</i></sup></td></tr>
+//! <tr><td><a href="struct.SharedPtr.html">SharedPtr<T></a></td><td>std::shared_ptr<T></td><td><sup><i>cannot hold opaque Rust type</i></sup></td></tr>
 //! <tr><td>[T; N]</td><td>std::array<T, N></td><td><sup><i>cannot hold opaque Rust or C++ type</i></sup></td></tr>
 //! <tr><td>Vec<T></td><td>rust::Vec<T></td><td><sup><i>cannot hold opaque C++ type</i></sup></td></tr>
 //! <tr><td><a href="struct.CxxVector.html">CxxVector<T></a></td><td>std::vector<T></td><td><sup><i>cannot be passed by value, cannot hold opaque Rust type</i></sup></td></tr>
@@ -359,7 +360,6 @@
 //! <tr><td>Option<T></td><td><sup><i>tbd</i></sup></td></tr>
 //! <tr><td><sup><i>tbd</i></sup></td><td>std::map<K, V></td></tr>
 //! <tr><td><sup><i>tbd</i></sup></td><td>std::unordered_map<K, V></td></tr>
-//! <tr><td><sup><i>tbd</i></sup></td><td>std::shared_ptr<T></td></tr>
 //! </table>
 
 #![no_std]
@@ -404,6 +404,7 @@
 mod rust_string;
 mod rust_type;
 mod rust_vec;
+mod shared_ptr;
 mod symbols;
 mod unique_ptr;
 mod unwind;
@@ -412,6 +413,7 @@
 pub use crate::cxx_vector::CxxVector;
 pub use crate::exception::Exception;
 pub use crate::extern_type::{kind, ExternType};
+pub use crate::shared_ptr::SharedPtr;
 pub use crate::unique_ptr::UniquePtr;
 pub use cxxbridge_macro::bridge;
 
@@ -448,6 +450,7 @@
     pub use crate::rust_string::RustString;
     pub use crate::rust_type::RustType;
     pub use crate::rust_vec::RustVec;
+    pub use crate::shared_ptr::SharedPtrTarget;
     pub use crate::unique_ptr::UniquePtrTarget;
     pub use crate::unwind::catch_unwind;
 }
diff --git a/src/shared_ptr.rs b/src/shared_ptr.rs
new file mode 100644
index 0000000..7fdb356
--- /dev/null
+++ b/src/shared_ptr.rs
@@ -0,0 +1,191 @@
+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::MaybeUninit;
+use core::ops::{Deref, DerefMut};
+use core::pin::Pin;
+
+/// BInding to C++ `std::shared_ptr<T>`.
+#[repr(C)]
+pub struct SharedPtr<T>
+where
+    T: SharedPtrTarget,
+{
+    repr: [*mut c_void; 2],
+    ty: PhantomData<T>,
+}
+
+impl<T> SharedPtr<T>
+where
+    T: SharedPtrTarget,
+{
+    /// Makes a new SharedPtr wrapping a null pointer.
+    ///
+    /// Matches the behavior of default-constructing a std::shared\_ptr.
+    pub fn null() -> Self {
+        let mut shared_ptr = MaybeUninit::<SharedPtr<T>>::uninit();
+        let new = shared_ptr.as_mut_ptr().cast();
+        unsafe {
+            T::__null(new);
+            shared_ptr.assume_init()
+        }
+    }
+
+    /// Allocates memory on the heap and makes a SharedPtr owner for it.
+    pub fn new(value: T) -> Self
+    where
+        T: ExternType<Kind = Trivial>,
+    {
+        let mut shared_ptr = MaybeUninit::<SharedPtr<T>>::uninit();
+        let new = shared_ptr.as_mut_ptr().cast();
+        unsafe {
+            T::__new(value, new);
+            shared_ptr.assume_init()
+        }
+    }
+
+    /// Chacks whether the SharedPtr does not own an object.
+    ///
+    /// This is the opposite of [std::shared_ptr\<T\>::operator bool](https://en.cppreference.com/w/cpp/memory/shared_ptr/operator_bool).
+    pub fn is_null(&self) -> bool {
+        let this = self as *const Self as *const c_void;
+        let ptr = unsafe { T::__get(this) };
+        ptr.is_null()
+    }
+
+    /// Returns a reference to the object owned by this SharedPtr if any,
+    /// otherwise None.
+    pub fn as_ref(&self) -> Option<&T> {
+        let this = self as *const Self as *const c_void;
+        unsafe { T::__get(this).as_ref() }
+    }
+
+    /// Returns a mutable pinned reference to the object owned by this SharedPtr
+    /// if any, otherwise None.
+    pub fn as_mut(&mut self) -> Option<Pin<&mut T>> {
+        let this = self as *mut Self as *mut c_void;
+        unsafe {
+            let mut_reference = (T::__get(this) as *mut T).as_mut()?;
+            Some(Pin::new_unchecked(mut_reference))
+        }
+    }
+
+    /// Returns a mutable pinned reference to the object owned by this
+    /// SharedPtr.
+    ///
+    /// # Panics
+    ///
+    /// Panics if the SharedPtr holds a null pointer.
+    pub fn pin_mut(&mut self) -> Pin<&mut T> {
+        match self.as_mut() {
+            Some(target) => target,
+            None => panic!("called pin_mut on a null SharedPtr<{}>", T::__NAME),
+        }
+    }
+}
+
+unsafe impl<T> Send for SharedPtr<T> where T: Send + Sync + SharedPtrTarget {}
+unsafe impl<T> Sync for SharedPtr<T> where T: Send + Sync + SharedPtrTarget {}
+
+impl<T> Clone for SharedPtr<T>
+where
+    T: SharedPtrTarget,
+{
+    fn clone(&self) -> Self {
+        let mut shared_ptr = MaybeUninit::<SharedPtr<T>>::uninit();
+        let new = shared_ptr.as_mut_ptr().cast();
+        let this = self as *const Self as *mut c_void;
+        unsafe {
+            T::__clone(this, new);
+            shared_ptr.assume_init()
+        }
+    }
+}
+
+impl<T> Drop for SharedPtr<T>
+where
+    T: SharedPtrTarget,
+{
+    fn drop(&mut self) {
+        let this = self as *mut Self as *mut c_void;
+        unsafe { T::__drop(this) }
+    }
+}
+
+impl<T> Deref for SharedPtr<T>
+where
+    T: SharedPtrTarget,
+{
+    type Target = T;
+
+    fn deref(&self) -> &Self::Target {
+        match self.as_ref() {
+            Some(target) => target,
+            None => panic!("called deref on a null SharedPtr<{}>", T::__NAME),
+        }
+    }
+}
+
+impl<T> DerefMut for SharedPtr<T>
+where
+    T: SharedPtrTarget + Unpin,
+{
+    fn deref_mut(&mut self) -> &mut Self::Target {
+        match self.as_mut() {
+            Some(target) => Pin::into_inner(target),
+            None => panic!("called deref_mut on a null SharedPtr<{}>", T::__NAME),
+        }
+    }
+}
+
+impl<T> Debug for SharedPtr<T>
+where
+    T: Debug + SharedPtrTarget,
+{
+    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+        match self.as_ref() {
+            None => formatter.write_str("nullptr"),
+            Some(value) => Debug::fmt(value, formatter),
+        }
+    }
+}
+
+impl<T> Display for SharedPtr<T>
+where
+    T: Display + SharedPtrTarget,
+{
+    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+        match self.as_ref() {
+            None => formatter.write_str("nullptr"),
+            Some(value) => Display::fmt(value, formatter),
+        }
+    }
+}
+
+// Methods are private; not intended to be implemented outside of cxxbridge
+// codebase.
+pub unsafe trait SharedPtrTarget {
+    #[doc(hidden)]
+    const __NAME: &'static dyn Display;
+    #[doc(hidden)]
+    unsafe fn __null(new: *mut c_void);
+    #[doc(hidden)]
+    unsafe fn __new(value: Self, new: *mut c_void)
+    where
+        Self: Sized,
+    {
+        // Opoaque C types do not get this method because they can never exist
+        // by value on the Rust side of the bridge.
+        let _ = value;
+        let _ = new;
+        unreachable!()
+    }
+    #[doc(hidden)]
+    unsafe fn __clone(this: *const c_void, new: *mut c_void);
+    #[doc(hidden)]
+    unsafe fn __get(this: *const c_void) -> *const Self;
+    #[doc(hidden)]
+    unsafe fn __drop(this: *mut c_void);
+}
diff --git a/syntax/check.rs b/syntax/check.rs
index 2ebbf05..6dce563 100644
--- a/syntax/check.rs
+++ b/syntax/check.rs
@@ -32,6 +32,7 @@
             Type::RustBox(ptr) => check_type_box(cx, ptr),
             Type::RustVec(ty) => check_type_rust_vec(cx, ty),
             Type::UniquePtr(ptr) => check_type_unique_ptr(cx, ptr),
+            Type::SharedPtr(ptr) => check_type_shared_ptr(cx, ptr),
             Type::CxxVector(ptr) => check_type_cxx_vector(cx, ptr),
             Type::Ref(ty) => check_type_ref(cx, ty),
             Type::Array(array) => check_type_array(cx, array),
@@ -115,6 +116,7 @@
     if let Type::Ident(ident) = &ptr.inner {
         if cx.types.rust.contains(&ident.rust) {
             cx.error(ptr, "unique_ptr of a Rust type is not supported yet");
+            return;
         }
 
         match Atom::from(&ident.rust) {
@@ -128,6 +130,29 @@
     cx.error(ptr, "unsupported unique_ptr target type");
 }
 
+fn check_type_shared_ptr(cx: &mut Check, ptr: &Ty1) {
+    if let Type::Ident(ident) = &ptr.inner {
+        if cx.types.rust.contains(&ident.rust) {
+            cx.error(ptr, "shared_ptr of a Rust type is not supported yet");
+            return;
+        }
+
+        match Atom::from(&ident.rust) {
+            None => return,
+            Some(CxxString) => {
+                cx.error(ptr, "std::shared_ptr<std::string> is not supported yet");
+                return;
+            }
+            _ => {}
+        }
+    } else if let Type::CxxVector(_) = &ptr.inner {
+        cx.error(ptr, "std::shared_ptr<std::vector> is not supported yet");
+        return;
+    }
+
+    cx.error(ptr, "unsupported shared_ptr target type");
+}
+
 fn check_type_cxx_vector(cx: &mut Check, ptr: &Ty1) {
     if let Type::Ident(ident) = &ptr.inner {
         if cx.types.rust.contains(&ident.rust) {
@@ -135,6 +160,7 @@
                 ptr,
                 "C++ vector containing a Rust type is not supported yet",
             );
+            return;
         }
 
         match Atom::from(&ident.rust) {
@@ -410,7 +436,7 @@
         return;
     }
 
-    if let Type::UniquePtr(ty) | Type::CxxVector(ty) = ty {
+    if let Type::UniquePtr(ty) | Type::SharedPtr(ty) | Type::CxxVector(ty) = ty {
         if let Type::Ident(inner) = &ty.inner {
             if Atom::from(&inner.rust).is_none() {
                 return;
@@ -473,6 +499,7 @@
 fn check_reserved_name(cx: &mut Check, ident: &Ident) {
     if ident == "Box"
         || ident == "UniquePtr"
+        || ident == "SharedPtr"
         || ident == "Vec"
         || ident == "CxxVector"
         || ident == "str"
@@ -493,6 +520,7 @@
         Type::RustBox(_)
         | Type::RustVec(_)
         | Type::UniquePtr(_)
+        | Type::SharedPtr(_)
         | Type::Ref(_)
         | Type::Str(_)
         | Type::SliceRef(_) => false,
@@ -564,6 +592,7 @@
         Type::RustBox(_) => "Box".to_owned(),
         Type::RustVec(_) => "Vec".to_owned(),
         Type::UniquePtr(_) => "unique_ptr".to_owned(),
+        Type::SharedPtr(_) => "shared_ptr".to_owned(),
         Type::Ref(_) => "reference".to_owned(),
         Type::Str(_) => "&str".to_owned(),
         Type::CxxVector(_) => "C++ vector".to_owned(),
diff --git a/syntax/error.rs b/syntax/error.rs
index d0ae021..a672329 100644
--- a/syntax/error.rs
+++ b/syntax/error.rs
@@ -29,7 +29,7 @@
 pub static BOX_CXX_TYPE: Error = Error {
     msg: "Box of a C++ type is not supported yet",
     label: None,
-    note: Some("hint: use UniquePtr<>"),
+    note: Some("hint: use UniquePtr<> or SharedPtr<>"),
 };
 
 pub static CXXBRIDGE_RESERVED: Error = Error {
@@ -47,7 +47,7 @@
 pub static CXX_TYPE_BY_VALUE: Error = Error {
     msg: "C++ type by value is not supported",
     label: None,
-    note: Some("hint: wrap it in a UniquePtr<>"),
+    note: Some("hint: wrap it in a UniquePtr<> or SharedPtr<>"),
 };
 
 pub static DISCRIMINANT_OVERFLOW: Error = Error {
diff --git a/syntax/impls.rs b/syntax/impls.rs
index 73cb1d9..9b154f6 100644
--- a/syntax/impls.rs
+++ b/syntax/impls.rs
@@ -45,6 +45,7 @@
             Type::Ident(t) => t.hash(state),
             Type::RustBox(t) => t.hash(state),
             Type::UniquePtr(t) => t.hash(state),
+            Type::SharedPtr(t) => t.hash(state),
             Type::Ref(t) => t.hash(state),
             Type::Str(t) => t.hash(state),
             Type::RustVec(t) => t.hash(state),
@@ -65,6 +66,7 @@
             (Type::Ident(lhs), Type::Ident(rhs)) => lhs == rhs,
             (Type::RustBox(lhs), Type::RustBox(rhs)) => lhs == rhs,
             (Type::UniquePtr(lhs), Type::UniquePtr(rhs)) => lhs == rhs,
+            (Type::SharedPtr(lhs), Type::SharedPtr(rhs)) => lhs == rhs,
             (Type::Ref(lhs), Type::Ref(rhs)) => lhs == rhs,
             (Type::Str(lhs), Type::Str(rhs)) => lhs == rhs,
             (Type::RustVec(lhs), Type::RustVec(rhs)) => lhs == rhs,
diff --git a/syntax/improper.rs b/syntax/improper.rs
index e0e5154..c0f6983 100644
--- a/syntax/improper.rs
+++ b/syntax/improper.rs
@@ -28,7 +28,7 @@
             | Type::Fn(_)
             | Type::Void(_)
             | Type::SliceRef(_) => Definite(true),
-            Type::UniquePtr(_) | Type::CxxVector(_) => Definite(false),
+            Type::UniquePtr(_) | Type::SharedPtr(_) | Type::CxxVector(_) => Definite(false),
             Type::Ref(ty) => self.determine_improper_ctype(&ty.inner),
             Type::Array(ty) => self.determine_improper_ctype(&ty.inner),
         }
diff --git a/syntax/mod.rs b/syntax/mod.rs
index e8bd290..12b269e 100644
--- a/syntax/mod.rs
+++ b/syntax/mod.rs
@@ -167,6 +167,7 @@
     RustBox(Box<Ty1>),
     RustVec(Box<Ty1>),
     UniquePtr(Box<Ty1>),
+    SharedPtr(Box<Ty1>),
     Ref(Box<Ref>),
     Str(Box<Ref>),
     CxxVector(Box<Ty1>),
diff --git a/syntax/parse.rs b/syntax/parse.rs
index a3f50ec..e4f0512 100644
--- a/syntax/parse.rs
+++ b/syntax/parse.rs
@@ -822,6 +822,16 @@
                             rangle: generic.gt_token,
                         })));
                     }
+                } else if ident == "SharedPtr" && generic.args.len() == 1 {
+                    if let GenericArgument::Type(arg) = &generic.args[0] {
+                        let inner = parse_type(arg)?;
+                        return Ok(Type::SharedPtr(Box::new(Ty1 {
+                            name: ident,
+                            langle: generic.lt_token,
+                            inner,
+                            rangle: generic.gt_token,
+                        })));
+                    }
                 } else if ident == "CxxVector" && generic.args.len() == 1 {
                     if let GenericArgument::Type(arg) = &generic.args[0] {
                         let inner = parse_type(arg)?;
diff --git a/syntax/tokens.rs b/syntax/tokens.rs
index edbf818..07738d8 100644
--- a/syntax/tokens.rs
+++ b/syntax/tokens.rs
@@ -20,9 +20,11 @@
                 }
                 ident.rust.to_tokens(tokens);
             }
-            Type::RustBox(ty) | Type::UniquePtr(ty) | Type::CxxVector(ty) | Type::RustVec(ty) => {
-                ty.to_tokens(tokens)
-            }
+            Type::RustBox(ty)
+            | Type::UniquePtr(ty)
+            | Type::SharedPtr(ty)
+            | Type::CxxVector(ty)
+            | Type::RustVec(ty) => ty.to_tokens(tokens),
             Type::Ref(r) | Type::Str(r) => r.to_tokens(tokens),
             Type::Array(a) => a.to_tokens(tokens),
             Type::Fn(f) => f.to_tokens(tokens),
@@ -44,7 +46,7 @@
     fn to_tokens(&self, tokens: &mut TokenStream) {
         let span = self.name.span();
         let name = self.name.to_string();
-        if let "UniquePtr" | "CxxVector" = name.as_str() {
+        if let "UniquePtr" | "SharedPtr" | "CxxVector" = name.as_str() {
             tokens.extend(quote_spanned!(span=> ::cxx::));
         } else if name == "Vec" {
             tokens.extend(quote_spanned!(span=> ::std::vec::));
diff --git a/syntax/types.rs b/syntax/types.rs
index c98fe85..70b381f 100644
--- a/syntax/types.rs
+++ b/syntax/types.rs
@@ -45,6 +45,7 @@
                 Type::Ident(_) | Type::Str(_) | Type::Void(_) => {}
                 Type::RustBox(ty)
                 | Type::UniquePtr(ty)
+                | Type::SharedPtr(ty)
                 | Type::CxxVector(ty)
                 | Type::RustVec(ty) => visit(all, &ty.inner),
                 Type::Ref(r) => visit(all, &r.inner),
@@ -254,7 +255,7 @@
                     Atom::from(&ident.rust) == Some(RustString)
                 }
             }
-            Type::RustVec(_) | Type::Array(_) => true,
+            Type::SharedPtr(_) | Type::RustVec(_) | Type::Array(_) => true,
             _ => false,
         }
     }
diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs
index fc69581..0c2b8b6 100644
--- a/tests/ffi/lib.rs
+++ b/tests/ffi/lib.rs
@@ -9,8 +9,9 @@
 pub mod cast;
 pub mod module;
 
-use cxx::{CxxString, CxxVector, UniquePtr};
+use cxx::{CxxString, CxxVector, SharedPtr, UniquePtr};
 use std::fmt::{self, Display};
+use std::mem::MaybeUninit;
 use std::os::raw::c_char;
 
 #[cxx::bridge(namespace = "tests")]
@@ -83,6 +84,7 @@
         fn c_return_shared() -> Shared;
         fn c_return_box() -> Box<R>;
         fn c_return_unique_ptr() -> UniquePtr<C>;
+        fn c_return_shared_ptr() -> SharedPtr<C>;
         fn c_return_ref(shared: &Shared) -> &usize;
         fn c_return_mut(shared: &mut Shared) -> &mut usize;
         fn c_return_str(shared: &Shared) -> &str;
@@ -198,6 +200,7 @@
         fn r_return_shared() -> Shared;
         fn r_return_box() -> Box<R>;
         fn r_return_unique_ptr() -> UniquePtr<C>;
+        fn r_return_shared_ptr() -> SharedPtr<C>;
         fn r_return_ref(shared: &Shared) -> &usize;
         fn r_return_mut(shared: &mut Shared) -> &mut usize;
         fn r_return_str(shared: &Shared) -> &str;
@@ -217,6 +220,7 @@
         fn r_take_shared(shared: Shared);
         fn r_take_box(r: Box<R>);
         fn r_take_unique_ptr(c: UniquePtr<C>);
+        fn r_take_shared_ptr(c: SharedPtr<C>);
         fn r_take_ref_r(r: &R);
         fn r_take_ref_c(c: &C);
         fn r_take_str(s: &str);
@@ -377,6 +381,18 @@
     unsafe { UniquePtr::from_raw(cxx_test_suite_get_unique_ptr()) }
 }
 
+fn r_return_shared_ptr() -> SharedPtr<ffi::C> {
+    extern "C" {
+        fn cxx_test_suite_get_shared_ptr(repr: *mut SharedPtr<ffi::C>);
+    }
+    let mut shared_ptr = MaybeUninit::<SharedPtr<ffi::C>>::uninit();
+    let repr = shared_ptr.as_mut_ptr();
+    unsafe {
+        cxx_test_suite_get_shared_ptr(repr);
+        shared_ptr.assume_init()
+    }
+}
+
 fn r_return_ref(shared: &ffi::Shared) -> &usize {
     &shared.z
 }
@@ -462,6 +478,10 @@
     let _ = c;
 }
 
+fn r_take_shared_ptr(c: SharedPtr<ffi::C>) {
+    let _ = c;
+}
+
 fn r_take_ref_r(r: &R) {
     let _ = r;
 }
diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc
index 6fd43ae..47d447c 100644
--- a/tests/ffi/tests.cc
+++ b/tests/ffi/tests.cc
@@ -57,6 +57,10 @@
   return std::unique_ptr<C>(new C{2020});
 }
 
+std::shared_ptr<C> c_return_shared_ptr() {
+  return std::shared_ptr<C>(new C{2020});
+}
+
 std::unique_ptr<::H::H> c_return_ns_unique_ptr() {
   return std::unique_ptr<::H::H>(new ::H::H{"hello"});
 }
@@ -479,6 +483,11 @@
   return std::unique_ptr<C>(new C{2020}).release();
 }
 
+extern "C" void
+cxx_test_suite_get_shared_ptr(std::shared_ptr<C> *repr) noexcept {
+  new (repr) std::shared_ptr<C>(new C{2020});
+}
+
 extern "C" std::string *cxx_test_suite_get_unique_ptr_string() noexcept {
   return std::unique_ptr<std::string>(new std::string("2020")).release();
 }
@@ -611,6 +620,7 @@
   ASSERT(r_return_shared().z == 2020);
   ASSERT(cxx_test_suite_r_is_correct(&*r_return_box()));
   ASSERT(r_return_unique_ptr()->get() == 2020);
+  ASSERT(r_return_shared_ptr()->get() == 2020);
   ASSERT(r_return_ref(Shared{2020}) == 2020);
   ASSERT(std::string(r_return_str(Shared{2020})) == "2020");
   ASSERT(std::string(r_return_rust_string()) == "2020");
@@ -624,6 +634,7 @@
   r_take_primitive(2020);
   r_take_shared(Shared{2020});
   r_take_unique_ptr(std::unique_ptr<C>(new C{2020}));
+  r_take_shared_ptr(std::shared_ptr<C>(new C{2020}));
   r_take_ref_c(C{2020});
   r_take_str(rust::Str("2020"));
   r_take_slice_char(rust::Slice<const char>(SLICE_DATA, sizeof(SLICE_DATA)));
diff --git a/tests/ffi/tests.h b/tests/ffi/tests.h
index 5791983..0e16ab7 100644
--- a/tests/ffi/tests.h
+++ b/tests/ffi/tests.h
@@ -77,6 +77,7 @@
 ::A::B::ABShared c_return_nested_ns_shared();
 rust::Box<R> c_return_box();
 std::unique_ptr<C> c_return_unique_ptr();
+std::shared_ptr<C> c_return_shared_ptr();
 std::unique_ptr<::H::H> c_return_ns_unique_ptr();
 const size_t &c_return_ref(const Shared &shared);
 const size_t &c_return_ns_ref(const ::A::AShared &shared);
diff --git a/tests/test.rs b/tests/test.rs
index 1b9a88e..d339152 100644
--- a/tests/test.rs
+++ b/tests/test.rs
@@ -36,6 +36,7 @@
     assert_eq!(2020, ffi::c_return_shared().z);
     assert_eq!(2020, ffi::c_return_box().0);
     ffi::c_return_unique_ptr();
+    ffi::c_return_shared_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));