Skip to content

Commit

Permalink
Implement new trait definition codegen 🚀 (#665)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
Robbepop authored Oct 20, 2021
1 parent d85565e commit e8d4739
Show file tree
Hide file tree
Showing 406 changed files with 11,734 additions and 4,716 deletions.
8 changes: 6 additions & 2 deletions .config/cargo_spellcheck.dic
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ defrag
defragmentation
deploy
dereferencing
deserializes
dispatchable
deserialize/S
dispatchable/S
encodable
evaluable
fuzzer
Expand Down Expand Up @@ -96,4 +96,8 @@ runtime/S
struct/S
vec/S
vector/S
implementer/S
deduplicated
wildcard/S
payability
unpayable
3 changes: 2 additions & 1 deletion .gitlab-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
44 changes: 44 additions & 0 deletions RELEASES.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,47 @@
# 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.
Expand Down
15 changes: 13 additions & 2 deletions crates/lang/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand All @@ -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 = []
68 changes: 68 additions & 0 deletions crates/lang/codegen/src/enforced_error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// 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()
}
}
104 changes: 104 additions & 0 deletions crates/lang/codegen/src/generator/arg_list.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
// 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
)
}
Loading

0 comments on commit e8d4739

Please sign in to comment.