-
Notifications
You must be signed in to change notification settings - Fork 455
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
Conversation
Note that this commit does not yet allow to write fallible ink! constructors.
let field_span = field.span(); | ||
let field_ty = &field.ty; | ||
quote_spanned!(field_span=> | ||
#field_ty: ::ink_storage::traits::SpreadAllocate |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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() { |
There was a problem hiding this comment.
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
)
There was a problem hiding this comment.
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 Report
@@ 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
Continue to review full report at Codecov.
|
There was a problem hiding this 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" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"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`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/// 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! |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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 enum
s 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
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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! :)
There was a problem hiding this comment.
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>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
d: <Box<i32> as ::ink_storage::traits::SpreadAllocate>::allocate_spread(__key_ptr), | |
c: <Box<i32> as ::ink_storage::traits::SpreadAllocate>::allocate_spread(__key_ptr), |
// 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 |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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)] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this needed?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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())); |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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 { |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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?
* 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]>
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.