Unverified Commit e8d47396 authored by Robin Freyler's avatar Robin Freyler Committed by GitHub
Browse files

Implement new trait definition codegen 🚀 (#665)

* fix spelling error in docs

* remove commented-out code

* move query_amount_{constructors,messages} utilities into own functions

* implement codegen for constructor decoder enum type

* slightly refactor AcceptsPayments and EnablesDynamicStorageAllocator

* fix bug that spans of ink! trait messages have not been registered

* add codegen for ExecuteDispatchable for constructor decoder

* add codegen for expr to query if any ink! message if payable

* implement preliminary codegen for new ink! dispatch entry points

* add ink_lang::execute_constructor_2 utility dispatch function

* demand ExecuteDispatch trait impl from generated decoder types

* trait-incrementer constructor now takes and sets initial value

* apply rustfmt

* implement message decoder type codegen for new dispatch

* enable entry point codegen for new dispatch

* fix doc comment

* remove old dispatch codegen

* rename dispatch2 module to dispatch

* apply rustfmt

* generate dispatch info trait impls always

They are useful generally and not only when compiled as root contract.

* no longer generate unnecessary braces in new dispatch codegen

* adjust UI tests for new dispatch codegen

* no longer derive core::fmt::Debug for dispatch decoder types

This fixes a doc test since this implied a Debug bound on all message and constructor inputs.

* fix bug in dispatcher utility method

* remove all no longer needed types and traits from ink_lang crate

Due to the new ink! codegen many of these definitions have become deprecated or obsolete.

* fix UI test

* silence non-minimal bool clippy warning in expanded code

* fix bug in dispatch codegen

Only check for payment if message is not payable and not all messages are not payable, too.

* add IsDocAttribute::extract_docs utility method for ink! IR

* overhaul ink! metadata codegen

This now properly takes into consideration the payable and selector properties of ink! trait messages.

* silence bad clippy warning

* rename execute_{message_2, constructor_2} to execute_{message, constructor}

* make ink! root contract messages revert state when returning Result::Err(_)

* fix some formatting issues

* extend is_result_{type, err} unit tests

* improve error messages for invalid ink! input and output types

This checks for ink! constructor and message inputs to be scale::Decode + 'static and for ink! output types to be scale::Encode + 'static.
Also added new UI tests to assert that this works for ink! message and constructor inputs.

* add missing UI test expectation files

* add UI tests for many ink! message and constructor inputs and outputs

* move noop contract UI test

* normalize UI test

* add passing UI tests for payable and selector properties

* improve passing ink! message selector UI test

* add passing UI test for ink! constructor selector property

* add UI tests for ink! storage structs with various fields

* add passing UI tests for ink! events

* add passing packed ink! storage struct UI test

* improve macro hygiene in StorageLayout derive impl

* add no-implicit-prelude passing UI test

Currently disabled since static_assertions dependency has macro hygiene problems with respect to no_implicit_prelude.

* add impl alias passing UI test

* modernize passing UI test for ink! storage struct derives

* modernize flipper example contract passing UI test

* add new trait-flipper example passing UI test

* add new passing UI test for #[ink(impl)] property

* rename 03-incrementer-contract UI test

* modernize example incrementer passing UI test

* apply rustfmt to UI test

* fix a doc test in ink_storage_derive crate

* add passing UI test for trait based incrementer example ink! smart contract

* apply rustfmt to UI test

* add passing UI tests for #[ink::contract] configs

* add passing UI test for #[ink::contract(env = ..)] config

* modernize env-access passing UI test

* modernize passing UI test to assert Rust items are properly expanded

* add passing UI test to assert existence of aliases for all env types

* rename remaining passing UI tests

* move commented out UI test to top

* move passing UI contract example tests to end

* fix StorageLayout derive unit tests

* replace vec! by plain old arrays in StorageLayout derive expansion

* remove plenty of usages of vec! macro in the ink! codebase

* improve macro hygiene in call_builder codegen

* slightly reformat erc20 UI test

* add ink! event passing UI tests

* move minimal ink! smart contract UI test case to front

* modernize simple definition UI test

* add simple no-implicit-prelude UI test for ink::trait_definition macro

The test case is disabled for now since the currently used parity-scale-codec produces macro hygiene errors.

* add some #[ink::trait_definition] UI tests

* rename #[ink::trait_definition] UI tests

* add UI tests for #[ink::trait_definition] with async and const messages

* add more UI tests for #[ink::trait_definition]

* normalize #[ink::trait_definition] UI tests

* normalize some UI tests

* add new UI tests for #[ink::trait_definition]

* add more UI tests for #[ink::trait_definition]

* add more UI tests for #[ink::trait_definition]

* generate guards for scale::Codec message inputs and outputs for ink! trait definitions

* add UI tests for non-codec message inputs and outputs for #[ink::trait_definition]

* fix spelling issue

* apply rustfmt to test case

* fix some spelling issues

* implement ink! trait message selector and payable property guards

* add UI tests for ink! trait message payable and selector property guards

* add passing UI tests for payable and selector property guards

* fix ERC-1155 example ink! smart contract

The contract broke due to ink! now checking if selectors of ink! trait message definitions and implemented ink! trait messages match.

* disallow #[ink(namespace)] property on ink! trait impl blocks

* add UI tests for #[ink(namespace)] attribute

* fix unit tests that make use of #[ink(namespace = ..)]

* update UI test case expectations

* use "diff" crate feature of trybuild crate

* fix some ink! specific error messages

* fix and improve some attribute error messages

* modernize failing #[ink::contract] UI tests

* fix some UI tests

* use wildcards in UI test inclusion pattern

* rename InkTraitDefinitionRegistry -> TraitDefinitionRegistry

* fix some tests

* apply rustfmt

* rename ink_lang::type_check module to codegen and make it a folder

* rename BaseEvent trait to ContractEventBase

* replace static_assertions usage by custom solution

This leads to fewer dependencies and also to better error messages.

* remove static_assertions dependency from ink_lang

* apply rustfmt

* fix some examples

* move TraitMessage{Payable,Selector} into codegen submodule

* remove superflous True trait from ink_lang

* add ExecuteMessageConfig

* refactor ink_lang dispatch definitions

* add show-codegen-docs crate feature to ink_lang to unhide the codegen module

* fix docs and add doc example tests to IsSameType

* add doc example tests to DispatchInput and DispatchOutput types

* fix spelling issues

* fix trait definition UI tests

* rename TraitImplementer -> TraitImplementedById and move into codegen module

* apply rustfmt

* move ImpliesReturn to ink_lang::codegen module and improve doc comments

* remove re-export of ImpliesReturn from ink_lang

It is now exported from the ink_lang::codegen sub module.

* fix spelling issues

* fix UI tests

* move ink! trait definition codegen definitions into trait_def submodule

* move TraitDefinitionRegistry into new ink_lang::reflect module

* unhide docs for TraitDefinitionRegistry

* enable no-implicit-prelude UI test

* add doc comment to reflect module

* update docs of TraitDefinitionRegistry

* move definitions for dispatch reflections into reflect submodule

* add usage example to COntractAmountDispatchables reflect trait

* add doc comment note to ContractAmountDispatchables trait

* add enforced newline to docs

* improve docs

* update docs for ContractDispatchableMessages trait

* improve doc test

* update docs of ContractDispatchableConstructors trait

* apply rustfmt

* fix hunspell dict

* remove non-existing optional UI test comments

* merge UI tests into one fat one

This (hopefully) fixes the flaky codecov CI for now ...

* use new selector_id! macro instead of raw values in doc tests

* add usage docs to DispatchableMessageInfo trait

* improve doc test

* add usage example to DispatchableConstructorInfo trait docs

* add note and usage example for ContractMessageDecoder docs

* add usage example to ContractConstructorDecoder docs

* improve docs of some reflect types in ink_lang

* improve docs

* move TraitMessageInfo into reflect submodule

* remove invalid static_assertions imports

* take &mut [u8; 32] instead of &mut [u8] for blake2b output

* make local_id of trait_def equal to selector of the message identifier

* modernize payable_message UI trait_def passing test

* add usage example to TraitMessageInfo trait

* fix composed selector calculation for empty ink! trait namespaces

* apply rustfmt

* extend usage example for TraitMessageInfo trait docs

* hopefully fix some weird spelling issues

* make ink! trait definitions actually use the namespace config

* extend usage example for TraitMessageInfo docs

* rework some ink! trait definition passing UI tests

* add new passing UI test for ink! trait definitions concerning namespaces

* add fail UI tests for ink! trait definitions concerning overlapping selectors

* move ContractName into reflect sub-module and add docs and usage example

* move ContractEnv trait to reflect module and add extensive docs and usage examples

* apply clippy suggestions

* move ContractReference trait to reflect module

* apply rustfmt

* fix some usage doc tests

* add extensive docs and usage example to ContractReference trait

* improve docs

* improve docs (2)

* move DispatchError to the reflect submodule

* add new event topic guards codegen to ink! codegen

* apply rustfmt and fix PhantomData import

* re-export RespectTopicLimit trait

* modernize trait_erc20 example contract

* move Env and StaticEnv into ink_lang::codegen module

* conditionally import EmitEvent trait anonymously for ink! impl blocks

* fix some UI tests

* move EmitEvent trait to ink_lang::codegen module

* fix UI tests

* fix UI tests

* add passing UI test for self.env() and Self::env() syntax

* add failing UI tests for missing #[ink(impl)] annotation

* move ContractEventBase trait to ink_lang::reflect submodule

* apply rustfmt

* add usage example to ContractEventBase docs

* move TraitModulePath to ink_lang::reflect submodule

* move ink! trait call builder codegen facilities to ink_lang::codegen submodule

* move ContractCallBuilder to ink_lang::codegen submodule

* move IsSameType and identity_type into ink_lang::codegen::utils module

* rename identity_type to consume_type

* apply rustfmt

* add usage examples to consume_type docs (+ missing rename)

* fix codegen to allow for environmental type usage in ink! trait definitions

* add UI test that uses environmental types in ink! trait definition

* apply rustfmt

* remove unused Selector::unique_id method

* remove Selector::from_bytes

Users should use Selector's From<[u8;4]> implementation instead.

* rename Selector::new -> Selector::compute

* fix some UI tests for rename

* change API: Selector::as_bytes -> Selector::to_bytes and return by value

* add payability to hunspell dict

* use ink! trait info object instead of uniqe trait ID for TraitCallForwarderFor trait

* adjust UI tests

* apply rustfmt

* remove TraitUniqueId trait

* remove TraitImplementedById and utilities to compute ink! trait verify hash

* remove unused CannotCallTraitConstructor variant

* Update to using trybuild version 1.0.49

This forces most or all of the failure UI tests to be adjusted slightly.

* add comment that explains why we have both result checks in execution

* introduce DecodeDispatch trait

This replaces the bare use of scale::Decode for ink! constructor and message decoders.
The advantage is that it can return DispatchError instead of scale::Error.
Also adds new UI test and docs with a usage example.

* use selector_bytes! in UI test

* slightly improve fail UI tests

* fix warning in passing UI test

* add new UI test to guard against trait message selector overlaps

* move UI tests from ink_lang_macro to ink_lang crate directory

* adjust UI tests after moving

* remove unused dev-dependencies from ink_lang_macro after moving UI tests

* fix compilation error with Wasm target

* fix CI with formatting checks for UI tests

* add unpayable to hunspell dictionary

* fix hunspell spelling issues

* move ink-experimental-engine to ink_lang crate

Used by unique topics test.

* update UI tests for new nightly compiler

* add missing fixed UI tests

* fix spelling issue in UI test

* add docs explaining usage of AccumulatorRef et.al. in delegator example

* extend is_result unit tests

* re-add pretty assertions

* normalize used version in ink-metadata crate

* add skeleton for ink! 3.0-rc7 in RELEASES

* add release information about this PR to RELEASES.md

* use ink! 3.0-rc6 in multisig example contract

* fix docs

* remove commented out code

* use unzip to partition input_bindings and input_types (thanks andrew!)

* improve #[ink(extension)] missing parameter error message
parent d85565ee
Pipeline #163034 failed with stages
in 13 minutes and 22 seconds
......@@ -37,8 +37,8 @@ defrag
defragmentation
deploy
dereferencing
deserializes
dispatchable
deserialize/S
dispatchable/S
encodable
evaluable
fuzzer
......@@ -96,4 +96,8 @@ runtime/S
struct/S
vec/S
vector/S
implementer/S
deduplicated
wildcard/S
payability
unpayable
......@@ -289,7 +289,8 @@ fmt:
script:
- cargo fmt --verbose --all -- --check
# For the UI tests we need to disable the license check
- cargo fmt --verbose --all -- --check --config=license_template_path="" crates/lang/macro/tests/ui/contract/{pass,fail}/*.rs
- cargo fmt --verbose --all -- --check --config=license_template_path="" crates/lang/tests/ui/contract/{pass,fail}/*.rs
- cargo fmt --verbose --all -- --check --config=license_template_path="" crates/lang/tests/ui/trait_def/{pass,fail}/*.rs
#### stage: examples
......
# Version 3.0-rc7 (UNRELEASED)
This is the 7th release candidate for ink! 3.0.
## Added
- The ink! codegen now heavily relies on static type information based on traits defined in `ink_lang`.
- Some of those traits and their carried information can be used for static reflection of ink!
smart contracts. Those types and traits reside in the new `ink_lang::reflect` module and is
publicly usable by ink! smart contract authors.
## Changed
- ink! Contract via `#[ink::contract]`:
- ink! smart contracts now always generated two contract types. Given `MyContract`:
- `MyContract` will still be the storage struct.
However, it can now additionally be used as static dependency in other smart contracts.
Static dependencies can be envisioned as being directly embedded into a smart contract.
- `MyContractRef` is pretty much the same of what we had gotten with the old `ink-as-dependency`.
It is a typed thin-wrapper around an `AccountId` that is mirroring the ink! smart contract's API
and implemented traits.
- ink! Trait Definitions via `#[ink::trait_definition]`:
- ink! trait definitions no longer can define trait constructors.
- ink! trait implementations now inherit `selector` and `payable` properties for trait messages.
- Now explicitly setting `selector` or `payable` property for an implemented ink! trait method
will only act as a guard that the set property is in fact the same as defined by the ink!
trait definition.
- Improve quite a few ink! specific compile errors:
- For example when using ink! messages and constructors that have inputs or outputs that cannot
be encoded or decoded using the SCALE codec.
- Simplified selector computation for ink! trait methods.
- Now selectors are encoded as `blake2b({namespace}::{trait_identifier}::{message_identifier})[0..4]`.
If no `namespace` is set for the ink! trait definition then the formula is
`blake2b({trait_identifier}::{message_identifier})[0..4]`.
Where `trait_identifier` and `message_identifier` both refer to the identifiers of the ink! trait
definition and ink! trait message respectively.
## Fixed
- Contracts that are compiled as root (the default) now properly revert the transaction if a message
returned `Result::Err`.
- This does not apply to ink! smart contracts that are used as dependencies. Therefore it is still possible to match against a result return type for a called dependency.
# Version 3.0-rc6
This is the 6th release candidate for ink! 3.0.
......
......@@ -25,10 +25,13 @@ ink_lang_macro = { version = "3.0.0-rc6", path = "macro", default-features = fal
scale = { package = "parity-scale-codec", version = "2", default-features = false, features = ["derive", "full"] }
derive_more = { version = "0.99", default-features = false, features = ["from"] }
static_assertions = "1.1"
[dev-dependencies]
# required for the doctest of `env_access::EnvAccess::instantiate_contract`
ink_lang_ir = { version = "3.0.0-rc6", path = "ir" }
ink_metadata = { version = "3.0.0-rc6", default-features = false, path = "../metadata" }
trybuild = { version = "1.0.49", features = ["diff"] }
# Required for the doctest of `env_access::EnvAccess::instantiate_contract`
scale-info = { version = "1.0", default-features = false, features = ["derive"] }
[features]
......@@ -43,3 +46,11 @@ std = [
"ink_lang_macro/std",
"scale/std",
]
show-codegen-docs = []
# Due to https://github.com/rust-lang/cargo/issues/6915 features that affect a dev-dependency
# currently can't be enabled by a parent crate, hence `["ink_env/ink-experimental-engine"]` does
# not work.
# After we switch to the new off-chain environment with https://github.com/paritytech/ink/issues/565
# we can remove the feature altogether.
ink-experimental-engine = []
// Copyright 2018-2021 Parity Technologies (UK) Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use impl_serde::serialize as serde_hex;
use quote::format_ident;
/// Errors which may occur when forwarding a call is not allowed.
///
/// We insert markers for these errors in the generated contract code.
/// This is necessary since we can't check these errors at compile time
/// of the contract.
/// `cargo-contract` checks the contract code for these error markers
/// when building a contract and fails if it finds markers.
#[derive(scale::Encode, scale::Decode)]
pub enum EnforcedErrors {
/// The below error represents calling a `&mut self` message in a context that
/// only allows for `&self` messages. This may happen under certain circumstances
/// when ink! trait implementations are involved with long-hand calling notation.
#[codec(index = 1)]
CannotCallTraitMessage {
/// The trait that defines the called message.
trait_ident: String,
/// The name of the called message.
message_ident: String,
/// The selector of the called message.
message_selector: [u8; 4],
/// Is `true` if the `self` receiver of the ink! message is `&mut self`.
message_is_mut: bool,
},
}
impl EnforcedErrors {
/// Create the identifier of an enforced ink! compilation error.
fn into_ident(self) -> syn::Ident {
format_ident!(
"__ink_enforce_error_{}",
serde_hex::to_hex(&scale::Encode::encode(&self), false)
)
}
/// Creates an enforced linker error to signal that an invalid
/// implementation of an ink! trait message has been called.
pub fn cannot_call_trait_message(
trait_ident: &syn::Ident,
message_ident: &syn::Ident,
message_selector: ir::Selector,
message_is_mut: bool,
) -> syn::Ident {
Self::CannotCallTraitMessage {
trait_ident: trait_ident.to_string(),
message_ident: message_ident.to_string(),
message_selector: message_selector.to_bytes(),
message_is_mut,
}
.into_ident()
}
}
// Copyright 2018-2021 Parity Technologies (UK) Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use heck::CamelCase;
use proc_macro2::{
Span,
TokenStream as TokenStream2,
};
use quote::{
format_ident,
quote,
quote_spanned,
};
/// Returns the associated output type for an ink! trait message.
pub fn output_ident(message_name: &syn::Ident) -> syn::Ident {
format_ident!("{}Output", message_name.to_string().to_camel_case())
}
/// Returns the sequence of artificial input parameter bindings for the message.
///
/// # Note
///
/// This returns `__ink_binding_N` for every message input where `N` is the number
/// of the input from first to last.
pub fn input_bindings(inputs: ir::InputsIter) -> Vec<syn::Ident> {
inputs
.enumerate()
.map(|(n, _)| format_ident!("__ink_binding_{}", n))
.collect::<Vec<_>>()
}
/// Returns the sequence of input types for the message.
pub fn input_types(inputs: ir::InputsIter) -> Vec<&syn::Type> {
inputs.map(|pat_type| &*pat_type.ty).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);
if input_types.len() != 1 {
// Pack all types into a tuple if they are not exactly 1.
// This results in `()` for zero input types.
quote! { ( #( #input_types ),* ) }
} else {
// Return the single type without turning it into a tuple.
quote! { #( #input_types )* }
}
}
/// Returns a tuple expression representing the bindings yielded by the inputs.
pub fn input_bindings_tuple(inputs: ir::InputsIter) -> TokenStream2 {
let input_bindings = input_bindings(inputs);
match input_bindings.len() {
0 => quote! { _ },
1 => quote! { #( #input_bindings ),* },
_ => quote! { ( #( #input_bindings ),* ) },
}
}
/// Builds up the `ink_env::call::utils::ArgumentList` type structure for the given types.
pub fn generate_argument_list<'b, Args>(args: Args) -> TokenStream2
where
Args: IntoIterator<Item = &'b syn::Type>,
<Args as IntoIterator>::IntoIter: Iterator,
{
use syn::spanned::Spanned as _;
args.into_iter().fold(
quote! { ::ink_env::call::utils::EmptyArgumentList },
|rest, arg| {
let span = arg.span();
quote_spanned!(span=>
::ink_env::call::utils::ArgumentList<::ink_env::call::utils::Argument<#arg>, #rest>
)
}
)
}
/// Generates code to uniquely identify a trait by its unique ID given only its identifier.
///
/// # Note
///
/// As with all Rust macros identifiers can shadow each other so the given identifier
/// needs to be valid for the scope in which the returned code is generated.
pub fn generate_reference_to_trait_info(
span: Span,
trait_path: &syn::Path,
) -> TokenStream2 {
quote_spanned!(span=>
<::ink_lang::reflect::TraitDefinitionRegistry<Environment>
as #trait_path>::__ink_TraitInfo
)
}
// Copyright 2018-2021 Parity Technologies (UK) Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::{
generator,
GenerateCode,
};
use derive_more::From;
use ir::Callable;
use proc_macro2::TokenStream as TokenStream2;
use quote::{
format_ident,
quote,
quote_spanned,
};
use syn::spanned::Spanned as _;
/// Generates code for the call builder of the ink! smart contract.
///
/// The call builder is the entity that builds up calls for calling of other
/// smart contract on-chain in a type safe way.
/// It implements all ink! traits that the associated ink! smart contract implements
/// so that their underlying implementation directly calls the respective ink!
/// trait implementation on-chain.
///
/// The ink! call builder of a smart contract is directly used by the storage
/// type of the smart contract itself as well by other entities that use the
/// smart contract via long-hand calling notation to incrementally build up calls.
#[derive(From)]
pub struct CallBuilder<'a> {
contract: &'a ir::Contract,
}
impl GenerateCode for CallBuilder<'_> {
fn generate_code(&self) -> TokenStream2 {
let call_builder_struct = self.generate_struct();
let auxiliary_trait_impls = self.generate_auxiliary_trait_impls();
let call_builder_impls = self.generate_call_forwarder_impls();
let call_builder_inherent_impls = self.generate_call_builder_inherent_impls();
quote! {
const _: () = {
#call_builder_struct
#auxiliary_trait_impls
#call_builder_impls
#call_builder_inherent_impls
};
}
}
}
impl CallBuilder<'_> {
/// Returns the identifier of the generated ink! call builder struct.
///
/// # Note
///
/// This identifier must not be used outside of the generated `const`
/// block in which the call builder type is going to be defined.
/// In order to refer to the call builder of an ink! smart contract
/// use the [`ink_lang::TraitCallBuilder`] trait implementation.
fn call_builder_ident() -> syn::Ident {
format_ident!("CallBuilder")
}
fn generate_struct(&self) -> TokenStream2 {
let span = self.contract.module().storage().span();
let storage_ident = self.contract.module().storage().ident();
let cb_ident = Self::call_builder_ident();
quote_spanned!(span=>
/// The ink! smart contract's call builder.
///
/// Implements the underlying on-chain calling of the ink! smart contract
/// messages and trait implementations in a type safe way.
#[repr(transparent)]
#[cfg_attr(feature = "std", derive(
::scale_info::TypeInfo,
::ink_storage::traits::StorageLayout,
))]
#[derive(
::core::fmt::Debug,
::ink_storage::traits::SpreadLayout,
::ink_storage::traits::PackedLayout,
::scale::Encode,
::scale::Decode,
::core::hash::Hash,
::core::cmp::PartialEq,
::core::cmp::Eq,
::core::clone::Clone,
)]
pub struct #cb_ident {
account_id: AccountId,
}
const _: () = {
impl ::ink_lang::codegen::ContractCallBuilder for #storage_ident {
type Type = #cb_ident;
}
impl ::ink_lang::reflect::ContractEnv for #cb_ident {
type Env = <#storage_ident as ::ink_lang::reflect::ContractEnv>::Env;
}
};
)
}
/// Generates some ink! specific auxiliary trait implementations for the
/// smart contract call builder type.
///
/// These are required to properly interoperate with the call builder.
fn generate_auxiliary_trait_impls(&self) -> TokenStream2 {
let span = self.contract.module().storage().span();
let cb_ident = Self::call_builder_ident();
quote_spanned!(span=>
impl ::ink_env::call::FromAccountId<Environment> for #cb_ident {
#[inline]
fn from_account_id(account_id: AccountId) -> Self {
Self { account_id }
}
}
impl ::ink_lang::ToAccountId<Environment> for #cb_ident {
#[inline]
fn to_account_id(&self) -> AccountId {
<AccountId as ::core::clone::Clone>::clone(&self.account_id)
}
}
)
}
/// Generate the `TraitCallForwarder` trait implementations for the call builder
/// for every ink! trait implemented by the associated ink! smart contract.
///
/// These call forwarder trait implementations are used to dispatch to the global
/// call builder for the respective ink! trait definition that is being called.
/// The call builder only forwards the actual calls to those global call builders
/// and does not have its own calling logic.
fn generate_call_forwarder_impls(&self) -> TokenStream2 {
self.contract
.module()
.impls()
.filter_map(|impl_block| {
// We are only interested in ink! trait implementation block.
impl_block.trait_path().map(|trait_path| {
self.generate_code_for_trait_impl(trait_path, impl_block)
})
})
.collect()
}
/// Generates code required by the ink! call builder of an ink! smart contract
/// for a single ink! trait definition that the contract implements.
fn generate_code_for_trait_impl(
&self,
trait_path: &syn::Path,
impl_block: &ir::ItemImpl,
) -> TokenStream2 {
let call_forwarder_impl =
self.generate_call_forwarder_for_trait_impl(trait_path, impl_block);
let ink_trait_impl = self.generate_ink_trait_impl(trait_path, impl_block);
quote! {
#call_forwarder_impl
#ink_trait_impl
}
}
/// Generates code for a single ink! trait implementation to forward calls for
/// the associated ink! smart contract call builder.
fn generate_call_forwarder_for_trait_impl(
&self,
trait_path: &syn::Path,
impl_block: &ir::ItemImpl,
) -> TokenStream2 {
let span = impl_block.span();
let cb_ident = Self::call_builder_ident();
let trait_info = generator::generate_reference_to_trait_info(span, trait_path);
quote_spanned!(span=>
#[doc(hidden)]
impl ::ink_lang::codegen::TraitCallForwarderFor<#trait_info> for #cb_ident {
type Forwarder = <<Self as #trait_path>::__ink_TraitInfo as ::ink_lang::codegen::TraitCallForwarder>::Forwarder;
#[inline(always)]
fn forward(&self) -> &Self::Forwarder {
// SAFETY:
//
// We convert from a shared reference to a type that thinly wraps
// only an `AccountId` to a shared reference to another type of which
// we know that it also thinly wraps an `AccountId`.
// Furthermore both types use `repr(transparent)`.
unsafe {
&*(&self.account_id as *const AccountId as *const Self::Forwarder)
}
}
#[inline(always)]
fn forward_mut(&mut self) -> &mut Self::Forwarder {
// SAFETY:
//
// We convert from a exclusive reference to a type that thinly wraps
// only an `AccountId` to a exclusive reference to another type of which
// we know that it also thinly wraps an `AccountId`.
// Furthermore both types use `repr(transparent)`.
unsafe {
&mut *(&mut self.account_id as *mut AccountId as *mut Self::Forwarder)
}
}
#[inline(always)]
fn build(&self) -> &<Self::Forwarder as ::ink_lang::codegen::TraitCallBuilder>::Builder {
<_ as ::ink_lang::codegen::TraitCallBuilder>::call(
<Self as ::ink_lang::codegen::TraitCallForwarderFor<#trait_info>>::forward(self)
)
}
#[inline(always)]
fn build_mut(&mut self)
-> &mut <Self::Forwarder as ::ink_lang::codegen::TraitCallBuilder>::Builder
{
<_ as ::ink_lang::codegen::TraitCallBuilder>::call_mut(
<Self as ::ink_lang::codegen::TraitCallForwarderFor<#trait_info>>::forward_mut(self)
)
}
}
)
}
/// Generates the actual ink! trait implementation for the generated call builder.
fn generate_ink_trait_impl(
&self,
trait_path: &syn::Path,
impl_block: &ir::ItemImpl,
) -> TokenStream2 {
let span = impl_block.span();
let cb_ident = Self::call_builder_ident();
let messages = impl_block
.iter_messages()
.map(|message| self.generate_ink_trait_impl_for_message(trait_path, message));
quote_spanned!(span=>
impl #trait_path for #cb_ident {
type __ink_TraitInfo = <::ink_lang::reflect::TraitDefinitionRegistry<Environment>
as #trait_path>::__ink_TraitInfo;
#( #messages )*
}
)
}
/// Generates the code for the ink! trait implementation of the call builder
/// of a single ink! trait message and its associated output type.
fn generate_ink_trait_impl_for_message(
&self,
trait_path: &syn::Path,
message: ir::CallableWithSelector<ir::Message>,
) -> TokenStream2 {
use ir::Callable as _;
let span = message.span();
let message_ident = message.ident();
let output_ident = generator::output_ident(message_ident);
let trait_info = generator::generate_reference_to_trait_info(span, trait_path);
let (input_bindings, input_types): (Vec<_>, Vec<_>) = message
.callable()
.inputs()
.map(|input| (&input.pat, &input.ty))
.unzip();
let mut_token = message
.receiver()
.is_ref_mut()
.then(|| Some(quote! { mut }));
let build_cmd = match message.receiver() {
ir::Receiver::Ref => quote! { build },
ir::Receiver::RefMut => quote! { build_mut },
};
let attrs = message.attrs();
quote_spanned!(span=>
type #output_ident = <<<
Self
as ::ink_lang::codegen::TraitCallForwarderFor<#trait_info>>::Forwarder
as ::ink_lang::codegen::TraitCallBuilder>::Builder
as #trait_path>::#output_ident;
#[inline]
#( #attrs )*
fn #message_ident(
& #mut_token self
#( , #input_bindings: #input_types )*
) -> Self::#output_ident {
<_ as #trait_path>::#message_ident(
<Self as ::ink_lang::codegen::TraitCallForwarderFor<#trait_info>>::#build_cmd(self)
#( , #input_bindings )*
)
}
)
}