Skip to content

Follow-up: SpreadLayout #995

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

Merged
merged 22 commits into from
Nov 9, 2021
Merged

Follow-up: SpreadLayout #995

merged 22 commits into from
Nov 9, 2021

Conversation

Robbepop
Copy link
Collaborator

@Robbepop Robbepop commented Oct 31, 2021

Implements

Partially implements

Note: Only partial since fallible ink! constructors remain artificially forbidden as of now. Another follow-up PR might change that soon.

Doesn't implement, but related

All in all this heavily simplifies working with the new SpreadLayout traits in ink! smart contracts. As a consequence we can further simplify #979 after merging this PR.

let field_span = field.span();
let field_ty = &field.ty;
quote_spanned!(field_span=>
#field_ty: ::ink_storage::traits::SpreadAllocate
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you will have a compilation error in case of two fields with the same type

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since trivial_bounds Rust feature is still unstable we have to remove this auto generated trait impl anyways unfortunately. Otherwise I guess it would be fine.

if config.dynamic_storage_alloc {
alloc::finalize();
let result = ManuallyDrop::new(private::Seal(f()));
match result.as_result() {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does it fix the issue #985? (if you apply this change for execute_message)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I now know what causes failure of result check. It is explained here:
https://github.com/dtolnay/case-studies/blob/master/autoref-specialization/README.md#limitations

Message return values are strictly more powerful than constructor return values therefore this cannot directly applied to fix the bug with messages. However, I am sure we can still fix the result bug with another technique.

This is due to the fact that trivial_bounds feature is not yet stable.
Otherwise this automatically generated implementation would be totally fine.
We can re-add this feature once Rust adds trivial trait bounds.
@codecov-commenter
Copy link

codecov-commenter commented Oct 31, 2021

Codecov Report

Merging #995 (bd84d77) into master (6f3d7c0) will increase coverage by 16.42%.
The diff coverage is 51.51%.

❗ Current head bd84d77 differs from pull request most recent head 6368f2b. Consider uploading reports for the commit 6368f2b to get more accurate results
Impacted file tree graph

@@             Coverage Diff             @@
##           master     #995       +/-   ##
===========================================
+ Coverage   62.53%   78.96%   +16.42%     
===========================================
  Files         244      245        +1     
  Lines        9220     9204       -16     
===========================================
+ Hits         5766     7268     +1502     
+ Misses       3454     1936     -1518     
Impacted Files Coverage Δ
crates/lang/codegen/src/generator/dispatch.rs 100.00% <ø> (+100.00%) ⬆️
crates/lang/codegen/src/generator/storage.rs 100.00% <ø> (+100.00%) ⬆️
crates/lang/src/codegen/dispatch/execution.rs 0.00% <0.00%> (ø)
crates/storage/src/traits/mod.rs 100.00% <ø> (+17.39%) ⬆️
crates/storage/derive/src/spread_allocate.rs 93.33% <93.33%> (ø)
crates/primitives/src/key.rs 93.42% <100.00%> (+0.17%) ⬆️
crates/env/src/backend.rs 0.00% <0.00%> (-80.65%) ⬇️
crates/lang/src/result_info.rs 100.00% <0.00%> (ø)
crates/env/src/call/selector.rs 100.00% <0.00%> (ø)
crates/env/src/call/call_builder.rs 0.00% <0.00%> (ø)
... and 67 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 6f3d7c0...6368f2b. Read the comment docs.

Copy link
Contributor

@HCastano HCastano left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a couple of things I haven't looked at yet, but for the most part I think it's looking good 🦾

fn derive_struct(s: synstructure::Structure) -> TokenStream2 {
assert!(
s.variants().len() == 1,
"can only operate on structs or enums with 1 variant"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
"can only operate on structs or enums with 1 variant"
"can only operate on structs"

}
}

/// Derives `ink_storage`'s `SpreadAllocate` trait for the given `struct` or `enum`.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// Derives `ink_storage`'s `SpreadAllocate` trait for the given `struct` or `enum`.
/// Derives `ink_storage`'s `SpreadAllocate` trait for the given `struct`.

///
/// # Note
///
/// As of now `enum` types are not supported!
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why aren't they supported yet?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SpreadAllocate is very similar to the Default trait. Both do default initialization of some kind. There is no trivial way to figure out the default variant of an enum.

Copy link
Contributor

@forgetso forgetso Nov 23, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be possible to implement SpreadAllocate for enums that implement the Default trait?

e.g. something like:

#[derive(Debug)]
pub enum Color {
    Red,
    Blue,
    Green,
    NoColor
}

impl Default for Color {
    fn default() -> Self { Color::NoColor }
}

I have a contract that has been making use of enums but I'm no longer able to compile it due to the new initialize_contract concept:

354 |             ink_lang::codegen::initialize_contract(|contract| {
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `SpreadAllocate` is not implemented for `Prosopo`
    |
note: required by a bound in `initialize_contract`
   --> /home/user/.cargo/git/checkouts/ink-e2bdf0affead2725/4391ae5/crates/lang/src/codegen/dispatch/execution.rs:142:33
    |
142 |     Contract: ContractRootKey + SpreadAllocate,
    |                                 ^^^^^^^^^^^^^^ required by this bound in `initialize_contract`

Do you have any other suggestions?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would definitely make sense to allow derive impls for enums with Default impl. Not sure how to implement this properly though. Maybe we could add a Self: Default where bound to the generated derive impl if the derive item is an enum:
https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=21d9e01c332a660d9e5ab22634250f1a

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I modified ink to allow me to pass an enum with Default as per your suggestion. I'm now able to use the SpreadAllocate derive function on my structs that contain enums.

    #[derive(Debug, SpreadLayout, SpreadAllocate)]
    #[cfg_attr(feature = "std", derive(scale_info::TypeInfo, StorageLayout))]
    pub struct ProviderAccountMap(Mapping<AccountId, Provider>); //Provider contains a multi value enum

    impl ProviderAccountMap
    {
        fn insert(&mut self, key: AccountId, value: Provider) {
            self.0.insert(key, &value);
        }
        fn get(&self, key: &AccountId) -> Option<Provider> {
            return self.0.get(key);
        }
    }

Unfortunately, I wasn't able to generalise this for T as scale_info::TypeInfo is not available within ink in no_std mode. I was trying to do something like this:

    pub struct AccountMap<T>(Mapping<AccountId, T>)
        where T: scale::Encode + scale::Decode + SpreadLayout + PackedLayout + scale_info::TypeInfo;
    
    impl<T> AccountMap<T>
        where
            T: PackedLayout + scale::Encode + scale::Decode + scale::EncodeLike + scale_info::TypeInfo
    {
        fn insert(&mut self, key: AccountId, value: T) {
            self.0.insert(key, &value);
        }
        fn get(&self, key: &AccountId) -> Option<T> {
            return self.0.get(key);
        }
    }

Is this possible within the smart contract or am I wasting my time?

Copy link
Collaborator Author

@Robbepop Robbepop Nov 24, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you please open an issue in order to resolve this?
I find it very unpleasant to talk about this feature request in a merged PR discussion.
Thank you! :)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done: #1042

struct NamedFields {
a: i32,
b: [u8; 32],
d: Box<i32>,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
d: Box<i32>,
c: Box<i32>,

😄

NamedFields {
a: <i32 as ::ink_storage::traits::SpreadAllocate>::allocate_spread(__key_ptr),
b: <[u8; 32] as ::ink_storage::traits::SpreadAllocate>::allocate_spread(__key_ptr),
d: <Box<i32> as ::ink_storage::traits::SpreadAllocate>::allocate_spread(__key_ptr),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
d: <Box<i32> as ::ink_storage::traits::SpreadAllocate>::allocate_spread(__key_ptr),
c: <Box<i32> as ::ink_storage::traits::SpreadAllocate>::allocate_spread(__key_ptr),

Comment on lines 15 to 18
// The `synstructure` crate currently expands to code that has a single
// `panic` inside an `if` block. We have to allow the following `clippy`
// lint until the fixing PR has been merged:
// PR: https://github.com/mystor/synstructure/pull/50
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should have a ink! issue attached to it, otherwise we'll forget to fix it once the PR gets merged

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I opened #1009.

/// Since it is private it can only be used from within this crate.
/// We need this type in order to properly seal the `ConstructorReturnType`
/// trait from unwanted external trait implementations.
#[repr(transparent)]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this needed?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I dont think it really is. However I tend to add transparent to generic thin wrappers to indicate their use.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So in this case you're using it as an indicator that people shouldn't be using the Seal per say, but instead should be using the T?

I'd argue that the naming and docs are enough, but I see what you're getting at

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seal is private so they cannot use it. It is more of a dev documentation to never ever make it public.

push_spread_root::<S>(&storage, &root_key);
if config.dynamic_storage_alloc {
alloc::finalize();
let result = ManuallyDrop::new(private::Seal(f()));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why don't we want this getting dropped here? From what I can tell f isn't used in the codegen after being passed through to here

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We dont want and dont need the otherwise automated Drop call.

/// Only `()` and `Result<(), E>` are allowed contract initializer return types.
/// For `WrapReturnType<C>` where `C` is the contract type the trait converts
/// `()` into `C` and `Result<(), E>` into `Result<C, E>`.
pub trait InitializerReturnType<C>: private::Sealed {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since there are only two basic cases here, I don't know if I would go ahead with a new trait for this. I'd just do the wrapping in-line

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How would this look like?

@HCastano HCastano merged commit c7cc828 into master Nov 9, 2021
@HCastano HCastano deleted the rf-implement-pr992 branch November 9, 2021 16:10
xgreenx pushed a commit to Supercolony-net/ink that referenced this pull request Feb 8, 2022
* implement const_fn Key::new constructor

* add ContractRootKey trait to ink_lang crate

* use qualified path

* re-export ContractRootKey trait

* automatically implement ContractRootKey for ink! contracts

* implement #[derive(SpreadAllocate)] macro & tests

* re-export #[derive(SpreadAllocate)] macro from ink_storage::traits

* automatically implement SpreadAllocate for the ink! storage struct

* extend execute_constructor to work with fallible ink! constructors

Note that this commit does not yet allow to write fallible ink! constructors.

* add initialize_contract utility method

* re-export initialize_contract utility method

* adjust ink! codegen for recent changes with execute_constructor

* fix clippy warning

* fix hunspell dictionary

* add missing docs to codegen types

* fix UI test

* remove automatic SpreadAllocate implementation

This is due to the fact that trivial_bounds feature is not yet stable.
Otherwise this automatically generated implementation would be totally fine.
We can re-add this feature once Rust adds trivial trait bounds.

* Satisfy OCD in test

* Remove mention of `enum`s from `derive_struct()`

* Remove outdated comment about Clippy lint

* RustFmt

Co-authored-by: Hernando Castano <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants