-
Notifications
You must be signed in to change notification settings - Fork 431
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
Generate metadata for constructor return type #1460
Conversation
fn generate_constructor_return_type(ret_ty: Option<&syn::Type>) -> TokenStream2 { | ||
/// Returns `true` if the given type is `Self`. | ||
fn type_is_self_val(ty: &syn::Type) -> bool { | ||
matches!(ty, syn::Type::Path(syn::TypePath { | ||
qself: None, | ||
path | ||
}) if path.is_ident("Self")) | ||
} | ||
|
||
match ret_ty { | ||
None => { | ||
quote! { | ||
::ink::metadata::ReturnTypeSpec::new(::core::option::Option::None) | ||
} | ||
} | ||
Some(ty) => { | ||
if type_is_self_val(ty) { | ||
return quote! { ::ink::metadata::ReturnTypeSpec::new(::core::option::Option::None)} | ||
} | ||
let type_spec = Self::generate_constructor_type_spec(ty); | ||
let type_token = Self::replace_self_with_unit(ty); | ||
quote! { | ||
if !::ink::is_result_type!(#type_token) { | ||
::ink::metadata::ReturnTypeSpec::new(::core::option::Option::None) | ||
} else { | ||
::ink::metadata::ReturnTypeSpec::new(#type_spec) | ||
} | ||
} | ||
} | ||
} | ||
} |
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 again does syntactical checks instead of using the Rust type system properly and will fail for various syntactic contexts. To do this properly you'd need to take a similar approach as in generate_trait_messages
with Rust type system reflection such as
let is_payable = quote! {{
<<::ink::reflect::TraitDefinitionRegistry<<#storage_ident as ::ink::reflect::ContractEnv>::Env>
as #trait_path>::__ink_TraitInfo
as ::ink::reflect::TraitMessageInfo<#local_id>>::PAYABLE
}};
We do have the information for ink! messages and constructors as well. They are identified using their selectors which are always known at proc. macro compile time. Therefore you can access them in this scope just like in the linked example.
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.
The metadata codegen takes place after the WASM blob compilation, hence at this stage all types have already been evaluated by compiler and we can work with syntax.
In fact, I generally do not do syntactical checks. I only check if the token is Self
here because metadata generation takes place outside the storage definition, hence Self
can not be evaluated there. Same logic applies for Result<Self, ...>
.
However, if the return type is the explicit type of a contract or Result
with explicit contract type for Ok
variant, then I do the type evaluation, hence why we have TransformResult
factory.
I can be sure that the Self
is valid in this context is because the WASM build is already done by now.
I am not sure how message selectors are relevant 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.
I think we may be able to refactor this to make it use the Rust type system, I'm looking into this now.
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've done an experiment with this in #1491, take a look and see what you think.
After few tweaks and additional tests here is the summary of what has been achieved: The metadata is successfully generated for these edges cases:
|
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.
LGTM!
Stuffed some minor changes into a piggyback PR #1473, please have a look.
The metadata is successfully generated for these edges cases:
...
Is this explicitly tested for somewhere? In the metadata I see only one case (<Result<u8, ()>
being tested for. Might be worthwhile to add some contract testcases.
Also don't forget about documentation (if not already done by the original PR).
* test changes * rename trait to ConstructorReturnSpec * adapt test
Codecov Report
@@ Coverage Diff @@
## master #1460 +/- ##
==========================================
- Coverage 71.04% 64.43% -6.62%
==========================================
Files 201 201
Lines 6189 6236 +47
==========================================
- Hits 4397 4018 -379
- Misses 1792 2218 +426
📣 We’re building smart automated test selection to slash your CI/CD build times. Learn more |
Made some more minor changes: #1475 Also this PR fixes something, but the CI was passing prior/without the fix. Doesn't this mean that we should add the missing test case here as well? |
* remove unecessary helper fn * fix some comments * rename var
Co-authored-by: Cyrill Leutwiler <cyrill@parity.io>
* Add test for metadata generator * nightly fmt
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.
Thanks!
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.
The resulting metadata looks to be correct, but the main problem is that at the moment the actually returned value will not be an encoded instance of the type in the metadata.
Also I think we can utilize the type system here as @Robbepop suggests, I am looking into this now.
@@ -68,7 +68,7 @@ where | |||
&Contract::KEY, | |||
contract, | |||
); | |||
Ok(()) | |||
ink_env::return_value(ReturnFlags::default().set_reverted(false), &()); |
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.
But what if the return type is Result<Self, _>
, the value will never be returned. The client decoding will fail because it will expect 0x00
for Ok
but will get empty bytes instead.
I suppose you have not been able to test this since there are no clients which are attempting to decode the return value?
One way to test it would be via the in-contract instantiation with CreateBuilder
. E.g used in delegator
.
|
||
#[test] | ||
fn constructor_return_type_works() { | ||
let expected_no_ret_type_spec = ":: ink :: metadata :: ReturnTypeSpec :: new (:: core :: option :: Option :: None)"; |
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's not a great way to test it using strings like this. It's brittle and dependent upon the codegen impl.
If we can come up with a good solution with types + reflection this will be easier to test.
fn generate_constructor_return_type(ret_ty: Option<&syn::Type>) -> TokenStream2 { | ||
/// Returns `true` if the given type is `Self`. | ||
fn type_is_self_val(ty: &syn::Type) -> bool { | ||
matches!(ty, syn::Type::Path(syn::TypePath { | ||
qself: None, | ||
path | ||
}) if path.is_ident("Self")) | ||
} | ||
|
||
match ret_ty { | ||
None => { | ||
quote! { | ||
::ink::metadata::ReturnTypeSpec::new(::core::option::Option::None) | ||
} | ||
} | ||
Some(ty) => { | ||
if type_is_self_val(ty) { | ||
return quote! { ::ink::metadata::ReturnTypeSpec::new(::core::option::Option::None)} | ||
} | ||
let type_spec = Self::generate_constructor_type_spec(ty); | ||
let type_token = Self::replace_self_with_unit(ty); | ||
quote! { | ||
if !::ink::is_result_type!(#type_token) { | ||
::ink::metadata::ReturnTypeSpec::new(::core::option::Option::None) | ||
} else { | ||
::ink::metadata::ReturnTypeSpec::new(#type_spec) | ||
} | ||
} | ||
} | ||
} | ||
} |
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 we may be able to refactor this to make it use the Rust type system, I'm looking into this now.
This is a follow up for #1446 and closes #1457