Repository: dtolnay/cxx (language: rust)

Issue:
impl UniquePtrTarget for T;
Builds on top of #325. Fixes #236.


Discussion / hints:
Can we do this without exposing - and requiring users to know - the internal trait names like `UniquePtrTarget`? In #308 dtolnay@ suggested something like `impl Vec<T> {}` and `impl UniquePtr<T> {}`.
Yes, probably, I will amend that way after I eventually rebase on top of your stuff.
One other thing to consider here. For Vec and Box after my PR #308, the following only fails at C++ compile time because we don't have traits similar to `UniquePtrTarget` and `VectorElement` for those that can be checked with a trait bound.

```
#[cxx::bridge]
mod here {
    struct Shared {
        z: usize,
    }
}

#[cxx::bridge]
mod there {
    type Shared = crate::here::Shared;

    extern "C" {
        fn c_take_rust_vec_shared(v: Vec<Shared>);
    }
}

fn main() {}
```

We might want to add those in a separate PR to improve the diagnostics, plus then we can point people at this option.

Fix patch:
diff --git a/gen/src/write.rs b/gen/src/write.rs
index 4276e9b09..b83611a3e 100644
--- a/gen/src/write.rs
+++ b/gen/src/write.rs
@@ -1056,14 +1056,18 @@ fn write_generic_instantiations(out: &mut OutFile, types: &Types) {
             }
         } 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).is_none()
+                    && (!types.aliases.contains_key(inner) || types.explicit_impls.contains(ty))
+                {
                     out.next_section();
                     write_unique_ptr(out, inner, types);
                 }
             }
         } 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).is_none()
+                    && (!types.aliases.contains_key(inner) || types.explicit_impls.contains(ty))
+                {
                     out.next_section();
                     write_cxx_vector(out, ty, inner, types);
                 }
diff --git a/macro/src/expand.rs b/macro/src/expand.rs
index e7b28b984..4778b14cf 100644
--- a/macro/src/expand.rs
+++ b/macro/src/expand.rs
@@ -39,7 +39,7 @@ fn expand(ffi: Module, apis: &[Api], types: &Types) -> TokenStream {
 
     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) => {
@@ -76,13 +76,17 @@ fn expand(ffi: Module, apis: &[Api], types: &Types) -> TokenStream {
             }
         } 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) {
+                if Atom::from(ident).is_none()
+                    && (!types.aliases.contains_key(ident) || types.explicit_impls.contains(ty))
+                {
                     expanded.extend(expand_unique_ptr(namespace, ident, types));
                 }
             }
         } 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).is_none()
+                    && (!types.aliases.contains_key(ident) || types.explicit_impls.contains(ty))
+                {
                     // Generate impl for CxxVector<T> if T is a struct or opaque
                     // C++ type. Impl for primitives is already provided by cxx
                     // crate.
diff --git a/syntax/check.rs b/syntax/check.rs
index f25694838..ff23ec9be 100644
--- a/syntax/check.rs
+++ b/syntax/check.rs
@@ -3,8 +3,8 @@ 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};
@@ -48,6 +48,7 @@ fn do_typecheck(cx: &mut Check) {
             Api::Enum(enm) => check_api_enum(cx, enm),
             Api::CxxType(ety) | Api::RustType(ety) => check_api_type(cx, ety),
             Api::CxxFunction(efn) | Api::RustFunction(efn) => check_api_fn(cx, efn),
+            Api::Impl(imp) => check_api_impl(cx, imp),
             _ => {}
         }
     }
@@ -286,6 +287,18 @@ fn check_api_fn(cx: &mut Check, efn: &ExternFn) {
     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).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() => {}
