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

Move storage derives to ink crate #1400

Merged
merged 14 commits into from
Sep 20, 2022
7 changes: 4 additions & 3 deletions .gitlab-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ variables:
# CI_IMAGE is changed to "-:staging" when the CI image gets rebuilt
# read more https://github.com/paritytech/scripts/pull/244
CI_IMAGE: "paritytech/ink-ci-linux:production"
PURELY_STD_CRATES: "ink/codegen storage/traits/codegen metadata engine"
ALSO_WASM_CRATES: "env storage storage/traits storage/traits/derive allocator prelude primitives ink ink/macro ink/ir"
PURELY_STD_CRATES: "ink/codegen metadata engine"
ALSO_WASM_CRATES: "env storage storage/traits allocator prelude primitives ink ink/macro ink/ir"
ALL_CRATES: "${PURELY_STD_CRATES} ${ALSO_WASM_CRATES}"
DELEGATOR_SUBCONTRACTS: "accumulator adder subber"
UPGRADEABLE_CONTRACTS: "forward-calls set-code-hash"
Expand Down Expand Up @@ -282,7 +282,7 @@ docs:
script:
- cargo doc --no-deps --all-features
-p scale-info -p ink_metadata -p ink_env
-p ink_storage -p ink_storage_traits -p ink_storage_codegen -p ink_storage_derive
-p ink_storage -p ink_storage_traits
-p ink_primitives -p ink_prelude
-p ink -p ink_macro -p ink_ir -p ink_codegen
- mv ${CARGO_TARGET_DIR}/doc ./crate-docs
Expand All @@ -302,6 +302,7 @@ codecov:
QUICKCHECK_TESTS: 1
INK_COVERAGE_REPORTING: "true"
CARGO_INCREMENTAL: 0
RUSTC_BOOTSTRAP: "1"
# Variables partly came from https://github.com/mozilla/grcov/blob/master/README.md
RUSTFLAGS: "-Zprofile -Zmir-opt-level=0 -Ccodegen-units=1
-Clink-dead-code -Copt-level=0 -Coverflow-checks=off"
Expand Down
1 change: 0 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ members = [
"crates/env",
"crates/storage",
"crates/storage/traits",
"crates/storage/traits/derive",
]
exclude = [
"examples/",
Expand Down
1 change: 0 additions & 1 deletion crates/ink/ir/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ include = ["Cargo.toml", "src/**/*.rs", "README.md", "LICENSE"]
name = "ink_ir"

[dependencies]
ink_storage_codegen = { version = "4.0.0-alpha.1", path = "../../storage/traits/codegen" }
quote = "1"
syn = { version = "1.0", features = ["parsing", "full", "visit", "extra-traits"] }
proc-macro2 = "1.0"
Expand Down
36 changes: 33 additions & 3 deletions crates/ink/ir/src/ir/storage_item/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,14 @@

mod config;

use crate::utils::find_storage_key_salt;
use config::StorageItemConfig;
use ink_storage_codegen::DeriveUtils;
use proc_macro2::TokenStream as TokenStream2;
use quote::{
quote,
ToTokens,
};
use std::collections::HashSet;

/// A checked ink! storage item with its configuration.
pub struct StorageItem {
Expand Down Expand Up @@ -59,7 +60,36 @@ impl StorageItem {

/// Returns all types that were used in the storage declaration.
pub fn all_used_types(&self) -> Vec<syn::Type> {
self.ast.all_types()
let res: Vec<_> = match self.data().clone() {
syn::Data::Struct(st) => {
st.fields.iter().map(|field| field.ty.clone()).collect()
}
syn::Data::Enum(en) => {
en.variants
.iter()
.flat_map(|variant| variant.fields.iter())
.map(|field| field.ty.clone())
.collect()
}
syn::Data::Union(un) => {
un.fields
.named
.iter()
.map(|field| field.ty.clone())
.collect()
}
};
let mut set = HashSet::new();
res.into_iter()
.filter(|ty| {
if !set.contains(ty) {
set.insert(ty.clone());
true
} else {
false
}
})
.collect()
}

/// Returns the config of the storage.
Expand Down Expand Up @@ -94,7 +124,7 @@ impl StorageItem {

/// Returns salt for storage key.
pub fn salt(&self) -> TokenStream2 {
if let Some(param) = self.ast.find_salt() {
if let Some(param) = find_storage_key_salt(&self.ast) {
param.ident.to_token_stream()
} else {
quote! { () }
Expand Down
21 changes: 21 additions & 0 deletions crates/ink/ir/src/ir/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -144,3 +144,24 @@ where
name
))
}

/// Finds the salt of a struct, enum or union.
/// The salt is any generic that has bound `StorageKey`.
/// In most cases it is parent storage key or auto-generated storage key.
ascjones marked this conversation as resolved.
Show resolved Hide resolved
pub fn find_storage_key_salt(input: &syn::DeriveInput) -> Option<syn::TypeParam> {
input.generics.params.iter().find_map(|param| {
if let syn::GenericParam::Type(type_param) = param {
if let Some(syn::TypeParamBound::Trait(trait_bound)) =
type_param.bounds.first()
{
let segments = &trait_bound.path.segments;
if let Some(last) = segments.last() {
if last.ident == "StorageKey" {
return Some(type_param.clone())
}
}
}
}
None
})
}
2 changes: 2 additions & 0 deletions crates/ink/macro/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@ ink_primitives = { version = "4.0.0-alpha.1", path = "../../primitives/", defaul

scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] }
syn = "1"
synstructure = "0.12.6"
proc-macro2 = "1"
quote = "1"

[dev-dependencies]
ink_env = { path = "../../env" }
Expand Down
144 changes: 129 additions & 15 deletions crates/ink/macro/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ mod chain_extension;
mod contract;
mod ink_test;
mod selector;
mod storage;
mod storage_item;
mod trait_def;

Expand Down Expand Up @@ -213,7 +214,7 @@ pub fn selector_bytes(input: TokenStream) -> TokenStream {
/// The user is able to use a variety of built-in facilities, combine them in various ways
/// or even provide their own implementations of storage data structures.
///
/// For more information visit the `ink_storage` crate documentation.
/// For more information visit the `ink::storage` crate documentation.
///
/// **Example:**
///
Expand Down Expand Up @@ -674,22 +675,22 @@ pub fn trait_definition(attr: TokenStream, item: TokenStream) -> TokenStream {
///
/// ```
/// use ink_prelude::vec::Vec;
/// use ink_storage::{
/// use ink::storage::{
/// Lazy,
/// Mapping,
/// };
/// use ink_storage::traits::{
/// use ink::storage::traits::{
/// StorageKey,
/// StorableHint,
/// };
/// use ink_storage::traits::Storable;
/// use ink::storage::traits::Storable;
///
/// // Deriving `scale::Decode` and `scale::Encode` also derives blanket implementation of all
/// // required traits to be storable.
/// #[derive(scale::Decode, scale::Encode)]
/// #[cfg_attr(
/// feature = "std",
/// derive(scale_info::TypeInfo, ink_storage::traits::StorageLayout)
/// derive(scale_info::TypeInfo, ink::storage::traits::StorageLayout)
/// )]
/// struct Packed {
/// s1: u128,
Expand All @@ -702,9 +703,9 @@ pub fn trait_definition(attr: TokenStream, item: TokenStream) -> TokenStream {
/// #[derive(scale::Decode, scale::Encode)]
/// #[cfg_attr(
/// feature = "std",
/// derive(scale_info::TypeInfo, ink_storage::traits::StorageLayout)
/// derive(scale_info::TypeInfo, ink::storage::traits::StorageLayout)
/// )]
/// struct PackedGeneric<T: ink_storage::traits::Packed> {
/// struct PackedGeneric<T: ink::storage::traits::Packed> {
/// s1: (u128, bool),
/// s2: Vec<T>,
/// s3: String,
Expand All @@ -722,9 +723,9 @@ pub fn trait_definition(attr: TokenStream, item: TokenStream) -> TokenStream {
/// #[derive(Storable, StorableHint, StorageKey)]
/// #[cfg_attr(
/// feature = "std",
/// derive(scale_info::TypeInfo, ink_storage::traits::StorageLayout)
/// derive(scale_info::TypeInfo, ink::storage::traits::StorageLayout)
/// )]
/// struct NonPackedGeneric<T: ink_storage::traits::Packed> {
/// struct NonPackedGeneric<T: ink::storage::traits::Packed> {
/// s1: u32,
/// s2: T,
/// s3: Mapping<u128, T>,
Expand All @@ -734,7 +735,7 @@ pub fn trait_definition(attr: TokenStream, item: TokenStream) -> TokenStream {
/// #[derive(scale::Decode, scale::Encode)]
/// #[cfg_attr(
/// feature = "std",
/// derive(scale_info::TypeInfo, ink_storage::traits::StorageLayout)
/// derive(scale_info::TypeInfo, ink::storage::traits::StorageLayout)
/// )]
/// struct PackedComplex {
/// s1: u128,
Expand All @@ -752,7 +753,7 @@ pub fn trait_definition(attr: TokenStream, item: TokenStream) -> TokenStream {
/// s5: Lazy<NonPacked>,
/// s6: PackedGeneric<Packed>,
/// s7: NonPackedGeneric<Packed>,
/// // Fails because: the trait `ink_storage::traits::Packed` is not implemented for `NonPacked`
/// // Fails because: the trait `ink::storage::traits::Packed` is not implemented for `NonPacked`
/// // s8: Mapping<u128, NonPacked>,
/// }
/// ```
Expand All @@ -769,16 +770,16 @@ pub fn trait_definition(attr: TokenStream, item: TokenStream) -> TokenStream {
///
/// **Usage Example:**
/// ```
/// use ink_storage::Mapping;
/// use ink_storage::traits::{
/// use ink::storage::Mapping;
/// use ink::storage::traits::{
/// StorableHint,
/// StorageKey,
/// };
/// use ink_storage::traits::Storable;
/// use ink::storage::traits::Storable;
///
/// #[ink::storage_item(derive = false)]
/// #[derive(StorableHint, Storable, StorageKey)]
/// struct NonPackedGeneric<T: ink_storage::traits::Packed> {
/// struct NonPackedGeneric<T: ink::storage::traits::Packed> {
/// s1: u32,
/// s2: Mapping<u128, T>,
/// }
Expand Down Expand Up @@ -1261,5 +1262,118 @@ pub fn chain_extension(attr: TokenStream, item: TokenStream) -> TokenStream {
chain_extension::generate(attr.into(), item.into()).into()
}

synstructure::decl_derive!(
[Storable] =>
/// Derives `ink::storage`'s `Storable` trait for the given `struct`, `enum` or `union`.
///
/// # Examples
///
/// ```
/// use ink::storage::traits::Storable;
///
/// #[derive(Storable)]
/// struct NamedFields {
/// a: u32,
/// b: [u32; 1],
/// }
///
/// let value = <NamedFields as Storable>::decode(&mut &[123, 123][..]);
/// ```
storage::storable_derive
);
synstructure::decl_derive!(
[StorableHint] =>
/// Derives `ink::storage`'s `StorableHint` trait for the given `struct` or `enum`.
///
/// If the type declaration contains generic `StorageKey`,
/// it will use it as salt to generate a combined storage key.
///
/// # Examples
///
/// ```
/// use ink::storage::traits::{
/// Storable,
/// StorableHint,
/// StorageKey,
/// AutoStorableHint,
/// AutoKey,
/// ManualKey,
/// };
///
/// #[derive(Default, StorableHint, Storable)]
/// struct NamedFields {
/// a: u32,
/// b: [u32; 32],
/// }
///
/// let _: NamedFields = <NamedFields as StorableHint<AutoKey>>::Type::default();
/// let _: NamedFields = <NamedFields as StorableHint<ManualKey<123>>>::Type::default();
/// ```
storage::storable_hint_derive
);
synstructure::decl_derive!(
[StorageKey] =>
/// Derives `ink::storage`'s `StorageKey` trait for the given `struct` or `enum`.
///
/// # Examples
///
/// ```
/// use ink::storage::traits::{
/// AutoStorableHint,
/// StorageKey,
/// ManualKey,
/// AutoKey,
/// };
///
/// #[derive(StorageKey)]
/// struct NamedFields {
/// a: u32,
/// b: [u32; 32],
/// }
///
/// assert_eq!(<NamedFields as StorageKey>::KEY, 0);
///
/// #[derive(StorageKey)]
/// struct NamedFieldsManualKey<KEY: StorageKey> {
/// a: <u32 as AutoStorableHint<ManualKey<0, KEY>>>::Type,
/// b: <[u32; 32] as AutoStorableHint<ManualKey<1, KEY>>>::Type,
/// }
///
/// assert_eq!(<NamedFieldsManualKey<()> as StorageKey>::KEY, 0);
/// assert_eq!(<NamedFieldsManualKey<AutoKey> as StorageKey>::KEY, 0);
/// assert_eq!(<NamedFieldsManualKey<ManualKey<123>> as StorageKey>::KEY, 123);
/// ```
storage::storage_key_derive
);
synstructure::decl_derive!(
[StorageLayout] =>
/// Derives `ink::storage`'s `StorageLayout` trait for the given `struct` or `enum`.
///
/// # Examples
///
/// ```
/// use ink_metadata::layout::Layout::Struct;
/// use ink::storage::traits::StorageLayout;
///
/// #[derive(StorageLayout)]
/// struct NamedFields {
/// a: u32,
/// b: [u32; 32],
/// }
///
/// let key = 0x123;
/// let mut value = NamedFields {
/// a: 123,
/// b: [22; 32],
/// };
///
/// if let Struct(layout) = <NamedFields as StorageLayout>::layout(&key) {
/// assert_eq!(*layout.fields()[0].name(), "a");
/// assert_eq!(*layout.fields()[1].name(), "b");
/// }
/// ```
storage::storage_layout_derive
);

#[cfg(test)]
pub use contract::generate_or_err;
Loading