Skip to content

Commit

Permalink
Add ExternType derive for opaque Rust types
Browse files Browse the repository at this point in the history
  • Loading branch information
dtolnay committed Nov 28, 2020
1 parent ecce017 commit 16e2620
Show file tree
Hide file tree
Showing 7 changed files with 62 additions and 7 deletions.
4 changes: 3 additions & 1 deletion gen/src/nested.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ fn sort_by_inner_namespace(apis: Vec<&Api>, depth: usize) -> NamespaceEntries {
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;
Expand Down Expand Up @@ -126,7 +126,9 @@ mod tests {
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![type](Span::call_site()),
name: Pair::new(ns, Ident::new(ident, Span::call_site())),
semi_token: Token![;](Span::call_site()),
Expand Down
2 changes: 2 additions & 0 deletions macro/src/derive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ pub fn expand_struct(strct: &Struct, actual_derives: &mut Option<TokenStream>) -
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)),
Expand Down Expand Up @@ -57,6 +58,7 @@ pub fn expand_enum(enm: &Enum, actual_derives: &mut Option<TokenStream>) -> Toke
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 => {
Expand Down
17 changes: 16 additions & 1 deletion macro/src/expand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -617,9 +617,24 @@ fn expand_rust_type_impl(ety: &ExternType) -> TokenStream {
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 {
Expand Down
30 changes: 25 additions & 5 deletions syntax/check.rs
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,13 @@ fn check_api_struct(cx: &mut Check, strct: &Struct) {
}
}

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(
Expand All @@ -258,18 +265,31 @@ fn check_api_enum(cx: &mut Check, enm: &Enum) {
}

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);
}
}
}

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),
Expand Down
10 changes: 10 additions & 0 deletions syntax/derive.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use proc_macro2::{Ident, Span};
use std::fmt::{self, Display};

#[derive(Copy, Clone)]
pub struct Derive {
Expand All @@ -13,6 +14,7 @@ pub enum Trait {
Debug,
Default,
Eq,
ExternType,
Hash,
Ord,
PartialEq,
Expand All @@ -27,6 +29,7 @@ impl Derive {
"Debug" => Trait::Debug,
"Default" => Trait::Default,
"Eq" => Trait::Eq,
"ExternType" => Trait::ExternType,
"Hash" => Trait::Hash,
"Ord" => Trait::Ord,
"PartialEq" => Trait::PartialEq,
Expand All @@ -52,6 +55,7 @@ impl AsRef<str> for Trait {
Trait::Debug => "Debug",
Trait::Default => "Default",
Trait::Eq => "Eq",
Trait::ExternType => "ExternType",
Trait::Hash => "Hash",
Trait::Ord => "Ord",
Trait::PartialEq => "PartialEq",
Expand All @@ -60,6 +64,12 @@ impl AsRef<str> for Trait {
}
}

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)
}
2 changes: 2 additions & 0 deletions syntax/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,9 @@ pub enum IncludeKind {
}

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![;],
Expand Down
4 changes: 4 additions & 0 deletions syntax/parse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -327,12 +327,14 @@ fn parse_extern_type(
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()
},
Expand All @@ -345,7 +347,9 @@ fn parse_extern_type(
Lang::Rust => Api::RustType,
};
api_type(ExternType {
lang,
doc,
derives,
type_token,
name: Pair::new(namespace, ident),
semi_token,
Expand Down

0 comments on commit 16e2620

Please sign in to comment.