Skip to content

Commit

Permalink
Merge pull request #523 from dtolnay/bound
Browse files Browse the repository at this point in the history
Parse trait bounded extern types
  • Loading branch information
dtolnay authored Nov 28, 2020
2 parents edff9c0 + b7eb65e commit ded2962
Show file tree
Hide file tree
Showing 6 changed files with 183 additions and 40 deletions.
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>;
| ^^^^^^^^^^^^^^^^^^^^^^^^^

0 comments on commit ded2962

Please sign in to comment.