Repository: dtolnay/cxx (rust)

Issue:
Add binding for std::weak_ptr
https://en.cppreference.com/w/cpp/memory/weak_ptr/weak_ptr

We support std::shared_ptr from #536, and weak_ptr often occurs in related code.

Pull request description:
Add Rust std::weak_ptr binding
Closes #607.

This is substantially equivalent to everything we have for SharedPtr except without Deref impl, and in addition these two functions to go back and forth:

```rust
impl<T> SharedPtr<T> {
    pub fn downgrade(self: &SharedPtr<T>) -> WeakPtr<T>;
}

impl<T> WeakPtr<T> {
    pub fn upgrade(&self) -> SharedPtr<T>;
}
```

which match the naming used in the standard library's [`Arc::downgrade`](https://doc.rust-lang.org/std/sync/struct.Arc.html#method.downgrade) and [`Weak::upgrade`](https://doc.rust-lang.org/std/sync/struct.Weak.html#method.upgrade).

Patch:
diff --git a/README.md b/README.md
index d21eebe6..f6a18ebb 100644
--- a/README.md
+++ b/README.md
@@ -351,7 +351,6 @@ matter of designing a nice API for each in its non-native language.
 <tr><td>Option&lt;T&gt;</td><td><sup><i>tbd</i></sup></td></tr>
 <tr><td><sup><i>tbd</i></sup></td><td>std::map&lt;K, V&gt;</td></tr>
 <tr><td><sup><i>tbd</i></sup></td><td>std::unordered_map&lt;K, V&gt;</td></tr>
-<tr><td><sup><i>tbd</i></sup></td><td>std::weak_ptr&lt;T&gt;</td></tr>
 </table>
 
 <br>
diff --git a/book/src/bindings.md b/book/src/bindings.md
index ec7e2d28..b6e0e306 100644
--- a/book/src/bindings.md
+++ b/book/src/bindings.md
@@ -52,5 +52,4 @@ matter of designing a nice API for each in its non-native language.
 <tr><td>Option&lt;T&gt;</td><td><sup><i>tbd</i></sup></td></tr>
 <tr><td><sup><i>tbd</i></sup></td><td>std::map&lt;K, V&gt;</td></tr>
 <tr><td><sup><i>tbd</i></sup></td><td>std::unordered_map&lt;K, V&gt;</td></tr>
-<tr><td><sup><i>tbd</i></sup></td><td>std::weak_ptr&lt;T&gt;</td></tr>
 </table>
