Skip to content

Commit

Permalink
Reworked the derive macro code
Browse files Browse the repository at this point in the history
This is prep work for implementing the system for remote / custom /
external types as described in mozilla#2087.

Consolidated the `derive_*_for_udl` macros into a single `udl_derive`
macro and added the `DeriveOptions` struct.  My plan is to re-use this
pattern remote types (with `generate_metadata: true, local_tag: true`).
This also simplifies the task of switching UDL to generate blanket FFI
trait impls.

Changed the specific derive code to use `DeriveOptions` plus an item
struct.  For example  RecordItem, which represents the parsed
DeriveInput for the record. This will make future changes easier since
we only need to modify one of these structs, not update all the function
signatures.

Changed the UDL derives to parse attributes exactly like regular
derives.  For example, we now wrap enums with `#[uniffi(flat_error)]`
rather than putting `flat_error` in the `derive_error_for_udl` attribute
macro.  This is prep work for remote types -- in that case I think we
should tell the user to define the item exactly like it was a regular
derive and not special case the attributes.

Removed the `#[uniffi(non_exhaustive)]` attribute and parse the real
`#[non_exhaustive]` attribute instead.  This is easy to do now with the
new system and also will be good for remote types.

Made EnumItem also parse error attributes.  I think this is simpler than
creating a separate EnumItem vs ErrorItem, at least for now.
  • Loading branch information
bendk committed May 2, 2024
1 parent a47508d commit 2445519
Show file tree
Hide file tree
Showing 14 changed files with 479 additions and 391 deletions.
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
error[E0533]: expected value, found struct variant `Self::DivisionByZero`
--> $OUT_DIR[uniffi_uitests]/errors.uniffi.rs
|
| / #[::uniffi::derive_error_for_udl(
| | flat_error,
| | with_try_read,
| | )]
| |__^ not a value
| #[::uniffi::udl_derive(Error)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not a value
|
= note: this error originates in the attribute macro `::uniffi::derive_error_for_udl` (in Nightly builds, run with -Z macro-backtrace for more info)
= note: this error originates in the attribute macro `::uniffi::udl_derive` (in Nightly builds, run with -Z macro-backtrace for more info)
6 changes: 3 additions & 3 deletions fixtures/uitests/tests/ui/interface_not_sync_and_send.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ note: required because it appears within the type `Counter`
note: required by a bound in `_::{closure#0}::assert_impl_all`
--> $OUT_DIR[uniffi_uitests]/counter.uniffi.rs
|
| #[::uniffi::derive_object_for_udl]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `assert_impl_all`
= note: this error originates in the macro `uniffi::deps::static_assertions::assert_impl_all` which comes from the expansion of the attribute macro `::uniffi::derive_object_for_udl` (in Nightly builds, run with -Z macro-backtrace for more info)
| #[::uniffi::udl_derive(Object)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `assert_impl_all`
= note: this error originates in the macro `uniffi::deps::static_assertions::assert_impl_all` which comes from the expansion of the attribute macro `::uniffi::udl_derive` (in Nightly builds, run with -Z macro-backtrace for more info)

error[E0277]: `Cell<u32>` cannot be shared between threads safely
--> $OUT_DIR[uniffi_uitests]/counter.uniffi.rs
Expand Down
9 changes: 4 additions & 5 deletions uniffi_bindgen/src/scaffolding/templates/EnumTemplate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,10 @@
// Forward work to `uniffi_macros` This keeps macro-based and UDL-based generated code consistent.
#}

#[::uniffi::derive_enum_for_udl(
{%- if e.is_non_exhaustive() -%}
non_exhaustive,
{%- endif %}
)]
#[::uniffi::udl_derive(Enum)]
{%- if e.is_non_exhaustive() %}
#[non_exhaustive]
{%- endif %}
enum r#{{ e.name() }} {
{%- for variant in e.variants() %}
r#{{ variant.name() }} {
Expand Down
21 changes: 10 additions & 11 deletions uniffi_bindgen/src/scaffolding/templates/ErrorTemplate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,16 @@
// Forward work to `uniffi_macros` This keeps macro-based and UDL-based generated code consistent.
#}

#[::uniffi::derive_error_for_udl(
{% if e.is_flat() -%}
flat_error,
{% if ci.should_generate_error_read(e) -%}
with_try_read,
{%- endif %}
{%- endif %}
{%- if e.is_non_exhaustive() -%}
non_exhaustive,
{%- endif %}
)]
#[::uniffi::udl_derive(Error)]
{% if e.is_flat() -%}
#[uniffi(flat_error)]
{% if ci.should_generate_error_read(e) -%}
#[uniffi(with_try_read)]
{%- endif %}
{%- endif %}
{%- if e.is_non_exhaustive() -%}
#[non_exhaustive]
{%- endif %}
enum r#{{ e.name() }} {
{%- for variant in e.variants() %}
r#{{ variant.name() }} {
Expand Down
2 changes: 1 addition & 1 deletion uniffi_bindgen/src/scaffolding/templates/ObjectTemplate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ pub trait r#{{ obj.name() }} {
#[uniffi::export(Eq)]
{% endmatch %}
{% endfor %}
#[::uniffi::derive_object_for_udl]
#[::uniffi::udl_derive(Object)]
struct {{ obj.rust_name() }} { }

