Parse trait bounded extern types
diff --git a/gen/src/nested.rs b/gen/src/nested.rs
index fe1664b..1f4fc5d 100644
--- a/gen/src/nested.rs
+++ b/gen/src/nested.rs
@@ -131,6 +131,8 @@
derives: Vec::new(),
type_token: Token),
name: Pair::new(ns, Ident::new(ident, Span::call_site())),
+ colon_token: None,
+ bounds: Vec::new(),
semi_token: Token),
trusted: false,
})
diff --git a/syntax/check.rs b/syntax/check.rs
index a60332d..15e19b8 100644
--- a/syntax/check.rs
+++ b/syntax/check.rs
@@ -3,7 +3,7 @@
use crate::syntax::types::TrivialReason;
use crate::syntax::{
error, ident, Api, Array, Enum, ExternFn, ExternType, Impl, Lang, Receiver, Ref, Signature,
- SliceRef, Struct, Trait, Ty1, Type, Types,
+ SliceRef, Struct, Trait, Ty1, Type, TypeAlias, Types,
};
use proc_macro2::{Delimiter, Group, Ident, TokenStream};
use quote::{quote, ToTokens};
@@ -43,12 +43,13 @@
for api in cx.apis {
match api {
+ Api::Include(_) => {}
Api::Struct(strct) => check_api_struct(cx, strct),
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::TypeAlias(alias) => check_api_type_alias(cx, alias),
Api::Impl(imp) => check_api_impl(cx, imp),
- Api::Include(_) | Api::TypeAlias(_) => {}
}
}
}
@@ -290,6 +291,12 @@
cx.error(derive, msg);
}
+ if !ety.bounds.is_empty() {
+ let bounds = &ety.bounds;
+ let span = quote!(#(#bounds)*);
+ cx.error(span, "extern type bounds are not implemented yet");
+ }
+
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),
@@ -387,6 +394,13 @@
check_multiple_arg_lifetimes(cx, efn);
}
+fn check_api_type_alias(cx: &mut Check, alias: &TypeAlias) {
+ for derive in &alias.derives {
+ let msg = format!("derive({}) on extern type alias is not supported", derive);
+ cx.error(derive, msg);
+ }
+}
+
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 {
diff --git a/syntax/mod.rs b/syntax/mod.rs
index e8288a9..c688f55 100644
--- a/syntax/mod.rs
+++ b/syntax/mod.rs
@@ -72,6 +72,8 @@
pub derives: Vec<Derive>,
pub type_token: Token![type],
pub name: Pair,
+ pub colon_token: Option<Token![:]>,
+ pub bounds: Vec<Derive>,
pub semi_token: Token![;],
pub trusted: bool,
}
@@ -108,6 +110,7 @@
pub struct TypeAlias {
pub doc: Doc,
+ pub derives: Vec<Derive>,
pub type_token: Token![type],
pub name: Pair,
pub eq_token: Token![=],
diff --git a/syntax/parse.rs b/syntax/parse.rs
index e46cdb4..aa86a9f 100644
--- a/syntax/parse.rs
+++ b/syntax/parse.rs
@@ -3,8 +3,8 @@
use crate::syntax::report::Errors;
use crate::syntax::Atom::*;
use crate::syntax::{
- attrs, error, Api, Array, Doc, Enum, ExternFn, ExternType, Impl, Include, IncludeKind, Lang,
- Namespace, Pair, Receiver, Ref, ResolvableName, Signature, SliceRef, Struct, Ty1, Type,
+ attrs, error, Api, Array, Derive, Doc, Enum, ExternFn, ExternType, Impl, Include, IncludeKind,
+ Lang, Namespace, Pair, Receiver, Ref, ResolvableName, Signature, SliceRef, Struct, Ty1, Type,
TypeAlias, Var, Variant,
};
use proc_macro2::{Delimiter, Group, Span, TokenStream, TokenTree};
@@ -14,8 +14,8 @@
use syn::{
Abi, Attribute, Error, Expr, Fields, FnArg, ForeignItem, ForeignItemFn, ForeignItemType,
GenericArgument, GenericParam, Generics, Ident, ItemEnum, ItemImpl, ItemStruct, Lit, LitStr,
- Pat, PathArguments, Result, ReturnType, Token, Type as RustType, TypeArray, TypeBareFn,
- TypePath, TypeReference,
+ Pat, PathArguments, Result, ReturnType, Token, TraitBound, TraitBoundModifier,
+ Type as RustType, TypeArray, TypeBareFn, TypeParamBound, TypePath, TypeReference,
};
pub mod kw {
@@ -257,7 +257,7 @@
}
}
ForeignItem::Verbatim(tokens) => {
- match parse_extern_verbatim(cx, tokens, lang, &namespace) {
+ match parse_extern_verbatim(cx, tokens, lang, trusted, &namespace) {
Ok(api) => items.push(api),
Err(err) => cx.push(err),
}
@@ -352,6 +352,8 @@
derives,
type_token,
name: Pair::new(namespace, ident),
+ colon_token: None,
+ bounds: Vec::new(),
semi_token,
trusted,
})
@@ -518,10 +520,10 @@
cx: &mut Errors,
tokens: &TokenStream,
lang: Lang,
+ trusted: bool,
namespace: &Namespace,
) -> Result<Api> {
- // type Alias = crate::path::to::Type;
- let parse = |input: ParseStream| -> Result<TypeAlias> {
+ |input: ParseStream| -> Result<Api> {
let attrs = input.call(Attribute::parse_outer)?;
let type_token: Token![type] = match input.parse()? {
Some(type_token) => type_token,
@@ -531,41 +533,137 @@
}
};
let ident: Ident = input.parse()?;
- let eq_token: Token![=] = input.parse()?;
- let ty: RustType = input.parse()?;
- let semi_token: Token![;] = input.parse()?;
- let mut doc = Doc::new();
- let mut namespace = namespace.clone();
- attrs::parse(
- cx,
- &attrs,
- attrs::Parser {
- doc: Some(&mut doc),
- namespace: Some(&mut namespace),
- ..Default::default()
- },
- );
-
- Ok(TypeAlias {
- doc,
- type_token,
- name: Pair::new(namespace, ident),
- eq_token,
- ty,
- semi_token,
- })
- };
-
- let type_alias = parse.parse2(tokens.clone())?;
- match lang {
- Lang::Cxx => Ok(Api::TypeAlias(type_alias)),
- Lang::Rust => {
- let (type_token, semi_token) = (type_alias.type_token, type_alias.semi_token);
- let span = quote!(#type_token #semi_token);
- let msg = "type alias in extern \"Rust\" block is not supported";
- Err(Error::new_spanned(span, msg))
+ let lookahead = input.lookahead1();
+ if lookahead.peek(Token![=]) {
+ // type Alias = crate::path::to::Type;
+ parse_type_alias(cx, attrs, type_token, ident, input, lang, namespace)
+ } else if lookahead.peek(Token![:]) {
+ // type Opaque: Bound2 + Bound2;
+ parse_extern_type_bounded(
+ cx, attrs, type_token, ident, input, lang, trusted, namespace,
+ )
+ } else {
+ Err(lookahead.error())
}
}
+ .parse2(tokens.clone())
+}
+
+fn parse_type_alias(
+ cx: &mut Errors,
+ attrs: Vec<Attribute>,
+ type_token: Token![type],
+ ident: Ident,
+ input: ParseStream,
+ lang: Lang,
+ namespace: &Namespace,
+) -> Result<Api> {
+ let eq_token: Token![=] = input.parse()?;
+ let ty: RustType = input.parse()?;
+ let semi_token: Token![;] = input.parse()?;
+
+ let mut doc = Doc::new();
+ let mut derives = Vec::new();
+ let mut namespace = namespace.clone();
+ attrs::parse(
+ cx,
+ &attrs,
+ attrs::Parser {
+ doc: Some(&mut doc),
+ derives: Some(&mut derives),
+ namespace: Some(&mut namespace),
+ ..Default::default()
+ },
+ );
+
+ if lang == Lang::Rust {
+ let span = quote!(#type_token #semi_token);
+ let msg = "type alias in extern \"Rust\" block is not supported";
+ return Err(Error::new_spanned(span, msg));
+ }
+
+ Ok(Api::TypeAlias(TypeAlias {
+ doc,
+ derives,
+ type_token,
+ name: Pair::new(namespace, ident),
+ eq_token,
+ ty,
+ semi_token,
+ }))
+}
+
+fn parse_extern_type_bounded(
+ cx: &mut Errors,
+ attrs: Vec<Attribute>,
+ type_token: Token![type],
+ ident: Ident,
+ input: ParseStream,
+ lang: Lang,
+ trusted: bool,
+ namespace: &Namespace,
+) -> Result<Api> {
+ let colon_token: Token![:] = input.parse()?;
+ let mut bounds = Vec::new();
+ loop {
+ match input.parse()? {
+ TypeParamBound::Trait(TraitBound {
+ paren_token: None,
+ modifier: TraitBoundModifier::None,
+ lifetimes: None,
+ path,
+ }) if if let Some(derive) = path.get_ident().and_then(Derive::from) {
+ bounds.push(derive);
+ true
+ } else {
+ false
+ } => {}
+ bound @ TypeParamBound::Trait(_) | bound @ TypeParamBound::Lifetime(_) => {
+ cx.error(bound, "unsupported trait");
+ }
+ }
+
+ let lookahead = input.lookahead1();
+ if lookahead.peek(Token![+]) {
+ input.parse::<Token![+]>()?;
+ } else if lookahead.peek(Token![;]) {
+ break;
+ } else {
+ return Err(lookahead.error());
+ }
+ }
+ let semi_token: Token![;] = input.parse()?;
+
+ let mut doc = Doc::new();
+ let mut derives = Vec::new();
+ let mut namespace = namespace.clone();
+ attrs::parse(
+ cx,
+ &attrs,
+ attrs::Parser {
+ doc: Some(&mut doc),
+ derives: Some(&mut derives),
+ namespace: Some(&mut namespace),
+ ..Default::default()
+ },
+ );
+
+ let api_type = match lang {
+ Lang::Cxx => Api::CxxType,
+ Lang::Rust => Api::RustType,
+ };
+
+ Ok(api_type(ExternType {
+ lang,
+ doc,
+ derives,
+ type_token,
+ name: Pair::new(namespace, ident),
+ colon_token: Some(colon_token),
+ bounds,
+ semi_token,
+ trusted,
+ }))
}
fn parse_impl(imp: ItemImpl) -> Result<Api> {
diff --git a/tests/ui/extern_type_bound.rs b/tests/ui/extern_type_bound.rs
new file mode 100644
index 0000000..958accd
--- /dev/null
+++ b/tests/ui/extern_type_bound.rs
@@ -0,0 +1,15 @@
+#[cxx::bridge]
+mod ffi {
+ extern "C++" {
+ type Opaque: PartialEq + PartialOrd;
+ }
+}
+
+#[cxx::bridge]
+mod ffi {
+ extern "C++" {
+ type Opaque: for<'de> Deserialize<'de>;
+ }
+}
+
+fn main() {}
diff --git a/tests/ui/extern_type_bound.stderr b/tests/ui/extern_type_bound.stderr
new file mode 100644
index 0000000..ca07ef7
--- /dev/null
+++ b/tests/ui/extern_type_bound.stderr
@@ -0,0 +1,11 @@
+error: extern type bounds are not implemented yet
+ --> $DIR/extern_type_bound.rs:4:22
+ |
+4 | type Opaque: PartialEq + PartialOrd;
+ | ^^^^^^^^^^^^^^^^^^^^^^
+
+error: unsupported trait
+ --> $DIR/extern_type_bound.rs:11:22
+ |
+11 | type Opaque: for<'de> Deserialize<'de>;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^