diff --git a/gen/src/write.rs b/gen/src/write.rs
index 981628cd..4e802da8 100644
--- a/gen/src/write.rs
+++ b/gen/src/write.rs
@@ -208,7 +208,7 @@ fn pick_includes_and_builtins(out: &mut OutFile, apis: &[Api]) {
             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::SharedPtr(_) | Type::WeakPtr(_) => out.include.memory = true,
             Type::Str(_) => out.builtin.rust_str = true,
             Type::CxxVector(_) => out.include.vector = true,
             Type::Fn(_) => out.builtin.rust_fn = true,
@@ -1172,6 +1172,11 @@ fn write_type(out: &mut OutFile, ty: &Type) {
             write_type(out, &ptr.inner);
             write!(out, ">");
         }
+        Type::WeakPtr(ptr) => {
+            write!(out, "::std::weak_ptr<");
+            write_type(out, &ptr.inner);
+            write!(out, ">");
+        }
         Type::CxxVector(ty) => {
             write!(out, "::std::vector<");
             write_type(out, &ty.inner);
@@ -1251,6 +1256,7 @@ fn write_space_after_type(out: &mut OutFile, ty: &Type) {
         | Type::RustBox(_)
         | Type::UniquePtr(_)
         | Type::SharedPtr(_)
+        | Type::WeakPtr(_)
         | Type::Str(_)
         | Type::CxxVector(_)
         | Type::RustVec(_)
@@ -1357,6 +1363,16 @@ fn write_generic_instantiations(out: &mut OutFile) {
                     write_shared_ptr(out, inner);
                 }
             }
+        } else if let Type::WeakPtr(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_weak_ptr(out, inner);
+                }
+            }
         } else if let Type::CxxVector(vector) = ty {
             if let Type::Ident(inner) = &vector.inner {
                 if Atom::from(&inner.rust).is_none()
@@ -1749,6 +1765,65 @@ fn write_shared_ptr(out: &mut OutFile, ident: &RustName) {
     writeln!(out, "}}");
 }
 
+fn write_weak_ptr(out: &mut OutFile, ident: &RustName) {
+    let resolved = out.types.resolve(ident);
+    let inner = resolved.to_fully_qualified();
+    let instance = ident.to_symbol(out.types);
+
+    out.include.new = true;
+    out.include.utility = true;
+
+    writeln!(
+        out,
+        "static_assert(sizeof(::std::weak_ptr<{}>) == 2 * sizeof(void *), \"\");",
+        inner,
+    );
+    writeln!(
+        out,
+        "static_assert(alignof(::std::weak_ptr<{}>) == alignof(void *), \"\");",
+        inner,
+    );
+    writeln!(
+        out,
+        "void cxxbridge1$weak_ptr${}$null(::std::weak_ptr<{}> *ptr) noexcept {{",
+        instance, inner,
+    );
+    writeln!(out, "  ::new (ptr) ::std::weak_ptr<{}>();", inner);
+    writeln!(out, "}}");
+    writeln!(
+        out,
+        "void cxxbridge1$weak_ptr${}$clone(const ::std::weak_ptr<{}>& self, ::std::weak_ptr<{}> *ptr) noexcept {{",
+        instance, inner, inner,
+    );
+    writeln!(out, "  ::new (ptr) ::std::weak_ptr<{}>(self);", inner);
+    writeln!(out, "}}");
+    writeln!(
+        out,
+        "void cxxbridge1$weak_ptr${}$downgrade(const ::std::shared_ptr<{}>& shared, ::std::weak_ptr<{}> *weak) noexcept {{",
+        instance, inner, inner,
+    );
+    writeln!(out, "  ::new (weak) ::std::weak_ptr<{}>(shared);", inner);
+    writeln!(out, "}}");
+    writeln!(
+        out,
+        "void cxxbridge1$weak_ptr${}$upgrade(const ::std::weak_ptr<{}>& weak, ::std::shared_ptr<{}> *shared) noexcept {{",
+        instance, inner, inner,
+    );
+    writeln!(
+        out,
+        "  ::new (shared) ::std::shared_ptr<{}>(weak.lock());",
+        inner,
+    );
+    writeln!(out, "}}");
+    writeln!(
+        out,
+        "void cxxbridge1$weak_ptr${}$drop(::std::weak_ptr<{}> *self) noexcept {{",
+        instance, inner,
+    );
+    writeln!(out, "  self->~weak_ptr();");
+    writeln!(out, "}}");
+}
+
 fn write_cxx_vector(out: &mut OutFile, element: &RustName) {
     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 d8925993..e53e5b63 100644
--- a/macro/src/expand.rs
+++ b/macro/src/expand.rs
@@ -101,6 +101,14 @@ fn expand(ffi: Module, apis: &[Api], types: &Types) -> TokenStream {
                     expanded.extend(expand_shared_ptr(ident, types, explicit_impl));
                 }
             }
+        } else if let Type::WeakPtr(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_weak_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()
@@ -1229,6 +1237,62 @@ fn expand_shared_ptr(ident: &RustName, types: &Types, explicit_impl: Option<&Imp
     }
 }
 
+fn expand_weak_ptr(ident: &RustName, types: &Types, explicit_impl: Option<&Impl>) -> TokenStream {
+    let name = ident.rust.to_string();
+    let prefix = format!("cxxbridge1$weak_ptr${}$", ident.to_symbol(types));
+    let link_null = format!("{}null", prefix);
+    let link_clone = format!("{}clone", prefix);
+    let link_downgrade = format!("{}downgrade", prefix);
+    let link_upgrade = format!("{}upgrade", prefix);
+    let link_drop = format!("{}drop", prefix);
+
+    let 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::WeakPtrTarget 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);
+            }
+            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 __downgrade(shared: *const ::std::ffi::c_void, weak: *mut ::std::ffi::c_void) {
+                extern "C" {
+                    #[link_name = #link_downgrade]
+                    fn __downgrade(shared: *const ::std::ffi::c_void, weak: *mut ::std::ffi::c_void);
+                }
+                __downgrade(shared, weak);
+            }
+            unsafe fn __upgrade(weak: *const ::std::ffi::c_void, shared: *mut ::std::ffi::c_void) {
+                extern "C" {
+                    #[link_name = #link_upgrade]
+                    fn __upgrade(weak: *const ::std::ffi::c_void, shared: *mut ::std::ffi::c_void);
+                }
+                __upgrade(weak, shared);
+            }
+            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: &RustName, explicit_impl: Option<&Impl>, types: &Types) -> TokenStream {
     let _ = explicit_impl;
     let name = elem.rust.to_string();
diff --git a/src/cxx.cc b/src/cxx.cc
index b15dcaa3..6b71656d 100644
--- a/src/cxx.cc
+++ b/src/cxx.cc
@@ -508,6 +508,31 @@ static_assert(sizeof(std::string) <= kMaxExpectedWordsInString * sizeof(void *),
   void cxxbridge1$std$shared_ptr$##RUST_TYPE##$drop(                           \
       const std::shared_ptr<CXX_TYPE> *self) noexcept {                        \
     self->~shared_ptr();                                                       \
+  }                                                                            \
+  static_assert(sizeof(std::weak_ptr<CXX_TYPE>) == 2 * sizeof(void *), "");    \
+  static_assert(alignof(std::weak_ptr<CXX_TYPE>) == alignof(void *), "");      \
+  void cxxbridge1$std$weak_ptr$##RUST_TYPE##$null(                             \
+      std::weak_ptr<CXX_TYPE> *ptr) noexcept {                                 \
+    new (ptr) std::weak_ptr<CXX_TYPE>();                                       \
+  }                                                                            \
+  void cxxbridge1$std$weak_ptr$##RUST_TYPE##$clone(                            \
+      const std::weak_ptr<CXX_TYPE> &self,                                     \
+      std::weak_ptr<CXX_TYPE> *ptr) noexcept {                                 \
+    new (ptr) std::weak_ptr<CXX_TYPE>(self);                                   \
+  }                                                                            \
+  void cxxbridge1$std$weak_ptr$##RUST_TYPE##$downgrade(                        \
+      const std::shared_ptr<CXX_TYPE> &shared,                                 \
+      std::weak_ptr<CXX_TYPE> *weak) noexcept {                                \
+    new (weak) std::weak_ptr<CXX_TYPE>(shared);                                \
+  }                                                                            \
+  void cxxbridge1$std$weak_ptr$##RUST_TYPE##$upgrade(                          \
+      const std::weak_ptr<CXX_TYPE> &weak,                                     \
+      std::shared_ptr<CXX_TYPE> *shared) noexcept {                            \
+    new (shared) std::shared_ptr<CXX_TYPE>(weak.lock());                       \
+  }                                                                            \
+  void cxxbridge1$std$weak_ptr$##RUST_TYPE##$drop(                             \
+      const std::weak_ptr<CXX_TYPE> *self) noexcept {                          \
+    self->~weak_ptr();                                                         \
   }
 
 // Usize and isize are the same type as one of the below.
