Skip to content

Commit

Permalink
Allow mutable parameters in messages (#2004)
Browse files Browse the repository at this point in the history
* adjust macro in parsing tests

* Add tests and filter out mut keyword

* add changelog entry

* add idents to dictionary

* remove integration test

* make clippy happy
  • Loading branch information
German authored Nov 28, 2023
1 parent 27407b5 commit 1bd7f4e
Show file tree
Hide file tree
Showing 7 changed files with 68 additions and 15 deletions.
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)
}
}
}

0 comments on commit 1bd7f4e

Please sign in to comment.