Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Parse trait bounded extern types #523

Merged
merged 1 commit into from
Nov 28, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions gen/src/nested.rs
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,8 @@ mod tests {
derives: Vec::new(),
type_token: Token![type](Span::call_site()),
name: Pair::new(ns, Ident::new(ident, Span::call_site())),
colon_token: None,
bounds: Vec::new(),
semi_token: Token![;](Span::call_site()),
trusted: false,
})
Expand Down
18 changes: 16 additions & 2 deletions syntax/check.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use crate::syntax::report::Errors;
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};
Expand Down Expand Up @@ -43,12 +43,13 @@ fn do_typecheck(cx: &mut Check) {

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(_) => {}
}
}
}
Expand Down Expand Up @@ -290,6 +291,12 @@ fn check_api_type(cx: &mut Check, ety: &ExternType) {
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),
Expand Down Expand Up @@ -387,6 +394,13 @@ fn check_api_fn(cx: &mut Check, efn: &ExternFn) {
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 {
Expand Down
3 changes: 3 additions & 0 deletions syntax/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@ pub struct ExternType {
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,
}
Expand Down Expand Up @@ -108,6 +110,7 @@ pub struct ExternFn {

pub struct TypeAlias {
pub doc: Doc,
pub derives: Vec<Derive>,
pub type_token: Token![type],
pub name: Pair,
pub eq_token: Token![=],
Expand Down
174 changes: 136 additions & 38 deletions syntax/parse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ use crate::syntax::file::{Item, ItemForeignMod};
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};
Expand All @@ -14,8 +14,8 @@ use syn::punctuated::Punctuated;
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 {
Expand Down Expand Up @@ -257,7 +257,7 @@ fn parse_foreign_mod(
}
}
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),
}
Expand Down Expand Up @@ -352,6 +352,8 @@ fn parse_extern_type(
derives,
type_token,
name: Pair::new(namespace, ident),
colon_token: None,
bounds: Vec::new(),
semi_token,
trusted,
})
Expand Down Expand Up @@ -518,10 +520,10 @@ fn parse_extern_verbatim(
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,
Expand All @@ -531,41 +533,137 @@ fn parse_extern_verbatim(
}
};
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()
},
);
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())
}

Ok(TypeAlias {
doc,
type_token,
name: Pair::new(namespace, ident),
eq_token,
ty,
semi_token,
})
};
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 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 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> {
Expand Down
15 changes: 15 additions & 0 deletions tests/ui/extern_type_bound.rs
Original file line number Diff line number Diff line change
@@ -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() {}
11 changes: 11 additions & 0 deletions tests/ui/extern_type_bound.stderr
Original file line number Diff line number Diff line change
@@ -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>;
| ^^^^^^^^^^^^^^^^^^^^^^^^^