diff --git a/src/lib.rs b/src/lib.rs
index ff723c31..76453ea6 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -360,7 +360,6 @@
 //! <tr><td>Option&lt;T&gt;</td><td><sup><i>tbd</i></sup></td></tr>
 //! <tr><td><sup><i>tbd</i></sup></td><td>std::map&lt;K, V&gt;</td></tr>
 //! <tr><td><sup><i>tbd</i></sup></td><td>std::unordered_map&lt;K, V&gt;</td></tr>
-//! <tr><td><sup><i>tbd</i></sup></td><td>std::weak_ptr&lt;T&gt;</td></tr>
 //! </table>
 
 #![no_std]
@@ -411,6 +410,7 @@ mod unique_ptr;
 mod unwind;
 #[path = "cxx_vector.rs"]
 pub mod vector;
+mod weak_ptr;
 
 pub use crate::exception::Exception;
 pub use crate::extern_type::{kind, ExternType};
@@ -419,6 +419,7 @@ pub use crate::string::CxxString;
 pub use crate::unique_ptr::UniquePtr;
 #[doc(inline)]
 pub use crate::vector::CxxVector;
+pub use crate::weak_ptr::WeakPtr;
 pub use cxxbridge_macro::bridge;
 
 /// For use in impls of the `ExternType` trait. See [`ExternType`].
