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

Allow mutable parameters in messages #2004

Merged
merged 6 commits into from
Nov 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .config/cargo_spellcheck.dic
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ fuzzer
getter
growable
ident
idents
interoperate
invariants
kB
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]

### Added
- Allow mutable parameters in messages - [#2004](https://github.com/paritytech/ink/pull/2004)
- [E2E] Allow testing with live-chain state - [#1949](https://github.com/paritytech/ink/pull/1949)
- [E2E] Call builders and extra gas margin option - [#1917](https://github.com/paritytech/ink/pull/1917)
- Linter: `storage_never_freed` lint - [#1932](https://github.com/paritytech/ink/pull/1932)
Expand Down
19 changes: 18 additions & 1 deletion crates/ink/codegen/src/generator/arg_list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ pub fn output_ident(message_name: &syn::Ident) -> syn::Ident {
format_ident!("{}Output", message_name.to_string().to_lower_camel_case())
}

/// Returns the sequence of artificial input parameter bindings for the message.
/// Returns the sequence of artificial input parameter bindings
/// for the message or constructor.
///
/// # Note
///
Expand All @@ -46,6 +47,22 @@ pub fn input_types(inputs: ir::InputsIter) -> Vec<&syn::Type> {
inputs.map(|pat_type| &*pat_type.ty).collect::<Vec<_>>()
}

/// Returns the sequence of input idents for the message.
pub fn input_message_idents(inputs: ir::InputsIter) -> Vec<&syn::Ident> {
inputs
.map(|input| {
match &*input.pat {
syn::Pat::Ident(ident) => &ident.ident,
_ => {
unreachable!(
"encountered ink! dispatch input with missing identifier"
)
}
}
})
.collect::<Vec<_>>()
}

/// Returns a tuple type representing the types yielded by the input types.
pub fn input_types_tuple(inputs: ir::InputsIter) -> TokenStream2 {
let input_types = input_types(inputs);
Expand Down
16 changes: 8 additions & 8 deletions crates/ink/codegen/src/generator/as_dependency/contract_ref.rs
Original file line number Diff line number Diff line change
Expand Up @@ -289,7 +289,7 @@ impl ContractRef<'_> {
ir::Receiver::RefMut => quote! { forward_mut },
};
let mut_token = message.receiver().is_ref_mut().then(|| quote! { mut });
let input_bindings = message.inputs().map(|input| &input.pat).collect::<Vec<_>>();
let input_idents = generator::input_message_idents(message.inputs());
let input_types = message.inputs().map(|input| &input.ty).collect::<Vec<_>>();
let cfg_attrs = message.get_cfg_attrs(span);
quote_spanned!(span=>
Expand All @@ -301,13 +301,13 @@ impl ContractRef<'_> {
#( #cfg_attrs )*
fn #message_ident(
& #mut_token self
#( , #input_bindings : #input_types )*
#( , #input_idents : #input_types )*
) -> Self::#output_ident {
<_ as #trait_path>::#message_ident(
<_ as ::ink::codegen::TraitCallForwarderFor<{#trait_info_id}>>::#forward_operator(
<Self as ::ink::codegen::TraitCallBuilder>::#call_operator(self),
)
#( , #input_bindings )*
#( , #input_idents )*
)
}
)
Expand Down Expand Up @@ -384,7 +384,7 @@ impl ContractRef<'_> {
ir::Receiver::RefMut => quote! { call_mut },
};
let mut_token = message.receiver().is_ref_mut().then(|| quote! { mut });
let input_bindings = message.inputs().map(|input| &input.pat).collect::<Vec<_>>();
let input_idents = generator::input_message_idents(message.inputs());
let input_types = message.inputs().map(|input| &input.ty).collect::<Vec<_>>();
let output_type = message.output().map(|ty| quote! { -> #ty });
let wrapped_output_type = message.wrapped_output();
Expand All @@ -393,9 +393,9 @@ impl ContractRef<'_> {
#[inline]
pub fn #message_ident(
& #mut_token self
#( , #input_bindings : #input_types )*
#( , #input_idents : #input_types )*
) #output_type {
self.#try_message_ident( #( #input_bindings, )* )
self.#try_message_ident( #( #input_idents, )* )
.unwrap_or_else(|error| ::core::panic!(
"encountered error while calling {}::{}: {:?}",
::core::stringify!(#storage_ident),
Expand All @@ -408,10 +408,10 @@ impl ContractRef<'_> {
#[inline]
pub fn #try_message_ident(
& #mut_token self
#( , #input_bindings : #input_types )*
#( , #input_idents : #input_types )*
) -> #wrapped_output_type {
<Self as ::ink::codegen::TraitCallBuilder>::#call_operator(self)
.#message_ident( #( #input_bindings ),* )
.#message_ident( #( #input_idents ),* )
.try_invoke()
.unwrap_or_else(|error| ::core::panic!(
"encountered error while calling {}::{}: {:?}",
Expand Down
1 change: 1 addition & 0 deletions crates/ink/codegen/src/generator/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ pub use self::{
generate_reference_to_trait_info,
input_bindings,
input_bindings_tuple,
input_message_idents,
input_types,
input_types_tuple,
output_ident,
Expand Down
30 changes: 24 additions & 6 deletions crates/ink/ir/src/ir/item_impl/message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -382,13 +382,23 @@ mod tests {

#[test]
fn inputs_works() {
macro_rules! expected_input {
( mut $name:ident: $ty:ty ) => {{
syn::parse_quote! {
mut $name: $ty
}
}};
( $name:ident: $ty:ty ) => {{
syn::parse_quote! {
$name: $ty
}
}};
}
macro_rules! expected_inputs {
( $( $name:ident: $ty:ty ),* ) => {{
( $( $($ts:ident)+: $ty:ty ),* ) => {{
vec![
$(
syn::parse_quote! {
$name: $ty
}
expected_input!($($ts)+: $ty)
),*
]
}};
Expand All @@ -410,12 +420,20 @@ mod tests {
fn my_message(&self, a: i32) {}
},
),
(
// Single mutable input:
expected_inputs!(mut a: i32),
syn::parse_quote! {
#[ink(message)]
fn my_message(&self, mut a: i32) {}
},
),
(
// Some inputs:
expected_inputs!(a: i32, b: u64, c: [u8; 32]),
expected_inputs!(a: i32, b: u64, mut c: [u8; 32]),
syn::parse_quote! {
#[ink(message)]
fn my_message(&self, a: i32, b: u64, c: [u8; 32]) {}
fn my_message(&self, a: i32, b: u64, mut c: [u8; 32]) {}
},
),
];
Expand Down
15 changes: 15 additions & 0 deletions integration-tests/mother/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#[ink::contract]
mod mother {
use ink::prelude::{
format,
string::{
String,
ToString,
Expand Down Expand Up @@ -181,6 +182,13 @@ mod mother {
pub fn debug_log(&mut self, _message: String) {
ink::env::debug_println!("debug_log: {}", _message);
}

/// Mutates the input string to return "Hello, { name }"
#[ink(message)]
pub fn mut_hello_world(&self, mut message: String) -> String {
message = format!("Hello, {}", message);
message
}
}

#[cfg(test)]
Expand Down Expand Up @@ -227,5 +235,12 @@ mod mother {
let mut contract = Mother::default();
let _ = contract.revert_or_trap(Some(Failure::Panic));
}

#[ink::test]
fn mut_works() {
let contract = Mother::default();
let res = contract.mut_hello_world("Alice".to_string());
assert_eq!("Hello, Alice", res)
}
}
}