diff --git a/.config/cargo_spellcheck.dic b/.config/cargo_spellcheck.dic index ca8ac821f9b..093974a6f84 100644 --- a/.config/cargo_spellcheck.dic +++ b/.config/cargo_spellcheck.dic @@ -52,6 +52,7 @@ fuzzer getter growable ident +idents interoperate invariants kB diff --git a/CHANGELOG.md b/CHANGELOG.md index 17f08c0858d..683c0830d61 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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) diff --git a/crates/ink/codegen/src/generator/arg_list.rs b/crates/ink/codegen/src/generator/arg_list.rs index 503bdee9fcd..e53d474db06 100644 --- a/crates/ink/codegen/src/generator/arg_list.rs +++ b/crates/ink/codegen/src/generator/arg_list.rs @@ -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 /// @@ -46,6 +47,22 @@ pub fn input_types(inputs: ir::InputsIter) -> Vec<&syn::Type> { inputs.map(|pat_type| &*pat_type.ty).collect::>() } +/// 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::>() +} + /// 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); diff --git a/crates/ink/codegen/src/generator/as_dependency/contract_ref.rs b/crates/ink/codegen/src/generator/as_dependency/contract_ref.rs index bbedecc333e..a2c026b1f91 100644 --- a/crates/ink/codegen/src/generator/as_dependency/contract_ref.rs +++ b/crates/ink/codegen/src/generator/as_dependency/contract_ref.rs @@ -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::>(); + let input_idents = generator::input_message_idents(message.inputs()); let input_types = message.inputs().map(|input| &input.ty).collect::>(); let cfg_attrs = message.get_cfg_attrs(span); quote_spanned!(span=> @@ -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( ::#call_operator(self), ) - #( , #input_bindings )* + #( , #input_idents )* ) } ) @@ -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::>(); + let input_idents = generator::input_message_idents(message.inputs()); let input_types = message.inputs().map(|input| &input.ty).collect::>(); let output_type = message.output().map(|ty| quote! { -> #ty }); let wrapped_output_type = message.wrapped_output(); @@ -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), @@ -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 { ::#call_operator(self) - .#message_ident( #( #input_bindings ),* ) + .#message_ident( #( #input_idents ),* ) .try_invoke() .unwrap_or_else(|error| ::core::panic!( "encountered error while calling {}::{}: {:?}", diff --git a/crates/ink/codegen/src/generator/mod.rs b/crates/ink/codegen/src/generator/mod.rs index 5021bc5ca4d..b306a84c6f5 100644 --- a/crates/ink/codegen/src/generator/mod.rs +++ b/crates/ink/codegen/src/generator/mod.rs @@ -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, diff --git a/crates/ink/ir/src/ir/item_impl/message.rs b/crates/ink/ir/src/ir/item_impl/message.rs index a60585f702e..af3bcf61aca 100644 --- a/crates/ink/ir/src/ir/item_impl/message.rs +++ b/crates/ink/ir/src/ir/item_impl/message.rs @@ -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) ),* ] }}; @@ -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]) {} }, ), ]; diff --git a/integration-tests/mother/lib.rs b/integration-tests/mother/lib.rs index b7ec82fec7a..98e6acf2b82 100755 --- a/integration-tests/mother/lib.rs +++ b/integration-tests/mother/lib.rs @@ -19,6 +19,7 @@ #[ink::contract] mod mother { use ink::prelude::{ + format, string::{ String, ToString, @@ -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)] @@ -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) + } } }