@@ -457,6 +458,7 @@ pub mod private {
     pub use crate::unique_ptr::UniquePtrTarget;
     pub use crate::unwind::catch_unwind;
     pub use crate::vector::VectorElement;
+    pub use crate::weak_ptr::WeakPtrTarget;
 }
 
 mod actually_private {
diff --git a/src/shared_ptr.rs b/src/shared_ptr.rs
index 9269e5a2..18f600fb 100644
--- a/src/shared_ptr.rs
+++ b/src/shared_ptr.rs
@@ -1,5 +1,6 @@
 use crate::kind::Trivial;
 use crate::string::CxxString;
+use crate::weak_ptr::{WeakPtr, WeakPtrTarget};
 use crate::ExternType;
 use core::ffi::c_void;
 use core::fmt::{self, Debug, Display};
@@ -61,6 +62,24 @@ where
         let this = self as *const Self as *const c_void;
         unsafe { T::__get(this).as_ref() }
     }
+
+    /// Constructs new WeakPtr as a non-owning reference to the object managed
+    /// by `self`. If `self` manages no object, the WeakPtr manages no object
+    /// too.
+    ///
+    /// Matches the behavior of [std::weak_ptr\<T\>::weak_ptr(const std::shared_ptr\<T\> \&)](https://en.cppreference.com/w/cpp/memory/weak_ptr/weak_ptr).
+    pub fn downgrade(self: &SharedPtr<T>) -> WeakPtr<T>
+    where
+        T: WeakPtrTarget,
+    {
+        let this = self as *const Self as *const c_void;
+        let mut weak_ptr = MaybeUninit::<WeakPtr<T>>::uninit();
+        let new = weak_ptr.as_mut_ptr().cast();
+        unsafe {
+            T::__downgrade(this, new);
+            weak_ptr.assume_init()
+        }
+    }
 }
 
 unsafe impl<T> Send for SharedPtr<T> where T: Send + Sync + SharedPtrTarget {}
