Add ExternType derive for opaque Rust types
diff --git a/gen/src/nested.rs b/gen/src/nested.rs
index 22b0c9f..fe1664b 100644
--- a/gen/src/nested.rs
+++ b/gen/src/nested.rs
@@ -52,7 +52,7 @@
mod tests {
use super::NamespaceEntries;
use crate::syntax::namespace::Namespace;
- use crate::syntax::{Api, Doc, ExternType, Pair};
+ use crate::syntax::{Api, Doc, ExternType, Lang, Pair};
use proc_macro2::{Ident, Span};
use std::iter::FromIterator;
use syn::Token;
@@ -126,7 +126,9 @@
fn make_api(ns: Option<&str>, ident: &str) -> Api {
let ns = ns.map_or(Namespace::ROOT, |ns| syn::parse_str(ns).unwrap());
Api::CxxType(ExternType {
+ lang: Lang::Rust,
doc: Doc::new(),
+ derives: Vec::new(),
type_token: Token),
name: Pair::new(ns, Ident::new(ident, Span::call_site())),
semi_token: Token),
diff --git a/macro/src/derive.rs b/macro/src/derive.rs
index a015ed0..3379c9c 100644
--- a/macro/src/derive.rs
+++ b/macro/src/derive.rs
@@ -16,6 +16,7 @@
Trait::Debug => expanded.extend(struct_debug(strct, span)),
Trait::Default => expanded.extend(struct_default(strct, span)),
Trait::Eq => traits.push(quote_spanned!(span=> ::std::cmp::Eq)),
+ Trait::ExternType => unreachable!(),
Trait::Hash => traits.push(quote_spanned!(span=> ::std::hash::Hash)),
Trait::Ord => expanded.extend(struct_ord(strct, span)),
Trait::PartialEq => traits.push(quote_spanned!(span=> ::std::cmp::PartialEq)),
@@ -57,6 +58,7 @@
traits.push(quote_spanned!(span=> ::std::cmp::Eq));
has_eq = true;
}
+ Trait::ExternType => unreachable!(),
Trait::Hash => traits.push(quote_spanned!(span=> ::std::hash::Hash)),
Trait::Ord => expanded.extend(enum_ord(enm, span)),
Trait::PartialEq => {
diff --git a/macro/src/expand.rs b/macro/src/expand.rs
index c218267..ae13beb 100644
--- a/macro/src/expand.rs
+++ b/macro/src/expand.rs
@@ -617,9 +617,24 @@
let span = ident.span();
let unsafe_impl = quote_spanned!(ety.type_token.span=> unsafe impl);
- quote_spanned! {span=>
+ let mut impls = quote_spanned! {span=>
#unsafe_impl ::cxx::private::RustType for #ident {}
+ };
+
+ for derive in &ety.derives {
+ if derive.what == Trait::ExternType {
+ let type_id = type_id(&ety.name);
+ let span = derive.span;
+ impls.extend(quote_spanned! {span=>
+ unsafe impl ::cxx::ExternType for #ident {
+ type Id = #type_id;
+ type Kind = ::cxx::kind::Opaque;
+ }
+ });
+ }
}
+
+ impls
}
fn expand_rust_type_assert_sized(ety: &ExternType) -> TokenStream {
diff --git a/syntax/check.rs b/syntax/check.rs
index 725105d..a60332d 100644
--- a/syntax/check.rs
+++ b/syntax/check.rs
@@ -232,6 +232,13 @@
}
}
+ for derive in &strct.derives {
+ if derive.what == Trait::ExternType {
+ let msg = format!("derive({}) on shared struct is not supported", derive);
+ cx.error(derive, msg);
+ }
+ }
+
for field in &strct.fields {
if let Type::Fn(_) = field.ty {
cx.error(
@@ -258,11 +265,9 @@
}
for derive in &enm.derives {
- if derive.what == Trait::Default {
- cx.error(
- derive,
- "derive(Default) on shared enums is not supported yet",
- );
+ if derive.what == Trait::Default || derive.what == Trait::ExternType {
+ let msg = format!("derive({}) on shared enum is not supported", derive);
+ cx.error(derive, msg);
}
}
}
@@ -270,6 +275,21 @@
fn check_api_type(cx: &mut Check, ety: &ExternType) {
check_reserved_name(cx, &ety.name.rust);
+ for derive in &ety.derives {
+ if derive.what == Trait::ExternType && ety.lang == Lang::Rust {
+ continue;
+ }
+ let lang = match ety.lang {
+ Lang::Rust => "Rust",
+ Lang::Cxx => "C++",
+ };
+ let msg = format!(
+ "derive({}) on opaque {} type is not supported yet",
+ derive, lang,
+ );
+ cx.error(derive, msg);
+ }
+
if 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),
diff --git a/syntax/derive.rs b/syntax/derive.rs
index 1121211..96b3eea 100644
--- a/syntax/derive.rs
+++ b/syntax/derive.rs
@@ -1,4 +1,5 @@
use proc_macro2::{Ident, Span};
+use std::fmt::{self, Display};
#[derive(Copy, Clone)]
pub struct Derive {
@@ -13,6 +14,7 @@
Debug,
Default,
Eq,
+ ExternType,
Hash,
Ord,
PartialEq,
@@ -27,6 +29,7 @@
"Debug" => Trait::Debug,
"Default" => Trait::Default,
"Eq" => Trait::Eq,
+ "ExternType" => Trait::ExternType,
"Hash" => Trait::Hash,
"Ord" => Trait::Ord,
"PartialEq" => Trait::PartialEq,
@@ -52,6 +55,7 @@
Trait::Debug => "Debug",
Trait::Default => "Default",
Trait::Eq => "Eq",
+ Trait::ExternType => "ExternType",
Trait::Hash => "Hash",
Trait::Ord => "Ord",
Trait::PartialEq => "PartialEq",
@@ -60,6 +64,12 @@
}
}
+impl Display for Derive {
+ fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+ formatter.write_str(self.what.as_ref())
+ }
+}
+
pub fn contains(derives: &[Derive], query: Trait) -> bool {
derives.iter().any(|derive| derive.what == query)
}
diff --git a/syntax/mod.rs b/syntax/mod.rs
index bd7efc0..e8288a9 100644
--- a/syntax/mod.rs
+++ b/syntax/mod.rs
@@ -67,7 +67,9 @@
}
pub struct ExternType {
+ pub lang: Lang,
pub doc: Doc,
+ pub derives: Vec<Derive>,
pub type_token: Token![type],
pub name: Pair,
pub semi_token: Token![;],
diff --git a/syntax/parse.rs b/syntax/parse.rs
index a009e19..e46cdb4 100644
--- a/syntax/parse.rs
+++ b/syntax/parse.rs
@@ -327,12 +327,14 @@
namespace: &Namespace,
) -> Api {
let mut doc = Doc::new();
+ let mut derives = Vec::new();
let mut namespace = namespace.clone();
attrs::parse(
cx,
&foreign_type.attrs,
attrs::Parser {
doc: Some(&mut doc),
+ derives: Some(&mut derives),
namespace: Some(&mut namespace),
..Default::default()
},
@@ -345,7 +347,9 @@
Lang::Rust => Api::RustType,
};
api_type(ExternType {
+ lang,
doc,
+ derives,
type_token,
name: Pair::new(namespace, ident),
semi_token,