diff --git a/syntax/file.rs b/syntax/file.rs
index 8b86adc73..931ce6e8d 100644
--- a/syntax/file.rs
+++ b/syntax/file.rs
@@ -2,8 +2,8 @@ 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, ItemStruct,
-    ItemUse, LitStr, Token, Visibility,
+    braced, token, Abi, Attribute, ForeignItem, Ident, Item as RustItem, ItemEnum, ItemImpl,
+    ItemStruct, ItemUse, LitStr, Token, Visibility,
 };
 
 pub struct Module {
@@ -22,6 +22,7 @@ pub enum Item {
     Enum(ItemEnum),
     ForeignMod(ItemForeignMod),
     Use(ItemUse),
+    Impl(ItemImpl),
     Other(RustItem),
 }
 
@@ -99,6 +100,7 @@ impl Parse for Item {
                 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 7545e92ce..74e779903 100644
--- a/syntax/ident.rs
+++ b/syntax/ident.rs
@@ -20,7 +20,7 @@ pub(crate) fn check_all(cx: &mut Check, namespace: &Namespace, apis: &[Api]) {
 
     for api in apis {
         match api {
-            Api::Include(_) => {}
+            Api::Include(_) | Api::Impl(_) => {}
             Api::Struct(strct) => {
                 check(cx, &strct.ident);
                 for field in &strct.fields {
diff --git a/syntax/mod.rs b/syntax/mod.rs
index 8ef3c8b9c..934f6c67a 100644
--- a/syntax/mod.rs
+++ b/syntax/mod.rs
@@ -42,6 +42,7 @@ pub enum Api {
     RustType(ExternType),
     RustFunction(ExternFn),
     TypeAlias(TypeAlias),
+    Impl(Impl),
 }
 
 pub struct ExternType {
@@ -87,6 +88,12 @@ pub struct TypeAlias {
     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],
diff --git a/syntax/parse.rs b/syntax/parse.rs
index cec818d37..49dc26b5b 100644
--- a/syntax/parse.rs
+++ b/syntax/parse.rs
@@ -3,17 +3,17 @@ 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, Lang, Receiver, Ref, 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, ItemEnum, 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 {
@@ -33,6 +33,10 @@ pub fn parse_items(cx: &mut Errors, items: Vec<Item>, trusted: bool) -> Vec<Api>
                 Err(err) => cx.push(err),
             },
             Item::ForeignMod(foreign_mod) => parse_foreign_mod(cx, foreign_mod, &mut apis, trusted),
+            Item::Impl(item) => match parse_impl(item) {
+                Ok(imp) => apis.push(imp),
+                Err(err) => cx.push(err),
+            },
             Item::Use(item) => cx.error(item, error::USE_NOT_ALLOWED),
             Item::Other(item) => cx.error(item, "unsupported item"),
         }
@@ -420,6 +424,37 @@ fn parse_extern_verbatim(cx: &mut Errors, tokens: &TokenStream, lang: Lang) -> R
     }
 }
 
+fn parse_impl(imp: ItemImpl) -> 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)?,
+        brace_token: imp.brace_token,
+    }))
+}
+
 fn parse_include(input: ParseStream) -> Result<String> {
     if input.peek(LitStr) {
         return Ok(input.parse::<LitStr>()?.value());
diff --git a/syntax/tokens.rs b/syntax/tokens.rs
index 4ed264a78..7618e99da 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, Signature, Slice, Struct, Ty1,
+    Type, TypeAlias, Var,
 };
 use proc_macro2::{Ident, Span, TokenStream};
 use quote::{quote_spanned, ToTokens};
@@ -121,6 +121,14 @@ impl ToTokens for ExternFn {
     }
 }
 
+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);
diff --git a/syntax/types.rs b/syntax/types.rs
index 6924a780b..2afce256c 100644
--- a/syntax/types.rs
+++ b/syntax/types.rs
@@ -15,6 +15,7 @@ pub struct Types<'a> {
     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 Type>,
 }
 
 impl<'a> Types<'a> {
@@ -26,6 +27,7 @@ impl<'a> Types<'a> {
         let mut rust = Set::new();
         let mut aliases = Map::new();
         let mut untrusted = Map::new();
+        let mut explicit_impls = Set::new();
 
         fn visit<'a>(all: &mut Set<&'a Type>, ty: &'a Type) {
             all.insert(ty);
@@ -133,6 +135,10 @@ impl<'a> Types<'a> {
                     cxx.insert(ident);
                     aliases.insert(ident, alias);
                 }
+                Api::Impl(imp) => {
+                    visit(&mut all, &imp.ty);
+                    explicit_impls.insert(&imp.ty);
+                }
             }
         }
 
@@ -179,6 +185,7 @@ impl<'a> Types<'a> {
             aliases,
             untrusted,
             required_trivial,
+            explicit_impls,
         }
     }
 


Test patch:
diff --git a/tests/ui/bad_explicit_impl.rs b/tests/ui/bad_explicit_impl.rs
new file mode 100644
index 000000000..210644661
--- /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 000000000..cd0a31738
--- /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/impl_trait_for_type.rs b/tests/ui/impl_trait_for_type.rs
new file mode 100644
index 000000000..9284f738b
--- /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 000000000..e05a461e0
--- /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/nonempty_impl_block.rs b/tests/ui/nonempty_impl_block.rs
new file mode 100644
index 000000000..239d1ece6
--- /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 000000000..e7881bb05
--- /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/unique_ptr_twice.rs b/tests/ui/unique_ptr_twice.rs
new file mode 100644
index 000000000..b6cb4d42b
--- /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 000000000..d77d3ff27
--- /dev/null
+++ b/tests/ui/unique_ptr_twice.stderr
@@ -0,0 +1,10 @@
+error[E0119]: conflicting implementations of trait `cxx::private::UniquePtrTarget` for type `here::C`:
+  --> $DIR/unique_ptr_twice.rs:10:1
+   |
+1  | #[cxx::bridge]
+   | -------------- first implementation here
+...
+10 | #[cxx::bridge]
+   | ^^^^^^^^^^^^^^ conflicting implementation for `here::C`
+   |
+   = note: this error originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info)