{%- for cons in obj.constructors() %}
Expand Down
2 changes: 1 addition & 1 deletion uniffi_bindgen/src/scaffolding/templates/RecordTemplate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// Forward work to `uniffi_macros` This keeps macro-based and UDL-based generated code consistent.
#}

#[::uniffi::derive_record_for_udl]
#[::uniffi::udl_derive(Record)]
struct r#{{ rec.name() }} {
{%- for field in rec.fields() %}
r#{{ field.name() }}: {{ field.as_type().borrow()|type_rs }},
Expand Down
119 changes: 119 additions & 0 deletions uniffi_macros/src/derive.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

//! General handling for the derive and udl_derive macros

use crate::util::kw;
use proc_macro2::{Ident, Span, TokenStream};
use quote::{quote, ToTokens};
use syn::{
parse::{Parse, ParseStream},
DeriveInput,
};

pub fn expand_derive(
kind: DeriveKind,
input: DeriveInput,
options: DeriveOptions,
) -> syn::Result<TokenStream> {
match kind {
DeriveKind::Record(_) => crate::record::expand_record(input, options),
DeriveKind::Object(_) => crate::object::expand_object(input, options),
DeriveKind::Enum(_) => crate::enum_::expand_enum(input, options),
DeriveKind::Error(_) => crate::error::expand_error(input, options),
}
}

pub enum DeriveKind {
Record(kw::Record),
Enum(kw::Enum),
Error(kw::Error),
Object(kw::Object),
}

impl Parse for DeriveKind {
fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
let lookahead = input.lookahead1();
if lookahead.peek(kw::Record) {
Ok(Self::Record(input.parse()?))
} else if lookahead.peek(kw::Enum) {
Ok(Self::Enum(input.parse()?))
} else if lookahead.peek(kw::Error) {
Ok(Self::Error(input.parse()?))
} else if lookahead.peek(kw::Object) {
Ok(Self::Object(input.parse()?))
} else {
Err(lookahead.error())
}
}
}

pub struct DeriveOptions {
/// Should we implement FFI traits for the local UniFfiTag only?
pub local_tag: bool,
/// Should we generate a metadata symbol?
pub generate_metadata: bool,
}

/// default() is used to construct a DeriveOptions for a regular `derive` invocation
impl Default for DeriveOptions {
fn default() -> Self {
Self {
local_tag: false,
generate_metadata: true,
}
}
}

impl DeriveOptions {
/// Construct DeriveOptions for `udl_derive`
pub fn udl_derive() -> Self {
Self {
local_tag: true,
generate_metadata: false,
}
}

/// Generate the impl header for a FFI trait
///
/// This will output something like `impl<UT> FfiConverter<UT> for #type`. The caller is
/// responsible for providing the body if the impl block.
pub fn ffi_impl_header(&self, trait_name: &str, ident: &impl ToTokens) -> TokenStream {
let trait_name = Ident::new(trait_name, Span::call_site());
if self.local_tag {
quote! { impl ::uniffi::#trait_name<crate::UniFfiTag> for #ident }
} else {
quote! { impl<UT> ::uniffi::#trait_name<UT> for #ident }
}
}

/// Generate a call to `derive_ffi_traits!` that will derive all the FFI traits
pub fn derive_all_ffi_traits(&self, ty: &Ident) -> TokenStream {
if self.local_tag {
quote! { ::uniffi::derive_ffi_traits!(local #ty); }
} else {
quote! { ::uniffi::derive_ffi_traits!(blanket #ty); }
}
}

/// Generate a call to `derive_ffi_traits!` that will derive some of the FFI traits
pub fn derive_ffi_traits(&self, ty: impl ToTokens, trait_names: &[&str]) -> TokenStream {
let trait_idents = trait_names
.iter()
.map(|name| Ident::new(name, Span::call_site()));
if self.local_tag {
quote! {
#(
::uniffi::derive_ffi_traits!(impl #trait_idents<crate::UniFfiTag> for #ty);
)*
}
} else {
quote! {
#(
::uniffi::derive_ffi_traits!(impl<UT> #trait_idents<UT> for #ty);
)*
}
}
}
}
Loading

0 comments on commit 2445519

Please sign in to comment.