diff --git a/src/weak_ptr.rs b/src/weak_ptr.rs
new file mode 100644
index 00000000..7fc4c78a
--- /dev/null
+++ b/src/weak_ptr.rs
@@ -0,0 +1,184 @@
+use crate::shared_ptr::{SharedPtr, SharedPtrTarget};
+use crate::string::CxxString;
+use core::ffi::c_void;
+use core::fmt::{self, Debug, Display};
+use core::marker::PhantomData;
+use core::mem::MaybeUninit;
+
+/// Binding to C++ `std::weak_ptr<T>`.
+///
+/// The typical way to construct a WeakPtr from Rust is by [downgrading] from a
+/// SharedPtr.
+///
+/// [downgrading]: crate::SharedPtr::downgrade
+#[repr(C)]
+pub struct WeakPtr<T>
+where
+    T: WeakPtrTarget,
+{
+    repr: [*mut c_void; 2],
+    ty: PhantomData<T>,
+}
+
+impl<T> WeakPtr<T>
+where
+    T: WeakPtrTarget,
+{
+    /// Makes a new WeakPtr wrapping a null pointer.
+    ///
+    /// Matches the behavior of default-constructing a std::weak\_ptr.
+    pub fn null() -> Self {
+        let mut weak_ptr = MaybeUninit::<WeakPtr<T>>::uninit();
+        let new = weak_ptr.as_mut_ptr().cast();
+        unsafe {
+            T::__null(new);
+            weak_ptr.assume_init()
+        }
+    }
+
+    /// Upgrades a non-owning reference into an owning reference if possible,
+    /// otherwise to a null reference.
+    ///
+    /// Matches the behavior of [std::weak_ptr\<T\>::lock](https://en.cppreference.com/w/cpp/memory/weak_ptr/lock).
+    pub fn upgrade(&self) -> SharedPtr<T>
+    where
+        T: SharedPtrTarget,
+    {
+        let this = self as *const Self as *const c_void;
+        let mut shared_ptr = MaybeUninit::<SharedPtr<T>>::uninit();
+        let new = shared_ptr.as_mut_ptr().cast();
+        unsafe {
+            T::__upgrade(this, new);
+            shared_ptr.assume_init()
+        }
+    }
+}
+
+unsafe impl<T> Send for WeakPtr<T> where T: Send + Sync + WeakPtrTarget {}
+unsafe impl<T> Sync for WeakPtr<T> where T: Send + Sync + WeakPtrTarget {}
+
+impl<T> Clone for WeakPtr<T>
+where
+    T: WeakPtrTarget,
+{
+    fn clone(&self) -> Self {
+        let mut weak_ptr = MaybeUninit::<WeakPtr<T>>::uninit();
+        let new = weak_ptr.as_mut_ptr().cast();
+        let this = self as *const Self as *mut c_void;
+        unsafe {
+            T::__clone(this, new);
+            weak_ptr.assume_init()
+        }
+    }
+}
+
+impl<T> Drop for WeakPtr<T>
+where
+    T: WeakPtrTarget,
+{
+    fn drop(&mut self) {
+        let this = self as *mut Self as *mut c_void;
+        unsafe { T::__drop(this) }
+    }
+}
+
+impl<T> Debug for WeakPtr<T>
+where
+    T: Debug + WeakPtrTarget + SharedPtrTarget,
+{
+    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+        Debug::fmt(&self.upgrade(), formatter)
+    }
+}
+
+// Methods are private; not intended to be implemented outside of cxxbridge
+// codebase.
+pub unsafe trait WeakPtrTarget {
+    #[doc(hidden)]
+    const __NAME: &'static dyn Display;
+    #[doc(hidden)]
+    unsafe fn __null(new: *mut c_void);
+    #[doc(hidden)]
+    unsafe fn __clone(this: *const c_void, new: *mut c_void);
+    #[doc(hidden)]
+    unsafe fn __downgrade(shared: *const c_void, new: *mut c_void);
+    #[doc(hidden)]
+    unsafe fn __upgrade(weak: *const c_void, shared: *mut c_void);
+    #[doc(hidden)]
+    unsafe fn __drop(this: *mut c_void);
+}
+
+macro_rules! impl_weak_ptr_target {
+    ($segment:expr, $name:expr, $ty:ty) => {
+        unsafe impl WeakPtrTarget for $ty {
+            const __NAME: &'static dyn Display = &$name;
+            unsafe fn __null(new: *mut c_void) {
+                extern "C" {
+                    attr! {
+                        #[link_name = concat!("cxxbridge1$std$weak_ptr$", $segment, "$null")]
+                        fn __null(new: *mut c_void);
+                    }
+                }
+                __null(new);
+            }
+            unsafe fn __clone(this: *const c_void, new: *mut c_void) {
+                extern "C" {
+                    attr! {
+                        #[link_name = concat!("cxxbridge1$std$weak_ptr$", $segment, "$clone")]
+                        fn __clone(this: *const c_void, new: *mut c_void);
+                    }
+                }
+                __clone(this, new);
+            }
+            unsafe fn __downgrade(shared: *const c_void, weak: *mut c_void) {
+                extern "C" {
+                    attr! {
+                        #[link_name = concat!("cxxbridge1$std$weak_ptr$", $segment, "$downgrade")]
+                        fn __downgrade(shared: *const c_void, weak: *mut c_void);
+                    }
+                }
+                __downgrade(shared, weak);
+            }
+            unsafe fn __upgrade(weak: *const c_void, shared: *mut c_void) {
+                extern "C" {
+                    attr! {
+                        #[link_name = concat!("cxxbridge1$std$weak_ptr$", $segment, "$upgrade")]
+                        fn __upgrade(weak: *const c_void, shared: *mut c_void);
+                    }
+                }
+                __upgrade(weak, shared);
+            }
+            unsafe fn __drop(this: *mut c_void) {
+                extern "C" {
+                    attr! {
+                        #[link_name = concat!("cxxbridge1$std$weak_ptr$", $segment, "$drop")]
+                        fn __drop(this: *mut c_void);
+                    }
+                }
+                __drop(this);
+            }
+        }
+    };
+}
+
+macro_rules! impl_weak_ptr_target_for_primitive {
+    ($ty:ident) => {
+        impl_weak_ptr_target!(stringify!($ty), stringify!($ty), $ty);
+    };
+}
+
+impl_weak_ptr_target_for_primitive!(bool);
+impl_weak_ptr_target_for_primitive!(u8);
+impl_weak_ptr_target_for_primitive!(u16);
+impl_weak_ptr_target_for_primitive!(u32);
+impl_weak_ptr_target_for_primitive!(u64);
+impl_weak_ptr_target_for_primitive!(usize);
+impl_weak_ptr_target_for_primitive!(i8);
+impl_weak_ptr_target_for_primitive!(i16);
+impl_weak_ptr_target_for_primitive!(i32);
+impl_weak_ptr_target_for_primitive!(i64);
+impl_weak_ptr_target_for_primitive!(isize);
+impl_weak_ptr_target_for_primitive!(f32);
+impl_weak_ptr_target_for_primitive!(f64);
+
+impl_weak_ptr_target!("string", "CxxString", CxxString);
diff --git a/syntax/check.rs b/syntax/check.rs
index ded5c530..39ea04ec 100644
--- a/syntax/check.rs
+++ b/syntax/check.rs
@@ -32,6 +32,7 @@ fn do_typecheck(cx: &mut Check) {
             Type::RustVec(ty) => check_type_rust_vec(cx, ty),
             Type::UniquePtr(ptr) => check_type_unique_ptr(cx, ptr),
             Type::SharedPtr(ptr) => check_type_shared_ptr(cx, ptr),
+            Type::WeakPtr(ptr) => check_type_weak_ptr(cx, ptr),
             Type::CxxVector(ptr) => check_type_cxx_vector(cx, ptr),
             Type::Ref(ty) => check_type_ref(cx, ty),
             Type::Array(array) => check_type_array(cx, array),
@@ -152,6 +153,27 @@ fn check_type_shared_ptr(cx: &mut Check, ptr: &Ty1) {
     cx.error(ptr, "unsupported shared_ptr target type");
 }
 
+fn check_type_weak_ptr(cx: &mut Check, ptr: &Ty1) {
+    if let Type::Ident(ident) = &ptr.inner {
+        if cx.types.rust.contains(&ident.rust) {
+            cx.error(ptr, "weak_ptr of a Rust type is not supported yet");
+            return;
+        }
+
+        match Atom::from(&ident.rust) {
+            None | Some(Bool) | Some(U8) | Some(U16) | Some(U32) | Some(U64) | Some(Usize)
+            | Some(I8) | Some(I16) | Some(I32) | Some(I64) | Some(Isize) | Some(F32)
+            | Some(F64) | Some(CxxString) => return,
+            Some(Char) | Some(RustString) => {}
+        }
+    } else if let Type::CxxVector(_) = &ptr.inner {
+        cx.error(ptr, "std::weak_ptr<std::vector> is not supported yet");
+        return;
+    }
+
+    cx.error(ptr, "unsupported weak_ptr target type");
+}
+
 fn check_type_cxx_vector(cx: &mut Check, ptr: &Ty1) {
     if let Type::Ident(ident) = &ptr.inner {
         if cx.types.rust.contains(&ident.rust) {
@@ -434,6 +456,7 @@ fn check_api_impl(cx: &mut Check, imp: &Impl) {
         | Type::RustVec(ty)
         | Type::UniquePtr(ty)
         | Type::SharedPtr(ty)
+        | Type::WeakPtr(ty)
         | Type::CxxVector(ty) => {
             if let Type::Ident(inner) = &ty.inner {
                 if Atom::from(&inner.rust).is_none() {
@@ -506,6 +529,7 @@ fn check_reserved_name(cx: &mut Check, ident: &Ident) {
     if ident == "Box"
         || ident == "UniquePtr"
         || ident == "SharedPtr"
+        || ident == "WeakPtr"
         || ident == "Vec"
         || ident == "CxxVector"
         || ident == "str"
@@ -527,6 +551,7 @@ fn is_unsized(cx: &mut Check, ty: &Type) -> bool {
         | Type::RustVec(_)
         | Type::UniquePtr(_)
         | Type::SharedPtr(_)
+        | Type::WeakPtr(_)
         | Type::Ref(_)
         | Type::Str(_)
         | Type::SliceRef(_) => false,
@@ -599,6 +624,7 @@ fn describe(cx: &mut Check, ty: &Type) -> String {
         Type::RustVec(_) => "Vec".to_owned(),
         Type::UniquePtr(_) => "unique_ptr".to_owned(),
         Type::SharedPtr(_) => "shared_ptr".to_owned(),
+        Type::WeakPtr(_) => "weak_ptr".to_owned(),
         Type::Ref(_) => "reference".to_owned(),
         Type::Str(_) => "&str".to_owned(),
         Type::CxxVector(_) => "C++ vector".to_owned(),
diff --git a/syntax/impls.rs b/syntax/impls.rs
index 9b154f68..d70e7cc5 100644
--- a/syntax/impls.rs
+++ b/syntax/impls.rs
@@ -46,6 +46,7 @@ impl Hash for Type {
             Type::RustBox(t) => t.hash(state),
             Type::UniquePtr(t) => t.hash(state),
             Type::SharedPtr(t) => t.hash(state),
+            Type::WeakPtr(t) => t.hash(state),
             Type::Ref(t) => t.hash(state),
             Type::Str(t) => t.hash(state),
             Type::RustVec(t) => t.hash(state),
@@ -67,6 +68,7 @@ impl PartialEq for Type {
             (Type::RustBox(lhs), Type::RustBox(rhs)) => lhs == rhs,
             (Type::UniquePtr(lhs), Type::UniquePtr(rhs)) => lhs == rhs,
             (Type::SharedPtr(lhs), Type::SharedPtr(rhs)) => lhs == rhs,
+            (Type::WeakPtr(lhs), Type::WeakPtr(rhs)) => lhs == rhs,
             (Type::Ref(lhs), Type::Ref(rhs)) => lhs == rhs,
             (Type::Str(lhs), Type::Str(rhs)) => lhs == rhs,
             (Type::RustVec(lhs), Type::RustVec(rhs)) => lhs == rhs,
diff --git a/syntax/improper.rs b/syntax/improper.rs
index c0f69832..d672e7d8 100644
--- a/syntax/improper.rs
+++ b/syntax/improper.rs
@@ -28,7 +28,9 @@ impl<'a> Types<'a> {
             | Type::Fn(_)
             | Type::Void(_)
             | Type::SliceRef(_) => Definite(true),
-            Type::UniquePtr(_) | Type::SharedPtr(_) | Type::CxxVector(_) => Definite(false),
+            Type::UniquePtr(_) | Type::SharedPtr(_) | Type::WeakPtr(_) | 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 7f8fd9a5..111cec34 100644
--- a/syntax/mod.rs
+++ b/syntax/mod.rs
@@ -170,6 +170,7 @@ pub enum Type {
     RustVec(Box<Ty1>),
     UniquePtr(Box<Ty1>),
     SharedPtr(Box<Ty1>),
+    WeakPtr(Box<Ty1>),
     Ref(Box<Ref>),
     Str(Box<Ref>),
     CxxVector(Box<Ty1>),
diff --git a/syntax/parse.rs b/syntax/parse.rs
index f72253ac..1ae4c2df 100644
--- a/syntax/parse.rs
+++ b/syntax/parse.rs
@@ -901,6 +901,16 @@ fn parse_type_path(ty: &TypePath) -> Result<Type> {
                             rangle: generic.gt_token,
                         })));
                     }
+                } else if ident == "WeakPtr" && generic.args.len() == 1 {
+                    if let GenericArgument::Type(arg) = &generic.args[0] {
+                        let inner = parse_type(arg)?;
+                        return Ok(Type::WeakPtr(Box::new(Ty1 {
+                            name: ident,
+                            langle: generic.lt_token,
+                            inner,
+                            rangle: generic.gt_token,
+                        })));
+                    }
                 } else if ident == "CxxVector" && generic.args.len() == 1 {
                     if let GenericArgument::Type(arg) = &generic.args[0] {
                         let inner = parse_type(arg)?;
diff --git a/syntax/pod.rs b/syntax/pod.rs
index d808a99a..e1c48313 100644
--- a/syntax/pod.rs
+++ b/syntax/pod.rs
@@ -26,6 +26,7 @@ impl<'a> Types<'a> {
             | Type::RustVec(_)
             | Type::UniquePtr(_)
             | Type::SharedPtr(_)
+            | Type::WeakPtr(_)
             | Type::CxxVector(_)
             | Type::Void(_) => false,
             Type::Ref(_) | Type::Str(_) | Type::Fn(_) | Type::SliceRef(_) => true,
diff --git a/syntax/tokens.rs b/syntax/tokens.rs
index ae2d1d9d..8348704b 100644
--- a/syntax/tokens.rs
+++ b/syntax/tokens.rs
@@ -23,6 +23,7 @@ impl ToTokens for Type {
             Type::RustBox(ty)
             | Type::UniquePtr(ty)
             | Type::SharedPtr(ty)
+            | Type::WeakPtr(ty)
             | Type::CxxVector(ty)
             | Type::RustVec(ty) => ty.to_tokens(tokens),
             Type::Ref(r) | Type::Str(r) => r.to_tokens(tokens),
@@ -46,7 +47,7 @@ impl ToTokens for Ty1 {
     fn to_tokens(&self, tokens: &mut TokenStream) {
         let span = self.name.span();
         let name = self.name.to_string();
-        if let "UniquePtr" | "SharedPtr" | "CxxVector" = name.as_str() {
+        if let "UniquePtr" | "SharedPtr" | "WeakPtr" | "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 10568549..f7682bce 100644
--- a/syntax/types.rs
+++ b/syntax/types.rs
@@ -45,6 +45,7 @@ impl<'a> Types<'a> {
                 Type::RustBox(ty)
                 | Type::UniquePtr(ty)
                 | Type::SharedPtr(ty)
+                | Type::WeakPtr(ty)
                 | Type::CxxVector(ty)
                 | Type::RustVec(ty) => visit(all, &ty.inner),
                 Type::Ref(r) => visit(all, &r.inner),


Test patch:
diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs
index c41433f9..2d564fac 100644
--- a/tests/ffi/lib.rs
+++ b/tests/ffi/lib.rs
@@ -178,6 +178,8 @@ pub mod ffi {
         fn c_method_mut_on_shared(self: &mut Shared) -> &mut usize;
         fn c_set_array(self: &mut Array, value: i32);
 
+        fn c_get_use_count(weak: &WeakPtr<C>) -> usize;
+
         #[rust_name = "i32_overloaded_method"]
         fn cOverloadedMethod(&self, x: i32) -> String;
         #[rust_name = "str_overloaded_method"]
diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc
index 0ecedb1b..50a11dae 100644
--- a/tests/ffi/tests.cc
+++ b/tests/ffi/tests.cc
@@ -538,6 +538,10 @@ const rust::Vec<uint8_t> &c_try_return_ref_rust_vec(const C &c) {
   throw std::runtime_error("unimplemented");
 }
 
+size_t c_get_use_count(const std::weak_ptr<C> &weak) noexcept {
+  return weak.use_count();
+}
+
 extern "C" C *cxx_test_suite_get_unique_ptr() noexcept {
   return std::unique_ptr<C>(new C{2020}).release();
 }
diff --git a/tests/ffi/tests.h b/tests/ffi/tests.h
index 1f83ce50..8235f5f8 100644
--- a/tests/ffi/tests.h
+++ b/tests/ffi/tests.h
@@ -168,6 +168,8 @@ rust::Vec<uint8_t> c_try_return_rust_vec();
 rust::Vec<rust::String> c_try_return_rust_vec_string();
 const rust::Vec<uint8_t> &c_try_return_ref_rust_vec(const C &c);
 
+size_t c_get_use_count(const std::weak_ptr<C> &weak) noexcept;
+
 void c_take_trivial_ptr(std::unique_ptr<D> d);
 void c_take_trivial_ref(const D &d);
 void c_take_trivial(D d);
diff --git a/tests/test.rs b/tests/test.rs
index 4427c7c8..273abe9c 100644
--- a/tests/test.rs
+++ b/tests/test.rs
@@ -1,5 +1,6 @@
 #![allow(clippy::assertions_on_constants, clippy::float_cmp, clippy::unit_cmp)]
 
+use cxx::SharedPtr;
 use cxx_test_suite::module::ffi2;
 use cxx_test_suite::{cast, ffi, R};
 use std::cell::Cell;
@@ -36,7 +37,6 @@ fn test_c_return() {
     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));
@@ -241,6 +241,20 @@ fn test_c_method_calls() {
     assert_eq!(array.a.len() as i32 * val, array.r_get_array_sum());
 }
 
+#[test]
+fn test_shared_ptr_weak_ptr() {
+    let shared_ptr = ffi::c_return_shared_ptr();
+    let weak_ptr = SharedPtr::downgrade(&shared_ptr);
+    assert_eq!(1, ffi::c_get_use_count(&weak_ptr));
+
+    assert!(!weak_ptr.upgrade().is_null());
+    assert_eq!(1, ffi::c_get_use_count(&weak_ptr));
+
+    drop(shared_ptr);
+    assert_eq!(0, ffi::c_get_use_count(&weak_ptr));
+    assert!(weak_ptr.upgrade().is_null());
+}
+
 #[test]
 fn test_c_ns_method_calls() {
     let unique_ptr = ffi2::ns_c_return_unique_ptr_ns();