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

Explicitly requesting an instantiation #336

Merged
merged 5 commits into from
Oct 4, 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
8 changes: 6 additions & 2 deletions gen/src/write.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1056,14 +1056,18 @@ fn write_generic_instantiations(out: &mut OutFile, types: &Types) {
}
} else if let Type::UniquePtr(ptr) = ty {
if let Type::Ident(inner) = &ptr.inner {
if Atom::from(inner).is_none() && !types.aliases.contains_key(inner) {
if Atom::from(inner).is_none()
&& (!types.aliases.contains_key(inner) || types.explicit_impls.contains(ty))
{
out.next_section();
write_unique_ptr(out, inner, types);
}
}
} else if let Type::CxxVector(ptr) = ty {
if let Type::Ident(inner) = &ptr.inner {
if Atom::from(inner).is_none() && !types.aliases.contains_key(inner) {
if Atom::from(inner).is_none()
&& (!types.aliases.contains_key(inner) || types.explicit_impls.contains(ty))
{
out.next_section();
write_cxx_vector(out, ty, inner, types);
}
Expand Down
10 changes: 7 additions & 3 deletions macro/src/expand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ fn expand(ffi: Module, apis: &[Api], types: &Types) -> TokenStream {

for api in apis {
match api {
Api::Include(_) | Api::RustType(_) => {}
Api::Include(_) | Api::RustType(_) | Api::Impl(_) => {}
Api::Struct(strct) => expanded.extend(expand_struct(strct)),
Api::Enum(enm) => expanded.extend(expand_enum(enm)),
Api::CxxType(ety) => {
Expand Down Expand Up @@ -76,13 +76,17 @@ fn expand(ffi: Module, apis: &[Api], types: &Types) -> TokenStream {
}
} else if let Type::UniquePtr(ptr) = ty {
if let Type::Ident(ident) = &ptr.inner {
if Atom::from(ident).is_none() && !types.aliases.contains_key(ident) {
if Atom::from(ident).is_none()
&& (!types.aliases.contains_key(ident) || types.explicit_impls.contains(ty))
{
expanded.extend(expand_unique_ptr(namespace, ident, types));
}
}
} else if let Type::CxxVector(ptr) = ty {
if let Type::Ident(ident) = &ptr.inner {
if Atom::from(ident).is_none() && !types.aliases.contains_key(ident) {
if Atom::from(ident).is_none()
&& (!types.aliases.contains_key(ident) || types.explicit_impls.contains(ty))
{
// Generate impl for CxxVector<T> if T is a struct or opaque
// C++ type. Impl for primitives is already provided by cxx
// crate.
Expand Down
17 changes: 15 additions & 2 deletions syntax/check.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ use crate::syntax::namespace::Namespace;
use crate::syntax::report::Errors;
use crate::syntax::types::TrivialReason;
use crate::syntax::{
error, ident, Api, Enum, ExternFn, ExternType, Lang, Receiver, Ref, Slice, Struct, Ty1, Type,
Types,
error, ident, Api, Enum, ExternFn, ExternType, Impl, Lang, Receiver, Ref, Slice, Struct, Ty1,
Type, Types,
};
use proc_macro2::{Delimiter, Group, Ident, TokenStream};
use quote::{quote, ToTokens};
Expand Down Expand Up @@ -48,6 +48,7 @@ fn do_typecheck(cx: &mut Check) {
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::Impl(imp) => check_api_impl(cx, imp),
_ => {}
}
}
Expand Down Expand Up @@ -286,6 +287,18 @@ fn check_api_fn(cx: &mut Check, efn: &ExternFn) {
check_multiple_arg_lifetimes(cx, efn);
}

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 {
if Atom::from(inner).is_none() {
return;
}
}
}

cx.error(imp, "unsupported Self type of explicit impl");
}

fn check_mut_return_restriction(cx: &mut Check, efn: &ExternFn) {
match &efn.ret {
Some(Type::Ref(ty)) if ty.mutability.is_some() => {}
Expand Down
6 changes: 4 additions & 2 deletions syntax/file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ use crate::syntax::namespace::Namespace;
use quote::quote;
use syn::parse::{Error, Parse, ParseStream, Result};
use syn::{
braced, token, Abi, Attribute, ForeignItem, Ident, Item as RustItem, ItemEnum, ItemStruct,
ItemUse, LitStr, Token, Visibility,
braced, token, Abi, Attribute, ForeignItem, Ident, Item as RustItem, ItemEnum, ItemImpl,
ItemStruct, ItemUse, LitStr, Token, Visibility,
};

pub struct Module {
Expand All @@ -22,6 +22,7 @@ pub enum Item {
Enum(ItemEnum),
ForeignMod(ItemForeignMod),
Use(ItemUse),
Impl(ItemImpl),
Other(RustItem),
}

Expand Down Expand Up @@ -99,6 +100,7 @@ impl Parse for Item {
brace_token: item.brace_token,
items: item.items,
})),
RustItem::Impl(item) => Ok(Item::Impl(ItemImpl { attrs, ..item })),
RustItem::Use(item) => Ok(Item::Use(ItemUse { attrs, ..item })),
other => Ok(Item::Other(other)),
}
Expand Down
2 changes: 1 addition & 1 deletion syntax/ident.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ pub(crate) fn check_all(cx: &mut Check, namespace: &Namespace, apis: &[Api]) {

for api in apis {
match api {
Api::Include(_) => {}
Api::Include(_) | Api::Impl(_) => {}
Api::Struct(strct) => {
check(cx, &strct.ident);
for field in &strct.fields {
Expand Down
7 changes: 7 additions & 0 deletions syntax/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ pub enum Api {
RustType(ExternType),
RustFunction(ExternFn),
TypeAlias(TypeAlias),
Impl(Impl),
}

pub struct ExternType {
Expand Down Expand Up @@ -87,6 +88,12 @@ pub struct TypeAlias {
pub semi_token: Token![;],
}

pub struct Impl {
pub impl_token: Token![impl],
pub ty: Type,
pub brace_token: Brace,
}

pub struct Signature {
pub unsafety: Option<Token![unsafe]>,
pub fn_token: Token![fn],
Expand Down
45 changes: 40 additions & 5 deletions syntax/parse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,17 @@ use crate::syntax::file::{Item, ItemForeignMod};
use crate::syntax::report::Errors;
use crate::syntax::Atom::*;
use crate::syntax::{
attrs, error, Api, Doc, Enum, ExternFn, ExternType, Lang, Receiver, Ref, Signature, Slice,
Struct, Ty1, Type, TypeAlias, Var, Variant,
attrs, error, Api, Doc, Enum, ExternFn, ExternType, Impl, Lang, Receiver, Ref, Signature,
Slice, Struct, Ty1, Type, TypeAlias, Var, Variant,
};
use proc_macro2::{TokenStream, TokenTree};
use proc_macro2::{Delimiter, Group, TokenStream, TokenTree};
use quote::{format_ident, quote, quote_spanned};
use syn::parse::{ParseStream, Parser};
use syn::punctuated::Punctuated;
use syn::{
Abi, Attribute, Error, Fields, FnArg, ForeignItem, ForeignItemFn, ForeignItemType,
GenericArgument, Ident, ItemEnum, ItemStruct, LitStr, Pat, PathArguments, Result, ReturnType,
Token, Type as RustType, TypeBareFn, TypePath, TypeReference, TypeSlice,
GenericArgument, Ident, ItemEnum, ItemImpl, ItemStruct, LitStr, Pat, PathArguments, Result,
ReturnType, Token, Type as RustType, TypeBareFn, TypePath, TypeReference, TypeSlice,
};

pub mod kw {
Expand All @@ -33,6 +33,10 @@ pub fn parse_items(cx: &mut Errors, items: Vec<Item>, trusted: bool) -> Vec<Api>
Err(err) => cx.push(err),
},
Item::ForeignMod(foreign_mod) => parse_foreign_mod(cx, foreign_mod, &mut apis, trusted),
Item::Impl(item) => match parse_impl(item) {
Ok(imp) => apis.push(imp),
Err(err) => cx.push(err),
},
Item::Use(item) => cx.error(item, error::USE_NOT_ALLOWED),
Item::Other(item) => cx.error(item, "unsupported item"),
}
Expand Down Expand Up @@ -420,6 +424,37 @@ fn parse_extern_verbatim(cx: &mut Errors, tokens: &TokenStream, lang: Lang) -> R
}
}

fn parse_impl(imp: ItemImpl) -> Result<Api> {
if !imp.items.is_empty() {
let mut span = Group::new(Delimiter::Brace, TokenStream::new());
span.set_span(imp.brace_token.span);
return Err(Error::new_spanned(span, "expected an empty impl block"));
}

let self_ty = &imp.self_ty;
if let Some((bang, path, for_token)) = &imp.trait_ {
let span = quote!(#bang #path #for_token #self_ty);
return Err(Error::new_spanned(
span,
"unexpected impl, expected something like `impl UniquePtr<T> {}`",
));
}

let generics = &imp.generics;
if !generics.params.is_empty() || generics.where_clause.is_some() {
return Err(Error::new_spanned(
imp,
"generic parameters on an impl is not supported",
));
}

Ok(Api::Impl(Impl {
impl_token: imp.impl_token,
ty: parse_type(&self_ty)?,
brace_token: imp.brace_token,
}))
}

fn parse_include(input: ParseStream) -> Result<String> {
if input.peek(LitStr) {
return Ok(input.parse::<LitStr>()?.value());
Expand Down
12 changes: 10 additions & 2 deletions syntax/tokens.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::syntax::atom::Atom::*;
use crate::syntax::{
Atom, Derive, Enum, ExternFn, ExternType, Receiver, Ref, Signature, Slice, Struct, Ty1, Type,
TypeAlias, Var,
Atom, Derive, Enum, ExternFn, ExternType, Impl, Receiver, Ref, Signature, Slice, Struct, Ty1,
Type, TypeAlias, Var,
};
use proc_macro2::{Ident, Span, TokenStream};
use quote::{quote_spanned, ToTokens};
Expand Down Expand Up @@ -121,6 +121,14 @@ impl ToTokens for ExternFn {
}
}

impl ToTokens for Impl {
fn to_tokens(&self, tokens: &mut TokenStream) {
self.impl_token.to_tokens(tokens);
self.ty.to_tokens(tokens);
self.brace_token.surround(tokens, |_tokens| {});
}
}

impl ToTokens for Signature {
fn to_tokens(&self, tokens: &mut TokenStream) {
self.fn_token.to_tokens(tokens);
Expand Down
7 changes: 7 additions & 0 deletions syntax/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ pub struct Types<'a> {
pub aliases: Map<&'a Ident, &'a TypeAlias>,
pub untrusted: Map<&'a Ident, &'a ExternType>,
pub required_trivial: Map<&'a Ident, TrivialReason<'a>>,
pub explicit_impls: Set<&'a Type>,
}

impl<'a> Types<'a> {
Expand All @@ -26,6 +27,7 @@ impl<'a> Types<'a> {
let mut rust = Set::new();
let mut aliases = Map::new();
let mut untrusted = Map::new();
let mut explicit_impls = Set::new();

fn visit<'a>(all: &mut Set<&'a Type>, ty: &'a Type) {
all.insert(ty);
Expand Down Expand Up @@ -133,6 +135,10 @@ impl<'a> Types<'a> {
cxx.insert(ident);
aliases.insert(ident, alias);
}
Api::Impl(imp) => {
visit(&mut all, &imp.ty);
explicit_impls.insert(&imp.ty);
}
}
}

Expand Down Expand Up @@ -179,6 +185,7 @@ impl<'a> Types<'a> {
aliases,
untrusted,
required_trivial,
explicit_impls,
}
}

Expand Down
10 changes: 10 additions & 0 deletions tests/ui/bad_explicit_impl.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#[cxx::bridge]
mod ffi {
struct S {
x: u8,
}

impl fn() -> &S {}
}

fn main() {}
5 changes: 5 additions & 0 deletions tests/ui/bad_explicit_impl.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
error: unsupported Self type of explicit impl
--> $DIR/bad_explicit_impl.rs:7:5
|
7 | impl fn() -> &S {}
| ^^^^^^^^^^^^^^^^^^
10 changes: 10 additions & 0 deletions tests/ui/impl_trait_for_type.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#[cxx::bridge]
mod ffi {
struct S {
x: u8,
}

impl UniquePtrTarget for S {}
}

fn main() {}
5 changes: 5 additions & 0 deletions tests/ui/impl_trait_for_type.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
error: unexpected impl, expected something like `impl UniquePtr<T> {}`
--> $DIR/impl_trait_for_type.rs:7:10
|
7 | impl UniquePtrTarget for S {}
| ^^^^^^^^^^^^^^^^^^^^^
12 changes: 12 additions & 0 deletions tests/ui/nonempty_impl_block.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#[cxx::bridge]
mod ffi {
struct S {
x: u8,
}

impl UniquePtr<S> {
fn new() -> Self;
}
}

fn main() {}
8 changes: 8 additions & 0 deletions tests/ui/nonempty_impl_block.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
error: expected an empty impl block
--> $DIR/nonempty_impl_block.rs:7:23
|
7 | impl UniquePtr<S> {
| _______________________^
8 | | fn new() -> Self;
9 | | }
| |_____^
19 changes: 19 additions & 0 deletions tests/ui/unique_ptr_twice.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#[cxx::bridge]
mod here {
extern "C" {
type C;
}

impl UniquePtr<C> {}
}

#[cxx::bridge]
mod there {
extern "C" {
type C = crate::here::C;
}

impl UniquePtr<C> {}
}

fn main() {}
10 changes: 10 additions & 0 deletions tests/ui/unique_ptr_twice.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
error[E0119]: conflicting implementations of trait `cxx::private::UniquePtrTarget` for type `here::C`:
--> $DIR/unique_ptr_twice.rs:10:1
|
1 | #[cxx::bridge]
| -------------- first implementation here
...
10 | #[cxx::bridge]
| ^^^^^^^^^^^^^^ conflicting implementation for `here::C`
|
= note: this error originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info)