Skip to content

Commit

Permalink
Merge pull request #336 from dtolnay/impl
Browse files Browse the repository at this point in the history
Explicitly requesting an instantiation
  • Loading branch information
dtolnay authored Oct 4, 2020
2 parents c86633d + 64cab48 commit 1cbd1b2
Show file tree
Hide file tree
Showing 17 changed files with 176 additions and 17 deletions.
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)

0 comments on commit 1cbd1b2

Please sign in to comment.