Skip to content

Commit

Permalink
Follow-up: Implement storage2 trait derive proc. macros (#399)
Browse files Browse the repository at this point in the history
* [core/derive] implement SpreadLayout and PackedLayout derives

* [core/derive] no longer is no_std (not useful)

Also simplified its dependencies a bit.

* [core/derive] apply rustfmt

* [core/derive] fix clippy warning
  • Loading branch information
Robbepop authored May 21, 2020
1 parent e1dcdc8 commit 0c253cb
Show file tree
Hide file tree
Showing 9 changed files with 1,464 additions and 80 deletions.
8 changes: 1 addition & 7 deletions core/derive/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,12 @@ readme = "../README.md"
proc-macro = true

[dependencies]
ink_primitives = { version = "2.1.0", path = "../../primitives", default-features = false }
quote = "1.0"
syn = { version = "1.0", features = ["full"] }
proc-macro2 = "1.0"
synstructure = "0.12"

[dev-dependencies]
ink_primitives = { version = "2.1.0", path = "../../primitives" }
ink_core = { version = "2.1.0", path = ".." }
trybuild = "1.0.24"

[features]
default = ["std"]
std = [
"ink_primitives/std",
]
47 changes: 47 additions & 0 deletions core/derive/src/allocate_using.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// Copyright 2018-2020 Parity Technologies (UK) Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use proc_macro2::TokenStream as TokenStream2;
use quote::quote;

pub(crate) fn allocate_using_derive(mut s: synstructure::Structure) -> TokenStream2 {
// We cannot implement AllocateUsing on enums because we cannot specify
// which variant we are going to use.
if let syn::Data::Enum(ref _enum_data) = s.ast().data {
panic!("cannot derive AllocateUsing for enums")
}
s.bind_with(|_| synstructure::BindStyle::Move);
s.add_bounds(synstructure::AddBounds::Fields);
// The `synstructure` crate treats every input as `enum`.
// So even `struct`s are treated as single-variant enums.
// Some line above we exclude `enum` for deriving this trait so
// all inputs (`struct` only) have exactly one variant which
// is the `struct` itself.
let body = s.variants()[0].construct(|field, _| {
let ty = &field.ty;
quote! {
<#ty as ink_core::storage::alloc::AllocateUsing>::allocate_using(alloc)
}
});
s.gen_impl(quote! {
gen impl ink_core::storage::alloc::AllocateUsing for @Self {
unsafe fn allocate_using<A>(alloc: &mut A) -> Self
where
A: ink_core::storage::alloc::Allocate,
{
#body
}
}
})
}
52 changes: 52 additions & 0 deletions core/derive/src/flush.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// Copyright 2018-2020 Parity Technologies (UK) Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use proc_macro2::TokenStream as TokenStream2;
use quote::quote;

pub(crate) fn flush_derive(mut s: synstructure::Structure) -> TokenStream2 {
if s.variants().is_empty() {
panic!("deriving Flush for empty enum is invalid")
}
s.bind_with(|_| synstructure::BindStyle::Move);
s.add_bounds(synstructure::AddBounds::Fields);
// Upon seeing the first variant we set this to true.
// This is needed so that we do not have a `match self`
// for empty enums which apparently causes errors.
// If there is a better solution to tackle this please
// update this.
let mut requires_match = false;
let body = s.each(|bi| {
requires_match = true;
quote! {
ink_core::storage::Flush::flush(#bi)
}
});
let body = if requires_match {
quote! {
match self {
#body
}
}
} else {
quote! {}
};
s.gen_impl(quote! {
gen impl ink_core::storage::Flush for @Self {
fn flush(&mut self) {
#body
}
}
})
}
89 changes: 16 additions & 73 deletions core/derive/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2018-2019 Parity Technologies (UK) Ltd.
// Copyright 2018-2020 Parity Technologies (UK) Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
Expand All @@ -12,80 +12,23 @@
// See the License for the specific language governing permissions and
// limitations under the License.

#![cfg_attr(not(feature = "std"), no_std)]

extern crate alloc;
extern crate proc_macro;

use proc_macro2::TokenStream as TokenStream2;
use quote::quote;
mod allocate_using;
mod flush;
mod packed_layout;
mod spread_layout;

#[cfg(test)]
mod tests;

use self::{
allocate_using::allocate_using_derive,
flush::flush_derive,
packed_layout::packed_layout_derive,
spread_layout::spread_layout_derive,
};
synstructure::decl_derive!([Flush] => flush_derive);
synstructure::decl_derive!([AllocateUsing] => allocate_using_derive);

pub(crate) fn flush_derive(mut s: synstructure::Structure) -> TokenStream2 {
if s.variants().is_empty() {
panic!("deriving Flush for empty enum is invalid")
}
s.bind_with(|_| synstructure::BindStyle::Move);
s.add_bounds(synstructure::AddBounds::Fields);
// Upon seeing the first variant we set this to true.
// This is needed so that we do not have a `match self`
// for empty enums which apparently causes errors.
// If there is a better solution to tackle this please
// update this.
let mut requires_match = false;
let body = s.each(|bi| {
requires_match = true;
quote! {
ink_core::storage::Flush::flush(#bi)
}
});
let body = if requires_match {
quote! {
match self {
#body
}
}
} else {
quote! {}
};
s.gen_impl(quote! {
gen impl ink_core::storage::Flush for @Self {
fn flush(&mut self) {
#body
}
}
})
}

pub(crate) fn allocate_using_derive(mut s: synstructure::Structure) -> TokenStream2 {
// We cannot implement AllocateUsing on enums because we cannot specify
// which variant we are going to use.
if let syn::Data::Enum(ref _enum_data) = s.ast().data {
panic!("cannot derive AllocateUsing for enums")
}
s.bind_with(|_| synstructure::BindStyle::Move);
s.add_bounds(synstructure::AddBounds::Fields);
// The `synstructure` crate treats every input as `enum`.
// So even `struct`s are treated as single-variant enums.
// Some line above we exclude `enum` for deriving this trait so
// all inputs (`struct` only) have exactly one variant which
// is the `struct` itself.
let body = s.variants()[0].construct(|field, _| {
let ty = &field.ty;
quote! {
<#ty as ink_core::storage::alloc::AllocateUsing>::allocate_using(alloc)
}
});
s.gen_impl(quote! {
gen impl ink_core::storage::alloc::AllocateUsing for @Self {
unsafe fn allocate_using<A>(alloc: &mut A) -> Self
where
A: ink_core::storage::alloc::Allocate,
{
#body
}
}
})
}
synstructure::decl_derive!([SpreadLayout] => spread_layout_derive);
synstructure::decl_derive!([PackedLayout] => packed_layout_derive);
46 changes: 46 additions & 0 deletions core/derive/src/packed_layout.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// Copyright 2018-2020 Parity Technologies (UK) Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use proc_macro2::TokenStream as TokenStream2;
use quote::quote;

/// Derives `ink_core`'s `PackedLayout` trait for the given `struct` or `enum`.
pub fn packed_layout_derive(mut s: synstructure::Structure) -> TokenStream2 {
s.bind_with(|_| synstructure::BindStyle::Move);
s.add_bounds(synstructure::AddBounds::Generics);

let pull_body = s.each(|binding| {
quote! { ::ink_core::storage2::traits::PackedLayout::pull_packed(#binding, __key); }
});
let push_body = s.each(|binding| {
quote! { ::ink_core::storage2::traits::PackedLayout::push_packed(#binding, __key); }
});
let clear_body = s.each(|binding| {
quote! { ::ink_core::storage2::traits::PackedLayout::clear_packed(#binding, __key); }
});

s.gen_impl(quote! {
gen impl ::ink_core::storage2::traits::PackedLayout for @Self {
fn pull_packed(&mut self, __key: &::ink_primitives::Key) {
match self { #pull_body }
}
fn push_packed(&self, __key: &::ink_primitives::Key) {
match self { #push_body }
}
fn clear_packed(&self, __key: &::ink_primitives::Key) {
match self { #clear_body }
}
}
})
}
Loading

0 comments on commit 0c253cb

Please sign in to comment.