From e8d4739649293a458c3958bec802d2d750067d98 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Thu, 21 Oct 2021 00:17:41 +0200 Subject: [PATCH] Implement new trait definition codegen :rocket: (#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 --- .config/cargo_spellcheck.dic | 8 +- .gitlab-ci.yml | 3 +- RELEASES.md | 44 + crates/lang/Cargo.toml | 15 +- crates/lang/codegen/src/enforced_error.rs | 68 + crates/lang/codegen/src/generator/arg_list.rs | 104 ++ .../generator/as_dependency/call_builder.rs | 404 ++++++ .../generator/as_dependency/contract_ref.rs | 411 ++++++ .../src/generator/as_dependency/mod.rs | 94 ++ crates/lang/codegen/src/generator/contract.rs | 9 +- .../codegen/src/generator/cross_calling.rs | 908 ------------- crates/lang/codegen/src/generator/dispatch.rs | 1192 +++++++++-------- crates/lang/codegen/src/generator/env.rs | 14 +- crates/lang/codegen/src/generator/events.rs | 78 +- .../lang/codegen/src/generator/item_impls.rs | 196 ++- crates/lang/codegen/src/generator/metadata.rs | 314 +++-- crates/lang/codegen/src/generator/mod.rs | 21 +- crates/lang/codegen/src/generator/storage.rs | 36 +- .../lang/codegen/src/generator/trait_def.rs | 117 -- .../src/generator/trait_def/call_builder.rs | 403 ++++++ .../src/generator/trait_def/call_forwarder.rs | 428 ++++++ .../src/generator/trait_def/definition.rs | 71 + .../codegen/src/generator/trait_def/mod.rs | 69 + .../src/generator/trait_def/trait_registry.rs | 313 +++++ crates/lang/codegen/src/lib.rs | 12 +- crates/lang/ir/src/ast/attr_args.rs | 9 +- crates/lang/ir/src/ir/attrs.rs | 96 +- crates/lang/ir/src/ir/blake2.rs | 2 +- crates/lang/ir/src/ir/chain_extension.rs | 3 +- crates/lang/ir/src/ir/item_impl/callable.rs | 40 +- .../lang/ir/src/ir/item_impl/constructor.rs | 5 +- crates/lang/ir/src/ir/item_impl/message.rs | 18 +- crates/lang/ir/src/ir/item_impl/mod.rs | 8 +- crates/lang/ir/src/ir/item_mod.rs | 29 +- crates/lang/ir/src/ir/mod.rs | 11 +- crates/lang/ir/src/ir/selector.rs | 109 +- crates/lang/ir/src/ir/trait_def.rs | 1116 --------------- crates/lang/ir/src/ir/trait_def/config.rs | 109 ++ crates/lang/ir/src/ir/trait_def/item/iter.rs | 93 ++ crates/lang/ir/src/ir/trait_def/item/mod.rs | 389 ++++++ .../ir/src/ir/trait_def/item/trait_item.rs | 196 +++ crates/lang/ir/src/ir/trait_def/mod.rs | 73 + crates/lang/ir/src/ir/trait_def/tests.rs | 446 ++++++ crates/lang/ir/src/ir/utils.rs | 13 + crates/lang/ir/src/lib.rs | 6 +- crates/lang/macro/Cargo.toml | 10 - crates/lang/macro/src/lib.rs | 35 +- crates/lang/macro/src/trait_def.rs | 8 +- crates/lang/macro/tests/compile_tests.rs | 77 -- .../fail/C-00-constructor-self-ref.rs | 19 - .../fail/C-00-constructor-self-ref.stderr | 5 - .../fail/C-01-constructor-self-mut.rs | 19 - .../fail/C-01-constructor-self-mut.stderr | 5 - .../fail/C-02-constructor-self-val.stderr | 5 - .../C-03-constructor-missing-return.stderr | 5 - .../fail/C-04-missing-constructor.stderr | 11 - .../fail/C-10-async-constructor.stderr | 5 - .../contract/fail/C-11-unsafe-constructor.rs | 19 - .../fail/C-11-unsafe-constructor.stderr | 5 - .../contract/fail/C-12-const-constructor.rs | 19 - .../fail/C-12-const-constructor.stderr | 5 - .../ui/contract/fail/C-13-abi-constructor.rs | 19 - .../contract/fail/C-13-abi-constructor.stderr | 5 - .../fail/C-15-payable-trait-constructor.rs | 27 - .../C-15-payable-trait-constructor.stderr | 11 - .../C-16-function-arg-struct-destructuring.rs | 24 - ...6-function-arg-struct-destructuring.stderr | 5 - .../fail/H-03-use-forbidden-idents.rs | 23 - .../fail/H-03-use-forbidden-idents.stderr | 11 - .../contract/fail/M-01-missing-message.stderr | 11 - .../fail/M-02-message-missing-self-arg.stderr | 6 - .../M-04-message-returns-non-codec.stderr | 12 - .../M-10-method-unknown-ink-marker.stderr | 5 - .../fail/N-02-namespace-invalid-type.stderr | 5 - .../fail/S-01-missing-storage-struct.stderr | 11 - .../fail/S-02-multiple-storage-structs.rs | 33 - .../fail/S-02-multiple-storage-structs.stderr | 23 - .../S-03-struct-unknown-ink-marker.stderr | 5 - .../fail/S-04-non-storage-ink-impls.rs | 39 - .../fail/S-04-non-storage-ink-impls.stderr | 39 - .../ui/contract/fail/S-05-storage-as-event.rs | 20 - .../fail/S-05-storage-as-event.stderr | 5 - .../ui/contract/fail/S-06-event-as-storage.rs | 20 - .../fail/S-06-event-as-storage.stderr | 5 - .../ui/contract/pass/01-noop-contract.rs | 19 - .../ui/contract/pass/02-flipper-contract.rs | 47 - .../contract/pass/03-incrementer-contract.rs | 45 - .../ui/contract/pass/06-non-ink-items.rs | 27 - .../pass/08-flipper-as-dependency-trait.rs | 40 - .../tests/ui/contract/pass/09-static-env.rs | 22 - .../pass/11-alias-storage-struct-impl.rs | 21 - crates/lang/src/codegen/dispatch/execution.rs | 171 +++ crates/lang/src/codegen/dispatch/info.rs | 22 + crates/lang/src/codegen/dispatch/mod.rs | 32 + .../lang/src/codegen/dispatch/type_check.rs | 69 + crates/lang/src/codegen/env.rs | 43 + .../src/{events.rs => codegen/event/emit.rs} | 23 +- crates/lang/src/codegen/event/mod.rs | 26 + crates/lang/src/codegen/event/topics.rs | 91 ++ crates/lang/src/codegen/implies_return.rs | 66 + crates/lang/src/codegen/is_same_type.rs | 45 + crates/lang/src/codegen/mod.rs | 54 + .../src/codegen/trait_def/call_builder.rs | 81 ++ crates/lang/src/codegen/trait_def/mod.rs | 28 + .../src/codegen/trait_def/trait_message.rs | 33 + .../lang/src/codegen/utils/identity_type.rs | 47 + crates/lang/src/codegen/utils/mod.rs | 23 + crates/lang/src/codegen/utils/same_type.rs | 45 + crates/lang/src/contract.rs | 36 - crates/lang/src/contract_ref.rs | 26 + crates/lang/src/cross_calling.rs | 56 - crates/lang/src/dispatcher.rs | 221 --- crates/lang/src/env_access.rs | 47 +- crates/lang/src/error.rs | 43 - crates/lang/src/lib.rs | 66 +- crates/lang/src/reflect/contract.rs | 190 +++ crates/lang/src/reflect/dispatch.rs | 651 +++++++++ crates/lang/src/reflect/event.rs | 52 + crates/lang/src/reflect/mod.rs | 55 + crates/lang/src/reflect/trait_def/info.rs | 154 +++ crates/lang/src/reflect/trait_def/mod.rs | 24 + crates/lang/src/reflect/trait_def/registry.rs | 63 + crates/lang/src/result_info.rs | 119 ++ crates/lang/src/traits.rs | 122 -- crates/lang/tests/compile_tests.rs | 35 + .../blake2b/fail/invalid_parameter_type_01.rs | 0 .../fail/invalid_parameter_type_01.stderr | 2 +- .../blake2b/fail/invalid_parameter_type_02.rs | 0 .../fail/invalid_parameter_type_02.stderr | 2 +- .../ui/blake2b/fail/missing_parameter.rs | 0 .../ui/blake2b/fail/missing_parameter.stderr | 2 +- .../ui/blake2b/fail/non_literal_parameter.rs | 0 .../blake2b/fail/non_literal_parameter.stderr | 2 +- .../tests/ui/blake2b/pass/bytestring_input.rs | 0 .../ui/blake2b/pass/no_implicit_prelude.rs | 0 .../tests/ui/blake2b/pass/string_input.rs | 0 .../tests/ui/chain_extension/E-01-simple.rs | 0 ...-compile-as-dependency-invalid-type-01.rs} | 6 +- ...pile-as-dependency-invalid-type-01.stderr} | 2 +- ...g-compile-as-dependency-invalid-type-02.rs | 19 + ...mpile-as-dependency-invalid-type-02.stderr | 5 + ...onfig-compile-as-dependency-missing-arg.rs | 19 + ...g-compile-as-dependency-missing-arg.stderr | 5 + ...amic-storage-allocator-invalid-type-01.rs} | 6 +- ...-storage-allocator-invalid-type-01.stderr} | 2 +- ...namic-storage-allocator-invalid-type-02.rs | 19 + ...c-storage-allocator-invalid-type-02.stderr | 5 + ...g-dynamic-storage-allocator-missing-arg.rs | 19 + ...namic-storage-allocator-missing-arg.stderr | 5 + .../tests/ui/contract/fail/constructor-abi.rs | 19 + .../ui/contract/fail/constructor-abi.stderr | 5 + .../ui/contract/fail/constructor-async.rs | 19 + .../ui/contract/fail/constructor-async.stderr | 5 + .../ui/contract/fail/constructor-const.rs | 19 + .../ui/contract/fail/constructor-const.stderr | 5 + .../fail/constructor-input-non-codec.rs | 22 + .../fail/constructor-input-non-codec.stderr | 41 + .../fail/constructor-input-pattern.rs | 19 + .../fail/constructor-input-pattern.stderr | 5 + .../fail/constructor-missing-return.rs} | 10 +- .../fail/constructor-missing-return.stderr | 5 + .../ui/contract/fail/constructor-payable.rs} | 10 +- .../contract/fail/constructor-payable.stderr} | 4 +- .../fail/constructor-self-receiver-01.rs} | 10 +- .../fail/constructor-self-receiver-01.stderr | 5 + .../fail/constructor-self-receiver-02.rs | 19 + .../fail/constructor-self-receiver-02.stderr | 5 + .../fail/constructor-self-receiver-03.rs | 19 + .../fail/constructor-self-receiver-03.stderr | 18 + .../fail/constructor-self-receiver-04.rs} | 10 +- .../fail/constructor-self-receiver-04.stderr | 5 + .../ui/contract/fail/constructor-unsafe.rs | 19 + .../contract/fail/constructor-unsafe.stderr | 5 + .../fail/event-conflicting-storage.rs | 23 + .../fail/event-conflicting-storage.stderr | 5 + .../fail/event-too-many-topics-anonymous.rs | 62 + .../event-too-many-topics-anonymous.stderr | 23 + .../ui/contract/fail/event-too-many-topics.rs | 58 + .../fail/event-too-many-topics.stderr | 23 + .../fail/impl-block-for-non-storage-01.rs | 31 + .../fail/impl-block-for-non-storage-01.stderr | 32 + .../fail/impl-block-for-non-storage-02.rs | 24 + .../fail/impl-block-for-non-storage-02.stderr | 8 + ...mpl-block-namespace-invalid-identifier.rs} | 6 +- ...block-namespace-invalid-identifier.stderr} | 2 +- .../impl-block-namespace-invalid-type.rs} | 6 +- .../impl-block-namespace-invalid-type.stderr | 5 + .../impl-block-namespace-missing-argument.rs} | 6 +- ...l-block-namespace-missing-argument.stderr} | 2 +- .../fail/impl-block-using-env-no-marker.rs | 29 + .../impl-block-using-env-no-marker.stderr | 11 + .../impl-block-using-static-env-no-marker.rs | 28 + ...pl-block-using-static-env-no-marker.stderr | 14 + .../contract/fail/message-input-non-codec.rs | 22 + .../fail/message-input-non-codec.stderr | 51 + .../contract/fail/message-input-pattern.rs} | 8 +- .../fail/message-input-pattern.stderr | 5 + .../fail/message-returns-non-codec.rs} | 12 +- .../fail/message-returns-non-codec.stderr | 52 + .../ui/contract/fail/message-returns-self.rs} | 6 +- .../fail/message-returns-self.stderr} | 2 +- .../fail/message-selector-invalid-type-01.rs} | 8 +- .../message-selector-invalid-type-01.stderr} | 2 +- .../fail/message-selector-invalid-type-02.rs} | 8 +- .../message-selector-invalid-type-02.stderr} | 2 +- .../fail/message-selector-missing-arg.rs | 19 + .../fail/message-selector-missing-arg.stderr | 5 + .../fail/message-self-receiver-invalid-01.rs | 19 + .../message-self-receiver-invalid-01.stderr | 5 + .../fail/message-self-receiver-invalid-02.rs | 19 + .../message-self-receiver-invalid-02.stderr | 5 + .../fail/message-self-receiver-invalid-03.rs | 19 + .../message-self-receiver-invalid-03.stderr | 5 + .../fail/message-self-receiver-missing.rs | 19 + .../fail/message-self-receiver-missing.stderr | 6 + .../fail/message-unknown-property.rs} | 10 +- .../fail/message-unknown-property.stderr | 5 + .../fail/module-missing-constructor.rs} | 8 +- .../fail/module-missing-constructor.stderr | 11 + .../contract/fail/module-missing-message.rs} | 6 +- .../fail/module-missing-message.stderr | 11 + .../contract/fail/module-missing-storage.rs} | 8 +- .../fail/module-missing-storage.stderr | 11 + .../contract/fail/module-multiple-storages.rs | 32 + .../fail/module-multiple-storages.stderr | 23 + .../fail/module-use-forbidden-idents.rs | 25 + .../fail/module-use-forbidden-idents.stderr | 83 ++ .../fail/storage-conflicting-event.rs | 20 + .../fail/storage-conflicting-event.stderr | 5 + .../contract/fail/storage-unknown-marker.rs} | 8 +- .../fail/storage-unknown-marker.stderr | 5 + .../fail/trait-impl-namespace-invalid.rs | 30 + .../fail/trait-impl-namespace-invalid.stderr | 9 + .../fail/trait-message-payable-mismatch.rs | 29 + .../trait-message-payable-mismatch.stderr | 8 + .../fail/trait-message-selector-mismatch.rs | 29 + .../trait-message-selector-mismatch.stderr | 8 + .../fail/trait-message-selector-overlap-1.rs | 51 + .../trait-message-selector-overlap-1.stderr | 8 + .../fail/trait-message-selector-overlap-2.rs | 51 + .../trait-message-selector-overlap-2.stderr | 8 + .../fail/trait-message-selector-overlap-3.rs | 51 + .../trait-message-selector-overlap-3.stderr | 8 + .../config-compile-as-dependency-false.rs | 19 + .../pass/config-compile-as-dependency-true.rs | 19 + .../ui/contract/pass/config-custom-env.rs | 34 + .../config-dynamic-storage-allocator-false.rs | 19 + .../config-dynamic-storage-allocator-true.rs | 19 + .../contract/pass/constructor-many-inputs.rs | 125 ++ .../ui/contract/pass/constructor-selector.rs | 68 + .../contract/pass/dispatch-decoder-works.rs | 132 ++ .../lang/tests/ui/contract/pass/env-access.rs | 40 + .../tests/ui/contract/pass/event-anonymous.rs | 115 ++ .../contract/pass/event-config-more-topics.rs | 86 ++ .../contract/pass/event-many-definitions.rs | 100 ++ .../contract/pass/event-single-definition.rs | 22 + .../tests/ui/contract/pass/event-topics.rs | 115 ++ .../ui/contract/pass/example-erc20-works.rs} | 7 +- .../ui/contract/pass/example-erc721-works.rs} | 0 .../contract/pass/example-flipper-works.rs} | 15 +- .../pass/example-incrementer-works.rs | 36 + .../pass/example-trait-flipper-works.rs | 50 + .../pass/example-trait-incrementer-works.rs | 75 ++ .../ui/contract/pass/impl-alias-storage.rs | 21 + .../ui/contract/pass/impl-block-namespace.rs | 22 + .../ui/contract/pass/impl-block-using-env.rs | 35 + .../ui/contract/pass/impl-with-property.rs | 23 + .../ui/contract/pass/message-many-inputs.rs | 222 +++ .../ui/contract/pass/message-many-outputs.rs | 72 + .../tests/ui/contract/pass/message-payable.rs | 27 + .../ui/contract/pass/message-selector.rs | 63 + .../ui/contract/pass/minimal-contract.rs | 19 + .../ui/contract/pass/module-env-types.rs | 25 + .../ui/contract/pass/module-non-ink-items.rs | 48 + .../ui/contract/pass/no-implicit-prelude.rs | 19 + .../ui/contract/pass/storage-many-fields.rs | 30 + .../ui/contract/pass/storage-packed-fields.rs | 51 + .../ui/contract/pass/storage-single-field.rs} | 11 +- .../ui/contract/pass/storage-with-derives.rs | 23 + .../pass/trait-message-payable-guard.rs | 29 + .../pass/trait-message-selector-guard.rs | 29 + .../fail/invalid_parameter_type_01.rs | 0 .../fail/invalid_parameter_type_01.stderr | 2 +- .../fail/invalid_parameter_type_02.rs | 0 .../fail/invalid_parameter_type_02.stderr | 2 +- .../selector_bytes/fail/missing_parameter.rs | 0 .../fail/missing_parameter.stderr | 2 +- .../fail/non_literal_parameter.rs | 0 .../fail/non_literal_parameter.stderr | 2 +- .../selector_bytes/pass/bytestring_input.rs | 2 +- .../pass/no_implicit_prelude.rs | 0 .../ui/selector_bytes/pass/string_input.rs | 2 +- .../fail/invalid_parameter_type_01.rs | 0 .../fail/invalid_parameter_type_01.stderr | 2 +- .../fail/invalid_parameter_type_02.rs | 0 .../fail/invalid_parameter_type_02.stderr | 2 +- .../ui/selector_id/fail/missing_parameter.rs | 0 .../selector_id/fail/missing_parameter.stderr | 2 +- .../selector_id/fail/non_literal_parameter.rs | 0 .../fail/non_literal_parameter.stderr | 2 +- .../ui/selector_id/pass/bytestring_input.rs | 2 +- .../selector_id/pass/no_implicit_prelude.rs | 0 .../tests/ui/selector_id/pass/string_input.rs | 2 +- .../fail/config_namespace_invalid_1.rs | 9 + .../fail/config_namespace_invalid_1.stderr | 5 + .../fail/config_namespace_invalid_2.rs | 9 + .../fail/config_namespace_invalid_2.stderr | 5 + .../fail/config_namespace_invalid_3.rs | 9 + .../fail/config_namespace_invalid_3.stderr | 5 + .../fail/config_namespace_invalid_4.rs | 9 + .../fail/config_namespace_invalid_4.stderr | 5 + .../trait_def/fail/definition_assoc_const.rs | 11 + .../fail/definition_assoc_const.stderr | 5 + .../trait_def/fail/definition_assoc_type.rs | 11 + .../fail/definition_assoc_type.stderr | 5 + .../trait_def/fail/definition_constructor.rs | 9 + .../fail/definition_constructor.stderr | 6 + .../ui/trait_def/fail/definition_empty.rs | 6 + .../ui/trait_def/fail/definition_empty.stderr | 5 + .../ui/trait_def/fail/definition_generic.rs | 9 + .../trait_def/fail/definition_generic.stderr | 5 + .../ui/trait_def/fail/definition_non_pub.rs | 9 + .../trait_def/fail/definition_non_pub.stderr | 7 + .../trait_def/fail/definition_rust_method.rs | 8 + .../fail/definition_rust_method.stderr | 5 + .../fail/definition_super_trait_invalid_1.rs | 11 + .../definition_super_trait_invalid_1.stderr | 5 + .../fail/definition_super_trait_invalid_2.rs | 15 + .../definition_super_trait_invalid_2.stderr | 5 + .../ui/trait_def/fail/definition_unsafe.rs | 9 + .../trait_def/fail/definition_unsafe.stderr | 5 + .../ui/trait_def/fail/message_abi_invalid.rs | 12 + .../trait_def/fail/message_abi_invalid.stderr | 5 + .../trait_def/fail/message_async_invalid.rs | 12 + .../fail/message_async_invalid.stderr | 5 + .../trait_def/fail/message_const_invalid.rs | 12 + .../fail/message_const_invalid.stderr | 5 + .../fail/message_constructor_conflict.rs | 9 + .../fail/message_constructor_conflict.stderr | 5 + .../ui/trait_def/fail/message_default_impl.rs | 9 + .../fail/message_default_impl.stderr | 5 + .../trait_def/fail/message_generic_invalid.rs | 12 + .../fail/message_generic_invalid.stderr | 5 + .../trait_def/fail/message_input_non_codec.rs | 11 + .../fail/message_input_non_codec.stderr | 40 + .../fail/message_input_pattern_invalid.rs | 9 + .../fail/message_input_pattern_invalid.stderr | 5 + .../fail/message_output_non_codec.rs | 11 + .../fail/message_output_non_codec.stderr | 36 + .../fail/message_payable_invalid_1.rs | 9 + .../fail/message_payable_invalid_1.stderr | 5 + .../fail/message_payable_invalid_2.rs | 9 + .../fail/message_payable_invalid_2.stderr | 5 + .../fail/message_receiver_invalid_1.rs | 9 + .../fail/message_receiver_invalid_1.stderr | 5 + .../fail/message_receiver_invalid_2.rs | 9 + .../fail/message_receiver_invalid_2.stderr | 5 + .../fail/message_receiver_invalid_3.rs | 9 + .../fail/message_receiver_invalid_3.stderr | 5 + .../fail/message_receiver_missing.rs | 9 + .../fail/message_receiver_missing.stderr | 5 + .../fail/message_selector_invalid_1.rs | 9 + .../fail/message_selector_invalid_1.stderr | 5 + .../fail/message_selector_invalid_2.rs | 9 + .../fail/message_selector_invalid_2.stderr | 5 + .../fail/message_selector_overlap.rs | 12 + .../fail/message_selector_overlap.stderr | 11 + .../trait_def/fail/message_unsafe_invalid.rs | 12 + .../fail/message_unsafe_invalid.stderr | 5 + .../pass/avoid_overlap_with_namespace.rs | 54 + .../tests/ui/trait_def/pass/many_inputs.rs | 31 + .../tests/ui/trait_def/pass/many_outputs.rs | 31 + .../ui/trait_def/pass/no-implicit-prelude.rs | 11 + .../ui/trait_def/pass/payable_message.rs | 53 + .../ui/trait_def/pass/simple_definition.rs | 11 + .../ui/trait_def/pass/using-env-types.rs | 14 + .../ui/trait_def/pass/valid_selectors.rs | 39 + .../pass/valid_selectors_namespace.rs | 42 + .../tests/ui/trait_def/pass/with_namespace.rs | 9 + .../lang/{macro => }/tests/unique_topics.rs | 0 crates/metadata/Cargo.toml | 4 +- crates/metadata/src/layout/tests.rs | 1 - crates/metadata/src/specs.rs | 15 +- crates/storage/derive/Cargo.toml | 7 +- crates/storage/derive/src/storage_layout.rs | 6 +- .../derive/src/tests/storage_layout.rs | 22 +- crates/storage/src/alloc/allocator.rs | 2 +- crates/storage/src/alloc/boxed/storage.rs | 2 +- .../src/collections/binary_heap/storage.rs | 2 +- .../src/collections/bitstash/storage.rs | 2 +- .../storage/src/collections/bitvec/storage.rs | 2 +- .../src/collections/hashmap/storage.rs | 2 +- .../src/collections/smallvec/storage.rs | 2 +- .../storage/src/collections/stash/storage.rs | 2 +- crates/storage/src/collections/vec/storage.rs | 2 +- crates/storage/src/traits/layout/impls.rs | 12 +- examples/delegator/accumulator/lib.rs | 6 +- examples/delegator/adder/lib.rs | 12 +- examples/delegator/lib.rs | 38 +- examples/delegator/subber/lib.rs | 12 +- examples/erc1155/lib.rs | 4 +- examples/erc20/lib.rs | 4 +- examples/multisig/lib.rs | 4 +- examples/trait-erc20/lib.rs | 24 +- examples/trait-flipper/lib.rs | 20 +- examples/trait-incrementer/lib.rs | 10 +- 406 files changed, 11734 insertions(+), 4716 deletions(-) create mode 100644 crates/lang/codegen/src/enforced_error.rs create mode 100644 crates/lang/codegen/src/generator/arg_list.rs create mode 100644 crates/lang/codegen/src/generator/as_dependency/call_builder.rs create mode 100644 crates/lang/codegen/src/generator/as_dependency/contract_ref.rs create mode 100644 crates/lang/codegen/src/generator/as_dependency/mod.rs delete mode 100644 crates/lang/codegen/src/generator/cross_calling.rs delete mode 100644 crates/lang/codegen/src/generator/trait_def.rs create mode 100644 crates/lang/codegen/src/generator/trait_def/call_builder.rs create mode 100644 crates/lang/codegen/src/generator/trait_def/call_forwarder.rs create mode 100644 crates/lang/codegen/src/generator/trait_def/definition.rs create mode 100644 crates/lang/codegen/src/generator/trait_def/mod.rs create mode 100644 crates/lang/codegen/src/generator/trait_def/trait_registry.rs delete mode 100644 crates/lang/ir/src/ir/trait_def.rs create mode 100644 crates/lang/ir/src/ir/trait_def/config.rs create mode 100644 crates/lang/ir/src/ir/trait_def/item/iter.rs create mode 100644 crates/lang/ir/src/ir/trait_def/item/mod.rs create mode 100644 crates/lang/ir/src/ir/trait_def/item/trait_item.rs create mode 100644 crates/lang/ir/src/ir/trait_def/mod.rs create mode 100644 crates/lang/ir/src/ir/trait_def/tests.rs delete mode 100644 crates/lang/macro/tests/compile_tests.rs delete mode 100644 crates/lang/macro/tests/ui/contract/fail/C-00-constructor-self-ref.rs delete mode 100644 crates/lang/macro/tests/ui/contract/fail/C-00-constructor-self-ref.stderr delete mode 100644 crates/lang/macro/tests/ui/contract/fail/C-01-constructor-self-mut.rs delete mode 100644 crates/lang/macro/tests/ui/contract/fail/C-01-constructor-self-mut.stderr delete mode 100644 crates/lang/macro/tests/ui/contract/fail/C-02-constructor-self-val.stderr delete mode 100644 crates/lang/macro/tests/ui/contract/fail/C-03-constructor-missing-return.stderr delete mode 100644 crates/lang/macro/tests/ui/contract/fail/C-04-missing-constructor.stderr delete mode 100644 crates/lang/macro/tests/ui/contract/fail/C-10-async-constructor.stderr delete mode 100644 crates/lang/macro/tests/ui/contract/fail/C-11-unsafe-constructor.rs delete mode 100644 crates/lang/macro/tests/ui/contract/fail/C-11-unsafe-constructor.stderr delete mode 100644 crates/lang/macro/tests/ui/contract/fail/C-12-const-constructor.rs delete mode 100644 crates/lang/macro/tests/ui/contract/fail/C-12-const-constructor.stderr delete mode 100644 crates/lang/macro/tests/ui/contract/fail/C-13-abi-constructor.rs delete mode 100644 crates/lang/macro/tests/ui/contract/fail/C-13-abi-constructor.stderr delete mode 100644 crates/lang/macro/tests/ui/contract/fail/C-15-payable-trait-constructor.rs delete mode 100644 crates/lang/macro/tests/ui/contract/fail/C-15-payable-trait-constructor.stderr delete mode 100644 crates/lang/macro/tests/ui/contract/fail/C-16-function-arg-struct-destructuring.rs delete mode 100644 crates/lang/macro/tests/ui/contract/fail/C-16-function-arg-struct-destructuring.stderr delete mode 100644 crates/lang/macro/tests/ui/contract/fail/H-03-use-forbidden-idents.rs delete mode 100644 crates/lang/macro/tests/ui/contract/fail/H-03-use-forbidden-idents.stderr delete mode 100644 crates/lang/macro/tests/ui/contract/fail/M-01-missing-message.stderr delete mode 100644 crates/lang/macro/tests/ui/contract/fail/M-02-message-missing-self-arg.stderr delete mode 100644 crates/lang/macro/tests/ui/contract/fail/M-04-message-returns-non-codec.stderr delete mode 100644 crates/lang/macro/tests/ui/contract/fail/M-10-method-unknown-ink-marker.stderr delete mode 100644 crates/lang/macro/tests/ui/contract/fail/N-02-namespace-invalid-type.stderr delete mode 100644 crates/lang/macro/tests/ui/contract/fail/S-01-missing-storage-struct.stderr delete mode 100644 crates/lang/macro/tests/ui/contract/fail/S-02-multiple-storage-structs.rs delete mode 100644 crates/lang/macro/tests/ui/contract/fail/S-02-multiple-storage-structs.stderr delete mode 100644 crates/lang/macro/tests/ui/contract/fail/S-03-struct-unknown-ink-marker.stderr delete mode 100644 crates/lang/macro/tests/ui/contract/fail/S-04-non-storage-ink-impls.rs delete mode 100644 crates/lang/macro/tests/ui/contract/fail/S-04-non-storage-ink-impls.stderr delete mode 100644 crates/lang/macro/tests/ui/contract/fail/S-05-storage-as-event.rs delete mode 100644 crates/lang/macro/tests/ui/contract/fail/S-05-storage-as-event.stderr delete mode 100644 crates/lang/macro/tests/ui/contract/fail/S-06-event-as-storage.rs delete mode 100644 crates/lang/macro/tests/ui/contract/fail/S-06-event-as-storage.stderr delete mode 100644 crates/lang/macro/tests/ui/contract/pass/01-noop-contract.rs delete mode 100644 crates/lang/macro/tests/ui/contract/pass/02-flipper-contract.rs delete mode 100644 crates/lang/macro/tests/ui/contract/pass/03-incrementer-contract.rs delete mode 100644 crates/lang/macro/tests/ui/contract/pass/06-non-ink-items.rs delete mode 100644 crates/lang/macro/tests/ui/contract/pass/08-flipper-as-dependency-trait.rs delete mode 100644 crates/lang/macro/tests/ui/contract/pass/09-static-env.rs delete mode 100644 crates/lang/macro/tests/ui/contract/pass/11-alias-storage-struct-impl.rs create mode 100644 crates/lang/src/codegen/dispatch/execution.rs create mode 100644 crates/lang/src/codegen/dispatch/info.rs create mode 100644 crates/lang/src/codegen/dispatch/mod.rs create mode 100644 crates/lang/src/codegen/dispatch/type_check.rs create mode 100644 crates/lang/src/codegen/env.rs rename crates/lang/src/{events.rs => codegen/event/emit.rs} (57%) create mode 100644 crates/lang/src/codegen/event/mod.rs create mode 100644 crates/lang/src/codegen/event/topics.rs create mode 100644 crates/lang/src/codegen/implies_return.rs create mode 100644 crates/lang/src/codegen/is_same_type.rs create mode 100644 crates/lang/src/codegen/mod.rs create mode 100644 crates/lang/src/codegen/trait_def/call_builder.rs create mode 100644 crates/lang/src/codegen/trait_def/mod.rs create mode 100644 crates/lang/src/codegen/trait_def/trait_message.rs create mode 100644 crates/lang/src/codegen/utils/identity_type.rs create mode 100644 crates/lang/src/codegen/utils/mod.rs create mode 100644 crates/lang/src/codegen/utils/same_type.rs delete mode 100644 crates/lang/src/contract.rs create mode 100644 crates/lang/src/contract_ref.rs delete mode 100644 crates/lang/src/cross_calling.rs delete mode 100644 crates/lang/src/dispatcher.rs delete mode 100644 crates/lang/src/error.rs create mode 100644 crates/lang/src/reflect/contract.rs create mode 100644 crates/lang/src/reflect/dispatch.rs create mode 100644 crates/lang/src/reflect/event.rs create mode 100644 crates/lang/src/reflect/mod.rs create mode 100644 crates/lang/src/reflect/trait_def/info.rs create mode 100644 crates/lang/src/reflect/trait_def/mod.rs create mode 100644 crates/lang/src/reflect/trait_def/registry.rs create mode 100644 crates/lang/src/result_info.rs delete mode 100644 crates/lang/src/traits.rs create mode 100644 crates/lang/tests/compile_tests.rs rename crates/lang/{macro => }/tests/ui/blake2b/fail/invalid_parameter_type_01.rs (100%) rename crates/lang/{macro => }/tests/ui/blake2b/fail/invalid_parameter_type_01.stderr (75%) rename crates/lang/{macro => }/tests/ui/blake2b/fail/invalid_parameter_type_02.rs (100%) rename crates/lang/{macro => }/tests/ui/blake2b/fail/invalid_parameter_type_02.stderr (74%) rename crates/lang/{macro => }/tests/ui/blake2b/fail/missing_parameter.rs (100%) rename crates/lang/{macro => }/tests/ui/blake2b/fail/missing_parameter.stderr (85%) rename crates/lang/{macro => }/tests/ui/blake2b/fail/non_literal_parameter.rs (100%) rename crates/lang/{macro => }/tests/ui/blake2b/fail/non_literal_parameter.stderr (75%) rename crates/lang/{macro => }/tests/ui/blake2b/pass/bytestring_input.rs (100%) rename crates/lang/{macro => }/tests/ui/blake2b/pass/no_implicit_prelude.rs (100%) rename crates/lang/{macro => }/tests/ui/blake2b/pass/string_input.rs (100%) rename crates/lang/{macro => }/tests/ui/chain_extension/E-01-simple.rs (100%) rename crates/lang/{macro/tests/ui/contract/fail/H-02-invalid-as-dependency.rs => tests/ui/contract/fail/config-compile-as-dependency-invalid-type-01.rs} (73%) rename crates/lang/{macro/tests/ui/contract/fail/H-02-invalid-as-dependency.stderr => tests/ui/contract/fail/config-compile-as-dependency-invalid-type-01.stderr} (70%) create mode 100644 crates/lang/tests/ui/contract/fail/config-compile-as-dependency-invalid-type-02.rs create mode 100644 crates/lang/tests/ui/contract/fail/config-compile-as-dependency-invalid-type-02.stderr create mode 100644 crates/lang/tests/ui/contract/fail/config-compile-as-dependency-missing-arg.rs create mode 100644 crates/lang/tests/ui/contract/fail/config-compile-as-dependency-missing-arg.stderr rename crates/lang/{macro/tests/ui/contract/fail/H-01-invalid-dyn-alloc.rs => tests/ui/contract/fail/config-dynamic-storage-allocator-invalid-type-01.rs} (77%) rename crates/lang/{macro/tests/ui/contract/fail/H-01-invalid-dyn-alloc.stderr => tests/ui/contract/fail/config-dynamic-storage-allocator-invalid-type-01.stderr} (70%) create mode 100644 crates/lang/tests/ui/contract/fail/config-dynamic-storage-allocator-invalid-type-02.rs create mode 100644 crates/lang/tests/ui/contract/fail/config-dynamic-storage-allocator-invalid-type-02.stderr create mode 100644 crates/lang/tests/ui/contract/fail/config-dynamic-storage-allocator-missing-arg.rs create mode 100644 crates/lang/tests/ui/contract/fail/config-dynamic-storage-allocator-missing-arg.stderr create mode 100644 crates/lang/tests/ui/contract/fail/constructor-abi.rs create mode 100644 crates/lang/tests/ui/contract/fail/constructor-abi.stderr create mode 100644 crates/lang/tests/ui/contract/fail/constructor-async.rs create mode 100644 crates/lang/tests/ui/contract/fail/constructor-async.stderr create mode 100644 crates/lang/tests/ui/contract/fail/constructor-const.rs create mode 100644 crates/lang/tests/ui/contract/fail/constructor-const.stderr create mode 100644 crates/lang/tests/ui/contract/fail/constructor-input-non-codec.rs create mode 100644 crates/lang/tests/ui/contract/fail/constructor-input-non-codec.stderr create mode 100644 crates/lang/tests/ui/contract/fail/constructor-input-pattern.rs create mode 100644 crates/lang/tests/ui/contract/fail/constructor-input-pattern.stderr rename crates/lang/{macro/tests/ui/contract/fail/C-03-constructor-missing-return.rs => tests/ui/contract/fail/constructor-missing-return.rs} (51%) create mode 100644 crates/lang/tests/ui/contract/fail/constructor-missing-return.stderr rename crates/lang/{macro/tests/ui/contract/fail/C-14-payable-constructor.rs => tests/ui/contract/fail/constructor-payable.rs} (56%) rename crates/lang/{macro/tests/ui/contract/fail/C-14-payable-constructor.stderr => tests/ui/contract/fail/constructor-payable.stderr} (70%) rename crates/lang/{macro/tests/ui/contract/fail/C-10-async-constructor.rs => tests/ui/contract/fail/constructor-self-receiver-01.rs} (54%) create mode 100644 crates/lang/tests/ui/contract/fail/constructor-self-receiver-01.stderr create mode 100644 crates/lang/tests/ui/contract/fail/constructor-self-receiver-02.rs create mode 100644 crates/lang/tests/ui/contract/fail/constructor-self-receiver-02.stderr create mode 100644 crates/lang/tests/ui/contract/fail/constructor-self-receiver-03.rs create mode 100644 crates/lang/tests/ui/contract/fail/constructor-self-receiver-03.stderr rename crates/lang/{macro/tests/ui/contract/fail/C-02-constructor-self-val.rs => tests/ui/contract/fail/constructor-self-receiver-04.rs} (54%) create mode 100644 crates/lang/tests/ui/contract/fail/constructor-self-receiver-04.stderr create mode 100644 crates/lang/tests/ui/contract/fail/constructor-unsafe.rs create mode 100644 crates/lang/tests/ui/contract/fail/constructor-unsafe.stderr create mode 100644 crates/lang/tests/ui/contract/fail/event-conflicting-storage.rs create mode 100644 crates/lang/tests/ui/contract/fail/event-conflicting-storage.stderr create mode 100644 crates/lang/tests/ui/contract/fail/event-too-many-topics-anonymous.rs create mode 100644 crates/lang/tests/ui/contract/fail/event-too-many-topics-anonymous.stderr create mode 100644 crates/lang/tests/ui/contract/fail/event-too-many-topics.rs create mode 100644 crates/lang/tests/ui/contract/fail/event-too-many-topics.stderr create mode 100644 crates/lang/tests/ui/contract/fail/impl-block-for-non-storage-01.rs create mode 100644 crates/lang/tests/ui/contract/fail/impl-block-for-non-storage-01.stderr create mode 100644 crates/lang/tests/ui/contract/fail/impl-block-for-non-storage-02.rs create mode 100644 crates/lang/tests/ui/contract/fail/impl-block-for-non-storage-02.stderr rename crates/lang/{macro/tests/ui/contract/fail/N-01-namespace-invalid-identifier.rs => tests/ui/contract/fail/impl-block-namespace-invalid-identifier.rs} (77%) rename crates/lang/{macro/tests/ui/contract/fail/N-01-namespace-invalid-identifier.stderr => tests/ui/contract/fail/impl-block-namespace-invalid-identifier.stderr} (69%) rename crates/lang/{macro/tests/ui/contract/fail/N-02-namespace-invalid-type.rs => tests/ui/contract/fail/impl-block-namespace-invalid-type.rs} (76%) create mode 100644 crates/lang/tests/ui/contract/fail/impl-block-namespace-invalid-type.stderr rename crates/lang/{macro/tests/ui/contract/fail/N-03-namespace-missing-argument.rs => tests/ui/contract/fail/impl-block-namespace-missing-argument.rs} (75%) rename crates/lang/{macro/tests/ui/contract/fail/N-03-namespace-missing-argument.stderr => tests/ui/contract/fail/impl-block-namespace-missing-argument.stderr} (70%) create mode 100644 crates/lang/tests/ui/contract/fail/impl-block-using-env-no-marker.rs create mode 100644 crates/lang/tests/ui/contract/fail/impl-block-using-env-no-marker.stderr create mode 100644 crates/lang/tests/ui/contract/fail/impl-block-using-static-env-no-marker.rs create mode 100644 crates/lang/tests/ui/contract/fail/impl-block-using-static-env-no-marker.stderr create mode 100644 crates/lang/tests/ui/contract/fail/message-input-non-codec.rs create mode 100644 crates/lang/tests/ui/contract/fail/message-input-non-codec.stderr rename crates/lang/{macro/tests/ui/contract/fail/M-02-message-missing-self-arg.rs => tests/ui/contract/fail/message-input-pattern.rs} (60%) create mode 100644 crates/lang/tests/ui/contract/fail/message-input-pattern.stderr rename crates/lang/{macro/tests/ui/contract/fail/M-04-message-returns-non-codec.rs => tests/ui/contract/fail/message-returns-non-codec.rs} (53%) create mode 100644 crates/lang/tests/ui/contract/fail/message-returns-non-codec.stderr rename crates/lang/{macro/tests/ui/contract/fail/M-03-message-returns-self.rs => tests/ui/contract/fail/message-returns-self.rs} (72%) rename crates/lang/{macro/tests/ui/contract/fail/M-03-message-returns-self.stderr => tests/ui/contract/fail/message-returns-self.stderr} (71%) rename crates/lang/{macro/tests/ui/contract/fail/M-05-message-invalid-selector.rs => tests/ui/contract/fail/message-selector-invalid-type-01.rs} (60%) rename crates/lang/{macro/tests/ui/contract/fail/M-05-message-invalid-selector.stderr => tests/ui/contract/fail/message-selector-invalid-type-01.stderr} (79%) rename crates/lang/{macro/tests/ui/contract/fail/M-06-message-invalid-selector-type.rs => tests/ui/contract/fail/message-selector-invalid-type-02.rs} (59%) rename crates/lang/{macro/tests/ui/contract/fail/M-06-message-invalid-selector-type.stderr => tests/ui/contract/fail/message-selector-invalid-type-02.stderr} (72%) create mode 100644 crates/lang/tests/ui/contract/fail/message-selector-missing-arg.rs create mode 100644 crates/lang/tests/ui/contract/fail/message-selector-missing-arg.stderr create mode 100644 crates/lang/tests/ui/contract/fail/message-self-receiver-invalid-01.rs create mode 100644 crates/lang/tests/ui/contract/fail/message-self-receiver-invalid-01.stderr create mode 100644 crates/lang/tests/ui/contract/fail/message-self-receiver-invalid-02.rs create mode 100644 crates/lang/tests/ui/contract/fail/message-self-receiver-invalid-02.stderr create mode 100644 crates/lang/tests/ui/contract/fail/message-self-receiver-invalid-03.rs create mode 100644 crates/lang/tests/ui/contract/fail/message-self-receiver-invalid-03.stderr create mode 100644 crates/lang/tests/ui/contract/fail/message-self-receiver-missing.rs create mode 100644 crates/lang/tests/ui/contract/fail/message-self-receiver-missing.stderr rename crates/lang/{macro/tests/ui/contract/fail/M-10-method-unknown-ink-marker.rs => tests/ui/contract/fail/message-unknown-property.rs} (65%) create mode 100644 crates/lang/tests/ui/contract/fail/message-unknown-property.stderr rename crates/lang/{macro/tests/ui/contract/fail/C-04-missing-constructor.rs => tests/ui/contract/fail/module-missing-constructor.rs} (52%) create mode 100644 crates/lang/tests/ui/contract/fail/module-missing-constructor.stderr rename crates/lang/{macro/tests/ui/contract/fail/M-01-missing-message.rs => tests/ui/contract/fail/module-missing-message.rs} (68%) create mode 100644 crates/lang/tests/ui/contract/fail/module-missing-message.stderr rename crates/lang/{macro/tests/ui/contract/fail/S-01-missing-storage-struct.rs => tests/ui/contract/fail/module-missing-storage.rs} (54%) create mode 100644 crates/lang/tests/ui/contract/fail/module-missing-storage.stderr create mode 100644 crates/lang/tests/ui/contract/fail/module-multiple-storages.rs create mode 100644 crates/lang/tests/ui/contract/fail/module-multiple-storages.stderr create mode 100644 crates/lang/tests/ui/contract/fail/module-use-forbidden-idents.rs create mode 100644 crates/lang/tests/ui/contract/fail/module-use-forbidden-idents.stderr create mode 100644 crates/lang/tests/ui/contract/fail/storage-conflicting-event.rs create mode 100644 crates/lang/tests/ui/contract/fail/storage-conflicting-event.stderr rename crates/lang/{macro/tests/ui/contract/fail/S-03-struct-unknown-ink-marker.rs => tests/ui/contract/fail/storage-unknown-marker.rs} (64%) create mode 100644 crates/lang/tests/ui/contract/fail/storage-unknown-marker.stderr create mode 100644 crates/lang/tests/ui/contract/fail/trait-impl-namespace-invalid.rs create mode 100644 crates/lang/tests/ui/contract/fail/trait-impl-namespace-invalid.stderr create mode 100644 crates/lang/tests/ui/contract/fail/trait-message-payable-mismatch.rs create mode 100644 crates/lang/tests/ui/contract/fail/trait-message-payable-mismatch.stderr create mode 100644 crates/lang/tests/ui/contract/fail/trait-message-selector-mismatch.rs create mode 100644 crates/lang/tests/ui/contract/fail/trait-message-selector-mismatch.stderr create mode 100644 crates/lang/tests/ui/contract/fail/trait-message-selector-overlap-1.rs create mode 100644 crates/lang/tests/ui/contract/fail/trait-message-selector-overlap-1.stderr create mode 100644 crates/lang/tests/ui/contract/fail/trait-message-selector-overlap-2.rs create mode 100644 crates/lang/tests/ui/contract/fail/trait-message-selector-overlap-2.stderr create mode 100644 crates/lang/tests/ui/contract/fail/trait-message-selector-overlap-3.rs create mode 100644 crates/lang/tests/ui/contract/fail/trait-message-selector-overlap-3.stderr create mode 100644 crates/lang/tests/ui/contract/pass/config-compile-as-dependency-false.rs create mode 100644 crates/lang/tests/ui/contract/pass/config-compile-as-dependency-true.rs create mode 100644 crates/lang/tests/ui/contract/pass/config-custom-env.rs create mode 100644 crates/lang/tests/ui/contract/pass/config-dynamic-storage-allocator-false.rs create mode 100644 crates/lang/tests/ui/contract/pass/config-dynamic-storage-allocator-true.rs create mode 100644 crates/lang/tests/ui/contract/pass/constructor-many-inputs.rs create mode 100644 crates/lang/tests/ui/contract/pass/constructor-selector.rs create mode 100644 crates/lang/tests/ui/contract/pass/dispatch-decoder-works.rs create mode 100644 crates/lang/tests/ui/contract/pass/env-access.rs create mode 100644 crates/lang/tests/ui/contract/pass/event-anonymous.rs create mode 100644 crates/lang/tests/ui/contract/pass/event-config-more-topics.rs create mode 100644 crates/lang/tests/ui/contract/pass/event-many-definitions.rs create mode 100644 crates/lang/tests/ui/contract/pass/event-single-definition.rs create mode 100644 crates/lang/tests/ui/contract/pass/event-topics.rs rename crates/lang/{macro/tests/ui/contract/pass/04-erc20-contract.rs => tests/ui/contract/pass/example-erc20-works.rs} (94%) rename crates/lang/{macro/tests/ui/contract/pass/05-erc721-contract.rs => tests/ui/contract/pass/example-erc721-works.rs} (100%) rename crates/lang/{macro/tests/ui/contract/pass/07-flipper-as-dependency.rs => tests/ui/contract/pass/example-flipper-works.rs} (72%) create mode 100644 crates/lang/tests/ui/contract/pass/example-incrementer-works.rs create mode 100644 crates/lang/tests/ui/contract/pass/example-trait-flipper-works.rs create mode 100644 crates/lang/tests/ui/contract/pass/example-trait-incrementer-works.rs create mode 100644 crates/lang/tests/ui/contract/pass/impl-alias-storage.rs create mode 100644 crates/lang/tests/ui/contract/pass/impl-block-namespace.rs create mode 100644 crates/lang/tests/ui/contract/pass/impl-block-using-env.rs create mode 100644 crates/lang/tests/ui/contract/pass/impl-with-property.rs create mode 100644 crates/lang/tests/ui/contract/pass/message-many-inputs.rs create mode 100644 crates/lang/tests/ui/contract/pass/message-many-outputs.rs create mode 100644 crates/lang/tests/ui/contract/pass/message-payable.rs create mode 100644 crates/lang/tests/ui/contract/pass/message-selector.rs create mode 100644 crates/lang/tests/ui/contract/pass/minimal-contract.rs create mode 100644 crates/lang/tests/ui/contract/pass/module-env-types.rs create mode 100644 crates/lang/tests/ui/contract/pass/module-non-ink-items.rs create mode 100644 crates/lang/tests/ui/contract/pass/no-implicit-prelude.rs create mode 100644 crates/lang/tests/ui/contract/pass/storage-many-fields.rs create mode 100644 crates/lang/tests/ui/contract/pass/storage-packed-fields.rs rename crates/lang/{macro/tests/ui/contract/pass/10-derive-for-storage.rs => tests/ui/contract/pass/storage-single-field.rs} (60%) create mode 100644 crates/lang/tests/ui/contract/pass/storage-with-derives.rs create mode 100644 crates/lang/tests/ui/contract/pass/trait-message-payable-guard.rs create mode 100644 crates/lang/tests/ui/contract/pass/trait-message-selector-guard.rs rename crates/lang/{macro => }/tests/ui/selector_bytes/fail/invalid_parameter_type_01.rs (100%) rename crates/lang/{macro => }/tests/ui/selector_bytes/fail/invalid_parameter_type_01.stderr (73%) rename crates/lang/{macro => }/tests/ui/selector_bytes/fail/invalid_parameter_type_02.rs (100%) rename crates/lang/{macro => }/tests/ui/selector_bytes/fail/invalid_parameter_type_02.stderr (72%) rename crates/lang/{macro => }/tests/ui/selector_bytes/fail/missing_parameter.rs (100%) rename crates/lang/{macro => }/tests/ui/selector_bytes/fail/missing_parameter.stderr (84%) rename crates/lang/{macro => }/tests/ui/selector_bytes/fail/non_literal_parameter.rs (100%) rename crates/lang/{macro => }/tests/ui/selector_bytes/fail/non_literal_parameter.stderr (72%) rename crates/lang/{macro => }/tests/ui/selector_bytes/pass/bytestring_input.rs (89%) rename crates/lang/{macro => }/tests/ui/selector_bytes/pass/no_implicit_prelude.rs (100%) rename crates/lang/{macro => }/tests/ui/selector_bytes/pass/string_input.rs (87%) rename crates/lang/{macro => }/tests/ui/selector_id/fail/invalid_parameter_type_01.rs (100%) rename crates/lang/{macro => }/tests/ui/selector_id/fail/invalid_parameter_type_01.stderr (73%) rename crates/lang/{macro => }/tests/ui/selector_id/fail/invalid_parameter_type_02.rs (100%) rename crates/lang/{macro => }/tests/ui/selector_id/fail/invalid_parameter_type_02.stderr (72%) rename crates/lang/{macro => }/tests/ui/selector_id/fail/missing_parameter.rs (100%) rename crates/lang/{macro => }/tests/ui/selector_id/fail/missing_parameter.stderr (84%) rename crates/lang/{macro => }/tests/ui/selector_id/fail/non_literal_parameter.rs (100%) rename crates/lang/{macro => }/tests/ui/selector_id/fail/non_literal_parameter.stderr (73%) rename crates/lang/{macro => }/tests/ui/selector_id/pass/bytestring_input.rs (88%) rename crates/lang/{macro => }/tests/ui/selector_id/pass/no_implicit_prelude.rs (100%) rename crates/lang/{macro => }/tests/ui/selector_id/pass/string_input.rs (86%) create mode 100644 crates/lang/tests/ui/trait_def/fail/config_namespace_invalid_1.rs create mode 100644 crates/lang/tests/ui/trait_def/fail/config_namespace_invalid_1.stderr create mode 100644 crates/lang/tests/ui/trait_def/fail/config_namespace_invalid_2.rs create mode 100644 crates/lang/tests/ui/trait_def/fail/config_namespace_invalid_2.stderr create mode 100644 crates/lang/tests/ui/trait_def/fail/config_namespace_invalid_3.rs create mode 100644 crates/lang/tests/ui/trait_def/fail/config_namespace_invalid_3.stderr create mode 100644 crates/lang/tests/ui/trait_def/fail/config_namespace_invalid_4.rs create mode 100644 crates/lang/tests/ui/trait_def/fail/config_namespace_invalid_4.stderr create mode 100644 crates/lang/tests/ui/trait_def/fail/definition_assoc_const.rs create mode 100644 crates/lang/tests/ui/trait_def/fail/definition_assoc_const.stderr create mode 100644 crates/lang/tests/ui/trait_def/fail/definition_assoc_type.rs create mode 100644 crates/lang/tests/ui/trait_def/fail/definition_assoc_type.stderr create mode 100644 crates/lang/tests/ui/trait_def/fail/definition_constructor.rs create mode 100644 crates/lang/tests/ui/trait_def/fail/definition_constructor.stderr create mode 100644 crates/lang/tests/ui/trait_def/fail/definition_empty.rs create mode 100644 crates/lang/tests/ui/trait_def/fail/definition_empty.stderr create mode 100644 crates/lang/tests/ui/trait_def/fail/definition_generic.rs create mode 100644 crates/lang/tests/ui/trait_def/fail/definition_generic.stderr create mode 100644 crates/lang/tests/ui/trait_def/fail/definition_non_pub.rs create mode 100644 crates/lang/tests/ui/trait_def/fail/definition_non_pub.stderr create mode 100644 crates/lang/tests/ui/trait_def/fail/definition_rust_method.rs create mode 100644 crates/lang/tests/ui/trait_def/fail/definition_rust_method.stderr create mode 100644 crates/lang/tests/ui/trait_def/fail/definition_super_trait_invalid_1.rs create mode 100644 crates/lang/tests/ui/trait_def/fail/definition_super_trait_invalid_1.stderr create mode 100644 crates/lang/tests/ui/trait_def/fail/definition_super_trait_invalid_2.rs create mode 100644 crates/lang/tests/ui/trait_def/fail/definition_super_trait_invalid_2.stderr create mode 100644 crates/lang/tests/ui/trait_def/fail/definition_unsafe.rs create mode 100644 crates/lang/tests/ui/trait_def/fail/definition_unsafe.stderr create mode 100644 crates/lang/tests/ui/trait_def/fail/message_abi_invalid.rs create mode 100644 crates/lang/tests/ui/trait_def/fail/message_abi_invalid.stderr create mode 100644 crates/lang/tests/ui/trait_def/fail/message_async_invalid.rs create mode 100644 crates/lang/tests/ui/trait_def/fail/message_async_invalid.stderr create mode 100644 crates/lang/tests/ui/trait_def/fail/message_const_invalid.rs create mode 100644 crates/lang/tests/ui/trait_def/fail/message_const_invalid.stderr create mode 100644 crates/lang/tests/ui/trait_def/fail/message_constructor_conflict.rs create mode 100644 crates/lang/tests/ui/trait_def/fail/message_constructor_conflict.stderr create mode 100644 crates/lang/tests/ui/trait_def/fail/message_default_impl.rs create mode 100644 crates/lang/tests/ui/trait_def/fail/message_default_impl.stderr create mode 100644 crates/lang/tests/ui/trait_def/fail/message_generic_invalid.rs create mode 100644 crates/lang/tests/ui/trait_def/fail/message_generic_invalid.stderr create mode 100644 crates/lang/tests/ui/trait_def/fail/message_input_non_codec.rs create mode 100644 crates/lang/tests/ui/trait_def/fail/message_input_non_codec.stderr create mode 100644 crates/lang/tests/ui/trait_def/fail/message_input_pattern_invalid.rs create mode 100644 crates/lang/tests/ui/trait_def/fail/message_input_pattern_invalid.stderr create mode 100644 crates/lang/tests/ui/trait_def/fail/message_output_non_codec.rs create mode 100644 crates/lang/tests/ui/trait_def/fail/message_output_non_codec.stderr create mode 100644 crates/lang/tests/ui/trait_def/fail/message_payable_invalid_1.rs create mode 100644 crates/lang/tests/ui/trait_def/fail/message_payable_invalid_1.stderr create mode 100644 crates/lang/tests/ui/trait_def/fail/message_payable_invalid_2.rs create mode 100644 crates/lang/tests/ui/trait_def/fail/message_payable_invalid_2.stderr create mode 100644 crates/lang/tests/ui/trait_def/fail/message_receiver_invalid_1.rs create mode 100644 crates/lang/tests/ui/trait_def/fail/message_receiver_invalid_1.stderr create mode 100644 crates/lang/tests/ui/trait_def/fail/message_receiver_invalid_2.rs create mode 100644 crates/lang/tests/ui/trait_def/fail/message_receiver_invalid_2.stderr create mode 100644 crates/lang/tests/ui/trait_def/fail/message_receiver_invalid_3.rs create mode 100644 crates/lang/tests/ui/trait_def/fail/message_receiver_invalid_3.stderr create mode 100644 crates/lang/tests/ui/trait_def/fail/message_receiver_missing.rs create mode 100644 crates/lang/tests/ui/trait_def/fail/message_receiver_missing.stderr create mode 100644 crates/lang/tests/ui/trait_def/fail/message_selector_invalid_1.rs create mode 100644 crates/lang/tests/ui/trait_def/fail/message_selector_invalid_1.stderr create mode 100644 crates/lang/tests/ui/trait_def/fail/message_selector_invalid_2.rs create mode 100644 crates/lang/tests/ui/trait_def/fail/message_selector_invalid_2.stderr create mode 100644 crates/lang/tests/ui/trait_def/fail/message_selector_overlap.rs create mode 100644 crates/lang/tests/ui/trait_def/fail/message_selector_overlap.stderr create mode 100644 crates/lang/tests/ui/trait_def/fail/message_unsafe_invalid.rs create mode 100644 crates/lang/tests/ui/trait_def/fail/message_unsafe_invalid.stderr create mode 100644 crates/lang/tests/ui/trait_def/pass/avoid_overlap_with_namespace.rs create mode 100644 crates/lang/tests/ui/trait_def/pass/many_inputs.rs create mode 100644 crates/lang/tests/ui/trait_def/pass/many_outputs.rs create mode 100644 crates/lang/tests/ui/trait_def/pass/no-implicit-prelude.rs create mode 100644 crates/lang/tests/ui/trait_def/pass/payable_message.rs create mode 100644 crates/lang/tests/ui/trait_def/pass/simple_definition.rs create mode 100644 crates/lang/tests/ui/trait_def/pass/using-env-types.rs create mode 100644 crates/lang/tests/ui/trait_def/pass/valid_selectors.rs create mode 100644 crates/lang/tests/ui/trait_def/pass/valid_selectors_namespace.rs create mode 100644 crates/lang/tests/ui/trait_def/pass/with_namespace.rs rename crates/lang/{macro => }/tests/unique_topics.rs (100%) diff --git a/.config/cargo_spellcheck.dic b/.config/cargo_spellcheck.dic index b7a8b4dc02c..0e090e288c4 100644 --- a/.config/cargo_spellcheck.dic +++ b/.config/cargo_spellcheck.dic @@ -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 diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index a37de721967..2991931cf4a 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -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 diff --git a/RELEASES.md b/RELEASES.md index 9be9236c05d..fc36993717e 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -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. diff --git a/crates/lang/Cargo.toml b/crates/lang/Cargo.toml index a1db80e8baf..c924cba86e5 100644 --- a/crates/lang/Cargo.toml +++ b/crates/lang/Cargo.toml @@ -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 = [] diff --git a/crates/lang/codegen/src/enforced_error.rs b/crates/lang/codegen/src/enforced_error.rs new file mode 100644 index 00000000000..ea92e2b8a51 --- /dev/null +++ b/crates/lang/codegen/src/enforced_error.rs @@ -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() + } +} diff --git a/crates/lang/codegen/src/generator/arg_list.rs b/crates/lang/codegen/src/generator/arg_list.rs new file mode 100644 index 00000000000..c239f4b2e62 --- /dev/null +++ b/crates/lang/codegen/src/generator/arg_list.rs @@ -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 { + inputs + .enumerate() + .map(|(n, _)| format_ident!("__ink_binding_{}", n)) + .collect::>() +} + +/// 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::>() +} + +/// 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, + ::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 + as #trait_path>::__ink_TraitInfo + ) +} diff --git a/crates/lang/codegen/src/generator/as_dependency/call_builder.rs b/crates/lang/codegen/src/generator/as_dependency/call_builder.rs new file mode 100644 index 00000000000..94cc3f9c9f9 --- /dev/null +++ b/crates/lang/codegen/src/generator/as_dependency/call_builder.rs @@ -0,0 +1,404 @@ +// 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 for #cb_ident { + #[inline] + fn from_account_id(account_id: AccountId) -> Self { + Self { account_id } + } + } + + impl ::ink_lang::ToAccountId for #cb_ident { + #[inline] + fn to_account_id(&self) -> AccountId { + ::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 = <::__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) -> &::Builder { + <_ as ::ink_lang::codegen::TraitCallBuilder>::call( + >::forward(self) + ) + } + + #[inline(always)] + fn build_mut(&mut self) + -> &mut ::Builder + { + <_ as ::ink_lang::codegen::TraitCallBuilder>::call_mut( + >::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 + 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, + ) -> 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( + >::#build_cmd(self) + #( , #input_bindings )* + ) + } + ) + } + + /// Generate call builder code for all ink! inherent ink! implementation blocks. + /// + /// # Note + /// + /// This does not provide implementations for ink! constructors as they + /// do not have a short-hand notations as their messages counterparts. + fn generate_call_builder_inherent_impls(&self) -> TokenStream2 { + self.contract + .module() + .impls() + .filter_map(|impl_block| { + impl_block + .trait_path() + .is_none() + .then(|| self.generate_call_builder_inherent_impl(impl_block)) + }) + .collect() + } + + /// Generate call builder code for a single inherent ink! implementation block. + /// + /// # Note + /// + /// Unlike as with ink! trait implementation blocks we do not have to generate + /// associate `*Output` types, ink! trait validating implementation blocks or + /// trait forwarder implementations. Instead we build the calls directly. + fn generate_call_builder_inherent_impl( + &self, + 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_call_builder_inherent_impl_for_message(message)); + quote_spanned!(span=> + impl #cb_ident { + #( #messages )* + } + ) + } + + /// Generate call builder code for a single inherent ink! message. + /// + /// # Note + /// + /// Unlike with ink! trait messages the call builder implements the call + /// building directly and does not forward to a trait call builder. + fn generate_call_builder_inherent_impl_for_message( + &self, + message: ir::CallableWithSelector, + ) -> TokenStream2 { + let span = message.span(); + let callable = message.callable(); + let message_ident = message.ident(); + let attrs = message.attrs(); + let selector = message.composed_selector(); + let selector_bytes = selector.hex_lits(); + let input_bindings = generator::input_bindings(callable.inputs()); + let input_types = generator::input_types(message.inputs()); + let arg_list = generator::generate_argument_list(input_types.iter().cloned()); + let mut_tok = callable.receiver().is_ref_mut().then(|| quote! { mut }); + let output = message.output(); + let output_sig = output.map_or_else( + || quote! { () }, + |output| quote! { ::ink_env::call::utils::ReturnType<#output> }, + ); + let output_span = output.span(); + let output_type = quote_spanned!(output_span=> + ::ink_env::call::CallBuilder< + Environment, + ::ink_env::call::utils::Set< ::AccountId >, + ::ink_env::call::utils::Unset< ::core::primitive::u64 >, + ::ink_env::call::utils::Unset< ::Balance >, + ::ink_env::call::utils::Set< ::ink_env::call::ExecutionInput<#arg_list> >, + ::ink_env::call::utils::Set<#output_sig>, + > + ); + quote_spanned!(span=> + #( #attrs )* + #[allow(clippy::type_complexity)] + #[inline] + pub fn #message_ident( + & #mut_tok self + #( , #input_bindings : #input_types )* + ) -> #output_type { + ::ink_env::call::build_call::() + .callee(::ink_lang::ToAccountId::to_account_id(self)) + .exec_input( + ::ink_env::call::ExecutionInput::new( + ::ink_env::call::Selector::new([ #( #selector_bytes ),* ]) + ) + #( + .push_arg(#input_bindings) + )* + ) + .returns::<#output_sig>() + } + ) + } +} diff --git a/crates/lang/codegen/src/generator/as_dependency/contract_ref.rs b/crates/lang/codegen/src/generator/as_dependency/contract_ref.rs new file mode 100644 index 00000000000..d9c724f716d --- /dev/null +++ b/crates/lang/codegen/src/generator/as_dependency/contract_ref.rs @@ -0,0 +1,411 @@ +// 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, + IsDocAttribute as _, +}; +use proc_macro2::TokenStream as TokenStream2; +use quote::{ + quote, + quote_spanned, +}; +use syn::spanned::Spanned as _; + +/// Generates code for the contract reference 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 ContractRef<'a> { + contract: &'a ir::Contract, +} + +impl GenerateCode for ContractRef<'_> { + fn generate_code(&self) -> TokenStream2 { + let contract_ref = self.generate_struct(); + let contract_ref_trait_impls = self.generate_contract_trait_impls(); + let contract_ref_inherent_impls = self.generate_contract_inherent_impls(); + let call_builder_trait_impl = self.generate_call_builder_trait_impl(); + let auxiliary_trait_impls = self.generate_auxiliary_trait_impls(); + quote! { + #contract_ref + #contract_ref_trait_impls + #contract_ref_inherent_impls + #call_builder_trait_impl + #auxiliary_trait_impls + } + } +} + +impl ContractRef<'_> { + /// Generates the identifier of the contract reference struct. + fn generate_contract_ref_ident(&self) -> syn::Ident { + quote::format_ident!("{}Ref", self.contract.module().storage().ident()) + } + + /// Generates the code for the struct representing the contract reference. + /// + /// The generated struct is the type onto which everything is implemented. + /// It is also the type that is going to be used by other smart contract + /// dynamically depending on the smart contract. It mirrors the smart contract + /// API but is just a typed thin-wrapper around an `AccountId`. + fn generate_struct(&self) -> TokenStream2 { + let span = self.contract.module().storage().span(); + let doc_attrs = self + .contract + .module() + .storage() + .attrs() + .iter() + .cloned() + .filter(syn::Attribute::is_doc_attribute); + let storage_ident = self.contract.module().storage().ident(); + let ref_ident = self.generate_contract_ref_ident(); + quote_spanned!(span=> + #[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, + )] + #( #doc_attrs )* + pub struct #ref_ident { + inner: <#storage_ident as ::ink_lang::codegen::ContractCallBuilder>::Type, + } + + const _: () = { + impl ::ink_lang::reflect::ContractReference for #storage_ident { + type Type = #ref_ident; + } + + impl ::ink_lang::reflect::ContractEnv for #ref_ident { + type Env = <#storage_ident as ::ink_lang::reflect::ContractEnv>::Env; + } + }; + ) + } + + /// Generates some ink! specific auxiliary trait implementations for the + /// smart contract reference type. + /// + /// These are required to properly interoperate with the contract reference. + fn generate_auxiliary_trait_impls(&self) -> TokenStream2 { + let span = self.contract.module().storage().span(); + let storage_ident = self.contract.module().storage().ident(); + let ref_ident = self.generate_contract_ref_ident(); + quote_spanned!(span=> + impl ::ink_env::call::FromAccountId for #ref_ident { + #[inline] + fn from_account_id(account_id: AccountId) -> Self { + Self { inner: <<#storage_ident + as ::ink_lang::codegen::ContractCallBuilder>::Type + as ::ink_env::call::FromAccountId>::from_account_id(account_id) + } + } + } + + impl ::ink_lang::ToAccountId for #ref_ident { + #[inline] + fn to_account_id(&self) -> AccountId { + <<#storage_ident as ::ink_lang::codegen::ContractCallBuilder>::Type + as ::ink_lang::ToAccountId>::to_account_id(&self.inner) + } + } + ) + } + + /// Generates the `CallBuilder` trait implementation for the contract reference. + /// + /// This creates the bridge between the ink! smart contract type and the + /// associated call builder. + fn generate_call_builder_trait_impl(&self) -> TokenStream2 { + let span = self.contract.module().storage().span(); + let ref_ident = self.generate_contract_ref_ident(); + let storage_ident = self.contract.module().storage().ident(); + quote_spanned!(span=> + const _: () = { + impl ::ink_lang::codegen::TraitCallBuilder for #ref_ident { + type Builder = <#storage_ident as ::ink_lang::codegen::ContractCallBuilder>::Type; + + #[inline] + fn call(&self) -> &Self::Builder { + &self.inner + } + + #[inline] + fn call_mut(&mut self) -> &mut Self::Builder { + &mut self.inner + } + } + }; + ) + } + + /// Generates the code for all ink! trait implementations of the contract itself. + /// + /// # Note + /// + /// The generated implementations must live outside of an artificial `const` block + /// in order to properly show their documentation using `rustdoc`. + fn generate_contract_trait_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_contract_trait_impl(trait_path, impl_block) + }) + }) + .collect() + } + + /// Generates the code for a single ink! trait implementation of the contract itself. + /// + /// The generated implementation mainly forwards the calls to the previously generated + /// associated call builder that implements each respective ink! trait. + fn generate_contract_trait_impl( + &self, + trait_path: &syn::Path, + impl_block: &ir::ItemImpl, + ) -> TokenStream2 { + let span = impl_block.span(); + let attrs = impl_block.attrs(); + let forwarder_ident = self.generate_contract_ref_ident(); + let messages = self.generate_contract_trait_impl_messages(trait_path, impl_block); + quote_spanned!(span=> + #( #attrs )* + impl #trait_path for #forwarder_ident { + #[doc(hidden)] + type __ink_TraitInfo = <::ink_lang::reflect::TraitDefinitionRegistry + as #trait_path>::__ink_TraitInfo; + + #messages + } + ) + } + + /// Generates the code for all messages of a single ink! trait implementation of + /// the ink! smart contract. + fn generate_contract_trait_impl_messages( + &self, + trait_path: &syn::Path, + impl_block: &ir::ItemImpl, + ) -> TokenStream2 { + impl_block + .iter_messages() + .map(|message| { + self.generate_contract_trait_impl_for_message(trait_path, message) + }) + .collect() + } + + /// Generates the code for a single message of a single ink! trait implementation + /// that is implemented by the ink! smart contract. + fn generate_contract_trait_impl_for_message( + &self, + trait_path: &syn::Path, + message: ir::CallableWithSelector, + ) -> TokenStream2 { + use ir::Callable as _; + let span = message.span(); + let trait_info = generator::generate_reference_to_trait_info(span, trait_path); + let message_ident = message.ident(); + let output_ident = generator::output_ident(message_ident); + let call_operator = match message.receiver() { + ir::Receiver::Ref => quote! { call }, + ir::Receiver::RefMut => quote! { call_mut }, + }; + let forward_operator = match message.receiver() { + ir::Receiver::Ref => quote! { forward }, + 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_types = message.inputs().map(|input| &input.ty).collect::>(); + quote_spanned!(span=> + type #output_ident = + <::Forwarder as #trait_path>::#output_ident; + + #[inline] + fn #message_ident( + & #mut_token self + #( , #input_bindings : #input_types )* + ) -> Self::#output_ident { + <_ as #trait_path>::#message_ident( + <_ as ::ink_lang::codegen::TraitCallForwarderFor<#trait_info>>::#forward_operator( + ::#call_operator(self), + ) + #( , #input_bindings )* + ) + } + ) + } + + /// Generates the code for all ink! inherent implementations of the contract itself. + /// + /// # Note + /// + /// The generated implementations must live outside of an artificial `const` block + /// in order to properly show their documentation using `rustdoc`. + fn generate_contract_inherent_impls(&self) -> TokenStream2 { + self.contract + .module() + .impls() + .filter_map(|impl_block| { + // We are only interested in ink! trait implementation block. + impl_block + .trait_path() + .is_none() + .then(|| self.generate_contract_inherent_impl(impl_block)) + }) + .collect() + } + + /// Generates the code for a single ink! inherent implementation of the contract itself. + /// + /// # Note + /// + /// This produces the short-hand calling notation for the inherent contract implementation. + /// The generated code simply forwards its calling logic to the associated call builder. + fn generate_contract_inherent_impl(&self, impl_block: &ir::ItemImpl) -> TokenStream2 { + let span = impl_block.span(); + let attrs = impl_block.attrs(); + let forwarder_ident = self.generate_contract_ref_ident(); + let messages = impl_block + .iter_messages() + .map(|message| self.generate_contract_inherent_impl_for_message(message)); + let constructors = impl_block.iter_constructors().map(|constructor| { + self.generate_contract_inherent_impl_for_constructor(constructor) + }); + quote_spanned!(span=> + #( #attrs )* + impl #forwarder_ident { + #( #constructors )* + #( #messages )* + } + ) + } + + /// Generates the code for a single ink! inherent message of the contract itself. + /// + /// # Note + /// + /// This produces the short-hand calling notation for the inherent contract message. + /// The generated code simply forwards its calling logic to the associated call builder. + fn generate_contract_inherent_impl_for_message( + &self, + message: ir::CallableWithSelector, + ) -> TokenStream2 { + use ir::Callable as _; + let span = message.span(); + let attrs = message.attrs(); + let storage_ident = self.contract.module().storage().ident(); + let message_ident = message.ident(); + let call_operator = match message.receiver() { + ir::Receiver::Ref => quote! { call }, + 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_types = message.inputs().map(|input| &input.ty).collect::>(); + let output_type = message.output().map(|ty| quote! { -> #ty }); + quote_spanned!(span=> + #( #attrs )* + #[inline] + pub fn #message_ident( + & #mut_token self + #( , #input_bindings : #input_types )* + ) #output_type { + ::#call_operator(self) + .#message_ident( #( #input_bindings ),* ) + .fire() + .unwrap_or_else(|error| ::core::panic!( + "encountered error while calling {}::{}: {:?}", + ::core::stringify!(#storage_ident), + ::core::stringify!(#message_ident), + error, + )) + } + ) + } + + /// Generates the code for a single ink! inherent constructor of the contract itself. + /// + /// # Note + /// + /// Unlike with ink! messages this does not forward to the call builder since constructor + /// calls in ink! do not have a short-hand notation and therefore this implements the + /// long-hand calling notation code directly. + fn generate_contract_inherent_impl_for_constructor( + &self, + constructor: ir::CallableWithSelector, + ) -> TokenStream2 { + let span = constructor.span(); + let attrs = constructor.attrs(); + let constructor_ident = constructor.ident(); + let selector_bytes = constructor.composed_selector().hex_lits(); + let input_bindings = generator::input_bindings(constructor.inputs()); + let input_types = generator::input_types(constructor.inputs()); + let arg_list = generator::generate_argument_list(input_types.iter().cloned()); + quote_spanned!(span => + #( #attrs )* + #[inline] + #[allow(clippy::type_complexity)] + pub fn #constructor_ident( + #( #input_bindings : #input_types ),* + ) -> ::ink_env::call::CreateBuilder< + Environment, + ::ink_env::call::utils::Unset, + ::ink_env::call::utils::Unset, + ::ink_env::call::utils::Unset, + ::ink_env::call::utils::Set<::ink_env::call::ExecutionInput<#arg_list>>, + ::ink_env::call::utils::Unset<::ink_env::call::state::Salt>, + Self, + > { + ::ink_env::call::build_create::() + .exec_input( + ::ink_env::call::ExecutionInput::new( + ::ink_env::call::Selector::new([ #( #selector_bytes ),* ]) + ) + #( + .push_arg(#input_bindings) + )* + ) + } + ) + } +} diff --git a/crates/lang/codegen/src/generator/as_dependency/mod.rs b/crates/lang/codegen/src/generator/as_dependency/mod.rs new file mode 100644 index 00000000000..4a6acacadbe --- /dev/null +++ b/crates/lang/codegen/src/generator/as_dependency/mod.rs @@ -0,0 +1,94 @@ +// 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. + +mod call_builder; +mod contract_ref; + +use self::{ + call_builder::CallBuilder, + contract_ref::ContractRef, +}; +use crate::{ + traits::GenerateCodeUsing, + GenerateCode, +}; +use derive_more::From; +use proc_macro2::TokenStream as TokenStream2; +use quote::quote; + +/// Generates `#[cfg(..)]` code to guard against compilation under `ink-as-dependency`. +#[derive(From)] +pub struct NotAsDependencyCfg<'a> { + contract: &'a ir::Contract, +} + +impl GenerateCode for NotAsDependencyCfg<'_> { + fn generate_code(&self) -> TokenStream2 { + if self.contract.config().is_compile_as_dependency_enabled() { + // We use `__ink_DO_NOT_COMPILE` in order to craft a `cfg` that + // never evaluates to `true` and therefore is always disabled. + return quote! { #[cfg(feature = "__ink_DO_NOT_COMPILE")] } + } + quote! { #[cfg(not(feature = "ink-as-dependency"))] } + } +} + +/// Generates `#[cfg(..)]` code to only allow compilation when `ink-as-dependency` is enabled. +/// +/// The `ink-as-dependency` can be enabled mainly by 2 different ways: +/// +/// - Enabling it in the associated `Cargo.toml` as crate feature. +/// - Note: This can be enabled by dependencies of an ink! smart contract. +/// - Enabling it in the configuration header with `#[ink::contract(compile_as_dependency = true)]`. +/// - If set here the contract will always be compiled as it is was a dependency. +#[derive(From)] +pub struct OnlyAsDependencyCfg<'a> { + contract: &'a ir::Contract, +} + +impl GenerateCode for OnlyAsDependencyCfg<'_> { + fn generate_code(&self) -> TokenStream2 { + if self.contract.config().is_compile_as_dependency_enabled() { + // We return no code since no code is required to disable compilation. + return quote! {} + } + quote! { + #[cfg(feature = "ink-as-dependency")] + } + } +} + +/// Generates code for generating a contract reference. +/// +/// Contract references are used to dynamically depend on a smart contract. +/// The contract reference is just a typed thin-wrapper around an `AccountId` +/// that implements an API mirrored by the smart contract. +#[derive(From)] +pub struct ContractReference<'a> { + /// The contract to generate code for. + contract: &'a ir::Contract, +} +impl_as_ref_for_generator!(ContractReference); + +impl GenerateCode for ContractReference<'_> { + /// Generates ink! contract code. + fn generate_code(&self) -> TokenStream2 { + let call_builder = self.generate_code_using::(); + let call_forwarder = self.generate_code_using::(); + quote! { + #call_builder + #call_forwarder + } + } +} diff --git a/crates/lang/codegen/src/generator/contract.rs b/crates/lang/codegen/src/generator/contract.rs index 0029bc49559..47074f9a8fc 100644 --- a/crates/lang/codegen/src/generator/contract.rs +++ b/crates/lang/codegen/src/generator/contract.rs @@ -39,10 +39,11 @@ impl GenerateCode for Contract<'_> { let env = self.generate_code_using::(); let storage = self.generate_code_using::(); let events = self.generate_code_using::(); - let dispatch = self.generate_code_using::(); + let dispatch2 = self.generate_code_using::(); let item_impls = self.generate_code_using::(); - let cross_calling = self.generate_code_using::(); let metadata = self.generate_code_using::(); + let contract_reference = + self.generate_code_using::(); let non_ink_items = self .contract .module() @@ -55,9 +56,9 @@ impl GenerateCode for Contract<'_> { #env #storage #events - #dispatch + #dispatch2 #item_impls - #cross_calling + #contract_reference #metadata #( #non_ink_items )* } diff --git a/crates/lang/codegen/src/generator/cross_calling.rs b/crates/lang/codegen/src/generator/cross_calling.rs deleted file mode 100644 index 0764bd3c437..00000000000 --- a/crates/lang/codegen/src/generator/cross_calling.rs +++ /dev/null @@ -1,908 +0,0 @@ -// 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::GenerateCode; -use derive_more::From; -use heck::CamelCase as _; -use impl_serde::serialize as serde_hex; -use ir::Callable; -use itertools::Itertools as _; -use proc_macro2::{ - Ident, - TokenStream as TokenStream2, -}; -use quote::{ - format_ident, - quote, - quote_spanned, -}; -use syn::spanned::Spanned as _; - -/// 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_mut: bool, - }, - /// The below error represents calling a constructor in a context that does - /// not allow calling it. This may happen when the constructor defined in a - /// trait is cross-called in another contract. - /// This is not allowed since the contract to which a call is forwarded must - /// already exist at the point when the call to it is made. - #[codec(index = 2)] - CannotCallTraitConstructor { - /// The trait that defines the called constructor. - trait_ident: String, - /// The name of the called constructor. - constructor_ident: String, - /// The selector of the called constructor. - constructor_selector: [u8; 4], - }, -} - -/// Generates `#[cfg(..)]` code to guard against compilation under `ink-as-dependency`. -#[derive(From)] -pub struct CrossCallingConflictCfg<'a> { - contract: &'a ir::Contract, -} - -impl GenerateCode for CrossCallingConflictCfg<'_> { - fn generate_code(&self) -> TokenStream2 { - if self.contract.config().is_compile_as_dependency_enabled() { - return quote! { #[cfg(feature = "__ink_DO_NOT_COMPILE")] } - } - quote! { #[cfg(not(feature = "ink-as-dependency"))] } - } -} - -/// Generates code for using this ink! contract as a dependency. -#[derive(From)] -pub struct CrossCalling<'a> { - contract: &'a ir::Contract, -} - -impl GenerateCode for CrossCalling<'_> { - fn generate_code(&self) -> TokenStream2 { - let storage = self.generate_storage(); - let standard_impls = self.generate_standard_impls(); - let call_forwarder = self.generate_call_forwarders(); - let impl_blocks = self.generate_impl_blocks(); - quote! { - #storage - #standard_impls - #call_forwarder - #impl_blocks - } - } -} - -impl CrossCalling<'_> { - /// Generates code for conditionally compiling code only if the contract - /// is compiled as dependency. - fn generate_cfg(&self) -> Option { - if self.contract.config().is_compile_as_dependency_enabled() { - return None - } - Some(quote! { - #[cfg(feature = "ink-as-dependency")] - }) - } - - /// Generates code for the ink! storage struct for cross-calling purposes. - /// - /// # Note - /// - /// This always consists of a single `AccountId` and can be viewed as a - /// reference to a live smart contract instance of the same type. It will - /// forward all calls via ink!'s provided cross-calling infrastructure - /// automatically over the chain. - fn generate_storage(&self) -> TokenStream2 { - let cfg = self.generate_cfg(); - let storage = self.contract.module().storage(); - let span = storage.span(); - let ident = storage.ident(); - let attrs = storage.attrs(); - quote_spanned!(span => - #cfg - #( #attrs )* - #[derive( - Clone, - Debug, - ::scale::Encode, - ::scale::Decode, - ::ink_storage::traits::SpreadLayout, - ::ink_storage::traits::PackedLayout, - )] - #[cfg_attr( - feature = "std", - derive( - ::scale_info::TypeInfo, - ::ink_storage::traits::StorageLayout, - ) - )] - pub struct #ident { - account_id: AccountId, - } - ) - } - - /// Generates code for the trait implementations required to make the - /// generated ink! storage struct for cross-calling work out-of-the-box - /// for the cross-calling infrastructure. - fn generate_standard_impls(&self) -> TokenStream2 { - let cfg = self.generate_cfg(); - let ident = self.contract.module().storage().ident(); - quote! { - #cfg - const _: () = { - impl ::ink_env::call::FromAccountId for #ident { - #[inline] - fn from_account_id(account_id: AccountId) -> Self { - Self { account_id } - } - } - - impl ::ink_lang::ToAccountId for #ident { - #[inline] - fn to_account_id(&self) -> AccountId { - self.account_id - } - } - }; - } - } - - /// Builds up the `ink_env::call::utils::ArgumentList` type structure for the given types. - fn generate_arg_list<'a, Args>(args: Args) -> TokenStream2 - where - Args: IntoIterator, - ::IntoIter: DoubleEndedIterator, - { - args.into_iter().fold( - quote! { ::ink_env::call::utils::EmptyArgumentList }, - |rest, arg| quote! { - ::ink_env::call::utils::ArgumentList<::ink_env::call::utils::Argument<#arg>, #rest> - } - ) - } - - /// Returns the identifier for the generated call forwarder utility. - fn call_forwarder_ident() -> Ident { - format_ident!("__ink_CallForwarder") - } - - fn generate_call_forwarder_trait_ghost_message( - message: ir::CallableWithSelector, - ) -> TokenStream2 { - let span = message.span(); - let ident = message.ident(); - let output_ident = format_ident!("{}Out", ident.to_string().to_camel_case()); - let composed_selector = message.composed_selector().as_bytes().to_owned(); - let trait_ident = message - .item_impl() - .trait_ident() - .expect("trait identifier must exist") - .to_string(); - let linker_error = EnforcedErrors::CannotCallTraitMessage { - trait_ident, - message_ident: ident.to_string(), - message_selector: composed_selector, - message_mut: message.receiver().is_ref_mut(), - }; - let linker_error_ident = format_ident!( - "__ink_enforce_error_{}", - serde_hex::to_hex(&scale::Encode::encode(&linker_error), false) - ); - let attrs = message.attrs(); - let input_bindings = message - .inputs() - .enumerate() - .map(|(n, _)| format_ident!("__ink_binding_{}", n)) - .collect::>(); - let input_types = message - .inputs() - .map(|pat_type| &*pat_type.ty) - .collect::>(); - let output_ty = message - .output() - .cloned() - .unwrap_or_else(|| syn::parse_quote! { () }); - let pub_tok = match message.item_impl().trait_path() { - Some(_) => None, - None => Some(quote! { pub }), - }; - let mut_tok = match message.receiver() { - ir::Receiver::Ref => None, - ir::Receiver::RefMut => Some(quote! { mut }), - }; - let error_ident = match option_env!("INK_COVERAGE_REPORTING") { - Some("true") => { - quote! { - // The code coverage reporting CI stage links dead code, - // hence we have to provide an `unreachable!` here. If - // the invalid implementation above is linked this results - // in a linker error. - ::core::unreachable!("this is an invalid ink! message call which should never be possible."); - } - } - _ => { - quote! { - extern { - fn #linker_error_ident() -> !; - } - unsafe { #linker_error_ident() } - } - } - }; - quote_spanned!(span=> - type #output_ident = #output_ty; - - #( #attrs )* - #[cold] - #[doc(hidden)] - #pub_tok fn #ident( - & #mut_tok self, - #( #input_bindings : #input_types ),* - ) -> Self::#output_ident { - #error_ident - } - ) - } - - fn generate_call_forwarder_trait_proper_message( - message: ir::CallableWithSelector, - ) -> TokenStream2 { - let span = message.span(); - let ident = message.ident(); - let output_ident = format_ident!("{}Out", ident.to_string().to_camel_case()); - let composed_selector = message.composed_selector().as_bytes().to_owned(); - let attrs = message.attrs(); - let input_bindings = message - .inputs() - .enumerate() - .map(|(n, _)| format_ident!("__ink_binding_{}", n)) - .collect::>(); - let input_types = message - .inputs() - .map(|pat_type| &*pat_type.ty) - .collect::>(); - let arg_list = Self::generate_arg_list(input_types.iter().cloned()); - let output = message.output(); - let output_sig = output.map_or_else( - || quote! { () }, - |output| quote! { ::ink_env::call::utils::ReturnType<#output> }, - ); - let pub_tok = match message.item_impl().trait_path() { - Some(_) => None, - None => Some(quote! { pub }), - }; - let receiver = match message.receiver() { - ir::Receiver::Ref => Some(quote! { &self }), - ir::Receiver::RefMut => Some(quote! { &mut self }), - }; - quote_spanned!(span=> - #[allow(clippy::type_complexity)] - type #output_ident = ::ink_env::call::CallBuilder< - Environment, - ::ink_env::call::utils::Set, - ::ink_env::call::utils::Unset, - ::ink_env::call::utils::Unset, - ::ink_env::call::utils::Set<::ink_env::call::ExecutionInput<#arg_list>>, - ::ink_env::call::utils::Set<#output_sig>, - >; - - #( #attrs )* - #[inline] - #pub_tok fn #ident( - #receiver #(, #input_bindings : #input_types )* - ) -> Self::#output_ident { - ::ink_env::call::build_call::() - .callee(::ink_lang::ToAccountId::to_account_id(self.contract)) - .exec_input( - ::ink_env::call::ExecutionInput::new( - ::ink_env::call::Selector::new([ #( #composed_selector ),* ]) - ) - #( - .push_arg(#input_bindings) - )* - ) - .returns::<#output_sig>() - } - ) - } - - /// Generates code for a single call forwarder trait message. - /// - /// The `mutable` parameter indicates whether only read-only (`false`) or - /// write-only (`true`) messages shall be valid calls. For non valid messages - /// an invalid implementation is provided so that actually calling those - /// will result in a compiler or linker error. - fn generate_call_forwarder_trait_message( - mutable: bool, - message: ir::CallableWithSelector, - ) -> TokenStream2 { - if mutable == message.receiver().is_ref_mut() { - Self::generate_call_forwarder_trait_proper_message(message) - } else { - Self::generate_call_forwarder_trait_ghost_message(message) - } - } - - /// Generates code for a single call forwarder trait constructor. - /// - /// Note that constructors never need to be forwarded and that we only - /// provide their implementations to satisfy the implementation block. - /// We generally try to generate code in a way that actually calling - /// those constructors will result in a compiler or linker error. - fn generate_call_forwarder_trait_constructor( - constructor: ir::CallableWithSelector, - ) -> TokenStream2 { - let span = constructor.span(); - let attrs = constructor.attrs(); - let ident = constructor.ident(); - let output_ident = format_ident!("{}Out", ident.to_string().to_camel_case()); - let composed_selector = constructor.composed_selector().as_bytes().to_owned(); - let trait_ident = constructor - .item_impl() - .trait_ident() - .expect("trait identifier must exist") - .to_string(); - let linker_error = EnforcedErrors::CannotCallTraitConstructor { - trait_ident, - constructor_ident: ident.to_string(), - constructor_selector: composed_selector, - }; - let linker_error_ident = format_ident!( - "__ink_enforce_error_{}", - serde_hex::to_hex(&scale::Encode::encode(&linker_error), false) - ); - let input_bindings = constructor - .inputs() - .enumerate() - .map(|(n, _)| format_ident!("__ink_binding_{}", n)) - .collect::>(); - let input_types = constructor - .inputs() - .map(|pat_type| &*pat_type.ty) - .collect::>(); - let error_ident = match option_env!("INK_COVERAGE_REPORTING") { - Some("true") => { - quote! { - // The code coverage reporting CI stage links dead code, - // hence we have to provide an `unreachable!` here. If - // the invalid implementation above is linked this results - // in a linker error. - ::core::unreachable!("this is an invalid ink! message call which should never be possible."); - } - } - _ => { - quote! { - extern { - fn #linker_error_ident() -> !; - } - unsafe { #linker_error_ident() } - } - } - }; - quote_spanned!(span => - type #output_ident = ::ink_lang::NeverReturns; - - #( #attrs )* - #[cold] - #[doc(hidden)] - fn #ident( - #( #input_bindings : #input_types ),* - ) -> Self::#output_ident { - #error_ident - } - ) - } - - /// Generates code for a single call forwarder trait implementation block. - /// - /// The `mutable` parameter indicates whether only read-only (`false`) or - /// write-only (`true`) messages and constructors are to be considered. - fn generate_call_forwarder_trait_impl_block( - &self, - mutable: bool, - item_impl: &ir::ItemImpl, - ) -> TokenStream2 { - assert!(item_impl.trait_path().is_some()); - let span = item_impl.span(); - let attrs = item_impl.attrs(); - let forwarder_ident = Self::call_forwarder_ident(); - let storage_ident = self.contract.module().storage().ident(); - let mut_tok = if mutable { Some(quote! { mut }) } else { None }; - let constructors = item_impl.iter_constructors().map(|constructor| { - Self::generate_call_forwarder_trait_constructor(constructor) - }); - let messages = item_impl - .iter_messages() - .map(|message| Self::generate_call_forwarder_trait_message(mutable, message)); - let trait_path = item_impl - .trait_path() - .expect("encountered missing trait path for trait impl block"); - let trait_ident = item_impl - .trait_ident() - .expect("encountered missing trait identifier for trait impl block"); - let hash = ir::InkTrait::compute_verify_hash( - trait_ident, - item_impl.iter_constructors().map(|constructor| { - let ident = constructor.ident().clone(); - let len_inputs = constructor.inputs().count(); - (ident, len_inputs) - }), - item_impl.iter_messages().map(|message| { - let ident = message.ident().clone(); - let len_inputs = message.inputs().count() + 1; - let is_mut = message.receiver().is_ref_mut(); - (ident, len_inputs, is_mut) - }), - ); - let checksum = u32::from_be_bytes([hash[0], hash[1], hash[2], hash[3]]) as usize; - quote_spanned!(span => - unsafe impl<'a> ::ink_lang::CheckedInkTrait<[(); #checksum]> for #forwarder_ident<&'a #mut_tok #storage_ident> {} - - #( #attrs )* - impl<'a> #trait_path for #forwarder_ident<&'a #mut_tok #storage_ident> { - type __ink_Checksum = [(); #checksum]; - - #( #constructors )* - #( #messages )* - } - ) - } - - fn generate_call_forwarder_inherent_message( - message: ir::CallableWithSelector, - ) -> TokenStream2 { - let span = message.span(); - let ident = message.ident(); - let composed_selector = message.composed_selector().as_bytes().to_owned(); - let attrs = message.attrs(); - let input_bindings = message - .inputs() - .enumerate() - .map(|(n, _)| format_ident!("__ink_binding_{}", n)) - .collect::>(); - let input_types = message - .inputs() - .map(|pat_type| &*pat_type.ty) - .collect::>(); - let arg_list = Self::generate_arg_list(input_types.iter().cloned()); - let output = message.output(); - let output_sig = output.map_or_else( - || quote! { () }, - |output| quote! { ::ink_env::call::utils::ReturnType<#output> }, - ); - let pub_tok = match message.item_impl().trait_path() { - Some(_) => None, - None => Some(quote! { pub }), - }; - quote_spanned!(span=> - #( #attrs )* - #[inline] - #[allow(clippy::type_complexity)] - #pub_tok fn #ident( - self, - #( #input_bindings : #input_types ),* - ) -> ::ink_env::call::CallBuilder< - Environment, - ::ink_env::call::utils::Set, - ::ink_env::call::utils::Unset, - ::ink_env::call::utils::Unset, - ::ink_env::call::utils::Set<::ink_env::call::ExecutionInput<#arg_list>>, - ::ink_env::call::utils::Set<#output_sig>, - > { - ::ink_env::call::build_call::() - .callee(::ink_lang::ToAccountId::to_account_id(self.contract)) - .exec_input( - ::ink_env::call::ExecutionInput::new( - ::ink_env::call::Selector::new([ #( #composed_selector ),* ]) - ) - #( - .push_arg(#input_bindings) - )* - ) - .returns::<#output_sig>() - } - ) - } - - /// Generates code for a single call forwarder inherent implementation block. - /// - /// The `mutable` parameter indicates whether only read-only (`false`) or - /// write-only (`true`) messages and constructors are to be considered. - fn generate_call_forwarder_inherent_impl_block( - &self, - mutable: bool, - item_impl: &ir::ItemImpl, - ) -> TokenStream2 { - assert!(item_impl.trait_path().is_none()); - let span = item_impl.span(); - let attrs = item_impl.attrs(); - let forwarder_ident = Self::call_forwarder_ident(); - let storage_ident = self.contract.module().storage().ident(); - let mut_tok = if mutable { Some(quote! { mut }) } else { None }; - let messages = item_impl - .iter_messages() - .filter(|message| mutable == message.receiver().is_ref_mut()) - .map(Self::generate_call_forwarder_inherent_message); - quote_spanned!(span => - #( #attrs )* - impl<'a> #forwarder_ident<&'a #mut_tok #storage_ident> { - #( #messages )* - } - ) - } - - /// Generates code for the call forwarder implementation blocks. - /// - /// The `mutable` parameter indicates whether only read-only (`false`) or - /// write-only (`true`) messages and constructors are to be considered. - fn generate_call_forwarder_impl_blocks(&self, mutable: bool) -> TokenStream2 { - let impl_blocks = self.contract.module().impls().map(|item_impl| { - match item_impl.trait_path() { - Some(_) => { - self.generate_call_forwarder_trait_impl_block(mutable, item_impl) - } - None => { - self.generate_call_forwarder_inherent_impl_block(mutable, item_impl) - } - } - }); - quote! { #( #impl_blocks )* } - } - - /// Generates code for the call forwarder utility struct. - fn generate_call_forwarders(&self) -> TokenStream2 { - let forwarder_ident = Self::call_forwarder_ident(); - let storage_ident = self.contract.module().storage().ident(); - let impl_blocks_ref = self.generate_call_forwarder_impl_blocks(false); - let impl_blocks_refmut = self.generate_call_forwarder_impl_blocks(true); - let cfg = self.generate_cfg(); - quote! { - #cfg - const _: () = { - impl<'a> ::ink_lang::ForwardCall for &'a #storage_ident { - type Forwarder = #forwarder_ident<&'a #storage_ident>; - - #[inline] - fn call(self) -> Self::Forwarder { - #forwarder_ident { contract: self } - } - } - - impl<'a> ::ink_lang::ForwardCallMut for &'a mut #storage_ident { - type Forwarder = #forwarder_ident<&'a mut #storage_ident>; - - #[inline] - fn call_mut(self) -> Self::Forwarder { - #forwarder_ident { contract: self } - } - } - - // Forwards contract messages to the chain. - #[doc(hidden)] - pub struct #forwarder_ident { - contract: T, - } - - #impl_blocks_ref - #impl_blocks_refmut - }; - } - } - - /// Generates the code to allow short-hand cross-chain contract calls for messages. - fn generate_trait_impl_block_message( - &self, - message: ir::CallableWithSelector, - ) -> TokenStream2 { - let storage_ident_str = self.contract.module().storage().ident().to_string(); - let span = message.span(); - let ident = message.ident(); - let output_ident = format_ident!("{}Out", ident.to_string().to_camel_case()); - let ident_str = ident.to_string(); - let trait_path = message - .item_impl() - .trait_path() - .expect("encountered missing trait path for trait impl block") - .segments - .iter() - .map(|path_segment| &path_segment.ident) - .map(ToString::to_string) - .join("::"); - let error_str = format!( - "encountered error while calling <{} as {}>::{}", - storage_ident_str, trait_path, ident_str - ); - let inputs_sig = message.inputs(); - let inputs_params = message.inputs().map(|pat_type| &pat_type.pat); - let output_ty = message - .output() - .cloned() - .unwrap_or_else(|| syn::parse_quote! { () }); - let receiver = message.receiver(); - let forward_ident = match receiver { - ir::Receiver::Ref => format_ident!("call"), - ir::Receiver::RefMut => format_ident!("call_mut"), - }; - let forward_trait = match receiver { - ir::Receiver::Ref => format_ident!("ForwardCall"), - ir::Receiver::RefMut => format_ident!("ForwardCallMut"), - }; - let opt_mut = match receiver { - ir::Receiver::Ref => None, - ir::Receiver::RefMut => Some(quote! { mut }), - }; - let opt_pub = match message.item_impl().trait_path() { - None => Some(quote! { pub }), - Some(_) => None, - }; - quote_spanned!(span => - type #output_ident = #output_ty; - - #[inline] - #opt_pub fn #ident( #receiver #(, #inputs_sig )* ) -> Self::#output_ident { - <&#opt_mut Self as ::ink_lang::#forward_trait>::#forward_ident(self) - .#ident( #( #inputs_params ),* ) - .fire() - .expect(#error_str) - } - ) - } - - /// Generates the code to allow cross-chain contract calls for trait constructors. - fn generate_trait_impl_block_constructor( - constructor: ir::CallableWithSelector, - ) -> TokenStream2 { - let span = constructor.span(); - let attrs = constructor.attrs(); - let ident = constructor.ident(); - let output_ident = format_ident!("{}Out", ident.to_string().to_camel_case()); - let composed_selector = constructor.composed_selector().as_bytes().to_owned(); - let input_bindings = constructor - .inputs() - .enumerate() - .map(|(n, _)| format_ident!("__ink_binding_{}", n)) - .collect::>(); - let input_types = constructor - .inputs() - .map(|pat_type| &*pat_type.ty) - .collect::>(); - let arg_list = Self::generate_arg_list(input_types.iter().cloned()); - quote_spanned!(span => - #[allow(clippy::type_complexity)] - type #output_ident = ::ink_env::call::CreateBuilder< - Environment, - ::ink_env::call::utils::Unset, - ::ink_env::call::utils::Unset, - ::ink_env::call::utils::Unset, - ::ink_env::call::utils::Set<::ink_env::call::ExecutionInput<#arg_list>>, - ::ink_env::call::utils::Unset<::ink_env::call::state::Salt>, - Self, - >; - - #( #attrs )* - #[inline] - fn #ident( - #( #input_bindings : #input_types ),* - ) -> Self::#output_ident { - ::ink_env::call::build_create::() - .exec_input( - ::ink_env::call::ExecutionInput::new( - ::ink_env::call::Selector::new([ #( #composed_selector ),* ]) - ) - #( - .push_arg(#input_bindings) - )* - ) - } - ) - } - - fn generate_trait_impl_block(&self, impl_block: &ir::ItemImpl) -> TokenStream2 { - assert!(impl_block.trait_path().is_some()); - let cfg = self.generate_cfg(); - let span = impl_block.span(); - let attrs = impl_block.attrs(); - let trait_path = impl_block - .trait_path() - .expect("encountered missing trait path"); - let trait_ident = impl_block - .trait_ident() - .expect("encountered missing trait identifier"); - let self_type = impl_block.self_type(); - let messages = impl_block - .iter_messages() - .map(|message| self.generate_trait_impl_block_message(message)); - let constructors = impl_block - .iter_constructors() - .map(Self::generate_trait_impl_block_constructor); - let hash = ir::InkTrait::compute_verify_hash( - trait_ident, - impl_block.iter_constructors().map(|constructor| { - let ident = constructor.ident().clone(); - let len_inputs = constructor.inputs().count(); - (ident, len_inputs) - }), - impl_block.iter_messages().map(|message| { - let ident = message.ident().clone(); - let len_inputs = message.inputs().count() + 1; - let is_mut = message.receiver().is_ref_mut(); - (ident, len_inputs, is_mut) - }), - ); - let checksum = u32::from_be_bytes([hash[0], hash[1], hash[2], hash[3]]) as usize; - quote_spanned!(span => - #cfg - unsafe impl ::ink_lang::CheckedInkTrait<[(); #checksum]> for #self_type {} - - #cfg - #( #attrs )* - impl #trait_path for #self_type { - type __ink_Checksum = [(); #checksum]; - - #( #messages )* - #( #constructors )* - } - ) - } - - /// Generates the code to allow short-hand cross-chain contract calls for constructors. - /// - /// # Note - /// - /// For constructors this is the only way they are able to be called. - fn generate_inherent_impl_block_constructor( - constructor: ir::CallableWithSelector, - ) -> TokenStream2 { - let span = constructor.span(); - let attrs = constructor.attrs(); - let ident = constructor.ident(); - let composed_selector = constructor.composed_selector().as_bytes().to_owned(); - let input_bindings = constructor - .inputs() - .enumerate() - .map(|(n, _)| format_ident!("__ink_binding_{}", n)) - .collect::>(); - let input_types = constructor - .inputs() - .map(|pat_type| &*pat_type.ty) - .collect::>(); - let arg_list = Self::generate_arg_list(input_types.iter().cloned()); - quote_spanned!(span => - #( #attrs )* - #[inline] - #[allow(clippy::type_complexity)] - pub fn #ident( - #( #input_bindings : #input_types ),* - ) -> ::ink_env::call::CreateBuilder< - Environment, - ::ink_env::call::utils::Unset, - ::ink_env::call::utils::Unset, - ::ink_env::call::utils::Unset, - ::ink_env::call::utils::Set<::ink_env::call::ExecutionInput<#arg_list>>, - ::ink_env::call::utils::Unset<::ink_env::call::state::Salt>, - Self, - > { - ::ink_env::call::build_create::() - .exec_input( - ::ink_env::call::ExecutionInput::new( - ::ink_env::call::Selector::new([ #( #composed_selector ),* ]) - ) - #( - .push_arg(#input_bindings) - )* - ) - } - ) - } - - /// Generates the code to allow short-hand cross-chain contract calls for messages. - fn generate_inherent_impl_block_message( - &self, - message: ir::CallableWithSelector, - ) -> TokenStream2 { - let storage_ident_str = self.contract.module().storage().ident().to_string(); - let span = message.span(); - let ident = message.ident(); - let ident_str = ident.to_string(); - let error_str = format!( - "encountered error while calling {}::{}", - storage_ident_str, ident_str - ); - let inputs_sig = message.inputs(); - let inputs_params = message.inputs().map(|pat_type| &pat_type.pat); - let output_sig = message.output().map(|output| quote! { -> #output }); - let receiver = message.receiver(); - let forward_ident = match receiver { - ir::Receiver::Ref => format_ident!("call"), - ir::Receiver::RefMut => format_ident!("call_mut"), - }; - let forward_trait = match receiver { - ir::Receiver::Ref => format_ident!("ForwardCall"), - ir::Receiver::RefMut => format_ident!("ForwardCallMut"), - }; - let opt_mut = match receiver { - ir::Receiver::Ref => None, - ir::Receiver::RefMut => Some(quote! { mut }), - }; - let opt_pub = match message.item_impl().trait_path() { - None => Some(quote! { pub }), - Some(_) => None, - }; - quote_spanned!(span => - #[inline] - #opt_pub fn #ident( #receiver #(, #inputs_sig )* ) #output_sig { - <&#opt_mut Self as ::ink_lang::#forward_trait>::#forward_ident(self) - .#ident( #( #inputs_params ),* ) - .fire() - .expect(#error_str) - } - ) - } - - fn generate_inherent_impl_block(&self, impl_block: &ir::ItemImpl) -> TokenStream2 { - assert!(impl_block.trait_path().is_none()); - let cfg = self.generate_cfg(); - let span = impl_block.span(); - let attrs = impl_block.attrs(); - let self_type = impl_block.self_type(); - let messages = impl_block - .iter_messages() - .map(|message| self.generate_inherent_impl_block_message(message)); - let constructors = impl_block.iter_constructors().map(|constructor| { - Self::generate_inherent_impl_block_constructor(constructor) - }); - quote_spanned!(span => - #cfg - #( #attrs )* - impl #self_type { - #( #messages )* - #( #constructors )* - } - ) - } - - fn generate_impl_blocks(&self) -> TokenStream2 { - let impl_blocks = self.contract.module().impls().map(|impl_block| { - match impl_block.trait_path() { - Some(_) => self.generate_trait_impl_block(impl_block), - None => self.generate_inherent_impl_block(impl_block), - } - }); - quote! { - #( #impl_blocks )* - } - } -} diff --git a/crates/lang/codegen/src/generator/dispatch.rs b/crates/lang/codegen/src/generator/dispatch.rs index a240b34fd43..6b01510bab2 100644 --- a/crates/lang/codegen/src/generator/dispatch.rs +++ b/crates/lang/codegen/src/generator/dispatch.rs @@ -12,6 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +use core::iter; + use crate::{ generator, GenerateCode, @@ -19,15 +21,11 @@ use crate::{ }; use derive_more::From; use ir::{ - Callable as _, + Callable, HexLiteral as _, }; -use proc_macro2::{ - Ident, - TokenStream as TokenStream2, -}; +use proc_macro2::TokenStream as TokenStream2; use quote::{ - format_ident, quote, quote_spanned, }; @@ -54,651 +52,721 @@ impl_as_ref_for_generator!(Dispatch); impl GenerateCode for Dispatch<'_> { fn generate_code(&self) -> TokenStream2 { - let no_cross_calling_cfg = - self.generate_code_using::(); - let entry_points = self.generate_entry_points(); - let dispatch_using_mode = self.generate_dispatch_using_mode(); - let dispatch_trait_impl_namespaces = self.generate_trait_impl_namespaces(); - let dispatch_trait_impls = self.generate_dispatch_trait_impls(); - let message_dispatch_enum = self.generate_message_dispatch_enum(); - let constructor_dispatch_enum = self.generate_constructor_dispatch_enum(); + let mut constructor_spans = Vec::new(); + let mut message_spans = Vec::new(); + + let cfg_not_as_dependency = + self.generate_code_using::(); + let amount_dispatchables = + self.generate_contract_amount_dispatchables_trait_impl(); + let contract_dispatchable_messages = + self.generate_contract_dispatchable_messages_trait_impl(&mut message_spans); + let contract_dispatchable_constructors = self + .generate_contract_dispatchable_constructors_trait_impl( + &mut constructor_spans, + ); + let contract_dispatchable_constructor_infos = + self.generate_dispatchable_constructor_infos(); + let contract_dispatchable_messages_infos = + self.generate_dispatchable_message_infos(); + let constructor_decoder_type = + self.generate_constructor_decoder_type(&constructor_spans); + let message_decoder_type = self.generate_message_decoder_type(&message_spans); + let entry_points = self.generate_entry_points(&message_spans); quote! { - // We do not generate contract dispatch code while the contract - // is being tested or the contract is a dependency of another - // since both resulting compilations do not require dispatching. + #amount_dispatchables + #contract_dispatchable_messages + #contract_dispatchable_constructors + #contract_dispatchable_constructor_infos + #contract_dispatchable_messages_infos + #constructor_decoder_type + #message_decoder_type + #[cfg(not(test))] - #no_cross_calling_cfg + #cfg_not_as_dependency const _: () = { #entry_points - #dispatch_using_mode - #dispatch_trait_impl_namespaces - #dispatch_trait_impls - #message_dispatch_enum - #constructor_dispatch_enum }; } } } impl Dispatch<'_> { - /// Generates the static ink! contract entry points. - /// - /// # Note - /// - /// Those are expected to exist by the smart contracts host module. - /// They guide the dispatch, set-up and tear-down of a smart contract. - fn generate_entry_points(&self) -> TokenStream2 { - let storage_ident = self.contract.module().storage().ident(); - let all_messages_deny_payment = self.all_messages_deny_payment(); - quote! { - #[cfg(not(test))] - #[no_mangle] - fn deploy() { - <#storage_ident as ::ink_lang::DispatchUsingMode>::dispatch_using_mode( - ::ink_lang::DispatchMode::Instantiate, - ).unwrap_or_else(|error| { - ::core::panic!("dispatching constructor failed: {}", error) - }) - } - - #[cfg(not(test))] - #[no_mangle] - fn call() { - if #all_messages_deny_payment { - ::ink_lang::deny_payment::<<#storage_ident as ::ink_lang::ContractEnv>::Env>() - .expect("caller transferred value even though all ink! message deny payments") - } - <#storage_ident as ::ink_lang::DispatchUsingMode>::dispatch_using_mode( - ::ink_lang::DispatchMode::Call, - ).unwrap_or_else(|error| { - ::core::panic!("dispatching message failed: {}", error) - }) - } - } - } - - /// Generates the `DispatchUsingMode` trait implementation to guide contract dispatch. - fn generate_dispatch_using_mode(&self) -> TokenStream2 { - let storage_ident = self.contract.module().storage().ident(); - quote! { - impl ::ink_lang::DispatchUsingMode for #storage_ident { - #[allow(unused_parens)] - fn dispatch_using_mode( - mode: ::ink_lang::DispatchMode - ) -> ::core::result::Result<(), ::ink_lang::DispatchError> { - match mode { - ::ink_lang::DispatchMode::Instantiate => { - <<#storage_ident as ::ink_lang::ConstructorDispatcher>::Type as ::ink_lang::Execute>::execute( - ::ink_env::decode_input::<<#storage_ident as ::ink_lang::ConstructorDispatcher>::Type>() - .map_err(|_| ::ink_lang::DispatchError::CouldNotReadInput)? - ) - } - ::ink_lang::DispatchMode::Call => { - <<#storage_ident as ::ink_lang::MessageDispatcher>::Type as ::ink_lang::Execute>::execute( - ::ink_env::decode_input::<<#storage_ident as ::ink_lang::MessageDispatcher>::Type>() - .map_err(|_| ::ink_lang::DispatchError::CouldNotReadInput)? - ) - } - } - } - } - } - } - - /// Returns the generated ink! namespace identifier for the given callable kind. - fn dispatch_trait_impl_namespace(kind: ir::CallableKind) -> Ident { - match kind { - ir::CallableKind::Constructor => format_ident!("__ink_ConstructorInfo"), - ir::CallableKind::Message => format_ident!("__ink_MessageInfo"), - } + /// Returns the number of dispatchable ink! constructors of the ink! smart contract. + fn query_amount_constructors(&self) -> usize { + self.contract + .module() + .impls() + .map(|item_impl| item_impl.iter_constructors()) + .flatten() + .count() } - /// Generates utility types to emulate namespaces to disambiguate dispatch trait - /// implementations for ink! messages and ink! constructors with overlapping - /// selectors. - fn generate_trait_impl_namespaces(&self) -> TokenStream2 { - let message_namespace = - Self::dispatch_trait_impl_namespace(ir::CallableKind::Message); - let constructor_namespace = - Self::dispatch_trait_impl_namespace(ir::CallableKind::Constructor); - quote! { - // Selector namespace for ink! messages of the root smart contract. - // - // # Note - // - // - We have separate namespaces for ink! messages and constructors to - // allow for overlapping selectors between them. - // - The `ID` const parameter uniquely identifies one of the ink! messages - // implemented by the root smart contract. - #[doc(hidden)] - pub struct #message_namespace {} - - // Selector namespace for ink! constructors of the root smart contract. - // - // # Note - // - // - We have separate namespaces for ink! messages and constructors to - // allow for overlapping selectors between them. - // - The `ID` const parameter uniquely identifies one of the ink! constructors - // implemented by the root smart contract. - #[doc(hidden)] - pub struct #constructor_namespace {} - } + /// Returns the number of dispatchable ink! messages of the ink! smart contract. + /// + /// This includes inherent ink! messages as well as trait ink! messages. + fn query_amount_messages(&self) -> usize { + self.contract + .module() + .impls() + .map(|item_impl| item_impl.iter_messages()) + .flatten() + .count() } - /// Generates code for the dispatch trait implementations for a generic ink! callable. - fn generate_trait_impls_for_callable( - &self, - cws: ir::CallableWithSelector<'_, C>, - ) -> TokenStream2 - where - C: ir::Callable + quote::ToTokens, - { - let callable = cws.callable(); - let callable_span = callable.span(); - let selector = cws.composed_selector(); - let (selector_bytes, selector_id) = ( - selector.hex_lits(), - selector.into_be_u32().hex_padded_suffixed(), - ); - let input_types = callable - .inputs() - .map(|pat_type| &pat_type.ty) - .collect::>(); + /// Generates code for the [`ink_lang::ContractDispatchables`] trait implementation. + /// + /// This trait implementation stores information of how many dispatchable + /// ink! messages and ink! constructors there are for the ink! smart contract. + fn generate_contract_amount_dispatchables_trait_impl(&self) -> TokenStream2 { + let span = self.contract.module().storage().span(); let storage_ident = self.contract.module().storage().ident(); - let namespace = Self::dispatch_trait_impl_namespace(cws.kind()); - let input_types_tuple = 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 )* } - }; - let fn_input_impl = quote_spanned!(callable.inputs_span() => - impl ::ink_lang::FnInput for #namespace::<#selector_id> { - type Input = #input_types_tuple; - } - ); - let fn_selector_impl = quote_spanned!(callable_span => - impl ::ink_lang::FnSelector for #namespace::<#selector_id> { - const SELECTOR: ::ink_env::call::Selector = ::ink_env::call::Selector::new([ - #( #selector_bytes ),* - ]); - } - ); - let fn_state_impl = quote_spanned!(callable_span => - impl ::ink_lang::FnState for #namespace::<#selector_id> { - type State = #storage_ident; + let count_messages = self.query_amount_messages(); + let count_constructors = self.query_amount_constructors(); + quote_spanned!(span=> + impl ::ink_lang::reflect::ContractAmountDispatchables for #storage_ident { + const MESSAGES: ::core::primitive::usize = #count_messages; + const CONSTRUCTORS: ::core::primitive::usize = #count_constructors; } - ); - quote! { - #fn_input_impl - #fn_selector_impl - #fn_state_impl - } + ) } - /// Returns a tuple of: - /// - /// - Vector over the generated identifier bindings (`__ink_binding_N`) for all inputs. - /// - `TokenStream` representing the binding identifiers as tuple (for equal to two or more inputs), - /// as single identifier (for exactly one input) or as wildcard (`_`) if there are - /// no input bindings. - /// - /// # Examples - /// - /// **No inputs:** - /// ``` - /// # use quote::quote; - /// # let x: (Vec<()>, _) = - /// ( vec![], - /// quote! { _ } ) - /// # ; - /// ``` - /// - /// **Exactly one input:** - /// ``` - /// # use quote::quote; - /// # let __ink_binding_0 = (); - /// ( vec![__ink_binding_0], - /// quote! { __ink_binding_0 } ) - /// # ; - /// ``` + /// Generates code for the [`ink_lang::ContractDispatchableMessages`] trait implementation. /// - /// **Multiple (equal to two or more) inputs:** - /// ``` - /// # use quote::quote; - /// # let __ink_binding_0 = (); - /// # let __ink_binding_1 = (); - /// ( vec![__ink_binding_0, __ink_binding_1, /* ... */], - /// quote! { (__ink_binding_0, __ink_binding_1, ..) } ) - /// # ; - /// ``` - fn generate_input_bindings(callable: &C) -> (Vec, TokenStream2) - where - C: ir::Callable, - { - let input_bindings = callable - .inputs() - .enumerate() - .map(|(n, _pat_type)| format_ident!("__ink_binding_{}", n)) - .collect::>(); - let inputs_as_tuple_or_wildcard = match input_bindings.len() { - 0 => quote! { _ }, - 1 => quote! { #( #input_bindings ),* }, - _ => quote! { ( #( #input_bindings ),* ) }, - }; - (input_bindings, inputs_as_tuple_or_wildcard) - } - - /// Generates all the dispatch trait implementations for the given ink! message. - fn generate_trait_impls_for_message( + /// This trait implementation stores the selector ID of each dispatchable + /// ink! messages of the ink! smart contract. + fn generate_contract_dispatchable_messages_trait_impl( &self, - cws: ir::CallableWithSelector<'_, ir::Message>, + message_spans: &mut Vec, ) -> TokenStream2 { - let message = cws.callable(); - let message_span = message.span(); - let selector = cws.composed_selector(); - let selector_id = selector.into_be_u32().hex_padded_suffixed(); - let output_tokens = message - .output() - .map(quote::ToTokens::to_token_stream) - .unwrap_or_else(|| quote! { () }); - let is_mut = message.receiver().is_ref_mut(); + let span = self.contract.module().storage().span(); let storage_ident = self.contract.module().storage().ident(); - let message_ident = message.ident(); - let namespace = Self::dispatch_trait_impl_namespace(ir::CallableKind::Message); - let fn_output_impl = quote_spanned!(message.output().span() => - impl ::ink_lang::FnOutput for #namespace::<#selector_id> { - #[allow(unused_parens)] - type Output = #output_tokens; - } - ); - let callable_impl = self.generate_trait_impls_for_callable(cws); - let (mut_token, message_trait_ident) = if is_mut { - ( - Some(syn::token::Mut::default()), - format_ident!("MessageMut"), - ) - } else { - (None, format_ident!("MessageRef")) - }; - let (input_bindings, inputs_as_tuple_or_wildcard) = - Self::generate_input_bindings(message); - let as_trait = cws.item_impl().trait_path().map(|trait_path| { - quote_spanned!(message_span => - as #trait_path - ) - }); - let message_impl = quote_spanned!(message_span => - impl ::ink_lang::#message_trait_ident for #namespace::<#selector_id> { - const CALLABLE: fn( - &#mut_token ::State, - ::Input - ) -> ::Output = |state, #inputs_as_tuple_or_wildcard| { - <#storage_ident #as_trait>::#message_ident(state, #( #input_bindings ),* ) - }; + let inherent_ids = self + .contract + .module() + .impls() + .filter(|item_impl| item_impl.trait_path().is_none()) + .map(|item_impl| item_impl.iter_messages()) + .flatten() + .map(|message| { + let span = message.span(); + message_spans.push(span); + let id = message + .composed_selector() + .into_be_u32() + .hex_padded_suffixed(); + quote_spanned!(span=> #id) + }) + .collect::>(); + let trait_ids = self + .contract + .module() + .impls() + .filter_map(|item_impl| { + item_impl + .trait_path() + .map(|trait_path| { + iter::repeat(trait_path).zip(item_impl.iter_messages()) + }) + }) + .flatten() + .map(|(trait_path, message)| { + let local_id = message.local_id().hex_padded_suffixed(); + let span = message.span(); + message_spans.push(span); + quote_spanned!(span=> + { + ::core::primitive::u32::from_be_bytes( + <<::ink_lang::reflect::TraitDefinitionRegistry<<#storage_ident as ::ink_lang::reflect::ContractEnv>::Env> + as #trait_path>::__ink_TraitInfo + as ::ink_lang::reflect::TraitMessageInfo<#local_id>>::SELECTOR + ) + } + ) + }); + quote_spanned!(span=> + impl ::ink_lang::reflect::ContractDispatchableMessages<{ + <#storage_ident as ::ink_lang::reflect::ContractAmountDispatchables>::MESSAGES + }> for #storage_ident { + const IDS: [ + ::core::primitive::u32; + <#storage_ident as ::ink_lang::reflect::ContractAmountDispatchables>::MESSAGES + ] = [ + #( #inherent_ids , )* + #( #trait_ids ),* + ]; } - ); - quote_spanned!(message_span => - #callable_impl - #fn_output_impl - #message_impl ) } - /// Generates all the dispatch trait implementations for the given ink! constructor. - fn generate_trait_impls_for_constructor( + /// Generates code for the [`ink_lang::ContractDispatchableConstructors`] trait implementation. + /// + /// This trait implementation stores the selector ID of each dispatchable + /// ink! constructor of the ink! smart contract. + fn generate_contract_dispatchable_constructors_trait_impl( &self, - cws: ir::CallableWithSelector<'_, ir::Constructor>, + constructor_spans: &mut Vec, ) -> TokenStream2 { - let constructor = cws.callable(); - let constructor_span = constructor.span(); - let selector = cws.composed_selector(); - let selector_id = selector.into_be_u32().hex_padded_suffixed(); + let span = self.contract.module().storage().span(); let storage_ident = self.contract.module().storage().ident(); - let constructor_ident = constructor.ident(); - let namespace = - Self::dispatch_trait_impl_namespace(ir::CallableKind::Constructor); - let callable_impl = self.generate_trait_impls_for_callable(cws); - let (input_bindings, inputs_as_tuple_or_wildcard) = - Self::generate_input_bindings(constructor); - let as_trait = cws.item_impl().trait_path().map(|trait_path| { - quote_spanned!(constructor_span => - as #trait_path - ) - }); - let constructor_impl = quote_spanned!(constructor_span => - impl ::ink_lang::Constructor for #namespace::<#selector_id> { - const CALLABLE: fn( - ::Input - ) -> ::State = |#inputs_as_tuple_or_wildcard| { - <#storage_ident #as_trait>::#constructor_ident(#( #input_bindings ),* ) - }; + let constructor_ids = self + .contract + .module() + .impls() + .map(|item_impl| item_impl.iter_constructors()) + .flatten() + .map(|constructor| { + let span = constructor.span(); + constructor_spans.push(span); + let id = constructor + .composed_selector() + .into_be_u32() + .hex_padded_suffixed(); + quote_spanned!(span=> #id) + }); + quote_spanned!(span=> + impl ::ink_lang::reflect::ContractDispatchableConstructors<{ + <#storage_ident as ::ink_lang::reflect::ContractAmountDispatchables>::CONSTRUCTORS + }> for #storage_ident { + const IDS: [ + ::core::primitive::u32; + <#storage_ident as ::ink_lang::reflect::ContractAmountDispatchables>::CONSTRUCTORS + ] = [ + #( #constructor_ids ),* + ]; } - ); - quote_spanned!(constructor_span => - #callable_impl - #constructor_impl ) } - /// Generate all dispatch trait implementations for ink! messages and ink! constructors. - fn generate_dispatch_trait_impls(&self) -> TokenStream2 { - let message_impls = self - .contract_messages() - .map(|message| self.generate_trait_impls_for_message(message)); - let constructor_impls = self - .contract_constructors() - .map(|constructor| self.generate_trait_impls_for_constructor(constructor)); - quote! { - #( #message_impls )* - #( #constructor_impls )* - } - } - - /// Generates variant identifiers for the generated dispatch enum. - /// - /// Since we want to avoid generating random names we generate identifiers - /// in terms of the selectors of the associated ink! messages or constructors. - /// - /// ## Example + /// Generate code for the [`ink_lang::DispatchableConstructorInfo`] trait implementations. /// - /// Given prefix of `"Message"` and selector with bytes `0xDEADBEEF` we - /// generate the following identifier: `__ink_Message_0xdeadbeef` - /// - /// This way it is clear that this is an ink! generated identifier and even - /// encodes the unique selector bytes to make the identifier unique. - fn generate_dispatch_variant_ident( - &self, - cws: ir::CallableWithSelector<'_, C>, - ) -> Ident - where - C: ir::Callable, - { - let selector_bytes = cws.composed_selector().as_bytes().to_owned(); - let prefix = match cws.callable().kind() { - ir::CallableKind::Message => "Message", - ir::CallableKind::Constructor => "Constructor", - }; - quote::format_ident!( - "__ink_{}_0x{:02X}{:02X}{:02X}{:02X}", - prefix, - selector_bytes[0], - selector_bytes[1], - selector_bytes[2], - selector_bytes[3] - ) - } + /// These trait implementations store relevant dispatch information for every + /// dispatchable ink! constructor of the ink! smart contract. + fn generate_dispatchable_constructor_infos(&self) -> TokenStream2 { + let span = self.contract.module().storage().span(); + let storage_ident = self.contract.module().storage().ident(); + let constructor_infos = self + .contract + .module() + .impls() + .map(|item_impl| item_impl.iter_constructors()) + .flatten() + .map(|constructor| { + let constructor_span = constructor.span(); + let constructor_ident = constructor.ident(); + let selector_id = constructor.composed_selector().into_be_u32().hex_padded_suffixed(); + let selector_bytes = constructor.composed_selector().hex_lits(); + let input_bindings = generator::input_bindings(constructor.inputs()); + let input_tuple_type = generator::input_types_tuple(constructor.inputs()); + let input_tuple_bindings = generator::input_bindings_tuple(constructor.inputs()); + quote_spanned!(constructor_span=> + impl ::ink_lang::reflect::DispatchableConstructorInfo<#selector_id> for #storage_ident { + type Input = #input_tuple_type; + type Storage = #storage_ident; - /// Generates one match arm of the dispatch `scale::Decode` implementation. - /// - /// # Note - /// - /// There is one match arm per ink! constructor or message for the dispatch - /// `scale::Decode` implementation. - fn generate_dispatch_variant_decode( - &self, - cws: ir::CallableWithSelector<'_, C>, - ) -> TokenStream2 - where - C: ir::Callable, - { - let selector_bytes = cws.composed_selector().hex_lits(); - let variant_ident = self.generate_dispatch_variant_ident(cws); - let variant_types = cws.callable().inputs().map(|arg| &arg.ty); - quote! { - [ #( #selector_bytes ),* ] => { - ::core::result::Result::Ok(Self::#variant_ident( - #( - <#variant_types as ::scale::Decode>::decode(input)? - ),* - )) - } - } + const CALLABLE: fn(Self::Input) -> Self::Storage = |#input_tuple_bindings| { + #storage_ident::#constructor_ident( #( #input_bindings ),* ) + }; + const SELECTOR: [::core::primitive::u8; 4usize] = [ #( #selector_bytes ),* ]; + const LABEL: &'static ::core::primitive::str = ::core::stringify!(#constructor_ident); + } + ) + }); + quote_spanned!(span=> + #( #constructor_infos )* + ) } - /// Generates one match arm of the dispatch variant enum. - /// - /// # Note + /// Generate code for the [`ink_lang::DispatchableConstructorInfo`] trait implementations. /// - /// There is one match arm per ink! constructor or message for the dispatch - /// `scale::Decode` implementation. - fn generate_dispatch_variant_arm( - &self, - cws: ir::CallableWithSelector<'_, C>, - ) -> TokenStream2 - where - C: ir::Callable, - { - let input_types = cws.callable().inputs().map(|arg| &arg.ty); - let variant_ident = self.generate_dispatch_variant_ident(cws); - quote! { - #variant_ident(#(#input_types),*) - } - } + /// These trait implementations store relevant dispatch information for every + /// dispatchable ink! constructor of the ink! smart contract. + fn generate_dispatchable_message_infos(&self) -> TokenStream2 { + let span = self.contract.module().storage().span(); + let storage_ident = self.contract.module().storage().ident(); + let inherent_message_infos = self + .contract + .module() + .impls() + .filter(|item_impl| item_impl.trait_path().is_none()) + .map(|item_impl| item_impl.iter_messages()) + .flatten() + .map(|message| { + let message_span = message.span(); + let message_ident = message.ident(); + let payable = message.is_payable(); + let mutates = message.receiver().is_ref_mut(); + let selector_id = message.composed_selector().into_be_u32().hex_padded_suffixed(); + let selector_bytes = message.composed_selector().hex_lits(); + let output_tuple_type = message + .output() + .map(quote::ToTokens::to_token_stream) + .unwrap_or_else(|| quote! { () }); + let input_bindings = generator::input_bindings(message.inputs()); + let input_tuple_type = generator::input_types_tuple(message.inputs()); + let input_tuple_bindings = generator::input_bindings_tuple(message.inputs()); + quote_spanned!(message_span=> + impl ::ink_lang::reflect::DispatchableMessageInfo<#selector_id> for #storage_ident { + type Input = #input_tuple_type; + type Output = #output_tuple_type; + type Storage = #storage_ident; - /// Returns `true` if all ink! messages of `self` deny payments. - /// - /// # Note - /// - /// This information is used to produce better code in this scenario. - fn all_messages_deny_payment(&self) -> bool { - self.contract + const CALLABLE: fn(&mut Self::Storage, Self::Input) -> Self::Output = + |storage, #input_tuple_bindings| { + #storage_ident::#message_ident( storage #( , #input_bindings )* ) + }; + const SELECTOR: [::core::primitive::u8; 4usize] = [ #( #selector_bytes ),* ]; + const PAYABLE: ::core::primitive::bool = #payable; + const MUTATES: ::core::primitive::bool = #mutates; + const LABEL: &'static ::core::primitive::str = ::core::stringify!(#message_ident); + } + ) + }); + let trait_message_infos = self + .contract .module() .impls() - .flat_map(ir::ItemImpl::iter_messages) - .all(|message| !message.is_payable()) + .filter_map(|item_impl| { + item_impl + .trait_path() + .map(|trait_path| { + let trait_ident = item_impl.trait_ident().expect( + "must have an ink! trait identifier if it is an ink! trait implementation" + ); + iter::repeat((trait_ident, trait_path)).zip(item_impl.iter_messages()) + }) + }) + .flatten() + .map(|((trait_ident, trait_path), message)| { + let message_span = message.span(); + let message_ident = message.ident(); + let mutates = message.receiver().is_ref_mut(); + let local_id = message.local_id().hex_padded_suffixed(); + let payable = quote! {{ + <<::ink_lang::reflect::TraitDefinitionRegistry<<#storage_ident as ::ink_lang::reflect::ContractEnv>::Env> + as #trait_path>::__ink_TraitInfo + as ::ink_lang::reflect::TraitMessageInfo<#local_id>>::PAYABLE + }}; + let selector = quote! {{ + <<::ink_lang::reflect::TraitDefinitionRegistry<<#storage_ident as ::ink_lang::reflect::ContractEnv>::Env> + as #trait_path>::__ink_TraitInfo + as ::ink_lang::reflect::TraitMessageInfo<#local_id>>::SELECTOR + }}; + let selector_id = quote! {{ + ::core::primitive::u32::from_be_bytes(#selector) + }}; + let output_tuple_type = message + .output() + .map(quote::ToTokens::to_token_stream) + .unwrap_or_else(|| quote! { () }); + let input_bindings = generator::input_bindings(message.inputs()); + let input_tuple_type = generator::input_types_tuple(message.inputs()); + let input_tuple_bindings = generator::input_bindings_tuple(message.inputs()); + let label = format!("{}::{}", trait_ident, message_ident); + quote_spanned!(message_span=> + impl ::ink_lang::reflect::DispatchableMessageInfo<#selector_id> for #storage_ident { + type Input = #input_tuple_type; + type Output = #output_tuple_type; + type Storage = #storage_ident; + + const CALLABLE: fn(&mut Self::Storage, Self::Input) -> Self::Output = + |storage, #input_tuple_bindings| { + #storage_ident::#message_ident( storage #( , #input_bindings )* ) + }; + const SELECTOR: [::core::primitive::u8; 4usize] = #selector; + const PAYABLE: ::core::primitive::bool = #payable; + const MUTATES: ::core::primitive::bool = #mutates; + const LABEL: &'static ::core::primitive::str = #label; + } + ) + }); + quote_spanned!(span=> + #( #inherent_message_infos )* + #( #trait_message_infos )* + ) } - /// Generates one match arm of the dispatch message for the `execute` implementation. - /// - /// # Note + /// Generates code for the entry points of the root ink! smart contract. /// - /// This is basically the code per ink! message that is going to be executed after - /// the dispatch has already taken place. - fn generate_dispatch_execute_message_arm( - &self, - cws: ir::CallableWithSelector<'_, ir::Message>, - ) -> TokenStream2 { + /// This generates the `deploy` and `call` functions with which the smart + /// contract runtime mainly interacts with the ink! smart contract. + fn generate_entry_points(&self, message_spans: &[proc_macro2::Span]) -> TokenStream2 { + let span = self.contract.module().storage().span(); let storage_ident = self.contract.module().storage().ident(); - let ident = self.generate_dispatch_variant_ident(cws); - let message = cws.callable(); - let arg_pats = message.inputs().map(|arg| &arg.pat).collect::>(); - let arg_inputs = if arg_pats.len() == 1 { - quote! { #(#arg_pats),* } - } else { - quote! { ( #(#arg_pats),* ) } - }; - let (mut_mod, msg_trait, exec_fn) = match message.receiver() { - ir::Receiver::RefMut => { - ( - Some(quote! { mut }), - quote! { MessageMut }, - quote! { execute_message_mut }, - ) - } - ir::Receiver::Ref => { - (None, quote! { MessageRef }, quote! { execute_message }) + let any_message_accept_payment = + self.any_message_accepts_payment_expr(message_spans); + quote_spanned!(span=> + #[cfg(not(test))] + #[no_mangle] + fn deploy() { + ::ink_env::decode_input::< + <#storage_ident as ::ink_lang::reflect::ContractConstructorDecoder>::Type>() + .map_err(|_| ::ink_lang::reflect::DispatchError::CouldNotReadInput) + .and_then(|decoder| { + <<#storage_ident as ::ink_lang::reflect::ContractConstructorDecoder>::Type + as ::ink_lang::reflect::ExecuteDispatchable>::execute_dispatchable(decoder) + }) + .unwrap_or_else(|error| { + ::core::panic!("dispatching ink! constructor failed: {}", error) + }) } - }; - let selector_id = cws.composed_selector().into_be_u32().hex_padded_suffixed(); - let namespace = Self::dispatch_trait_impl_namespace(ir::CallableKind::Message); - // If all ink! messages deny payment we can move the payment check to before - // the message dispatch which is more efficient. - let accepts_payments = cws.is_payable() || self.all_messages_deny_payment(); - let is_dynamic_storage_allocation_enabled = self - .contract - .config() - .is_dynamic_storage_allocator_enabled(); - quote! { - Self::#ident(#(#arg_pats),*) => { - ::ink_lang::#exec_fn::<<#storage_ident as ::ink_lang::ContractEnv>::Env, #namespace::<#selector_id>, _>( - ::ink_lang::AcceptsPayments(#accepts_payments), - ::ink_lang::EnablesDynamicStorageAllocator(#is_dynamic_storage_allocation_enabled), - move |state: &#mut_mod #storage_ident| { - <#namespace::<#selector_id> as ::ink_lang::#msg_trait>::CALLABLE( - state, #arg_inputs - ) - } - ) + + #[cfg(not(test))] + #[no_mangle] + fn call() { + if !#any_message_accept_payment { + ::ink_lang::codegen::deny_payment::<<#storage_ident as ::ink_lang::reflect::ContractEnv>::Env>() + .unwrap_or_else(|error| ::core::panic!("{}", error)) + } + ::ink_env::decode_input::< + <#storage_ident as ::ink_lang::reflect::ContractMessageDecoder>::Type>() + .map_err(|_| ::ink_lang::reflect::DispatchError::CouldNotReadInput) + .and_then(|decoder| { + <<#storage_ident as ::ink_lang::reflect::ContractMessageDecoder>::Type + as ::ink_lang::reflect::ExecuteDispatchable>::execute_dispatchable(decoder) + }) + .unwrap_or_else(|error| { + ::core::panic!("dispatching ink! message failed: {}", error) + }) } - } + ) } - /// Returns an iterator over all ink! messages of the ink! contract. - fn contract_messages( + /// Generates code for the ink! constructor decoder type of the ink! smart contract. + /// + /// This type can be used in order to decode the input bytes received by a call to `deploy` + /// into one of the available dispatchable ink! constructors and their arguments. + fn generate_constructor_decoder_type( &self, - ) -> impl Iterator> { - self.contract - .module() - .impls() - .map(|impl_item| impl_item.iter_messages()) - .flatten() - } + constructor_spans: &[proc_macro2::Span], + ) -> TokenStream2 { + assert_eq!(constructor_spans.len(), self.query_amount_constructors()); - /// Generates the entire dispatch variant enum for all ink! messages. - fn generate_message_dispatch_enum(&self) -> TokenStream2 { + /// Expands into the token sequence to represent the + /// input type of the ink! constructor at the given index. + fn expand_constructor_input( + span: proc_macro2::Span, + storage_ident: &syn::Ident, + constructor_index: usize, + ) -> TokenStream2 { + quote_spanned!(span=> + <#storage_ident as ::ink_lang::reflect::DispatchableConstructorInfo<{ + <#storage_ident as ::ink_lang::reflect::ContractDispatchableConstructors<{ + <#storage_ident as ::ink_lang::reflect::ContractAmountDispatchables>::CONSTRUCTORS + }>>::IDS[#constructor_index] + }>>::Input + ) + } + + /// Returns the n-th ink! constructor identifier for the decoder type. + fn constructor_variant_ident(n: usize) -> syn::Ident { + quote::format_ident!("Constructor{}", n) + } + + let span = self.contract.module().storage().span(); let storage_ident = self.contract.module().storage().ident(); - let message_variants = self - .contract_messages() - .map(|message| self.generate_dispatch_variant_arm(message)); - let decode_message = self - .contract_messages() - .map(|message| self.generate_dispatch_variant_decode(message)); - let execute_variants = self - .contract_messages() - .map(|message| self.generate_dispatch_execute_message_arm(message)); - quote! { - const _: () = { - #[doc(hidden)] - pub enum __ink_MessageDispatchEnum { - #( #message_variants ),* + let count_constructors = self.query_amount_constructors(); + let constructors_variants = (0..count_constructors).map(|index| { + let constructor_span = constructor_spans[index]; + let constructor_ident = constructor_variant_ident(index); + let constructor_input = + expand_constructor_input(constructor_span, storage_ident, index); + quote_spanned!(constructor_span=> + #constructor_ident(#constructor_input) + ) + }); + let constructor_match = (0..count_constructors).map(|index| { + let constructor_span = constructor_spans[index]; + let constructor_ident = constructor_variant_ident(index); + let constructor_selector = quote_spanned!(span=> + <#storage_ident as ::ink_lang::reflect::DispatchableConstructorInfo<{ + <#storage_ident as ::ink_lang::reflect::ContractDispatchableConstructors<{ + <#storage_ident as ::ink_lang::reflect::ContractAmountDispatchables>::CONSTRUCTORS + }>>::IDS[#index] + }>>::SELECTOR + ); + let constructor_input = expand_constructor_input(constructor_span, storage_ident, index); + quote_spanned!(constructor_span=> + #constructor_selector => { + ::core::result::Result::Ok(Self::#constructor_ident( + <#constructor_input as ::scale::Decode>::decode(input) + .map_err(|_| ::ink_lang::reflect::DispatchError::InvalidParameters)? + )) } - - impl ::ink_lang::MessageDispatcher for #storage_ident { - type Type = __ink_MessageDispatchEnum; + ) + }); + let constructor_execute = (0..count_constructors).map(|index| { + let constructor_span = constructor_spans[index]; + let constructor_ident = constructor_variant_ident(index); + let constructor_callable = quote_spanned!(constructor_span=> + <#storage_ident as ::ink_lang::reflect::DispatchableConstructorInfo<{ + <#storage_ident as ::ink_lang::reflect::ContractDispatchableConstructors<{ + <#storage_ident as ::ink_lang::reflect::ContractAmountDispatchables>::CONSTRUCTORS + }>>::IDS[#index] + }>>::CALLABLE + ); + let is_dynamic_storage_allocation_enabled = self + .contract + .config() + .is_dynamic_storage_allocator_enabled(); + quote_spanned!(constructor_span=> + Self::#constructor_ident(input) => { + ::ink_lang::codegen::execute_constructor::<#storage_ident, _>( + ::ink_lang::codegen::ExecuteConstructorConfig { + dynamic_storage_alloc: #is_dynamic_storage_allocation_enabled + }, + move || { #constructor_callable(input) } + ) + } + ) + }); + quote_spanned!(span=> + const _: () = { + #[allow(non_camel_case_types)] + pub enum __ink_ConstructorDecoder { + #( #constructors_variants ),* } - impl ::scale::Decode for __ink_MessageDispatchEnum { - fn decode(input: &mut I) -> ::core::result::Result { - match <[::core::primitive::u8; 4usize] as ::scale::Decode>::decode(input)? { - #( #decode_message )* + impl ::ink_lang::reflect::DecodeDispatch for __ink_ConstructorDecoder { + fn decode_dispatch(input: &mut I) + -> ::core::result::Result + where + I: ::scale::Input, + { + match <[::core::primitive::u8; 4usize] as ::scale::Decode>::decode(input) + .map_err(|_| ::ink_lang::reflect::DispatchError::InvalidSelector)? + { + #( #constructor_match , )* _invalid => ::core::result::Result::Err( - <::scale::Error as ::core::convert::From<&'static ::core::primitive::str>>::from( - "encountered unknown ink! message selector" - ) + ::ink_lang::reflect::DispatchError::UnknownSelector ) } } } - impl ::ink_lang::Execute for __ink_MessageDispatchEnum { - fn execute(self) -> ::core::result::Result<(), ::ink_lang::DispatchError> { + impl ::scale::Decode for __ink_ConstructorDecoder { + fn decode(input: &mut I) -> ::core::result::Result + where + I: ::scale::Input, + { + ::decode_dispatch(input) + .map_err(::core::convert::Into::into) + } + } + + impl ::ink_lang::reflect::ExecuteDispatchable for __ink_ConstructorDecoder { + fn execute_dispatchable(self) -> ::core::result::Result<(), ::ink_lang::reflect::DispatchError> { match self { - #( #execute_variants )* + #( #constructor_execute ),* } } } + + impl ::ink_lang::reflect::ContractConstructorDecoder for #storage_ident { + type Type = __ink_ConstructorDecoder; + } }; - } + ) } - /// Generates one match arm of the dispatch constructor for the `execute` implementation. - /// - /// # Note + /// Generates code for the ink! message decoder type of the ink! smart contract. /// - /// This is basically the code per ink! constructor that is going to be executed after - /// the dispatch has already taken place. - fn generate_dispatch_execute_constructor_arm( + /// This type can be used in order to decode the input bytes received by a call to `call` + /// into one of the available dispatchable ink! messages and their arguments. + fn generate_message_decoder_type( &self, - cws: ir::CallableWithSelector<'_, ir::Constructor>, + message_spans: &[proc_macro2::Span], ) -> TokenStream2 { - let ident = self.generate_dispatch_variant_ident(cws); - let constructor = cws.callable(); - let arg_pats = constructor.inputs().map(|arg| &arg.pat).collect::>(); - let arg_inputs = if arg_pats.len() == 1 { - quote! { #(#arg_pats),* } - } else { - quote! { ( #(#arg_pats),* ) } - }; - let selector_id = cws.composed_selector().into_be_u32().hex_padded_suffixed(); - let namespace = - Self::dispatch_trait_impl_namespace(ir::CallableKind::Constructor); - let is_dynamic_storage_allocation_enabled = self - .contract - .config() - .is_dynamic_storage_allocator_enabled(); - quote! { - Self::#ident(#(#arg_pats),*) => { - ::ink_lang::execute_constructor::<#namespace::<#selector_id>, _>( - ::ink_lang::EnablesDynamicStorageAllocator(#is_dynamic_storage_allocation_enabled), - move || { - <#namespace::<#selector_id> as ::ink_lang::Constructor>::CALLABLE( - #arg_inputs - ) - } - ) - } + assert_eq!(message_spans.len(), self.query_amount_messages()); + + /// Expands into the token sequence to represent the + /// input type of the ink! message at the given index. + fn expand_message_input( + span: proc_macro2::Span, + storage_ident: &syn::Ident, + message_index: usize, + ) -> TokenStream2 { + quote_spanned!(span=> + <#storage_ident as ::ink_lang::reflect::DispatchableMessageInfo<{ + <#storage_ident as ::ink_lang::reflect::ContractDispatchableMessages<{ + <#storage_ident as ::ink_lang::reflect::ContractAmountDispatchables>::MESSAGES + }>>::IDS[#message_index] + }>>::Input + ) } - } - /// Returns an iterator over all ink! constructors of the ink! contract. - fn contract_constructors( - &self, - ) -> impl Iterator> { - self.contract - .module() - .impls() - .map(|impl_item| impl_item.iter_constructors()) - .flatten() - } + /// Returns the n-th ink! message identifier for the decoder type. + fn message_variant_ident(n: usize) -> syn::Ident { + quote::format_ident!("Message{}", n) + } - /// Generates the entire dispatch variant enum for all ink! messages. - fn generate_constructor_dispatch_enum(&self) -> TokenStream2 { + let span = self.contract.module().storage().span(); let storage_ident = self.contract.module().storage().ident(); - let message_variants = self - .contract_constructors() - .map(|message| self.generate_dispatch_variant_arm(message)); - let decode_message = self - .contract_constructors() - .map(|message| self.generate_dispatch_variant_decode(message)); - let execute_variants = self - .contract_constructors() - .map(|cws| self.generate_dispatch_execute_constructor_arm(cws)); - quote! { - const _: () = { - #[doc(hidden)] - pub enum __ink_ConstructorDispatchEnum { - #( #message_variants ),* + let count_messages = self.query_amount_messages(); + let message_variants = (0..count_messages).map(|index| { + let message_span = message_spans[index]; + let message_ident = message_variant_ident(index); + let message_input = expand_message_input(message_span, storage_ident, index); + quote_spanned!(message_span=> + #message_ident(#message_input) + ) + }); + let message_match = (0..count_messages).map(|index| { + let message_span = message_spans[index]; + let message_ident = message_variant_ident(index); + let message_selector = quote_spanned!(span=> + <#storage_ident as ::ink_lang::reflect::DispatchableMessageInfo<{ + <#storage_ident as ::ink_lang::reflect::ContractDispatchableMessages<{ + <#storage_ident as ::ink_lang::reflect::ContractAmountDispatchables>::MESSAGES + }>>::IDS[#index] + }>>::SELECTOR + ); + let message_input = expand_message_input(message_span, storage_ident, index); + quote_spanned!(message_span=> + #message_selector => { + ::core::result::Result::Ok(Self::#message_ident( + <#message_input as ::scale::Decode>::decode(input) + .map_err(|_| ::ink_lang::reflect::DispatchError::InvalidParameters)? + )) + } + ) + }); + let any_message_accept_payment = + self.any_message_accepts_payment_expr(message_spans); + let message_execute = (0..count_messages).map(|index| { + let message_span = message_spans[index]; + let message_ident = message_variant_ident(index); + let message_callable = quote_spanned!(message_span=> + <#storage_ident as ::ink_lang::reflect::DispatchableMessageInfo<{ + <#storage_ident as ::ink_lang::reflect::ContractDispatchableMessages<{ + <#storage_ident as ::ink_lang::reflect::ContractAmountDispatchables>::MESSAGES + }>>::IDS[#index] + }>>::CALLABLE + ); + let message_output = quote_spanned!(message_span=> + <#storage_ident as ::ink_lang::reflect::DispatchableMessageInfo<{ + <#storage_ident as ::ink_lang::reflect::ContractDispatchableMessages<{ + <#storage_ident as ::ink_lang::reflect::ContractAmountDispatchables>::MESSAGES + }>>::IDS[#index] + }>>::Output + ); + let accepts_payment = quote_spanned!(message_span=> + false || + !#any_message_accept_payment || + <#storage_ident as ::ink_lang::reflect::DispatchableMessageInfo<{ + <#storage_ident as ::ink_lang::reflect::ContractDispatchableMessages<{ + <#storage_ident as ::ink_lang::reflect::ContractAmountDispatchables>::MESSAGES + }>>::IDS[#index] + }>>::PAYABLE + ); + let mutates_storage = quote_spanned!(message_span=> + <#storage_ident as ::ink_lang::reflect::DispatchableMessageInfo<{ + <#storage_ident as ::ink_lang::reflect::ContractDispatchableMessages<{ + <#storage_ident as ::ink_lang::reflect::ContractAmountDispatchables>::MESSAGES + }>>::IDS[#index] + }>>::MUTATES + ); + let may_revert = ::core::cfg!(not(feature = "ink-as-dependency")); + let is_dynamic_storage_allocation_enabled = self + .contract + .config() + .is_dynamic_storage_allocator_enabled(); + quote_spanned!(message_span=> + Self::#message_ident(input) => { + ::ink_lang::codegen::execute_message::< + #storage_ident, + #message_output, + _ + >( + ::ink_lang::codegen::ExecuteMessageConfig { + payable: #accepts_payment, + mutates: #mutates_storage, + may_revert: #may_revert, + dynamic_storage_alloc: #is_dynamic_storage_allocation_enabled, + }, + move |storage: &mut #storage_ident| { #message_callable(storage, input) } + ) } + ) + }); - impl ::ink_lang::ConstructorDispatcher for #storage_ident { - type Type = __ink_ConstructorDispatchEnum; + quote_spanned!(span=> + const _: () = { + #[allow(non_camel_case_types)] + // #[derive(::core::fmt::Debug, ::core::cmp::PartialEq)] + pub enum __ink_MessageDecoder { + #( #message_variants ),* } - impl ::scale::Decode for __ink_ConstructorDispatchEnum { - fn decode(input: &mut I) -> ::core::result::Result { - match <[::core::primitive::u8; 4usize] as ::scale::Decode>::decode(input)? { - #( #decode_message )* + impl ::ink_lang::reflect::DecodeDispatch for __ink_MessageDecoder { + fn decode_dispatch(input: &mut I) + -> ::core::result::Result + where + I: ::scale::Input, + { + match <[::core::primitive::u8; 4usize] as ::scale::Decode>::decode(input) + .map_err(|_| ::ink_lang::reflect::DispatchError::InvalidSelector)? + { + #( #message_match , )* _invalid => ::core::result::Result::Err( - <::scale::Error as ::core::convert::From<&'static ::core::primitive::str>>::from( - "encountered unknown ink! constructor selector" - ) + ::ink_lang::reflect::DispatchError::UnknownSelector ) } } } - impl ::ink_lang::Execute for __ink_ConstructorDispatchEnum { - fn execute(self) -> ::core::result::Result<(), ::ink_lang::DispatchError> { + impl ::scale::Decode for __ink_MessageDecoder { + fn decode(input: &mut I) -> ::core::result::Result + where + I: ::scale::Input, + { + ::decode_dispatch(input) + .map_err(::core::convert::Into::into) + } + } + + impl ::ink_lang::reflect::ExecuteDispatchable for __ink_MessageDecoder { + #[allow(clippy::nonminimal_bool)] + fn execute_dispatchable(self) -> ::core::result::Result<(), ::ink_lang::reflect::DispatchError> { match self { - #( #execute_variants )* + #( #message_execute ),* } } } + + impl ::ink_lang::reflect::ContractMessageDecoder for #storage_ident { + type Type = __ink_MessageDecoder; + } }; - } + ) + } + + /// Generates code to express if any dispatchable ink! message accepts payment. + /// + /// This information can be used to speed-up dispatch since denying of payment + /// can be generalized to work before dispatch happens if none of the ink! messages + /// accept payment anyways. + fn any_message_accepts_payment_expr( + &self, + message_spans: &[proc_macro2::Span], + ) -> TokenStream2 { + assert_eq!(message_spans.len(), self.query_amount_messages()); + + let span = self.contract.module().storage().span(); + let storage_ident = self.contract.module().storage().ident(); + let count_messages = self.query_amount_messages(); + let message_is_payable = (0..count_messages).map(|index| { + let message_span = message_spans[index]; + quote_spanned!(message_span=> + <#storage_ident as ::ink_lang::reflect::DispatchableMessageInfo<{ + <#storage_ident as ::ink_lang::reflect::ContractDispatchableMessages<{ + <#storage_ident as ::ink_lang::reflect::ContractAmountDispatchables>::MESSAGES + }>>::IDS[#index] + }>>::PAYABLE + ) + }); + quote_spanned!(span=> + { false #( || #message_is_payable )* } + ) } } diff --git a/crates/lang/codegen/src/generator/env.rs b/crates/lang/codegen/src/generator/env.rs index bb9682f1cd1..e1396b016f7 100644 --- a/crates/lang/codegen/src/generator/env.rs +++ b/crates/lang/codegen/src/generator/env.rs @@ -28,17 +28,17 @@ impl GenerateCode for Env<'_> { let env = self.contract.config().env(); let storage_ident = self.contract.module().storage().ident(); quote! { - impl ::ink_lang::ContractEnv for #storage_ident { + impl ::ink_lang::reflect::ContractEnv for #storage_ident { type Env = #env; } - type Environment = <#storage_ident as ::ink_lang::ContractEnv>::Env; + type Environment = <#storage_ident as ::ink_lang::reflect::ContractEnv>::Env; - type AccountId = <<#storage_ident as ::ink_lang::ContractEnv>::Env as ::ink_env::Environment>::AccountId; - type Balance = <<#storage_ident as ::ink_lang::ContractEnv>::Env as ::ink_env::Environment>::Balance; - type Hash = <<#storage_ident as ::ink_lang::ContractEnv>::Env as ::ink_env::Environment>::Hash; - type Timestamp = <<#storage_ident as ::ink_lang::ContractEnv>::Env as ::ink_env::Environment>::Timestamp; - type BlockNumber = <<#storage_ident as ::ink_lang::ContractEnv>::Env as ::ink_env::Environment>::BlockNumber; + type AccountId = <<#storage_ident as ::ink_lang::reflect::ContractEnv>::Env as ::ink_env::Environment>::AccountId; + type Balance = <<#storage_ident as ::ink_lang::reflect::ContractEnv>::Env as ::ink_env::Environment>::Balance; + type Hash = <<#storage_ident as ::ink_lang::reflect::ContractEnv>::Env as ::ink_env::Environment>::Hash; + type Timestamp = <<#storage_ident as ::ink_lang::reflect::ContractEnv>::Env as ::ink_env::Environment>::Timestamp; + type BlockNumber = <<#storage_ident as ::ink_lang::reflect::ContractEnv>::Env as ::ink_env::Environment>::BlockNumber; } } } diff --git a/crates/lang/codegen/src/generator/events.rs b/crates/lang/codegen/src/generator/events.rs index a73e22688da..3401da156bb 100644 --- a/crates/lang/codegen/src/generator/events.rs +++ b/crates/lang/codegen/src/generator/events.rs @@ -12,11 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::{ - generator, - GenerateCode, - GenerateCodeUsing as _, -}; +use crate::GenerateCode; use derive_more::From; use proc_macro2::{ Span, @@ -61,19 +57,16 @@ impl<'a> Events<'a> { /// them first into the automatically generated base trait of the contract. fn generate_emit_event_trait_impl(&self) -> TokenStream2 { let storage_ident = &self.contract.module().storage().ident(); - let no_cross_calling_cfg = - self.generate_code_using::(); quote! { const _: () = { - #no_cross_calling_cfg - impl<'a> ::ink_lang::EmitEvent<#storage_ident> for ::ink_lang::EnvAccess<'a, Environment> { + impl<'a> ::ink_lang::codegen::EmitEvent<#storage_ident> for ::ink_lang::EnvAccess<'a, Environment> { fn emit_event(self, event: E) where - E: Into<<#storage_ident as ::ink_lang::BaseEvent>::Type>, + E: Into<<#storage_ident as ::ink_lang::reflect::ContractEventBase>::Type>, { ::ink_env::emit_event::< Environment, - <#storage_ident as ::ink_lang::BaseEvent>::Type + <#storage_ident as ::ink_lang::reflect::ContractEventBase>::Type >(event.into()); } } @@ -86,8 +79,6 @@ impl<'a> Events<'a> { /// serialized and emitted to apply their unique event discriminant (ID). fn generate_event_base(&self) -> TokenStream2 { let storage_ident = &self.contract.module().storage().ident(); - let no_cross_calling_cfg = - self.generate_code_using::(); let event_idents = self .contract .module() @@ -97,21 +88,18 @@ impl<'a> Events<'a> { let base_event_ident = proc_macro2::Ident::new("__ink_EventBase", Span::call_site()); quote! { - #no_cross_calling_cfg #[derive(::scale::Encode, ::scale::Decode)] pub enum #base_event_ident { #( #event_idents(#event_idents), )* } - #no_cross_calling_cfg const _: () = { - impl ::ink_lang::BaseEvent for #storage_ident { + impl ::ink_lang::reflect::ContractEventBase for #storage_ident { type Type = #base_event_ident; } }; #( - #no_cross_calling_cfg const _: () = { impl From<#event_idents> for #base_event_ident { fn from(event: #event_idents) -> Self { @@ -127,7 +115,6 @@ impl<'a> Events<'a> { const AMOUNT: usize = 0; } - #no_cross_calling_cfg impl ::ink_env::Topics for #base_event_ident { type RemainingTopics = __ink_UndefinedAmountOfTopics; @@ -154,55 +141,34 @@ impl<'a> Events<'a> { /// Generate checks to guard against too many topics in event definitions. fn generate_topics_guard(&self, event: &ir::Event) -> TokenStream2 { + let span = event.span(); let storage_ident = self.contract.module().storage().ident(); let event_ident = event.ident(); let len_topics = event.fields().filter(|event| event.is_topic).count(); - let span = event.span(); + let max_len_topics = quote_spanned!(span=> + <<#storage_ident as ::ink_lang::reflect::ContractEnv>::Env + as ::ink_env::Environment>::MAX_EVENT_TOPICS + ); quote_spanned!(span=> - const _: () = { - #[allow(non_camel_case_types)] - pub enum __ink_CheckSatisfied {} - pub enum EventTopicsWithinBounds {} - impl ::ink_lang::True for __ink_CheckSatisfied {} - #[doc(hidden)] - pub trait CompliesWithTopicLimit {} - impl CompliesWithTopicLimit for __ink_CheckSatisfied {} - - #[allow(non_camel_case_types)] - pub trait __ink_RenameBool { - type Type; - } - impl __ink_RenameBool for [(); true as usize] { - type Type = __ink_CheckSatisfied; - } - impl __ink_RenameBool for [(); false as usize] { - type Type = #event_ident; - } - - #[allow(non_upper_case_globals)] - const __ink_MAX_EVENT_TOPICS: usize = < - <#storage_ident as ::ink_lang::ContractEnv>::Env as ::ink_env::Environment - >::MAX_EVENT_TOPICS; + impl ::ink_lang::codegen::EventLenTopics for #event_ident { + type LenTopics = ::ink_lang::codegen::EventTopics<#len_topics>; + } - fn __ink_ensure_max_event_topics(_: T) - where - T: __ink_RenameBool, - ::Type: CompliesWithTopicLimit, - {} - let _ = __ink_ensure_max_event_topics::<[(); (#len_topics <= __ink_MAX_EVENT_TOPICS) as usize]>; - }; + const _: () = ::ink_lang::codegen::utils::consume_type::< + ::ink_lang::codegen::EventRespectsTopicLimit< + #event_ident, + { #max_len_topics }, + > + >(); ) } /// Generates the guard code that protects against having too many topics defined on an ink! event. fn generate_topic_guards(&'a self) -> impl Iterator + 'a { - let no_cross_calling_cfg = - self.generate_code_using::(); self.contract.module().events().map(move |event| { let span = event.span(); let topics_guard = self.generate_topics_guard(event); quote_spanned!(span => - #no_cross_calling_cfg #topics_guard ) }) @@ -210,8 +176,6 @@ impl<'a> Events<'a> { /// Generates the `Topics` trait implementations for the user defined events. fn generate_topics_impls(&'a self) -> impl Iterator + 'a { - let no_cross_calling_cfg = - self.generate_code_using::(); let contract_ident = self.contract.module().storage().ident(); self.contract.module().events().map(move |event| { let span = event.span(); @@ -258,7 +222,6 @@ impl<'a> Events<'a> { n => quote_spanned!(span=> [::ink_env::topics::state::HasRemainingTopics; #n]), }; quote_spanned!(span => - #no_cross_calling_cfg const _: () = { impl ::ink_env::Topics for #event_ident { type RemainingTopics = #remaining_topics_ty; @@ -287,8 +250,6 @@ impl<'a> Events<'a> { /// Generates all the user defined event struct definitions. fn generate_event_structs(&'a self) -> impl Iterator + 'a { - let no_cross_calling_cfg = - self.generate_code_using::(); self.contract.module().events().map(move |event| { let span = event.span(); let ident = event.ident(); @@ -305,7 +266,6 @@ impl<'a> Events<'a> { ) }); quote_spanned!(span => - #no_cross_calling_cfg #( #attrs )* #[derive(scale::Encode, scale::Decode)] pub struct #ident { diff --git a/crates/lang/codegen/src/generator/item_impls.rs b/crates/lang/codegen/src/generator/item_impls.rs index c65a4b1d557..28fe7fb6cc7 100644 --- a/crates/lang/codegen/src/generator/item_impls.rs +++ b/crates/lang/codegen/src/generator/item_impls.rs @@ -12,14 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::{ - generator, - GenerateCode, - GenerateCodeUsing as _, -}; +use core::iter; + +use crate::GenerateCode; use derive_more::From; use heck::CamelCase as _; -use ir::Callable as _; +use ir::{ + Callable as _, + HexLiteral, +}; use proc_macro2::TokenStream as TokenStream2; use quote::{ format_ident, @@ -43,36 +44,140 @@ impl GenerateCode for ItemImpls<'_> { .module() .impls() .map(|item_impl| self.generate_item_impl(item_impl)); - let no_cross_calling_cfg = - self.generate_code_using::(); + let inout_guards = self.generate_input_output_guards(); + let trait_message_property_guards = self.generate_trait_message_property_guards(); + let use_emit_event = + self.contract.module().events().next().is_some().then(|| { + // Required to make `self.env().emit_event(..)` syntax available. + quote! { use ::ink_lang::codegen::EmitEvent as _; } + }); quote! { - #no_cross_calling_cfg const _: () = { - use ::ink_lang::{Env as _, EmitEvent as _, StaticEnv as _}; + // Required to make `self.env()` and `Self::env()` syntax available. + use ::ink_lang::codegen::{Env as _, StaticEnv as _}; + #use_emit_event #( #item_impls )* + #inout_guards + #trait_message_property_guards }; } } } impl ItemImpls<'_> { - /// Generates the code for the given ink! constructor within a trait implementation block. - fn generate_trait_constructor(constructor: &ir::Constructor) -> TokenStream2 { - let span = constructor.span(); - let attrs = constructor.attrs(); - let vis = constructor.visibility(); - let ident = constructor.ident(); - let output_ident = format_ident!("{}Out", ident.to_string().to_camel_case()); - let inputs = constructor.inputs(); - let statements = constructor.statements(); - quote_spanned!(span => - type #output_ident = Self; + /// Generates code to guard annotated ink! trait message properties. + /// + /// These guarded properties include `selector` and `payable`. + /// If an ink! trait message is annotated with `#[ink(payable)]` + /// or `#[ink(selector = ..)]` then code is generated to guard that + /// the given argument to `payable` or `selector` is equal to + /// what the associated ink! trait definition defines for the same + /// ink! message. + fn generate_trait_message_property_guards(&self) -> TokenStream2 { + let storage_span = self.contract.module().storage().span(); + let storage_ident = self.contract.module().storage().ident(); + let trait_message_guards = self + .contract + .module() + .impls() + .filter_map(|item_impl| item_impl.trait_path().map(|trait_path| { + iter::repeat(trait_path).zip(item_impl.iter_messages()) + })) + .flatten() + .map(|(trait_path, message)| { + let message_span = message.span(); + let message_local_id = message.local_id().hex_padded_suffixed(); + let message_guard_payable = message.is_payable().then(|| { + quote_spanned!(message_span=> + const _: ::ink_lang::codegen::TraitMessagePayable<{ + <<::ink_lang::reflect::TraitDefinitionRegistry<<#storage_ident as ::ink_lang::reflect::ContractEnv>::Env> + as #trait_path>::__ink_TraitInfo + as ::ink_lang::reflect::TraitMessageInfo<#message_local_id>>::PAYABLE + }> = ::ink_lang::codegen::TraitMessagePayable::; + ) + }); + let message_guard_selector = message.user_provided_selector().map(|selector| { + let given_selector = selector.into_be_u32().hex_padded_suffixed(); + quote_spanned!(message_span=> + const _: ::ink_lang::codegen::TraitMessageSelector<{ + ::core::primitive::u32::from_be_bytes( + <<::ink_lang::reflect::TraitDefinitionRegistry<<#storage_ident as ::ink_lang::reflect::ContractEnv>::Env> + as #trait_path>::__ink_TraitInfo + as ::ink_lang::reflect::TraitMessageInfo<#message_local_id>>::SELECTOR + ) + }> = ::ink_lang::codegen::TraitMessageSelector::<#given_selector>; + ) + }); + quote_spanned!(message_span=> + #message_guard_payable + #message_guard_selector + ) + }); + quote_spanned!(storage_span=> + #( #trait_message_guards )* + ) + } - #( #attrs )* - #vis fn #ident( #( #inputs ),* ) -> Self::#output_ident { - #( #statements )* - } + /// Generates code to assert that ink! input and output types meet certain properties. + fn generate_input_output_guards(&self) -> TokenStream2 { + let storage_span = self.contract.module().storage().span(); + let constructor_input_guards = self + .contract + .module() + .impls() + .map(|item_impl| item_impl.iter_constructors()) + .flatten() + .map(|constructor| { + let constructor_span = constructor.span(); + let constructor_inputs = constructor.inputs().map(|input| { + let span = input.span(); + let input_type = &*input.ty; + quote_spanned!(span=> + let _: () = ::ink_lang::codegen::utils::consume_type::< + ::ink_lang::codegen::DispatchInput<#input_type> + >(); + ) + }); + quote_spanned!(constructor_span=> + #( #constructor_inputs )* + ) + }); + let message_inout_guards = self + .contract + .module() + .impls() + .map(|item_impl| item_impl.iter_messages()) + .flatten() + .map(|message| { + let message_span = message.span(); + let message_inputs = message.inputs().map(|input| { + let span = input.span(); + let input_type = &*input.ty; + quote_spanned!(span=> + let _: () = ::ink_lang::codegen::utils::consume_type::< + ::ink_lang::codegen::DispatchInput<#input_type> + >(); + ) + }); + let message_output = message.output().map(|output_type| { + let span = output_type.span(); + quote_spanned!(span=> + let _: () = ::ink_lang::codegen::utils::consume_type::< + ::ink_lang::codegen::DispatchOutput<#output_type> + >(); + ) + }); + quote_spanned!(message_span=> + #( #message_inputs )* + #message_output + ) + }); + quote_spanned!(storage_span=> + const _: () = { + #( #constructor_input_guards )* + #( #message_inout_guards )* + }; ) } @@ -83,7 +188,7 @@ impl ItemImpls<'_> { let vis = message.visibility(); let receiver = message.receiver(); let ident = message.ident(); - let output_ident = format_ident!("{}Out", ident.to_string().to_camel_case()); + let output_ident = format_ident!("{}Output", ident.to_string().to_camel_case()); let inputs = message.inputs(); let output = message .output() @@ -107,46 +212,17 @@ impl ItemImpls<'_> { let messages = item_impl .iter_messages() .map(|cws| Self::generate_trait_message(cws.callable())); - let constructors = item_impl - .iter_constructors() - .map(|cws| Self::generate_trait_constructor(cws.callable())); - let other_items = item_impl - .items() - .iter() - .filter_map(ir::ImplItem::filter_map_other_item) - .map(ToTokens::to_token_stream); let trait_path = item_impl .trait_path() .expect("encountered missing trait path for trait impl block"); - let trait_ident = item_impl - .trait_ident() - .expect("encountered missing trait identifier for trait impl block"); let self_type = item_impl.self_type(); - let hash = ir::InkTrait::compute_verify_hash( - trait_ident, - item_impl.iter_constructors().map(|constructor| { - let ident = constructor.ident().clone(); - let len_inputs = constructor.inputs().count(); - (ident, len_inputs) - }), - item_impl.iter_messages().map(|message| { - let ident = message.ident().clone(); - let len_inputs = message.inputs().count() + 1; - let is_mut = message.receiver().is_ref_mut(); - (ident, len_inputs, is_mut) - }), - ); - let checksum = u32::from_be_bytes([hash[0], hash[1], hash[2], hash[3]]) as usize; quote_spanned!(span => - unsafe impl ::ink_lang::CheckedInkTrait<[(); #checksum]> for #self_type {} - #( #attrs )* impl #trait_path for #self_type { - type __ink_Checksum = [(); #checksum]; + type __ink_TraitInfo = <::ink_lang::reflect::TraitDefinitionRegistry + as #trait_path>::__ink_TraitInfo; - #( #constructors )* #( #messages )* - #( #other_items )* } ) } @@ -219,10 +295,8 @@ impl ItemImpls<'_> { let span = self_ty.span(); let storage_ident = self.contract.module().storage().ident(); quote_spanned!(span => - ::ink_lang::static_assertions::assert_type_eq_all!( - #self_ty, - #storage_ident, - ); + const _: ::ink_lang::codegen::utils::IsSameType<#storage_ident> = + ::ink_lang::codegen::utils::IsSameType::<#self_ty>::new(); ) } diff --git a/crates/lang/codegen/src/generator/metadata.rs b/crates/lang/codegen/src/generator/metadata.rs index 2d58d29f368..81c3a94e8aa 100644 --- a/crates/lang/codegen/src/generator/metadata.rs +++ b/crates/lang/codegen/src/generator/metadata.rs @@ -12,9 +12,18 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::GenerateCode; +use crate::{ + generator, + GenerateCode, + GenerateCodeUsing as _, +}; +use ::core::iter; use derive_more::From; -use ir::Callable as _; +use ir::{ + Callable as _, + HexLiteral, + IsDocAttribute, +}; use proc_macro2::TokenStream as TokenStream2; use quote::{ quote, @@ -28,35 +37,24 @@ pub struct Metadata<'a> { /// The contract to generate code for. contract: &'a ir::Contract, } - -impl Metadata<'_> { - fn generate_cgf(&self) -> TokenStream2 { - if self.contract.config().is_compile_as_dependency_enabled() { - return quote! { #[cfg(feature = "__ink_DO_NOT_COMPILE")] } - } - quote! { #[cfg(not(feature = "ink-as-dependency"))] } - } -} +impl_as_ref_for_generator!(Metadata); impl GenerateCode for Metadata<'_> { fn generate_code(&self) -> TokenStream2 { let contract = self.generate_contract(); let layout = self.generate_layout(); - let no_cross_calling_cfg = self.generate_cgf(); + let cfg_not_as_dependency = + self.generate_code_using::(); quote! { #[cfg(feature = "std")] - #no_cross_calling_cfg + #cfg_not_as_dependency const _: () = { #[no_mangle] pub fn __ink_generate_metadata() -> ::ink_metadata::MetadataVersioned { - let contract: ::ink_metadata::ContractSpec = { - #contract - }; - let layout: ::ink_metadata::layout::Layout = { - #layout - }; - ::ink_metadata::InkProject::new(layout, contract).into() + <::ink_metadata::InkProject as ::core::convert::Into<::ink_metadata::MetadataVersioned>>::into( + ::ink_metadata::InkProject::new(#layout, #contract) + ) } }; } @@ -65,22 +63,27 @@ impl GenerateCode for Metadata<'_> { impl Metadata<'_> { fn generate_layout(&self) -> TokenStream2 { - let contract_ident = self.contract.module().storage().ident(); - quote! { - <#contract_ident as ::ink_storage::traits::StorageLayout>::layout( + let storage_span = self.contract.module().storage().span(); + let storage_ident = self.contract.module().storage().ident(); + quote_spanned!(storage_span=> + <#storage_ident as ::ink_storage::traits::StorageLayout>::layout( &mut <::ink_primitives::KeyPtr as ::core::convert::From<::ink_primitives::Key>>::from( <::ink_primitives::Key as ::core::convert::From<[::core::primitive::u8; 32usize]>>::from([0x00_u8; 32usize]) ) ) - } + ) } fn generate_contract(&self) -> TokenStream2 { let constructors = self.generate_constructors(); let messages = self.generate_messages(); let events = self.generate_events(); - let docs = self.generate_docs(); - + let docs = self + .contract + .module() + .attrs() + .iter() + .filter_map(|attr| attr.extract_docs()); quote! { ::ink_metadata::ContractSpec::new() .constructors([ @@ -99,81 +102,50 @@ impl Metadata<'_> { } } - /// Extracts the doc strings from the given slice of attributes. - fn extract_doc_comments( - attributes: &[syn::Attribute], - ) -> impl Iterator + '_ { - attributes - .iter() - .filter_map(|attribute| { - match attribute.parse_meta() { - Ok(syn::Meta::NameValue(name_value)) => Some(name_value), - Ok(_) | Err(_) => None, - } - }) - .filter(|name_value| name_value.path.is_ident("doc")) - .filter_map(|name_value| { - match name_value.lit { - syn::Lit::Str(lit_str) => Some(lit_str.value()), - _ => None, - } - }) - } - - /// Generates ink! metadata for all contract constructors. + /// Generates ink! metadata for all ink! smart contract constructors. + #[allow(clippy::redundant_closure)] // We are getting arcane lifetime errors otherwise. fn generate_constructors(&self) -> impl Iterator + '_ { self.contract .module() .impls() - .flat_map(|impl_block| { - let trait_ident = impl_block - .trait_path() - .map(|path| path.segments.last().map(|seg| &seg.ident)) - .flatten(); - impl_block - .iter_constructors() - .map(move |constructor| (trait_ident, constructor)) - }) - .map(|(trait_ident, constructor)| { - let span = constructor.span(); - let attrs = constructor.attrs(); - let docs = Self::extract_doc_comments(attrs); - let selector_bytes = constructor.composed_selector().hex_lits(); - let constructor = constructor.callable(); - let ident = constructor.ident(); - let args = constructor.inputs().map(Self::generate_message_param); - let constr = match trait_ident { - Some(trait_ident) => { - quote_spanned!(span => from_trait_and_name( - ::core::stringify!(#trait_ident), - ::core::stringify!(#ident) - )) - } - None => { - quote_spanned!(span => from_name(::core::stringify!(#ident))) - } - }; - quote_spanned!(span => - ::ink_metadata::ConstructorSpec::#constr - .selector([ - #( #selector_bytes ),* - ]) - .args([ - #( #args ),* - ]) - .docs([ - #( #docs ),* - ]) - .done() - ) - }) + .map(|item_impl| item_impl.iter_constructors()) + .flatten() + .map(|constructor| Self::generate_constructor(constructor)) + } + + /// Generates ink! metadata for a single ink! constructor. + fn generate_constructor( + constructor: ir::CallableWithSelector, + ) -> TokenStream2 { + let span = constructor.span(); + let docs = constructor + .attrs() + .iter() + .filter_map(|attr| attr.extract_docs()); + let selector_bytes = constructor.composed_selector().hex_lits(); + let constructor = constructor.callable(); + let ident = constructor.ident(); + let args = constructor.inputs().map(Self::generate_dispatch_argument); + quote_spanned!(span=> + ::ink_metadata::ConstructorSpec::from_name(::core::stringify!(#ident)) + .selector([ + #( #selector_bytes ),* + ]) + .args([ + #( #args ),* + ]) + .docs([ + #( #docs ),* + ]) + .done() + ) } /// Generates the ink! metadata for the given parameter and parameter type. - fn generate_message_param(pat_type: &syn::PatType) -> TokenStream2 { + fn generate_dispatch_argument(pat_type: &syn::PatType) -> TokenStream2 { let ident = match &*pat_type.pat { syn::Pat::Ident(ident) => &ident.ident, - _ => unreachable!("encountered unexpected non identifier in ink! parameter"), + _ => unreachable!("encountered ink! dispatch input with missing identifier"), }; let type_spec = Self::generate_type_spec(&pat_type.ty); quote! { @@ -199,11 +171,11 @@ impl Metadata<'_> { let segs = path .segments .iter() - .map(|seg| seg.ident.to_string()) + .map(|seg| &seg.ident) .collect::>(); quote! { ::ink_metadata::TypeSpec::with_name_segs::<#ty, _>( - ::core::iter::IntoIterator::into_iter([ #( #segs ),* ]) + ::core::iter::IntoIterator::into_iter([ #( ::core::stringify!(#segs) ),* ]) .map(::core::convert::AsRef::as_ref) ) } @@ -212,43 +184,39 @@ impl Metadata<'_> { } } - fn generate_messages(&self) -> impl Iterator + '_ { + /// Generates the ink! metadata for all ink! smart contract messages. + fn generate_messages(&self) -> Vec { + let mut messages = Vec::new(); + let inherent_messages = self.generate_inherent_messages(); + let trait_messages = self.generate_trait_messages(); + messages.extend(inherent_messages); + messages.extend(trait_messages); + messages + } + + /// Generates the ink! metadata for all inherent ink! smart contract messages. + fn generate_inherent_messages(&self) -> Vec { self.contract .module() .impls() - .flat_map(|impl_block| { - let trait_ident = impl_block - .trait_path() - .map(|path| path.segments.last().map(|seg| &seg.ident)) - .flatten(); - impl_block - .iter_messages() - .map(move |message| (trait_ident, message)) - }) - .map(|(trait_ident, message)| { + .filter(|item_impl| item_impl.trait_path().is_none()) + .map(|item_impl| item_impl.iter_messages()) + .flatten() + .map(|message| { let span = message.span(); - let attrs = message.attrs(); - let docs = Self::extract_doc_comments(attrs); + let docs = message + .attrs() + .iter() + .filter_map(|attr| attr.extract_docs()); let selector_bytes = message.composed_selector().hex_lits(); let is_payable = message.is_payable(); let message = message.callable(); let mutates = message.receiver().is_ref_mut(); let ident = message.ident(); - let args = message.inputs().map(Self::generate_message_param); + let args = message.inputs().map(Self::generate_dispatch_argument); let ret_ty = Self::generate_return_type(message.output()); - let constr = match trait_ident { - Some(trait_ident) => { - quote_spanned!(span => from_trait_and_name( - ::core::stringify!(#trait_ident), - ::core::stringify!(#ident), - )) - } - None => { - quote_spanned!(span => from_name(::core::stringify!(#ident))) - } - }; quote_spanned!(span => - ::ink_metadata::MessageSpec::#constr + ::ink_metadata::MessageSpec::from_name(::core::stringify!(#ident)) .selector([ #( #selector_bytes ),* ]) @@ -264,6 +232,68 @@ impl Metadata<'_> { .done() ) }) + .collect() + } + + /// Generates the ink! metadata for all inherent ink! smart contract messages. + fn generate_trait_messages(&self) -> Vec { + let storage_ident = self.contract.module().storage().ident(); + self.contract + .module() + .impls() + .filter_map(|item_impl| { + item_impl + .trait_path() + .map(|trait_path| { + let trait_ident = item_impl.trait_ident().expect( + "must have an ink! trait identifier if it is an ink! trait implementation" + ); + iter::repeat((trait_ident, trait_path)).zip(item_impl.iter_messages()) + }) + }) + .flatten() + .map(|((trait_ident, trait_path), message)| { + let message_span = message.span(); + let message_ident = message.ident(); + let message_docs = message + .attrs() + .iter() + .filter_map(|attr| attr.extract_docs()); + let message_args = message + .inputs() + .map(Self::generate_dispatch_argument); + let mutates = message.receiver().is_ref_mut(); + let local_id = message.local_id().hex_padded_suffixed(); + let is_payable = quote! {{ + <<::ink_lang::reflect::TraitDefinitionRegistry<<#storage_ident as ::ink_lang::reflect::ContractEnv>::Env> + as #trait_path>::__ink_TraitInfo + as ::ink_lang::reflect::TraitMessageInfo<#local_id>>::PAYABLE + }}; + let selector = quote! {{ + <<::ink_lang::reflect::TraitDefinitionRegistry<<#storage_ident as ::ink_lang::reflect::ContractEnv>::Env> + as #trait_path>::__ink_TraitInfo + as ::ink_lang::reflect::TraitMessageInfo<#local_id>>::SELECTOR + }}; + let ret_ty = Self::generate_return_type(message.output()); + quote_spanned!(message_span=> + ::ink_metadata::MessageSpec::from_trait_and_name( + ::core::stringify!(#trait_ident), + ::core::stringify!(#message_ident) + ) + .selector(#selector) + .args([ + #( #message_args ),* + ]) + .returns(#ret_ty) + .mutates(#mutates) + .payable(#is_payable) + .docs([ + #( #message_docs ),* + ]) + .done() + ) + }) + .collect() } /// Generates ink! metadata for the given return type. @@ -288,7 +318,7 @@ impl Metadata<'_> { self.contract.module().events().map(|event| { let span = event.span(); let ident = event.ident(); - let docs = Self::extract_doc_comments(event.attrs()); + let docs = event.attrs().iter().filter_map(|attr| attr.extract_docs()); let args = Self::generate_event_args(event); quote_spanned!(span => ::ink_metadata::EventSpec::new(::core::stringify!(#ident)) @@ -309,8 +339,10 @@ impl Metadata<'_> { let span = event_field.span(); let ident = event_field.ident(); let is_topic = event_field.is_topic; - let attrs = event_field.attrs(); - let docs = Self::extract_doc_comments(&attrs); + let docs = event_field + .attrs() + .into_iter() + .filter_map(|attr| attr.extract_docs()); let ty = Self::generate_type_spec(event_field.ty()); quote_spanned!(span => ::ink_metadata::EventParamSpec::new(::core::stringify!(#ident)) @@ -323,39 +355,39 @@ impl Metadata<'_> { ) }) } - - /// Generates the documentation for the contract module. - fn generate_docs(&self) -> impl Iterator + '_ { - Self::extract_doc_comments(self.contract.module().attrs()) - } } #[cfg(test)] mod tests { use super::*; + /// Extracts and collects the contents of the Rust documentation attributes. + fn extract_doc_attributes(attrs: &[syn::Attribute]) -> Vec { + attrs + .iter() + .filter_map(|attr| attr.extract_docs()) + .collect() + } + #[test] fn extract_doc_comments_works() { assert_eq!( - Metadata::extract_doc_comments(&[syn::parse_quote!( #[doc = r"content"] )]) - .collect::>(), + extract_doc_attributes(&[syn::parse_quote!( #[doc = r"content"] )]), vec!["content".to_string()], ); assert_eq!( - Metadata::extract_doc_comments(&[syn::parse_quote!( + extract_doc_attributes(&[syn::parse_quote!( /// content - )]) - .collect::>(), + )]), vec![" content".to_string()], ); assert_eq!( - Metadata::extract_doc_comments(&[syn::parse_quote!( + extract_doc_attributes(&[syn::parse_quote!( /** * Multi-line comments ... * May span many lines */ - )]) - .collect::>(), + )]), vec![r" * Multi-line comments ... * May span many lines @@ -363,7 +395,7 @@ mod tests { .to_string()], ); assert_eq!( - Metadata::extract_doc_comments(&[ + extract_doc_attributes(&[ syn::parse_quote!( /// multiple ), @@ -376,8 +408,7 @@ mod tests { syn::parse_quote!( /// comments ), - ]) - .collect::>(), + ]), vec![ " multiple".to_string(), " single".to_string(), @@ -386,7 +417,7 @@ mod tests { ], ); assert_eq!( - Metadata::extract_doc_comments(&[ + extract_doc_attributes(&[ syn::parse_quote!( #[doc = r"a"] ), syn::parse_quote!( #[non_doc] ), syn::parse_quote!( #[doc = r"b"] ), @@ -396,8 +427,7 @@ mod tests { syn::parse_quote!( #[doc = r"d"] ), syn::parse_quote!( #[doc(Nope)] ), syn::parse_quote!( #[doc = r"e"] ), - ]) - .collect::>(), + ]), vec![ "a".to_string(), "b".to_string(), diff --git a/crates/lang/codegen/src/generator/mod.rs b/crates/lang/codegen/src/generator/mod.rs index e83203118c5..1130957265c 100644 --- a/crates/lang/codegen/src/generator/mod.rs +++ b/crates/lang/codegen/src/generator/mod.rs @@ -26,10 +26,11 @@ macro_rules! impl_as_ref_for_generator { }; } +mod arg_list; +mod as_dependency; mod blake2b; mod chain_extension; mod contract; -mod cross_calling; mod dispatch; mod env; mod events; @@ -41,13 +42,23 @@ mod storage; mod trait_def; pub use self::{ + arg_list::{ + generate_argument_list, + generate_reference_to_trait_info, + input_bindings, + input_bindings_tuple, + input_types, + input_types_tuple, + output_ident, + }, + as_dependency::{ + ContractReference, + NotAsDependencyCfg, + OnlyAsDependencyCfg, + }, blake2b::Blake2x256, chain_extension::ChainExtension, contract::Contract, - cross_calling::{ - CrossCalling, - CrossCallingConflictCfg, - }, dispatch::Dispatch, env::Env, events::Events, diff --git a/crates/lang/codegen/src/generator/storage.rs b/crates/lang/codegen/src/generator/storage.rs index 6634e8d06c3..c68e29824a4 100644 --- a/crates/lang/codegen/src/generator/storage.rs +++ b/crates/lang/codegen/src/generator/storage.rs @@ -12,11 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::{ - generator, - GenerateCode, - GenerateCodeUsing, -}; +use crate::GenerateCode; use derive_more::From; use proc_macro2::TokenStream as TokenStream2; use quote::{ @@ -40,18 +36,16 @@ impl GenerateCode for Storage<'_> { let use_emit_event = self.contract.module().events().next().is_some().then(|| { // Required to allow for `self.env().emit_event(..)` in messages and constructors. - quote! { use ::ink_lang::EmitEvent as _; } + quote! { use ::ink_lang::codegen::EmitEvent as _; } }); - let cfg = self.generate_code_using::(); quote_spanned!(storage_span => #access_env_impls #storage_struct - #cfg const _: () = { - // Used to make `self.env()` available in message code. + // Used to make `self.env()` and `Self::env()` available in message code. #[allow(unused_imports)] - use ::ink_lang::{ + use ::ink_lang::codegen::{ Env as _, StaticEnv as _, }; @@ -64,26 +58,24 @@ impl GenerateCode for Storage<'_> { impl Storage<'_> { fn generate_access_env_trait_impls(&self) -> TokenStream2 { let storage_ident = &self.contract.module().storage().ident(); - let cfg = self.generate_code_using::(); quote! { - #cfg const _: () = { - impl<'a> ::ink_lang::Env for &'a #storage_ident { + impl<'a> ::ink_lang::codegen::Env for &'a #storage_ident { type EnvAccess = ::ink_lang::EnvAccess< - 'a, <#storage_ident as ::ink_lang::ContractEnv>::Env>; + 'a, <#storage_ident as ::ink_lang::reflect::ContractEnv>::Env>; fn env(self) -> Self::EnvAccess { - <::EnvAccess + <::EnvAccess as ::core::default::Default>::default() } } - impl<'a> ::ink_lang::StaticEnv for #storage_ident { + impl<'a> ::ink_lang::codegen::StaticEnv for #storage_ident { type EnvAccess = ::ink_lang::EnvAccess< - 'static, <#storage_ident as ::ink_lang::ContractEnv>::Env>; + 'static, <#storage_ident as ::ink_lang::reflect::ContractEnv>::Env>; fn env() -> Self::EnvAccess { - <::EnvAccess + <::EnvAccess as ::core::default::Default>::default() } } @@ -98,9 +90,7 @@ impl Storage<'_> { let ident = storage.ident(); let attrs = storage.attrs(); let fields = storage.fields(); - let cfg = self.generate_code_using::(); quote_spanned!( span => - #cfg #(#attrs)* #[cfg_attr( feature = "std", @@ -111,6 +101,12 @@ impl Storage<'_> { pub struct #ident { #( #fields ),* } + + const _: () = { + impl ::ink_lang::reflect::ContractName for #ident { + const NAME: &'static str = ::core::stringify!(#ident); + } + }; ) } } diff --git a/crates/lang/codegen/src/generator/trait_def.rs b/crates/lang/codegen/src/generator/trait_def.rs deleted file mode 100644 index 39490e11c3b..00000000000 --- a/crates/lang/codegen/src/generator/trait_def.rs +++ /dev/null @@ -1,117 +0,0 @@ -// 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::GenerateCode; -use derive_more::From; -use heck::CamelCase as _; -use proc_macro2::TokenStream as TokenStream2; -use quote::{ - format_ident, - quote, - quote_spanned, -}; - -/// Generator to create the ink! storage struct and important trait implementations. -#[derive(From)] -pub struct TraitDefinition<'a> { - trait_def: &'a ir::InkTrait, -} - -impl<'a> TraitDefinition<'a> { - fn generate_for_constructor( - constructor: ir::InkTraitConstructor<'a>, - ) -> TokenStream2 { - let span = constructor.span(); - let attrs = constructor.attrs(); - let sig = constructor.sig(); - let ident = &sig.ident; - let output_ident = format_ident!("{}Out", ident.to_string().to_camel_case()); - let inputs = &sig.inputs; - quote_spanned!(span => - /// Output type of the respective trait constructor. - type #output_ident; - - #(#attrs)* - fn #ident(#inputs) -> Self::#output_ident; - ) - } - - fn generate_for_message(message: ir::InkTraitMessage<'a>) -> TokenStream2 { - let span = message.span(); - let attrs = message.attrs(); - let sig = message.sig(); - let ident = &sig.ident; - let inputs = &sig.inputs; - let output = match &sig.output { - syn::ReturnType::Default => quote! { () }, - syn::ReturnType::Type(_, ty) => quote! { #ty }, - }; - let output_ident = format_ident!("{}Out", ident.to_string().to_camel_case()); - quote_spanned!(span => - /// Output type of the respective trait message. - type #output_ident: ::ink_lang::ImpliesReturn<#output>; - - #(#attrs)* - fn #ident(#inputs) -> Self::#output_ident; - ) - } -} - -impl GenerateCode for TraitDefinition<'_> { - fn generate_code(&self) -> TokenStream2 { - let span = self.trait_def.span(); - let attrs = self.trait_def.attrs(); - let hash = self.trait_def.verify_hash(); - let ident = self.trait_def.ident(); - let helper_ident = format_ident!( - "__ink_Checked{}_0x{:X}{:X}{:X}{:X}", - ident, - hash[0], - hash[1], - hash[2], - hash[3] - ); - let verify_hash_id = - u32::from_be_bytes([hash[0], hash[1], hash[2], hash[3]]) as usize; - let constructors = self - .trait_def - .iter_items() - .flat_map(ir::InkTraitItem::filter_map_constructor) - .map(Self::generate_for_constructor); - let messages = self - .trait_def - .iter_items() - .flat_map(ir::InkTraitItem::filter_map_message) - .map(Self::generate_for_message); - quote_spanned!(span => - #(#attrs)* - pub trait #ident: ::ink_lang::CheckedInkTrait<[(); #verify_hash_id]> { - #[doc(hidden)] - #[allow(non_camel_case_types)] - type __ink_Checksum: #helper_ident; - - #(#constructors)* - #(#messages)* - } - - #[doc(hidden)] - #[allow(non_camel_case_types)] - pub unsafe trait #helper_ident {} - - const _: () = { - unsafe impl #helper_ident for [(); #verify_hash_id] {} - }; - ) - } -} diff --git a/crates/lang/codegen/src/generator/trait_def/call_builder.rs b/crates/lang/codegen/src/generator/trait_def/call_builder.rs new file mode 100644 index 00000000000..11d484ccc72 --- /dev/null +++ b/crates/lang/codegen/src/generator/trait_def/call_builder.rs @@ -0,0 +1,403 @@ +// 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 super::TraitDefinition; +use crate::{ + generator, + traits::GenerateCode, +}; +use derive_more::From; +use proc_macro2::{ + Span, + TokenStream as TokenStream2, +}; +use quote::{ + quote, + quote_spanned, +}; + +impl<'a> TraitDefinition<'a> { + /// Generates code for the global trait call builder for an ink! trait. + /// + /// # Note + /// + /// - The generated call builder type implements the ink! trait definition + /// and allows to build up contract calls that allow for customization by + /// the user to provide gas limit, endowment etc. + /// - The call builder is used directly by the generated call forwarder. + /// There exists one global call forwarder and call builder pair for every + /// ink! trait definition. + pub fn generate_call_builder(&self) -> TokenStream2 { + CallBuilder::from(*self).generate_code() + } + + /// The identifier of the ink! trait call builder. + pub fn call_builder_ident(&self) -> syn::Ident { + self.append_trait_suffix(CallBuilder::SUFFIX) + } +} + +/// Generates code for the global ink! trait call builder. +#[derive(From)] +struct CallBuilder<'a> { + trait_def: TraitDefinition<'a>, +} + +impl GenerateCode for CallBuilder<'_> { + fn generate_code(&self) -> TokenStream2 { + let struct_definition = self.generate_struct_definition(); + let storage_layout_impl = self.generate_storage_layout_impl(); + let spread_layout_impl = self.generate_spread_layout_impl(); + let packed_layout_impl = self.generate_packed_layout_impl(); + let auxiliary_trait_impls = self.generate_auxiliary_trait_impls(); + let to_from_account_id_impls = self.generate_to_from_account_id_impls(); + let ink_trait_impl = self.generate_ink_trait_impl(); + quote! { + #struct_definition + #storage_layout_impl + #spread_layout_impl + #packed_layout_impl + #auxiliary_trait_impls + #to_from_account_id_impls + #ink_trait_impl + } + } +} + +impl CallBuilder<'_> { + /// The name suffix for ink! trait call builder. + const SUFFIX: &'static str = "TraitCallBuilder"; + + /// Returns the span of the ink! trait definition. + fn span(&self) -> Span { + self.trait_def.span() + } + + /// Returns the identifier of the ink! trait call builder. + fn ident(&self) -> syn::Ident { + self.trait_def.call_builder_ident() + } + + /// Generates the struct type definition for the account wrapper type. + /// + /// This type is going to implement the trait so that invoking its trait + /// methods will perform contract calls via contract's pallet contract + /// execution abstraction. + /// + /// # Note + /// + /// Unlike the layout specific traits it is possible to derive the SCALE + /// `Encode` and `Decode` traits since they generate trait bounds per field + /// instead of per generic parameter which is exactly what we need here. + /// However, it should be noted that this is not Rust default behavior. + fn generate_struct_definition(&self) -> TokenStream2 { + let span = self.span(); + let call_builder_ident = self.ident(); + quote_spanned!(span => + /// The global call builder type for all trait implementers. + /// + /// All calls to types (contracts) implementing the trait will be built by this type. + #[doc(hidden)] + #[allow(non_camel_case_types)] + #[derive(::scale::Encode, ::scale::Decode)] + #[repr(transparent)] + pub struct #call_builder_ident + where + E: ::ink_env::Environment, + { + account_id: ::AccountId, + } + ) + } + + /// Generates the `StorageLayout` trait implementation for the account wrapper. + /// + /// # Note + /// + /// Due to the generic parameter `E` and Rust's default rules for derive generated + /// trait bounds it is not recommended to derive the `StorageLayout` trait implementation. + fn generate_storage_layout_impl(&self) -> TokenStream2 { + let span = self.span(); + let call_builder_ident = self.ident(); + quote_spanned!(span=> + #[cfg(feature = "std")] + impl ::ink_storage::traits::StorageLayout + for #call_builder_ident + where + E: ::ink_env::Environment, + ::AccountId: ::ink_storage::traits::StorageLayout, + { + fn layout( + __key_ptr: &mut ::ink_storage::traits::KeyPtr, + ) -> ::ink_metadata::layout::Layout { + ::ink_metadata::layout::Layout::Struct( + ::ink_metadata::layout::StructLayout::new([ + ::ink_metadata::layout::FieldLayout::new( + ::core::option::Option::Some("account_id"), + <::AccountId + as ::ink_storage::traits::StorageLayout>::layout(__key_ptr) + ) + ]) + ) + } + } + ) + } + + /// Generates the `SpreadLayout` trait implementation for the account wrapper. + /// + /// # Note + /// + /// Due to the generic parameter `E` and Rust's default rules for derive generated + /// trait bounds it is not recommended to derive the `SpreadLayout` trait implementation. + fn generate_spread_layout_impl(&self) -> TokenStream2 { + let span = self.span(); + let call_builder_ident = self.ident(); + quote_spanned!(span=> + /// We require this manual implementation since the derive produces incorrect trait bounds. + impl ::ink_storage::traits::SpreadLayout + for #call_builder_ident + where + E: ::ink_env::Environment, + ::AccountId: ::ink_storage::traits::SpreadLayout, + { + const FOOTPRINT: ::core::primitive::u64 = 1; + const REQUIRES_DEEP_CLEAN_UP: ::core::primitive::bool = false; + + #[inline] + fn pull_spread(ptr: &mut ::ink_primitives::KeyPtr) -> Self { + Self { + account_id: <::AccountId + as ::ink_storage::traits::SpreadLayout>::pull_spread(ptr) + } + } + + #[inline] + fn push_spread(&self, ptr: &mut ::ink_primitives::KeyPtr) { + <::AccountId + as ::ink_storage::traits::SpreadLayout>::push_spread(&self.account_id, ptr) + } + + #[inline] + fn clear_spread(&self, ptr: &mut ::ink_primitives::KeyPtr) { + <::AccountId + as ::ink_storage::traits::SpreadLayout>::clear_spread(&self.account_id, ptr) + } + } + ) + } + + /// Generates the `PackedLayout` trait implementation for the account wrapper. + /// + /// # Note + /// + /// Due to the generic parameter `E` and Rust's default rules for derive generated + /// trait bounds it is not recommended to derive the `PackedLayout` trait implementation. + fn generate_packed_layout_impl(&self) -> TokenStream2 { + let span = self.span(); + let call_builder_ident = self.ident(); + quote_spanned!(span=> + /// We require this manual implementation since the derive produces incorrect trait bounds. + impl ::ink_storage::traits::PackedLayout + for #call_builder_ident + where + E: ::ink_env::Environment, + ::AccountId: ::ink_storage::traits::PackedLayout, + { + #[inline(always)] + fn pull_packed(&mut self, _at: &::ink_primitives::Key) {} + #[inline(always)] + fn push_packed(&self, _at: &::ink_primitives::Key) {} + #[inline(always)] + fn clear_packed(&self, _at: &::ink_primitives::Key) {} + } + ) + } + + /// Generates trait implementations for auxiliary traits for the account wrapper. + /// + /// # Note + /// + /// Auxiliary traits currently include: + /// + /// - `Clone`: To allow cloning contract references in the long run. + /// - `Debug`: To better debug internal contract state. + fn generate_auxiliary_trait_impls(&self) -> TokenStream2 { + let span = self.span(); + let call_builder_ident = self.ident(); + quote_spanned!(span=> + /// We require this manual implementation since the derive produces incorrect trait bounds. + impl ::core::clone::Clone for #call_builder_ident + where + E: ::ink_env::Environment, + ::AccountId: ::core::clone::Clone, + { + #[inline] + fn clone(&self) -> Self { + Self { + account_id: ::core::clone::Clone::clone(&self.account_id), + } + } + } + + /// We require this manual implementation since the derive produces incorrect trait bounds. + impl ::core::fmt::Debug for #call_builder_ident + where + E: ::ink_env::Environment, + ::AccountId: ::core::fmt::Debug, + { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + f.debug_struct(::core::stringify!(#call_builder_ident)) + .field("account_id", &self.account_id) + .finish() + } + } + ) + } + + /// Generate trait implementations for `FromAccountId` and `ToAccountId` for the account wrapper. + /// + /// # Note + /// + /// This allows user code to conveniently transform from and to `AccountId` when + /// interacting with typed contracts. + fn generate_to_from_account_id_impls(&self) -> TokenStream2 { + let span = self.span(); + let call_builder_ident = self.ident(); + quote_spanned!(span=> + impl ::ink_env::call::FromAccountId + for #call_builder_ident + where + E: ::ink_env::Environment, + { + #[inline] + fn from_account_id(account_id: ::AccountId) -> Self { + Self { account_id } + } + } + + impl ::ink_lang::ToAccountId for #call_builder_ident + where + E: ::ink_env::Environment, + { + #[inline] + fn to_account_id(&self) -> ::AccountId { + <::AccountId as ::core::clone::Clone>::clone(&self.account_id) + } + } + ) + } + + /// Generates the implementation of the associated ink! trait definition. + /// + /// # Note + /// + /// The implemented messages call the SEAL host runtime in order to dispatch + /// the respective ink! trait message calls of the called smart contract + /// instance. + /// The way these messages are built-up allows the caller to customize message + /// parameters such as gas limit and transferred balance. + fn generate_ink_trait_impl(&self) -> TokenStream2 { + let span = self.trait_def.span(); + let trait_ident = self.trait_def.trait_def.item().ident(); + let trait_info_ident = self.trait_def.trait_info_ident(); + let builder_ident = self.ident(); + let message_impls = self.generate_ink_trait_impl_messages(); + quote_spanned!(span=> + impl ::ink_lang::reflect::ContractEnv for #builder_ident + where + E: ::ink_env::Environment, + { + type Env = E; + } + + impl #trait_ident for #builder_ident + where + E: ::ink_env::Environment, + { + #[doc(hidden)] + #[allow(non_camel_case_types)] + type __ink_TraitInfo = #trait_info_ident; + + #message_impls + } + ) + } + + /// Generate the code for all ink! trait messages implemented by the trait call builder. + fn generate_ink_trait_impl_messages(&self) -> TokenStream2 { + let messages = self.trait_def.trait_def.item().iter_items().filter_map( + |(item, selector)| { + item.filter_map_message().map(|message| { + self.generate_ink_trait_impl_for_message(&message, selector) + }) + }, + ); + quote! { + #( #messages )* + } + } + + /// Generate the code for a single ink! trait message implemented by the trait call builder. + fn generate_ink_trait_impl_for_message( + &self, + message: &ir::InkTraitMessage, + selector: ir::Selector, + ) -> TokenStream2 { + let span = message.span(); + let message_ident = message.ident(); + let attrs = message.attrs(); + let output_ident = generator::output_ident(message_ident); + let output = message.output(); + let output_sig = output.map_or_else( + || quote! { () }, + |output| quote! { ::ink_env::call::utils::ReturnType<#output> }, + ); + let selector_bytes = selector.hex_lits(); + let input_bindings = generator::input_bindings(message.inputs()); + let input_types = generator::input_types(message.inputs()); + let arg_list = generator::generate_argument_list(input_types.iter().cloned()); + let mut_tok = message.mutates().then(|| quote! { mut }); + quote_spanned!(span => + #[allow(clippy::type_complexity)] + type #output_ident = ::ink_env::call::CallBuilder< + Self::Env, + ::ink_env::call::utils::Set< ::AccountId >, + ::ink_env::call::utils::Unset< ::core::primitive::u64 >, + ::ink_env::call::utils::Unset< ::Balance >, + ::ink_env::call::utils::Set< ::ink_env::call::ExecutionInput<#arg_list> >, + ::ink_env::call::utils::Set<#output_sig>, + >; + + #( #attrs )* + #[inline] + fn #message_ident( + & #mut_tok self + #( , #input_bindings : #input_types )* + ) -> Self::#output_ident { + ::ink_env::call::build_call::() + .callee(::ink_lang::ToAccountId::to_account_id(self)) + .exec_input( + ::ink_env::call::ExecutionInput::new( + ::ink_env::call::Selector::new([ #( #selector_bytes ),* ]) + ) + #( + .push_arg(#input_bindings) + )* + ) + .returns::<#output_sig>() + } + ) + } +} diff --git a/crates/lang/codegen/src/generator/trait_def/call_forwarder.rs b/crates/lang/codegen/src/generator/trait_def/call_forwarder.rs new file mode 100644 index 00000000000..26003527c09 --- /dev/null +++ b/crates/lang/codegen/src/generator/trait_def/call_forwarder.rs @@ -0,0 +1,428 @@ +// 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 super::TraitDefinition; +use crate::{ + generator, + traits::GenerateCode, +}; +use derive_more::From; +use proc_macro2::{ + Span, + TokenStream as TokenStream2, +}; +use quote::{ + quote, + quote_spanned, +}; + +impl<'a> TraitDefinition<'a> { + /// Generates code for the global trait call forwarder for an ink! trait. + /// + /// # Note + /// + /// - The generated call forwarder type implements the ink! trait definition + /// and allows to build up contract calls that allow for customization by + /// the user to provide gas limit, endowment etc. + /// - The call forwarder is associated to the call builder for the same ink! + /// trait definition and handles all ink! trait calls into another contract + /// instance on-chain. For constructing custom calls it forwards to the call + /// builder. + pub fn generate_call_forwarder(&self) -> TokenStream2 { + CallForwarder::from(*self).generate_code() + } + + /// The identifier of the ink! trait call forwarder. + pub fn call_forwarder_ident(&self) -> syn::Ident { + self.append_trait_suffix(CallForwarder::SUFFIX) + } +} + +/// Generates code for the global ink! trait call forwarder. +#[derive(From)] +struct CallForwarder<'a> { + trait_def: TraitDefinition<'a>, +} + +impl GenerateCode for CallForwarder<'_> { + fn generate_code(&self) -> TokenStream2 { + let struct_definition = self.generate_struct_definition(); + let storage_layout_impl = self.generate_storage_layout_impl(); + let spread_layout_impl = self.generate_spread_layout_impl(); + let packed_layout_impl = self.generate_packed_layout_impl(); + let auxiliary_trait_impls = self.generate_auxiliary_trait_impls(); + let to_from_account_id_impls = self.generate_to_from_account_id_impls(); + let call_builder_impl = self.generate_call_builder_trait_impl(); + let ink_trait_impl = self.generate_ink_trait_impl(); + quote! { + #struct_definition + #storage_layout_impl + #spread_layout_impl + #packed_layout_impl + #auxiliary_trait_impls + #to_from_account_id_impls + #call_builder_impl + #ink_trait_impl + } + } +} + +impl CallForwarder<'_> { + /// The name suffix for ink! trait call forwarder. + const SUFFIX: &'static str = "TraitCallForwarder"; + + /// Returns the span of the ink! trait definition. + fn span(&self) -> Span { + self.trait_def.span() + } + + /// Returns the identifier of the ink! trait call forwarder. + fn ident(&self) -> syn::Ident { + self.trait_def.call_forwarder_ident() + } + + /// Generates the struct type definition for the account wrapper type. + /// + /// This type is going to implement the trait so that invoking its trait + /// methods will perform contract calls via contract's pallet contract + /// execution abstraction. + /// + /// # Note + /// + /// Unlike the layout specific traits it is possible to derive the SCALE + /// `Encode` and `Decode` traits since they generate trait bounds per field + /// instead of per generic parameter which is exactly what we need here. + /// However, it should be noted that this is not Rust default behavior. + fn generate_struct_definition(&self) -> TokenStream2 { + let span = self.span(); + let call_forwarder_ident = self.ident(); + quote_spanned!(span => + /// The global call forwarder for the ink! trait definition. + /// + /// All cross-contract calls to contracts implementing the associated ink! trait + /// will be handled by this type. + #[doc(hidden)] + #[allow(non_camel_case_types)] + #[derive(::scale::Encode, ::scale::Decode)] + #[repr(transparent)] + pub struct #call_forwarder_ident + where + E: ::ink_env::Environment, + { + builder: ::Builder, + } + ) + } + + /// Generates the `StorageLayout` trait implementation for the account wrapper. + /// + /// # Note + /// + /// Due to the generic parameter `E` and Rust's default rules for derive generated + /// trait bounds it is not recommended to derive the `StorageLayout` trait implementation. + fn generate_storage_layout_impl(&self) -> TokenStream2 { + let span = self.span(); + let call_forwarder_ident = self.ident(); + quote_spanned!(span=> + #[cfg(feature = "std")] + impl ::ink_storage::traits::StorageLayout + for #call_forwarder_ident + where + E: ::ink_env::Environment, + ::AccountId: ::ink_storage::traits::StorageLayout, + { + fn layout( + __key_ptr: &mut ::ink_storage::traits::KeyPtr, + ) -> ::ink_metadata::layout::Layout { + <::Builder + as ::ink_storage::traits::StorageLayout>::layout(__key_ptr) + } + } + ) + } + + /// Generates the `SpreadLayout` trait implementation for the account wrapper. + /// + /// # Note + /// + /// Due to the generic parameter `E` and Rust's default rules for derive generated + /// trait bounds it is not recommended to derive the `SpreadLayout` trait implementation. + fn generate_spread_layout_impl(&self) -> TokenStream2 { + let span = self.span(); + let call_forwarder_ident = self.ident(); + quote_spanned!(span=> + impl ::ink_storage::traits::SpreadLayout + for #call_forwarder_ident + where + E: ::ink_env::Environment, + ::AccountId: ::ink_storage::traits::SpreadLayout, + { + const FOOTPRINT: ::core::primitive::u64 = 1; + const REQUIRES_DEEP_CLEAN_UP: ::core::primitive::bool = false; + + #[inline] + fn pull_spread(ptr: &mut ::ink_primitives::KeyPtr) -> Self { + Self { + builder: <::Builder + as ::ink_storage::traits::SpreadLayout>::pull_spread(ptr) + } + } + + #[inline] + fn push_spread(&self, ptr: &mut ::ink_primitives::KeyPtr) { + <::Builder + as ::ink_storage::traits::SpreadLayout>::push_spread(&self.builder, ptr) + } + + #[inline] + fn clear_spread(&self, ptr: &mut ::ink_primitives::KeyPtr) { + <::Builder + as ::ink_storage::traits::SpreadLayout>::clear_spread(&self.builder, ptr) + } + } + ) + } + + /// Generates the `PackedLayout` trait implementation for the account wrapper. + /// + /// # Note + /// + /// Due to the generic parameter `E` and Rust's default rules for derive generated + /// trait bounds it is not recommended to derive the `PackedLayout` trait implementation. + fn generate_packed_layout_impl(&self) -> TokenStream2 { + let span = self.span(); + let call_forwarder_ident = self.ident(); + quote_spanned!(span=> + impl ::ink_storage::traits::PackedLayout + for #call_forwarder_ident + where + E: ::ink_env::Environment, + ::AccountId: ::ink_storage::traits::PackedLayout, + { + #[inline] + fn pull_packed(&mut self, _at: &::ink_primitives::Key) {} + #[inline] + fn push_packed(&self, _at: &::ink_primitives::Key) {} + #[inline] + fn clear_packed(&self, _at: &::ink_primitives::Key) {} + } + ) + } + + /// Generates trait implementations for auxiliary traits for the account wrapper. + /// + /// # Note + /// + /// Auxiliary traits currently include: + /// + /// - `Clone`: To allow cloning contract references in the long run. + /// - `Debug`: To better debug internal contract state. + fn generate_auxiliary_trait_impls(&self) -> TokenStream2 { + let span = self.span(); + let call_forwarder_ident = self.ident(); + quote_spanned!(span=> + impl ::core::clone::Clone for #call_forwarder_ident + where + E: ::ink_env::Environment, + ::AccountId: ::core::clone::Clone, + { + #[inline] + fn clone(&self) -> Self { + Self { + builder: <::Builder + as ::core::clone::Clone>::clone(&self.builder), + } + } + } + + impl ::core::fmt::Debug for #call_forwarder_ident + where + E: ::ink_env::Environment, + ::AccountId: ::core::fmt::Debug, + { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + f.debug_struct(::core::stringify!(#call_forwarder_ident)) + .field("account_id", &self.builder.account_id) + .finish() + } + } + ) + } + + /// Generate trait impls for `FromAccountId` and `ToAccountId` for the account wrapper. + /// + /// # Note + /// + /// This allows user code to conveniently transform from and to `AccountId` when + /// interacting with typed contracts. + fn generate_to_from_account_id_impls(&self) -> TokenStream2 { + let span = self.span(); + let call_forwarder_ident = self.ident(); + quote_spanned!(span=> + impl ::ink_env::call::FromAccountId + for #call_forwarder_ident + where + E: ::ink_env::Environment, + { + #[inline] + fn from_account_id(account_id: ::AccountId) -> Self { + Self { builder: <::Builder + as ::ink_env::call::FromAccountId>::from_account_id(account_id) } + } + } + + impl ::ink_lang::ToAccountId for #call_forwarder_ident + where + E: ::ink_env::Environment, + { + #[inline] + fn to_account_id(&self) -> ::AccountId { + <::Builder + as ::ink_lang::ToAccountId>::to_account_id(&self.builder) + } + } + ) + } + + /// Generate the trait implementation for `CallBuilder` for the ink! trait call forwarder. + /// + /// # Note + /// + /// Through the implementation of this trait it is possible to refer to the + /// ink! trait call builder that is associated to this ink! trait call forwarder. + fn generate_call_builder_trait_impl(&self) -> TokenStream2 { + let span = self.trait_def.span(); + let call_forwarder_ident = self.ident(); + let call_builder_ident = self.trait_def.call_builder_ident(); + quote_spanned!(span=> + /// This trait allows to bridge from call forwarder to call builder. + /// + /// Also this explains why we designed the generated code so that we have + /// both types and why the forwarder is a thin-wrapper around the builder + /// as this allows to perform this operation safely. + impl ::ink_lang::codegen::TraitCallBuilder for #call_forwarder_ident + where + E: ::ink_env::Environment, + { + type Builder = #call_builder_ident; + + #[inline] + fn call(&self) -> &::Builder { + &self.builder + } + + #[inline] + fn call_mut(&mut self) -> &mut ::Builder { + &mut self.builder + } + } + ) + } + + /// Generates the implementation of the associated ink! trait definition. + /// + /// # Note + /// + /// The implementation mainly forwards to the associated ink! call builder + /// of the same ink! trait definition. + fn generate_ink_trait_impl(&self) -> TokenStream2 { + let span = self.trait_def.span(); + let trait_ident = self.trait_def.trait_def.item().ident(); + let trait_info_ident = self.trait_def.trait_info_ident(); + let forwarder_ident = self.ident(); + let message_impls = self.generate_ink_trait_impl_messages(); + quote_spanned!(span=> + impl ::ink_lang::reflect::ContractEnv for #forwarder_ident + where + E: ::ink_env::Environment, + { + type Env = E; + } + + impl #trait_ident for #forwarder_ident + where + E: ::ink_env::Environment, + { + #[doc(hidden)] + #[allow(non_camel_case_types)] + type __ink_TraitInfo = #trait_info_ident; + + #message_impls + } + ) + } + + /// Generate the code for all ink! trait messages implemented by the trait call forwarder. + fn generate_ink_trait_impl_messages(&self) -> TokenStream2 { + let messages = + self.trait_def + .trait_def + .item() + .iter_items() + .filter_map(|(item, _)| { + item.filter_map_message() + .map(|message| self.generate_ink_trait_impl_for_message(&message)) + }); + quote! { + #( #messages )* + } + } + + /// Generate the code for a single ink! trait message implemented by the trait call forwarder. + fn generate_ink_trait_impl_for_message( + &self, + message: &ir::InkTraitMessage, + ) -> TokenStream2 { + let span = message.span(); + let trait_ident = self.trait_def.trait_def.item().ident(); + let forwarder_ident = self.ident(); + let message_ident = message.ident(); + let attrs = message.attrs(); + let output_ident = generator::output_ident(message_ident); + let output_type = message + .output() + .cloned() + .unwrap_or_else(|| syn::parse_quote!(())); + let input_bindings = message.inputs().map(|input| &input.pat).collect::>(); + let input_types = message.inputs().map(|input| &input.ty).collect::>(); + let call_op = match message.receiver() { + ir::Receiver::Ref => quote! { call }, + ir::Receiver::RefMut => quote! { call_mut }, + }; + let mut_tok = message.mutates().then(|| quote! { mut }); + let panic_str = format!( + "encountered error while calling <{} as {}>::{}", + forwarder_ident, trait_ident, message_ident, + ); + quote_spanned!(span => + type #output_ident = #output_type; + + #( #attrs )* + #[inline] + fn #message_ident( + & #mut_tok self + #( , #input_bindings : #input_types )* + ) -> Self::#output_ident { + <::Builder as #trait_ident>::#message_ident( + ::#call_op(self) + #( + , #input_bindings + )* + ) + .fire() + .unwrap_or_else(|err| ::core::panic!("{}: {:?}", #panic_str, err)) + } + ) + } +} diff --git a/crates/lang/codegen/src/generator/trait_def/definition.rs b/crates/lang/codegen/src/generator/trait_def/definition.rs new file mode 100644 index 00000000000..230b0b9c211 --- /dev/null +++ b/crates/lang/codegen/src/generator/trait_def/definition.rs @@ -0,0 +1,71 @@ +// 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. + +//! Generates the ink! trait definition item. + +use super::TraitDefinition; +use heck::CamelCase as _; +use proc_macro2::TokenStream as TokenStream2; +use quote::{ + format_ident, + quote, + quote_spanned, +}; + +impl<'a> TraitDefinition<'a> { + fn generate_for_message(message: ir::InkTraitMessage<'a>) -> TokenStream2 { + let span = message.span(); + let attrs = message.attrs(); + let sig = message.sig(); + let ident = &sig.ident; + let inputs = &sig.inputs; + let output = match &sig.output { + syn::ReturnType::Default => quote! { () }, + syn::ReturnType::Type(_, ty) => quote! { #ty }, + }; + let output_ident = format_ident!("{}Output", ident.to_string().to_camel_case()); + quote_spanned!(span => + /// Output type of the respective trait message. + type #output_ident: ::ink_lang::codegen::ImpliesReturn<#output>; + + #(#attrs)* + fn #ident(#inputs) -> Self::#output_ident; + ) + } +} + +impl TraitDefinition<'_> { + pub(super) fn generate_trait_definition(&self) -> TokenStream2 { + let item = self.trait_def.item(); + let span = item.span(); + let attrs = item.attrs(); + let ident = item.ident(); + let messages = item + .iter_items() + .map(|(item, _)| item) + .flat_map(ir::InkTraitItem::filter_map_message) + .map(Self::generate_for_message); + quote_spanned!(span => + #(#attrs)* + pub trait #ident: ::ink_lang::reflect::ContractEnv { + /// Holds general and global information about the trait. + #[doc(hidden)] + #[allow(non_camel_case_types)] + type __ink_TraitInfo: ::ink_lang::codegen::TraitCallForwarder; + + #(#messages)* + } + ) + } +} diff --git a/crates/lang/codegen/src/generator/trait_def/mod.rs b/crates/lang/codegen/src/generator/trait_def/mod.rs new file mode 100644 index 00000000000..f9e53bec316 --- /dev/null +++ b/crates/lang/codegen/src/generator/trait_def/mod.rs @@ -0,0 +1,69 @@ +// 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. + +mod call_builder; +mod call_forwarder; +mod definition; +mod trait_registry; + +use crate::GenerateCode; +use derive_more::From; +use proc_macro2::{ + Span, + TokenStream as TokenStream2, +}; +use quote::{ + format_ident, + quote_spanned, +}; + +/// Generator to create the ink! storage struct and important trait implementations. +#[derive(From, Copy, Clone)] +pub struct TraitDefinition<'a> { + trait_def: &'a ir::InkTraitDefinition, +} + +impl<'a> TraitDefinition<'a> { + /// Appends the trait suffix to the string and forms an identifier. + /// + /// This appends the `_$NAME_$TRAIT_ID` string to the prefix string + /// were `$NAME` is the non-unique name of the trait and `$TRAIT_ID` + /// is the hex representation of the unique 4-byte trait identifier. + fn append_trait_suffix(&self, prefix: &str) -> syn::Ident { + format_ident!("__ink_{}{}", prefix, self.trait_def.item().ident(),) + } + + /// Returns the span of the underlying ink! trait definition. + fn span(&self) -> Span { + self.trait_def.item().span() + } +} + +impl GenerateCode for TraitDefinition<'_> { + fn generate_code(&self) -> TokenStream2 { + let span = self.trait_def.item().span(); + let trait_definition = self.generate_trait_definition(); + let trait_registry = self.generate_trait_registry_impl(); + let trait_call_builder = self.generate_call_builder(); + let trait_call_forwarder = self.generate_call_forwarder(); + quote_spanned!(span => + #trait_definition + const _: () = { + #trait_registry + #trait_call_builder + #trait_call_forwarder + }; + ) + } +} diff --git a/crates/lang/codegen/src/generator/trait_def/trait_registry.rs b/crates/lang/codegen/src/generator/trait_def/trait_registry.rs new file mode 100644 index 00000000000..0ddda318549 --- /dev/null +++ b/crates/lang/codegen/src/generator/trait_def/trait_registry.rs @@ -0,0 +1,313 @@ +// 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. + +//! The global registry with which it is possible to refer back to the global +//! trait call builder and call forwarder types using only the trait identifier. +//! +//! This works by making the global trait registry type defined in the `ink_lang` +//! crate implement each and every ink! trait definition and defining associated +//! types for the trait's respective call builder and call forwarder. + +use super::TraitDefinition; +use crate::{ + generator::{self,}, + traits::GenerateCode, + EnforcedErrors, +}; +use derive_more::From; +use proc_macro2::{ + Span, + TokenStream as TokenStream2, +}; +use quote::{ + format_ident, + quote, + quote_spanned, +}; +use syn::{ + parse_quote, + spanned::Spanned, +}; + +impl<'a> TraitDefinition<'a> { + /// Generates the code for the global trait registry implementation. + /// + /// This also generates the code for the global trait info object which + /// implements some `ink_lang` traits to provide common information about + /// the ink! trait definition such as its unique identifier. + pub fn generate_trait_registry_impl(&self) -> TokenStream2 { + TraitRegistry::from(*self).generate_code() + } + + /// Returns the identifier for the ink! trait definition info object. + pub fn trait_info_ident(&self) -> syn::Ident { + self.append_trait_suffix("TraitInfo") + } +} + +/// Generates code for the global ink! trait registry implementation. +#[derive(From)] +struct TraitRegistry<'a> { + trait_def: TraitDefinition<'a>, +} + +impl GenerateCode for TraitRegistry<'_> { + fn generate_code(&self) -> TokenStream2 { + let registry_impl = self.generate_registry_impl(); + let trait_info = self.generate_trait_info_object(); + quote! { + #registry_impl + #trait_info + } + } +} + +impl TraitRegistry<'_> { + /// Returns the span of the ink! trait definition. + fn span(&self) -> Span { + self.trait_def.span() + } + + /// Returns the identifier of the ink! trait definition. + fn trait_ident(&self) -> &syn::Ident { + self.trait_def.trait_def.item().ident() + } + + /// Generates the global trait registry implementation for the ink! trait. + /// + /// This makes it possible to refer back to the global call forwarder and + /// call builder specific to this ink! trait from anywhere with just the Rust + /// trait identifier which allows for type safe access. + /// + /// # Note + /// + /// Through this implementation we register the previously defined ink! trait + /// call forwarder and call builder types as such for the ink! trait. + /// + /// This is done by the fact that ink! implements all ink! traits by the + /// [`ink_lang::TraitDefinitionRegistry`] type and uses the `__ink_ConcreteImplementer` + /// associated type to refer back to the actual call forwarder and call builder types. + fn generate_registry_impl(&self) -> TokenStream2 { + let span = self.span(); + let name = self.trait_ident(); + let trait_info_ident = self.trait_def.trait_info_ident(); + let messages = self.generate_registry_messages(); + quote_spanned!(span=> + impl #name for ::ink_lang::reflect::TraitDefinitionRegistry + where + E: ::ink_env::Environment, + { + /// Holds general and global information about the trait. + #[doc(hidden)] + #[allow(non_camel_case_types)] + type __ink_TraitInfo = #trait_info_ident; + + #messages + } + ) + } + + /// Generate the code for all ink! trait messages implemented by the trait registry. + fn generate_registry_messages(&self) -> TokenStream2 { + let messages = self.trait_def.trait_def.item().iter_items().filter_map( + |(item, selector)| { + item.filter_map_message() + .map(|message| self.generate_registry_for_message(&message, selector)) + }, + ); + quote! { + #( #messages )* + } + } + + /// Generates code to assert that ink! input and output types meet certain properties. + fn generate_inout_guards_for_message(message: &ir::InkTraitMessage) -> TokenStream2 { + let message_span = message.span(); + let message_inputs = message.inputs().map(|input| { + let input_span = input.span(); + let input_type = &*input.ty; + quote_spanned!(input_span=> + let _: () = ::ink_lang::codegen::utils::consume_type::< + ::ink_lang::codegen::DispatchInput<#input_type> + >(); + ) + }); + let message_output = message.output().map(|output_type| { + let output_span = output_type.span(); + quote_spanned!(output_span=> + let _: () = ::ink_lang::codegen::utils::consume_type::< + ::ink_lang::codegen::DispatchOutput<#output_type> + >(); + ) + }); + quote_spanned!(message_span=> + #( #message_inputs )* + #message_output + ) + } + + /// Generate the code for a single ink! trait message implemented by the trait registry. + /// + /// Generally the implementation of any ink! trait of the ink! trait registry + fn generate_registry_for_message( + &self, + message: &ir::InkTraitMessage, + selector: ir::Selector, + ) -> TokenStream2 { + let span = message.span(); + let ident = message.ident(); + let attrs = message.attrs(); + let output_ident = generator::output_ident(message.ident()); + let output_type = message + .output() + .cloned() + .unwrap_or_else(|| parse_quote! { () }); + let mut_token = message.receiver().is_ref_mut().then(|| quote! { mut }); + let (input_bindings, input_types) = + Self::input_bindings_and_types(message.inputs()); + let linker_error_ident = EnforcedErrors::cannot_call_trait_message( + self.trait_ident(), + message.ident(), + selector, + message.mutates(), + ); + let inout_guards = Self::generate_inout_guards_for_message(message); + let impl_body = match option_env!("INK_COVERAGE_REPORTING") { + Some("true") => { + quote! { + // The code coverage reporting CI stage links dead code, + // hence we have to provide an `unreachable!` here. If + // the invalid implementation above is linked this results + // in a linker error. + ::core::unreachable!( + "this is an invalid ink! message call which should never be possible." + ); + } + } + _ => { + quote! { + /// We enforce linking errors in case this is ever actually called. + /// These linker errors are properly resolved by the cargo-contract tool. + extern { + fn #linker_error_ident() -> !; + } + unsafe { #linker_error_ident() } + } + } + }; + quote_spanned!(span=> + type #output_ident = #output_type; + + #( #attrs )* + #[cold] + #[doc(hidden)] + fn #ident( + & #mut_token self + #( , #input_bindings : #input_types )* + ) -> Self::#output_ident { + #inout_guards + #impl_body + } + ) + } + + /// Returns a pair of input bindings `__ink_bindings_N` and types. + fn input_bindings_and_types( + inputs: ir::InputsIter, + ) -> (Vec, Vec<&syn::Type>) { + inputs + .enumerate() + .map(|(n, pat_type)| { + let binding = format_ident!("__ink_binding_{}", n); + let ty = &*pat_type.ty; + (binding, ty) + }) + .unzip() + } + + /// Phantom type that implements the following traits for every ink! trait: + /// + /// - `ink_lang::TraitCallForwarder` + /// + /// It is mainly used to access global information about the ink! trait. + fn generate_trait_info_object(&self) -> TokenStream2 { + let span = self.span(); + let trait_ident = self.trait_ident(); + let trait_info_ident = self.trait_def.trait_info_ident(); + let trait_call_forwarder = self.trait_def.call_forwarder_ident(); + let trait_message_info = self.generate_info_for_trait_messages(); + quote_spanned!(span => + #[doc(hidden)] + #[allow(non_camel_case_types)] + pub struct #trait_info_ident { + marker: ::core::marker::PhantomData E>, + } + + #trait_message_info + + impl ::ink_lang::reflect::TraitModulePath for #trait_info_ident + where + E: ::ink_env::Environment, + { + const PATH: &'static ::core::primitive::str = ::core::module_path!(); + + const NAME: &'static ::core::primitive::str = ::core::stringify!(#trait_ident); + } + + impl ::ink_lang::codegen::TraitCallForwarder for #trait_info_ident + where + E: ::ink_env::Environment, + { + type Forwarder = #trait_call_forwarder; + } + ) + } + + /// Generates the [`::ink_lang::reflect::TraitMessageInfo`] implementations for all + /// ink! messages defined by the ink! trait definition. + fn generate_info_for_trait_messages(&self) -> TokenStream2 { + let span = self.span(); + let message_impls = self.trait_def.trait_def.item().iter_items().filter_map( + |(trait_item, selector)| { + trait_item.filter_map_message().map(|message| { + self.generate_info_for_trait_for_message(&message, selector) + }) + }, + ); + quote_spanned!(span=> + #( #message_impls )* + ) + } + + /// Generates the [`::ink_lang::reflect::TraitMessageInfo`] implementation for a single + /// ink! message defined by the ink! trait definition. + fn generate_info_for_trait_for_message( + &self, + message: &ir::InkTraitMessage, + selector: ir::Selector, + ) -> TokenStream2 { + let span = message.span(); + let trait_info_ident = self.trait_def.trait_info_ident(); + let local_id = message.local_id(); + let selector_bytes = selector.hex_lits(); + let is_payable = message.ink_attrs().is_payable(); + quote_spanned!(span=> + impl ::ink_lang::reflect::TraitMessageInfo<#local_id> for #trait_info_ident { + const PAYABLE: ::core::primitive::bool = #is_payable; + + const SELECTOR: [::core::primitive::u8; 4usize] = [ #( #selector_bytes ),* ]; + } + ) + } +} diff --git a/crates/lang/codegen/src/lib.rs b/crates/lang/codegen/src/lib.rs index 8f60c7e585f..82be128b88f 100644 --- a/crates/lang/codegen/src/lib.rs +++ b/crates/lang/codegen/src/lib.rs @@ -12,12 +12,16 @@ // See the License for the specific language governing permissions and // limitations under the License. +mod enforced_error; mod generator; mod traits; -use self::traits::{ - GenerateCode, - GenerateCodeUsing, +use self::{ + enforced_error::EnforcedErrors, + traits::{ + GenerateCode, + GenerateCodeUsing, + }, }; use proc_macro2::TokenStream as TokenStream2; @@ -31,7 +35,7 @@ impl<'a> CodeGenerator for &'a ir::Contract { type Generator = generator::Contract<'a>; } -impl<'a> CodeGenerator for &'a ir::InkTrait { +impl<'a> CodeGenerator for &'a ir::InkTraitDefinition { type Generator = generator::TraitDefinition<'a>; } diff --git a/crates/lang/ir/src/ast/attr_args.rs b/crates/lang/ir/src/ast/attr_args.rs index 187e19ede5b..a32459ae311 100644 --- a/crates/lang/ir/src/ast/attr_args.rs +++ b/crates/lang/ir/src/ast/attr_args.rs @@ -24,6 +24,7 @@ use syn::{ ParseStream, }, punctuated::Punctuated, + spanned::Spanned, Token, }; @@ -130,9 +131,15 @@ impl MetaNameValue { name: syn::Path, input: ParseStream, ) -> Result { + let span = name.span(); Ok(MetaNameValue { name, - eq_token: input.parse()?, + eq_token: input.parse().map_err(|_error| { + format_err!( + span, + "ink! config options require an argument separated by '='", + ) + })?, value: input.parse()?, }) } diff --git a/crates/lang/ir/src/ir/attrs.rs b/crates/lang/ir/src/ir/attrs.rs index 65851c6afcc..99ba725f941 100644 --- a/crates/lang/ir/src/ir/attrs.rs +++ b/crates/lang/ir/src/ir/attrs.rs @@ -31,6 +31,35 @@ use proc_macro2::{ use std::collections::HashMap; use syn::spanned::Spanned; +/// An extension trait for [`syn::Attribute`] in order to query for documentation. +pub trait IsDocAttribute { + /// Returns `true` if the attribute is a Rust documentation attribute. + fn is_doc_attribute(&self) -> bool; + + /// Returns the contents of the Rust documentation attribute or `None`. + fn extract_docs(&self) -> Option; +} + +impl IsDocAttribute for syn::Attribute { + fn is_doc_attribute(&self) -> bool { + self.path.is_ident("doc") + } + + fn extract_docs(&self) -> Option { + if !self.is_doc_attribute() { + return None + } + if let Ok(syn::Meta::NameValue(syn::MetaNameValue { + lit: syn::Lit::Str(lit_str), + .. + })) = self.parse_meta() + { + return Some(lit_str.value()) + } + None + } +} + /// Either an ink! specific attribute, or another uninterpreted attribute. #[derive(Debug, PartialEq, Eq)] pub enum Attribute { @@ -467,7 +496,7 @@ impl core::fmt::Display for AttributeArg { Self::Constructor => write!(f, "constructor"), Self::Payable => write!(f, "payable"), Self::Selector(selector) => { - write!(f, "selector = {:?}", selector.as_bytes()) + write!(f, "selector = {:?}", selector.to_bytes()) } Self::Extension(extension) => { write!(f, "extension = {:?}", extension.into_u32()) @@ -483,7 +512,7 @@ impl core::fmt::Display for AttributeArg { } /// An ink! namespace applicable to a trait implementation block. -#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[derive(Debug, Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct Namespace { /// The underlying bytes. bytes: Vec, @@ -584,6 +613,7 @@ where /// - If there are duplicate ink! attributes. /// - If the first ink! attribute is not matching the expected. /// - If there are conflicting ink! attributes. +/// - if there are no ink! attributes. pub fn sanitize_attributes( parent_span: Span, attrs: I, @@ -609,6 +639,45 @@ where Ok((normalized, other_attrs)) } +/// Sanitizes the given optional attributes. +/// +/// This partitions the attributes into ink! and non-ink! attributes. +/// If there are ink! attributes they are normalized and deduplicated. +/// Also checks to guard against conflicting ink! attributes are provided. +/// +/// Returns the optional partitioned ink! and non-ink! attributes. +/// +/// # Parameters +/// +/// The `is_conflicting_attr` closure returns `Ok` if the attribute does not conflict, +/// returns `Err(None)` if the attribute conflicts but without providing further reasoning +/// and `Err(Some(reason))` if the attribute conflicts given additional context information. +/// +/// # Errors +/// +/// - If there are invalid ink! attributes. +/// - If there are duplicate ink! attributes. +/// - If there are conflicting ink! attributes. +pub fn sanitize_optional_attributes( + parent_span: Span, + attrs: I, + is_conflicting_attr: C, +) -> Result<(Option, Vec), syn::Error> +where + I: IntoIterator, + C: FnMut(&ir::AttributeFrag) -> Result<(), Option>, +{ + let (ink_attrs, rust_attrs) = ir::partition_attributes(attrs)?; + if ink_attrs.is_empty() { + return Ok((None, rust_attrs)) + } + let normalized = ir::InkAttribute::from_expanded(ink_attrs).map_err(|err| { + err.into_combine(format_err!(parent_span, "at this invocation",)) + })?; + normalized.ensure_no_conflicts(is_conflicting_attr)?; + Ok((Some(normalized), rust_attrs)) +} + impl Attribute { /// Returns `Ok` if the given iterator yields no duplicate ink! attributes. /// @@ -749,7 +818,7 @@ impl TryFrom for AttributeFrag { error ) })?; - let selector = Selector::from_bytes(selector_u32.to_be_bytes()); + let selector = Selector::from(selector_u32.to_be_bytes()); return Ok(AttributeFrag { ast: meta, arg: AttributeArg::Selector(selector), @@ -779,7 +848,7 @@ impl TryFrom for AttributeFrag { ), }) } - return Err(format_err!(name_value, "expecteded string type for `namespace` argument, e.g. #[ink(namespace = \"hello\")]")) + return Err(format_err!(name_value, "expected string type for `namespace` argument, e.g. #[ink(namespace = \"hello\")]")) } if name_value.path.is_ident("extension") { if let syn::Lit::Int(lit_int) = &name_value.lit { @@ -837,6 +906,11 @@ impl TryFrom for AttributeFrag { "topic" => Ok(AttributeArg::Topic), "payable" => Ok(AttributeArg::Payable), "impl" => Ok(AttributeArg::Implementation), + "selector" => Err(format_err!( + meta, + "encountered #[ink(selector)] that is missing its u32 parameter. \ + Did you mean #[ink(selector = value: u32)] ?" + )), "namespace" => Err(format_err!( meta, "encountered #[ink(namespace)] that is missing its string parameter. \ @@ -844,8 +918,8 @@ impl TryFrom for AttributeFrag { )), "extension" => Err(format_err!( meta, - "encountered #[ink(extension)] that is missing its N parameter. \ - Did you mean #[ink(extension = N: u32)] ?" + "encountered #[ink(extension)] that is missing its `id` parameter. \ + Did you mean #[ink(extension = id: u32)] ?" )), "handle_status" => Err(format_err!( meta, @@ -1042,7 +1116,7 @@ mod tests { #[ink(selector = 42)] }, Ok(test::Attribute::Ink(vec![AttributeArg::Selector( - Selector::from_bytes([0, 0, 0, 42]), + Selector::from([0, 0, 0, 42]), )])), ); assert_attribute_try_from( @@ -1050,7 +1124,7 @@ mod tests { #[ink(selector = 0xDEADBEEF)] }, Ok(test::Attribute::Ink(vec![AttributeArg::Selector( - Selector::from_bytes([0xDE, 0xAD, 0xBE, 0xEF]), + Selector::from([0xDE, 0xAD, 0xBE, 0xEF]), )])), ); } @@ -1119,7 +1193,7 @@ mod tests { syn::parse_quote! { #[ink(namespace = 42)] }, - Err("expecteded string type for `namespace` argument, e.g. #[ink(namespace = \"hello\")]"), + Err("expected string type for `namespace` argument, e.g. #[ink(namespace = \"hello\")]"), ); } @@ -1186,8 +1260,8 @@ mod tests { #[ink(extension)] }, Err( - "encountered #[ink(extension)] that is missing its N parameter. \ - Did you mean #[ink(extension = N: u32)] ?", + "encountered #[ink(extension)] that is missing its `id` parameter. \ + Did you mean #[ink(extension = id: u32)] ?", ), ); } diff --git a/crates/lang/ir/src/ir/blake2.rs b/crates/lang/ir/src/ir/blake2.rs index 46bf73d0c6a..43bd5f6b836 100644 --- a/crates/lang/ir/src/ir/blake2.rs +++ b/crates/lang/ir/src/ir/blake2.rs @@ -17,7 +17,7 @@ use proc_macro2::TokenStream as TokenStream2; use syn::spanned::Spanned as _; /// Computes the BLAKE-2b 256-bit hash for the given input and stores it in output. -pub fn blake2b_256(input: &[u8], output: &mut [u8]) { +pub fn blake2b_256(input: &[u8], output: &mut [u8; 32]) { use ::blake2::digest::{ Update as _, VariableOutput as _, diff --git a/crates/lang/ir/src/ir/chain_extension.rs b/crates/lang/ir/src/ir/chain_extension.rs index 90f83c5a59c..e589f547477 100644 --- a/crates/lang/ir/src/ir/chain_extension.rs +++ b/crates/lang/ir/src/ir/chain_extension.rs @@ -783,7 +783,8 @@ mod tests { } ); assert_ink_chain_extension_eq_err!( - error: "encountered #[ink(extension)] that is missing its N parameter. Did you mean #[ink(extension = N: u32)] ?", + error: "encountered #[ink(extension)] that is missing its `id` parameter. \ + Did you mean #[ink(extension = id: u32)] ?", pub trait MyChainExtension { #[ink(extension)] fn has_self_receiver(); diff --git a/crates/lang/ir/src/ir/item_impl/callable.rs b/crates/lang/ir/src/ir/item_impl/callable.rs index 4ee6ca7238e..d7ab1bca1cb 100644 --- a/crates/lang/ir/src/ir/item_impl/callable.rs +++ b/crates/lang/ir/src/ir/item_impl/callable.rs @@ -182,8 +182,9 @@ pub trait Callable { /// # Details /// /// Given -/// - the callable's identifier `i` -/// - the optionally set callable's selector `s` +/// +/// - the identifier `i` of the callable +/// - the optionally set selector `s` of the callable /// - the `impl` blocks trait path in case it implements a trait, `P` /// - 16 kB blocks optional user provided namespace `S` /// @@ -343,7 +344,7 @@ where } } }; - ir::Selector::new(&joined) + ir::Selector::compute(&joined) } /// Ensures that common invariants of externally callable ink! entities are met. @@ -406,7 +407,7 @@ pub(super) fn ensure_callable_invariants( if method_item.sig.abi.is_some() { return Err(format_err_spanned!( method_item.sig.abi, - "ink! {}s must have explicit ABI", + "ink! {}s must not have explicit ABI", kind, )) } @@ -484,19 +485,24 @@ pub struct InputsIter<'a> { iter: syn::punctuated::Iter<'a, syn::FnArg>, } -impl<'a> From<&'a ir::Message> for InputsIter<'a> { - fn from(message: &'a ir::Message) -> Self { +impl<'a> InputsIter<'a> { + /// Creates a new inputs iterator over the given `syn` punctuation. + pub(crate) fn new

(inputs: &'a syn::punctuated::Punctuated) -> Self { Self { - iter: message.item.sig.inputs.iter(), + iter: inputs.iter(), } } } +impl<'a> From<&'a ir::Message> for InputsIter<'a> { + fn from(message: &'a ir::Message) -> Self { + Self::new(&message.item.sig.inputs) + } +} + impl<'a> From<&'a ir::Constructor> for InputsIter<'a> { fn from(constructor: &'a ir::Constructor) -> Self { - Self { - iter: constructor.item.sig.inputs.iter(), - } + Self::new(&constructor.item.sig.inputs) } } @@ -514,6 +520,12 @@ impl<'a> Iterator for InputsIter<'a> { } } +impl<'a> ExactSizeIterator for InputsIter<'a> { + fn len(&self) -> usize { + self.iter.len() + } +} + #[cfg(test)] mod tests { use super::*; @@ -542,8 +554,8 @@ mod tests { impl ExpectedSelector { pub fn expected_selector(self) -> ir::Selector { match self { - Self::Raw(raw_selector) => ir::Selector::from_bytes(raw_selector), - Self::Blake2(blake2_input) => ir::Selector::new(&blake2_input), + Self::Raw(raw_selector) => ir::Selector::from(raw_selector), + Self::Blake2(blake2_input) => ir::Selector::compute(&blake2_input), } } } @@ -606,13 +618,13 @@ mod tests { assert_compose_selector::( syn::parse_quote! { #[ink(impl, namespace = "my_namespace")] - impl MyTrait for MyStorage {} + impl MyStorage {} }, syn::parse_quote! { #[ink(message)] fn my_message(&self) {} }, - b"my_namespace::MyTrait::my_message".to_vec(), + b"my_namespace::my_message".to_vec(), ); assert_compose_selector::( syn::parse_quote! { diff --git a/crates/lang/ir/src/ir/item_impl/constructor.rs b/crates/lang/ir/src/ir/item_impl/constructor.rs index 6d32e646275..f44be1ca1a6 100644 --- a/crates/lang/ir/src/ir/item_impl/constructor.rs +++ b/crates/lang/ir/src/ir/item_impl/constructor.rs @@ -501,7 +501,10 @@ mod tests { }, ]; for item_method in item_methods { - assert_try_from_fails(item_method, "ink! constructors must have explicit ABI") + assert_try_from_fails( + item_method, + "ink! constructors must not have explicit ABI", + ) } } diff --git a/crates/lang/ir/src/ir/item_impl/message.rs b/crates/lang/ir/src/ir/item_impl/message.rs index 90b44e9941c..9659531f801 100644 --- a/crates/lang/ir/src/ir/item_impl/message.rs +++ b/crates/lang/ir/src/ir/item_impl/message.rs @@ -19,7 +19,10 @@ use super::{ InputsIter, Visibility, }; -use crate::ir; +use crate::ir::{ + self, + utils, +}; use core::convert::TryFrom; use proc_macro2::{ Ident, @@ -279,6 +282,17 @@ impl Message { syn::ReturnType::Type(_, return_type) => Some(return_type), } } + + /// Returns a local ID unique to the ink! message with respect to its implementation block. + /// + /// # Note + /// + /// It is a compile error if two ink! trait messages share the same local ID. + /// Although the above scenario is very unlikely since the local ID is computed + /// solely by the identifier of the ink! message. + pub fn local_id(&self) -> u32 { + utils::local_message_id(self.ident()) + } } #[cfg(test)] @@ -675,7 +689,7 @@ mod tests { }, ]; for item_method in item_methods { - assert_try_from_fails(item_method, "ink! messages must have explicit ABI") + assert_try_from_fails(item_method, "ink! messages must not have explicit ABI") } } diff --git a/crates/lang/ir/src/ir/item_impl/mod.rs b/crates/lang/ir/src/ir/item_impl/mod.rs index 6f8f9dc8e07..1e7ed9acd50 100644 --- a/crates/lang/ir/src/ir/item_impl/mod.rs +++ b/crates/lang/ir/src/ir/item_impl/mod.rs @@ -292,7 +292,7 @@ impl TryFrom for ItemImpl { } } let (ink_attrs, other_attrs) = ir::partition_attributes(item_impl.attrs)?; - let mut namespace = None; + let mut namespace: Option = None; if !ink_attrs.is_empty() { let normalized = ir::InkAttribute::from_expanded(ink_attrs).map_err(|err| { @@ -308,6 +308,12 @@ impl TryFrom for ItemImpl { })?; namespace = normalized.namespace(); } + if namespace.is_some() && is_trait_impl { + return Err(format_err!( + impl_block_span, + "namespace ink! property is not allowed on ink! trait implementation blocks", + )) + } Ok(Self { attrs: other_attrs, defaultness: item_impl.defaultness, diff --git a/crates/lang/ir/src/ir/item_mod.rs b/crates/lang/ir/src/ir/item_mod.rs index d6c1b31034d..858035889df 100644 --- a/crates/lang/ir/src/ir/item_mod.rs +++ b/crates/lang/ir/src/ir/item_mod.rs @@ -196,7 +196,7 @@ impl ItemMod { #[ink(namespace = N:string)] on the implementation block to \ disambiguate overlapping selectors.", kind, - selector.as_bytes(), + selector.to_bytes(), ) .into_combine(format_err!( first_span, @@ -785,33 +785,6 @@ mod tests { ); } - #[test] - fn namespaced_overlapping_trait_impls_works() { - assert!( - >::try_from(syn::parse_quote! { - mod my_module { - #[ink(storage)] - pub struct MyStorage {} - - #[ink(namespace = "first")] - impl first::MyTrait for MyStorage { - #[ink(constructor)] - fn my_constructor() -> Self {} - - #[ink(message)] - fn my_message(&self) {} - } - - impl second::MyTrait for MyStorage { - #[ink(message)] - fn my_message(&self) {} - } - } - }) - .is_ok() - ); - } - #[test] fn allow_overlap_between_messages_and_constructors() { assert!( diff --git a/crates/lang/ir/src/ir/mod.rs b/crates/lang/ir/src/ir/mod.rs index b65bb7c1c39..5cbca003dcf 100644 --- a/crates/lang/ir/src/ir/mod.rs +++ b/crates/lang/ir/src/ir/mod.rs @@ -44,13 +44,17 @@ use self::attrs::{ first_ink_attribute, partition_attributes, sanitize_attributes, + sanitize_optional_attributes, AttributeArg, AttributeArgKind, AttributeFrag, InkAttribute, }; pub use self::{ - attrs::Namespace, + attrs::{ + IsDocAttribute, + Namespace, + }, blake2::{ blake2b_256, Blake2x256Macro, @@ -91,10 +95,11 @@ pub use self::{ selector::{ Selector, SelectorMacro, + TraitPrefix, }, trait_def::{ - InkTrait, - InkTraitConstructor, + InkItemTrait, + InkTraitDefinition, InkTraitItem, InkTraitMessage, IterInkTraitItems, diff --git a/crates/lang/ir/src/ir/selector.rs b/crates/lang/ir/src/ir/selector.rs index 1cd6e649c4b..59dc6615741 100644 --- a/crates/lang/ir/src/ir/selector.rs +++ b/crates/lang/ir/src/ir/selector.rs @@ -29,27 +29,106 @@ pub struct Selector { bytes: [u8; 4], } -impl Selector { - /// Creates a new selector from the given raw bytes. - pub fn from_bytes(bytes: [u8; 4]) -> Self { - Self { bytes } +/// The trait prefix to compute a composed selector for trait implementation blocks. +#[derive(Debug, Copy, Clone)] +pub struct TraitPrefix<'a> { + /// The namespace of the ink! trait definition. + /// + /// By default this is equal to the `module_path!` at the ink! trait definition site. + /// It can be customized by the ink! trait definition author using `#[ink(namespace = N)]` + /// ink! attribute. + namespace: Option<&'a syn::LitStr>, + /// The Rust identifier of the ink! trait definition. + trait_ident: &'a syn::Ident, +} + +impl<'a> TraitPrefix<'a> { + /// Creates a new trait prefix. + pub fn new(trait_ident: &'a syn::Ident, namespace: Option<&'a syn::LitStr>) -> Self { + Self { + namespace, + trait_ident, + } } + /// Returns a vector over the bytes of the namespace. + pub fn namespace_bytes(&self) -> Vec { + self.namespace + .map(|namespace| namespace.value().into_bytes()) + .unwrap_or_default() + } + + /// Returns a shared reference to the Rust identifier of the trait. + pub fn trait_ident(&self) -> &'a syn::Ident { + self.trait_ident + } +} + +impl Selector { /// Computes the BLAKE-2 256-bit based selector from the given input bytes. - pub fn new(input: &[u8]) -> Self { + pub fn compute(input: &[u8]) -> Self { let mut output = [0; 32]; blake2b_256(input, &mut output); - Self::from_bytes([output[0], output[1], output[2], output[3]]) + Self::from([output[0], output[1], output[2], output[3]]) } - /// Returns the underlying four bytes. - pub fn as_bytes(&self) -> &[u8; 4] { - &self.bytes + /// # Note + /// + /// - `trait_prefix` is `None` when computing the selector of ink! constructors + /// and messages in inherent implementation blocks. + /// - `trait_prefix` is `Some` when computing the selector of ink! constructors + /// and messages in trait implementation blocks. In this case the `namespace` + /// is either the full path of the trait definition gained by Rust's + /// `module_path!` macro by default or it is customized by manual application + /// of the `#[ink(namespace = "my_namespace")]` ink! attribute. In the + /// example `my_namespace` concatenated with `::` and the identifier of the + /// trait definition would then be part of the provided `trait_prefix` parameter. + /// - `fn_ident` refers to the ink! constructor or message identifier. + /// + /// # Inherent Implementation Blocks + /// + /// For inherent implementation blocks, when `trait_prefix` is `None` the composed + /// selector is computed as follows: + /// + /// 1. Apply `BLAKE2` 256-bit hash `H` on the bytes of the ASCII representation of + /// the `fn_ident` identifier. + /// 1. The first 4 bytes of `H` make up the selector. + /// + /// # Trait Implementation Blocks + /// + /// For trait implementation blocks, when `trait_prefix` is + /// `Some((namespace, trait_ident))` the composed selector is computed as follows: + /// + /// 1. Compute the ASCII byte representation of `fn_ident` and call it `F`. + /// 1. Compute the ASCII byte representation of `namespace` and call it `N`. + /// 1. Compute the ASCII byte representation of `trait_ident` and call it `T`. + /// 1. Concatenate `N`, `T` and `F` using `::` as separator and call it `C`. + /// 1. Apply the `BLAKE2` 256-bit hash `H` of `C`. + /// 1. The first 4 bytes of `H` make up the selector. + pub fn compose<'a, T>(trait_prefix: T, fn_ident: &syn::Ident) -> Self + where + T: Into>>, + { + let fn_ident = fn_ident.to_string().into_bytes(); + let input_bytes: Vec = match trait_prefix.into() { + Some(trait_prefix) => { + let namespace = trait_prefix.namespace_bytes(); + let trait_ident = trait_prefix.trait_ident().to_string().into_bytes(); + let separator = &b"::"[..]; + if namespace.is_empty() { + [&trait_ident[..], &fn_ident[..]].join(separator) + } else { + [&namespace[..], &trait_ident[..], &fn_ident[..]].join(separator) + } + } + None => fn_ident.to_vec(), + }; + Self::compute(&input_bytes) } - /// Returns a unique identifier as `usize`. - pub fn unique_id(self) -> usize { - self.into_be_u32() as usize + /// Returns the underlying four bytes. + pub fn to_bytes(&self) -> [u8; 4] { + self.bytes } /// Returns the big-endian `u32` representation of the selector bytes. @@ -65,7 +144,7 @@ impl Selector { impl From<[u8; 4]> for Selector { fn from(bytes: [u8; 4]) -> Self { - Self::from_bytes(bytes) + Self { bytes } } } @@ -122,7 +201,7 @@ impl TryFrom for SelectorMacro { )) } }; - let selector = Selector::new(&input_bytes); + let selector = Selector::compute(&input_bytes); Ok(Self { selector, input: lit, @@ -137,7 +216,7 @@ mod tests { #[test] fn hex_lits_works() { - let hex_lits = Selector::from_bytes([0xC0, 0xDE, 0xCA, 0xFE]).hex_lits(); + let hex_lits = Selector::from([0xC0, 0xDE, 0xCA, 0xFE]).hex_lits(); assert_eq!( hex_lits, [ diff --git a/crates/lang/ir/src/ir/trait_def.rs b/crates/lang/ir/src/ir/trait_def.rs deleted file mode 100644 index 33481c569e8..00000000000 --- a/crates/lang/ir/src/ir/trait_def.rs +++ /dev/null @@ -1,1116 +0,0 @@ -// 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::{ - ir, - ir::idents_lint, -}; -use core::convert::TryFrom; -use proc_macro2::{ - Ident, - Span, - TokenStream as TokenStream2, -}; -use syn::{ - spanned::Spanned as _, - Result, -}; - -/// A checked ink! trait definition. -#[derive(Debug, PartialEq, Eq)] -pub struct InkTrait { - item: syn::ItemTrait, -} - -impl TryFrom for InkTrait { - type Error = syn::Error; - - fn try_from(item_trait: syn::ItemTrait) -> core::result::Result { - idents_lint::ensure_no_ink_identifiers(&item_trait)?; - Self::analyse_properties(&item_trait)?; - Self::analyse_items(&item_trait)?; - Ok(Self { item: item_trait }) - } -} - -impl InkTrait { - /// Returns the hash to verify that the trait definition has been checked. - pub fn compute_verify_hash( - trait_name: &Ident, - constructors: C, - messages: M, - ) -> [u8; 32] - where - // Name and number of inputs. - C: Iterator, - // Name, number of inputs and true if message may mutate storage. - M: Iterator, - { - let mut constructors = constructors - .map(|(name, len_inputs)| { - [name.to_string(), len_inputs.to_string()].join(":") - }) - .collect::>(); - let mut messages = messages - .map(|(name, len_inputs, mutability)| { - let mutability = match mutability { - true => "w", - false => "r", - }; - [ - name.to_string(), - len_inputs.to_string(), - mutability.to_string(), - ] - .join(":") - }) - .collect::>(); - constructors.sort_unstable(); - messages.sort_unstable(); - let joined_constructors = constructors.join(","); - let joined_messages = messages.join(","); - let mut buffer = vec!["__ink_trait".to_string(), trait_name.to_string()]; - if !joined_constructors.is_empty() { - buffer.push(joined_constructors); - } - if !joined_messages.is_empty() { - buffer.push(joined_messages); - } - let buffer = buffer.join("::").into_bytes(); - use blake2::digest::generic_array::sequence::Split as _; - let (head_32, _rest) = - ::digest(&buffer).split(); - head_32.into() - } - - /// Returns the hash to verify that the trait definition has been checked. - pub fn verify_hash(&self) -> [u8; 32] { - let trait_name = self.ident(); - Self::compute_verify_hash( - trait_name, - self.iter_items() - .flat_map(InkTraitItem::filter_map_constructor) - .map(|constructor| { - let name = constructor.sig().ident.clone(); - let len_inputs = constructor.sig().inputs.len(); - (name, len_inputs) - }), - self.iter_items() - .flat_map(InkTraitItem::filter_map_message) - .map(|message| { - let name = message.sig().ident.clone(); - let len_inputs = message.sig().inputs.len(); - let mutability = message.mutates(); - (name, len_inputs, mutability) - }), - ) - } -} - -/// Iterator over all the ink! trait items of an ink! trait definition. -pub struct IterInkTraitItems<'a> { - iter: core::slice::Iter<'a, syn::TraitItem>, -} - -impl<'a> IterInkTraitItems<'a> { - /// Creates a new iterator yielding ink! trait items. - fn new(item_trait: &'a InkTrait) -> Self { - Self { - iter: item_trait.item.items.iter(), - } - } -} - -impl<'a> Iterator for IterInkTraitItems<'a> { - type Item = InkTraitItem<'a>; - - fn next(&mut self) -> Option { - 'outer: loop { - match self.iter.next() { - None => return None, - Some(syn::TraitItem::Method(method)) => { - let first_attr = ir::first_ink_attribute(&method.attrs) - .ok() - .flatten() - .expect("unexpected missing ink! attribute for trait method") - .first() - .kind() - .clone(); - match first_attr { - ir::AttributeArg::Constructor => { - return Some(InkTraitItem::Constructor(InkTraitConstructor { - item: method, - })) - } - ir::AttributeArg::Message => { - return Some(InkTraitItem::Message(InkTraitMessage { - item: method, - })) - } - _ => continue 'outer, - } - } - Some(_) => continue 'outer, - } - } - } -} - -/// An ink! item within an ink! trait definition. -#[derive(Debug, Clone)] -pub enum InkTraitItem<'a> { - Constructor(InkTraitConstructor<'a>), - Message(InkTraitMessage<'a>), -} - -impl<'a> InkTraitItem<'a> { - /// Returns `Some` if the ink! trait item is a constructor. - pub fn filter_map_constructor(self) -> Option> { - match self { - Self::Constructor(ink_trait_constructor) => Some(ink_trait_constructor), - _ => None, - } - } - - /// Returns `Some` if the ink! trait item is a message. - pub fn filter_map_message(self) -> Option> { - match self { - Self::Message(ink_trait_message) => Some(ink_trait_message), - _ => None, - } - } -} - -/// Returns all non-ink! attributes. -/// -/// # Panics -/// -/// If there are malformed ink! attributes in the input. -fn extract_rust_attributes(attributes: &[syn::Attribute]) -> Vec { - let (_ink_attrs, rust_attrs) = ir::partition_attributes(attributes.to_vec()) - .expect("encountered unexpected invalid ink! attributes"); - rust_attrs -} - -/// A checked ink! constructor of an ink! trait definition. -#[derive(Debug, Clone)] -pub struct InkTraitConstructor<'a> { - item: &'a syn::TraitItemMethod, -} - -impl<'a> InkTraitConstructor<'a> { - /// Returns all non-ink! attributes. - pub fn attrs(&self) -> Vec { - extract_rust_attributes(&self.item.attrs) - } - - /// Returns the original signature of the ink! constructor. - pub fn sig(&self) -> &syn::Signature { - &self.item.sig - } - - /// Returns the span of the ink! constructor. - pub fn span(&self) -> Span { - self.item.span() - } -} - -/// A checked ink! message of an ink! trait definition. -#[derive(Debug, Clone)] -pub struct InkTraitMessage<'a> { - item: &'a syn::TraitItemMethod, -} - -impl<'a> InkTraitMessage<'a> { - /// Returns all non-ink! attributes. - pub fn attrs(&self) -> Vec { - extract_rust_attributes(&self.item.attrs) - } - - /// Returns the original signature of the ink! message. - pub fn sig(&self) -> &syn::Signature { - &self.item.sig - } - - /// Returns the span of the ink! message. - pub fn span(&self) -> Span { - self.item.span() - } - - /// Returns `true` if the ink! message may mutate the contract storage. - pub fn mutates(&self) -> bool { - self.sig() - .receiver() - .map(|fn_arg| { - match fn_arg { - syn::FnArg::Receiver(receiver) if receiver.mutability.is_some() => { - true - } - syn::FnArg::Typed(pat_type) => { - match &*pat_type.ty { - syn::Type::Reference(reference) - if reference.mutability.is_some() => - { - true - } - _ => false, - } - } - _ => false, - } - }) - .expect("encountered missing receiver for ink! message") - } -} - -impl InkTrait { - /// Returns `Ok` if the trait matches all requirements for an ink! trait definition. - pub fn new(attr: TokenStream2, input: TokenStream2) -> Result { - if !attr.is_empty() { - return Err(format_err_spanned!( - attr, - "unexpected attribute input for ink! trait definition" - )) - } - let item_trait = syn::parse2::(input)?; - InkTrait::try_from(item_trait) - } - - /// Returns span of the ink! trait definition. - pub fn span(&self) -> Span { - self.item.span() - } - - /// Returns the attributes of the ink! trait definition. - pub fn attrs(&self) -> &[syn::Attribute] { - &self.item.attrs - } - - /// Returns the identifier of the ink! trait definition. - pub fn ident(&self) -> &Ident { - &self.item.ident - } - - /// Returns an iterator yielding the ink! specific items of the ink! trait definition. - pub fn iter_items(&self) -> IterInkTraitItems { - IterInkTraitItems::new(self) - } - - /// Analyses the properties of the ink! trait definition. - /// - /// # Errors - /// - /// - If the trait has been defined as `unsafe`. - /// - If the trait is an automatically implemented trait (`auto trait`). - /// - If the trait is generic over some set of types. - /// - If the trait's visibility is not public (`pub`). - fn analyse_properties(item_trait: &syn::ItemTrait) -> Result<()> { - if let Some(unsafety) = &item_trait.unsafety { - return Err(format_err_spanned!( - unsafety, - "ink! trait definitions cannot be unsafe" - )) - } - if let Some(auto) = &item_trait.auto_token { - return Err(format_err_spanned!( - auto, - "ink! trait definitions cannot be automatically implemented traits" - )) - } - if !item_trait.generics.params.is_empty() { - return Err(format_err_spanned!( - item_trait.generics.params, - "ink! trait definitions must not be generic" - )) - } - if !matches!(item_trait.vis, syn::Visibility::Public(_)) { - return Err(format_err_spanned!( - item_trait.vis, - "ink! trait definitions must have public visibility" - )) - } - if !item_trait.supertraits.is_empty() { - return Err(format_err_spanned!( - item_trait.supertraits, - "ink! trait definitions with super-traits are not supported, yet" - )) - } - Ok(()) - } - - /// Returns `Ok` if all trait items respects the requirements for an ink! trait definition. - /// - /// # Errors - /// - /// - If the trait contains an unsupported trait item such as - /// - associated constants (`const`) - /// - associated types (`type`) - /// - macros definitions or usages - /// - unknown token sequences (`Verbatim`s) - /// - methods with default implementations - /// - If the trait contains methods which do not respect the ink! trait definition requirements: - /// - All trait methods need to be declared as either `#[ink(message)]` or `#[ink(constructor)]` - /// and need to respect their respective rules. - /// - /// # Note - /// - /// Associated types and constants might be allowed in the future. - fn analyse_items(item_trait: &syn::ItemTrait) -> Result<()> { - for trait_item in &item_trait.items { - match trait_item { - syn::TraitItem::Const(const_trait_item) => { - return Err(format_err_spanned!( - const_trait_item, - "associated constants in ink! trait definitions are not supported, yet" - )) - } - syn::TraitItem::Macro(macro_trait_item) => { - return Err(format_err_spanned!( - macro_trait_item, - "macros in ink! trait definitions are not supported" - )) - } - syn::TraitItem::Type(type_trait_item) => { - return Err(format_err_spanned!( - type_trait_item, - "associated types in ink! trait definitions are not supported, yet" - )) - } - syn::TraitItem::Verbatim(verbatim) => { - return Err(format_err_spanned!( - verbatim, - "encountered unsupported item in ink! trait definition" - )) - } - syn::TraitItem::Method(method_trait_item) => { - Self::analyse_methods(method_trait_item)?; - } - unknown => { - return Err(format_err_spanned!( - unknown, - "encountered unknown or unsupported item in ink! trait definition" - )) - } - } - } - Ok(()) - } - - /// Analyses an ink! method that can be either an ink! message or constructor. - /// - /// # Errors - /// - /// - If the method declared as `unsafe`, `const` or `async`. - /// - If the method has some explicit API. - /// - If the method is variadic or has generic parameters. - /// - If the method does not respect the properties of either an - /// ink! message or ink! constructor. - fn analyse_methods(method: &syn::TraitItemMethod) -> Result<()> { - if let Some(default_impl) = &method.default { - return Err(format_err_spanned!( - default_impl, - "ink! trait methods with default implementations are not supported" - )) - } - if let Some(constness) = &method.sig.constness { - return Err(format_err_spanned!( - constness, - "const ink! trait methods are not supported" - )) - } - if let Some(asyncness) = &method.sig.asyncness { - return Err(format_err_spanned!( - asyncness, - "async ink! trait methods are not supported" - )) - } - if let Some(unsafety) = &method.sig.unsafety { - return Err(format_err_spanned!( - unsafety, - "unsafe ink! trait methods are not supported" - )) - } - if let Some(abi) = &method.sig.abi { - return Err(format_err_spanned!( - abi, - "ink! trait methods with non default ABI are not supported" - )) - } - if let Some(variadic) = &method.sig.variadic { - return Err(format_err_spanned!( - variadic, - "variadic ink! trait methods are not supported" - )) - } - if !method.sig.generics.params.is_empty() { - return Err(format_err_spanned!( - method.sig.generics.params, - "generic ink! trait methods are not supported" - )) - } - match ir::first_ink_attribute(&method.attrs) { - Ok(Some(ink_attr)) => { - match ink_attr.first().kind() { - ir::AttributeArg::Message => { - Self::analyse_message(method)?; - } - ir::AttributeArg::Constructor => { - Self::analyse_constructor(method)?; - } - _unsupported => { - return Err(format_err_spanned!( - method, - "encountered unsupported ink! attribute for ink! trait method", - )) - } - } - } - Ok(None) => { - return Err(format_err_spanned!( - method, - "missing #[ink(message)] or #[ink(constructor)] flags on ink! trait method" - )) - } - Err(err) => return Err(err), - } - Ok(()) - } - - /// Analyses the properties of an ink! constructor. - /// - /// # Errors - /// - /// - If the constructor has a `self` receiver as first argument. - /// - If the constructor has no `Self` return type. - fn analyse_constructor(constructor: &syn::TraitItemMethod) -> Result<()> { - ir::sanitize_attributes( - constructor.span(), - constructor.attrs.clone(), - &ir::AttributeArgKind::Constructor, - |arg| { - match arg.kind() { - ir::AttributeArg::Constructor => Ok(()), - _ => Err(None), - } - }, - )?; - if let Some(receiver) = constructor.sig.receiver() { - return Err(format_err_spanned!( - receiver, - "ink! constructors must not have a `self` receiver", - )) - } - match &constructor.sig.output { - syn::ReturnType::Default => { - return Err(format_err_spanned!( - constructor.sig, - "ink! constructors must return Self" - )) - } - syn::ReturnType::Type(_, ty) => { - match &**ty { - syn::Type::Path(type_path) => { - if !type_path.path.is_ident("Self") { - return Err(format_err_spanned!( - type_path.path, - "ink! constructors must return Self" - )) - } - } - unknown => { - return Err(format_err_spanned!( - unknown, - "ink! constructors must return Self" - )) - } - } - } - } - Ok(()) - } - - /// Analyses the properties of an ink! message. - /// - /// # Errors - /// - /// - If the message has no `&self` or `&mut self` receiver. - fn analyse_message(message: &syn::TraitItemMethod) -> Result<()> { - ir::sanitize_attributes( - message.span(), - message.attrs.clone(), - &ir::AttributeArgKind::Message, - |arg| { - match arg.kind() { - ir::AttributeArg::Message => Ok(()), - _ => Err(None), - } - }, - )?; - match message.sig.receiver() { - None | Some(syn::FnArg::Typed(_)) => { - return Err(format_err_spanned!( - message.sig, - "missing or malformed `&self` or `&mut self` receiver for ink! message", - )) - } - Some(syn::FnArg::Receiver(receiver)) => { - if receiver.reference.is_none() { - return Err(format_err_spanned!( - receiver, - "self receiver of ink! message must be `&self` or `&mut self`" - )) - } - } - } - Ok(()) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - /// Checks if the token stream in `$trait_def` results in the expected error message. - macro_rules! assert_ink_trait_eq_err { - ( error: $err_str:literal, $($trait_def:tt)* ) => { - assert_eq!( - >::try_from(syn::parse_quote! { - $( $trait_def )* - }) - .map_err(|err| err.to_string()), - Err( - $err_str.to_string() - ) - ) - }; - } - - #[test] - fn unsafe_trait_def_is_denied() { - assert_ink_trait_eq_err!( - error: "ink! trait definitions cannot be unsafe", - pub unsafe trait MyTrait {} - ); - } - - #[test] - fn auto_trait_def_is_denied() { - assert_ink_trait_eq_err!( - error: "ink! trait definitions cannot be automatically implemented traits", - pub auto trait MyTrait {} - ); - } - - #[test] - fn non_pub_trait_def_is_denied() { - assert_ink_trait_eq_err!( - error: "ink! trait definitions must have public visibility", - trait MyTrait {} - ); - assert_ink_trait_eq_err!( - error: "ink! trait definitions must have public visibility", - pub(crate) trait MyTrait {} - ); - } - - #[test] - fn generic_trait_def_is_denied() { - assert_ink_trait_eq_err!( - error: "ink! trait definitions must not be generic", - pub trait MyTrait {} - ); - } - - #[test] - fn trait_def_with_supertraits_is_denied() { - assert_ink_trait_eq_err!( - error: "ink! trait definitions with super-traits are not supported, yet", - pub trait MyTrait: SuperTrait {} - ); - } - - #[test] - fn trait_def_containing_const_item_is_denied() { - assert_ink_trait_eq_err!( - error: "associated constants in ink! trait definitions are not supported, yet", - pub trait MyTrait { - const T: i32; - } - ); - } - - #[test] - fn trait_def_containing_associated_type_is_denied() { - assert_ink_trait_eq_err!( - error: "associated types in ink! trait definitions are not supported, yet", - pub trait MyTrait { - type Type; - } - ); - } - - #[test] - fn trait_def_containing_macro_is_denied() { - assert_ink_trait_eq_err!( - error: "macros in ink! trait definitions are not supported", - pub trait MyTrait { - my_macro_call!(); - } - ); - } - - #[test] - fn trait_def_containing_non_flagged_method_is_denied() { - assert_ink_trait_eq_err!( - error: "missing #[ink(message)] or #[ink(constructor)] flags on ink! trait method", - pub trait MyTrait { - fn non_flagged_1(&self); - } - ); - assert_ink_trait_eq_err!( - error: "missing #[ink(message)] or #[ink(constructor)] flags on ink! trait method", - pub trait MyTrait { - fn non_flagged_2(&mut self); - } - ); - assert_ink_trait_eq_err!( - error: "missing #[ink(message)] or #[ink(constructor)] flags on ink! trait method", - pub trait MyTrait { - fn non_flagged_3() -> Self; - } - ); - } - - #[test] - fn trait_def_containing_default_implemented_methods_is_denied() { - assert_ink_trait_eq_err!( - error: "ink! trait methods with default implementations are not supported", - pub trait MyTrait { - #[ink(constructor)] - fn default_implemented() -> Self {} - } - ); - assert_ink_trait_eq_err!( - error: "ink! trait methods with default implementations are not supported", - pub trait MyTrait { - #[ink(message)] - fn default_implemented(&self) {} - } - ); - } - - #[test] - fn trait_def_containing_const_methods_is_denied() { - assert_ink_trait_eq_err!( - error: "const ink! trait methods are not supported", - pub trait MyTrait { - #[ink(constructor)] - const fn const_constructor() -> Self; - } - ); - assert_ink_trait_eq_err!( - error: "const ink! trait methods are not supported", - pub trait MyTrait { - #[ink(message)] - const fn const_message(&self); - } - ); - } - - #[test] - fn trait_def_containing_async_methods_is_denied() { - assert_ink_trait_eq_err!( - error: "async ink! trait methods are not supported", - pub trait MyTrait { - #[ink(constructor)] - async fn const_constructor() -> Self; - } - ); - assert_ink_trait_eq_err!( - error: "async ink! trait methods are not supported", - pub trait MyTrait { - #[ink(message)] - async fn const_message(&self); - } - ); - } - - #[test] - fn trait_def_containing_unsafe_methods_is_denied() { - assert_ink_trait_eq_err!( - error: "unsafe ink! trait methods are not supported", - pub trait MyTrait { - #[ink(constructor)] - unsafe fn const_constructor() -> Self; - } - ); - assert_ink_trait_eq_err!( - error: "unsafe ink! trait methods are not supported", - pub trait MyTrait { - #[ink(message)] - unsafe fn const_message(&self); - } - ); - } - - #[test] - fn trait_def_containing_methods_using_explicit_abi_is_denied() { - assert_ink_trait_eq_err!( - error: "ink! trait methods with non default ABI are not supported", - pub trait MyTrait { - #[ink(constructor)] - extern fn const_constructor() -> Self; - } - ); - assert_ink_trait_eq_err!( - error: "ink! trait methods with non default ABI are not supported", - pub trait MyTrait { - #[ink(message)] - extern fn const_message(&self); - } - ); - } - - #[test] - fn trait_def_containing_variadic_methods_is_denied() { - assert_ink_trait_eq_err!( - error: "variadic ink! trait methods are not supported", - pub trait MyTrait { - #[ink(constructor)] - fn const_constructor(...) -> Self; - } - ); - assert_ink_trait_eq_err!( - error: "variadic ink! trait methods are not supported", - pub trait MyTrait { - #[ink(message)] - fn const_message(&self, ...); - } - ); - } - - #[test] - fn trait_def_containing_generic_methods_is_denied() { - assert_ink_trait_eq_err!( - error: "generic ink! trait methods are not supported", - pub trait MyTrait { - #[ink(constructor)] - fn const_constructor() -> Self; - } - ); - assert_ink_trait_eq_err!( - error: "generic ink! trait methods are not supported", - pub trait MyTrait { - #[ink(message)] - fn const_message(&self); - } - ); - } - - #[test] - fn trait_def_containing_method_with_unsupported_ink_attribute_is_denied() { - assert_ink_trait_eq_err!( - error: "encountered unsupported ink! attribute for ink! trait method", - pub trait MyTrait { - #[ink(payable)] - fn unsupported_ink_attribute(&self); - } - ); - assert_ink_trait_eq_err!( - error: "unknown ink! attribute (path)", - pub trait MyTrait { - #[ink(unknown)] - fn unknown_ink_attribute(&self); - } - ); - } - - #[test] - fn trait_def_containing_invalid_constructor_is_denied() { - assert_ink_trait_eq_err!( - error: "ink! constructors must not have a `self` receiver", - pub trait MyTrait { - #[ink(constructor)] - fn has_self_receiver(&self) -> Self; - } - ); - assert_ink_trait_eq_err!( - error: "ink! constructors must not have a `self` receiver", - pub trait MyTrait { - #[ink(constructor)] - fn has_self_receiver(&mut self) -> Self; - } - ); - assert_ink_trait_eq_err!( - error: "ink! constructors must not have a `self` receiver", - pub trait MyTrait { - #[ink(constructor)] - fn has_self_receiver(self) -> Self; - } - ); - assert_ink_trait_eq_err!( - error: "ink! constructors must not have a `self` receiver", - pub trait MyTrait { - #[ink(constructor)] - fn has_self_receiver(self: &Self) -> Self; - } - ); - assert_ink_trait_eq_err!( - error: "ink! constructors must not have a `self` receiver", - pub trait MyTrait { - #[ink(constructor)] - fn has_self_receiver(self: Self) -> Self; - } - ); - assert_ink_trait_eq_err!( - error: "ink! constructors must return Self", - pub trait MyTrait { - #[ink(constructor)] - fn does_not_return_self(); - } - ); - assert_ink_trait_eq_err!( - error: "ink! constructors must return Self", - pub trait MyTrait { - #[ink(constructor)] - fn does_not_return_self() -> i32; - } - ); - } - - #[test] - fn trait_def_containing_invalid_message_is_denied() { - assert_ink_trait_eq_err!( - error: "missing or malformed `&self` or `&mut self` receiver for ink! message", - pub trait MyTrait { - #[ink(message)] - fn does_not_return_self(); - } - ); - assert_ink_trait_eq_err!( - error: "missing or malformed `&self` or `&mut self` receiver for ink! message", - pub trait MyTrait { - #[ink(message)] - fn does_not_return_self(self: &Self); - } - ); - assert_ink_trait_eq_err!( - error: "self receiver of ink! message must be `&self` or `&mut self`", - pub trait MyTrait { - #[ink(message)] - fn does_not_return_self(self); - } - ); - } - - #[test] - fn trait_def_containing_constructor_with_invalid_ink_attributes_is_denied() { - assert_ink_trait_eq_err!( - error: "encountered duplicate ink! attribute", - pub trait MyTrait { - #[ink(constructor)] - #[ink(constructor)] - fn does_not_return_self() -> Self; - } - ); - assert_ink_trait_eq_err!( - error: "encountered conflicting ink! attribute argument", - pub trait MyTrait { - #[ink(constructor)] - #[ink(message)] - fn does_not_return_self() -> Self; - } - ); - assert_ink_trait_eq_err!( - error: "encountered conflicting ink! attribute argument", - pub trait MyTrait { - #[ink(constructor)] - #[ink(payable)] - fn does_not_return_self() -> Self; - } - ); - } - - #[test] - fn trait_def_containing_message_with_invalid_ink_attributes_is_denied() { - assert_ink_trait_eq_err!( - error: "encountered duplicate ink! attribute", - pub trait MyTrait { - #[ink(message)] - #[ink(message)] - fn does_not_return_self(&self); - } - ); - assert_ink_trait_eq_err!( - error: "encountered conflicting ink! attribute argument", - pub trait MyTrait { - #[ink(message)] - #[ink(constructor)] - fn does_not_return_self(&self); - } - ); - assert_ink_trait_eq_err!( - error: "encountered conflicting ink! attribute argument", - pub trait MyTrait { - #[ink(message)] - #[ink(payable)] - fn does_not_return_self(&self); - } - ); - } - - #[test] - fn trait_def_is_ok() { - assert!( - >::try_from(syn::parse_quote! { - pub trait MyTrait { - #[ink(constructor)] - fn my_constructor() -> Self; - #[ink(message)] - fn my_message(&self); - #[ink(message)] - fn my_message_mut(&mut self); - } - }) - .is_ok() - ) - } - - #[test] - fn iter_constructors_works() { - let ink_trait = - >::try_from(syn::parse_quote! { - pub trait MyTrait { - #[ink(constructor)] - fn constructor_1() -> Self; - #[ink(constructor)] - fn constructor_2() -> Self; - #[ink(message)] - fn message_1(&self); - #[ink(message)] - fn message_2(&mut self); - } - }) - .unwrap(); - let actual = ink_trait - .iter_items() - .flat_map(|item| { - item.filter_map_constructor() - .map(|constructor| constructor.sig().ident.to_string()) - }) - .collect::>(); - let expected = vec!["constructor_1".to_string(), "constructor_2".to_string()]; - assert_eq!(actual, expected); - } - - #[test] - fn iter_messages_works() { - let ink_trait = - >::try_from(syn::parse_quote! { - pub trait MyTrait { - #[ink(constructor)] - fn constructor_1() -> Self; - #[ink(constructor)] - fn constructor_2() -> Self; - #[ink(message)] - fn message_1(&self); - #[ink(message)] - fn message_2(&mut self); - } - }) - .unwrap(); - let actual = ink_trait - .iter_items() - .flat_map(|item| { - item.filter_map_message() - .map(|message| message.sig().ident.to_string()) - }) - .collect::>(); - let expected = vec!["message_1".to_string(), "message_2".to_string()]; - assert_eq!(actual, expected); - } - - fn assert_verify_hash2_works_with(ink_trait: InkTrait, expected: &str) { - let expected = expected.to_string().into_bytes(); - let actual = ink_trait.verify_hash(); - let expected: [u8; 32] = { - use blake2::digest::generic_array::sequence::Split as _; - let (head_32, _rest) = - ::digest(&expected).split(); - head_32.into() - }; - assert_eq!(actual, expected); - } - - macro_rules! ink_trait { - ( $($tt:tt)* ) => {{ - >::try_from(syn::parse_quote! { - $( $tt )* - }) - .unwrap() - }}; - } - - #[test] - fn verify_hash_works() { - let ink_trait = ink_trait! { - pub trait MyTrait { - #[ink(constructor)] - fn constructor_1() -> Self; - #[ink(constructor)] - fn constructor_2(a: i32, b: i32) -> Self; - #[ink(message)] - fn message_1(&self); - #[ink(message)] - fn message_2(&mut self, a: i32, b: i32) -> i32; - } - }; - assert_verify_hash2_works_with( - ink_trait, - "__ink_trait::MyTrait::constructor_1:0,constructor_2:2::message_1:1:r,message_2:3:w" - ); - } - - #[test] - fn verify_hash_works_without_constructors() { - let ink_trait = ink_trait! { - pub trait MyTrait { - #[ink(message)] - fn message_1(&self); - #[ink(message)] - fn message_2(&mut self, a: i32, b: i32) -> i32; - } - }; - assert_verify_hash2_works_with( - ink_trait, - "__ink_trait::MyTrait::message_1:1:r,message_2:3:w", - ); - } - - #[test] - fn verify_hash_works_without_messages() { - let ink_trait = ink_trait! { - pub trait MyTrait { - #[ink(constructor)] - fn constructor_1() -> Self; - #[ink(constructor)] - fn constructor_2(a: i32, b: i32) -> Self; - } - }; - assert_verify_hash2_works_with( - ink_trait, - "__ink_trait::MyTrait::constructor_1:0,constructor_2:2", - ); - } -} diff --git a/crates/lang/ir/src/ir/trait_def/config.rs b/crates/lang/ir/src/ir/trait_def/config.rs new file mode 100644 index 00000000000..5cd790e8baf --- /dev/null +++ b/crates/lang/ir/src/ir/trait_def/config.rs @@ -0,0 +1,109 @@ +// 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::{ + ast, + error::ExtError as _, +}; +use core::convert::TryFrom; +use syn::spanned::Spanned; + +/// The ink! configuration. +#[derive(Debug, Default, PartialEq, Eq)] +pub struct TraitDefinitionConfig { + /// Captures the optional custom namespace for the ink! trait definition. + /// + /// # Note + /// + /// The namespace configuration parameter is used to influence the generated + /// selectors of the ink! trait messages. This is useful to disambiguate + /// ink! trait definitions with equal names. + namespace: Option, +} + +impl TraitDefinitionConfig { + /// Sets the namespace of the ink! trait definition configuration. + /// + /// # Note + /// + /// This is a test-only API. + #[cfg(test)] + pub fn with_namespace(mut self, namespace: &str) -> Self { + self.namespace = + Some(syn::LitStr::new(namespace, proc_macro2::Span::call_site())); + self + } +} + +/// Return an error to notify about duplicate ink! trait definition configuration arguments. +fn duplicate_config_err(fst: F, snd: S, name: &str) -> syn::Error +where + F: Spanned, + S: Spanned, +{ + format_err!( + snd.span(), + "encountered duplicate ink! trait definition `{}` configuration argument", + name, + ) + .into_combine(format_err!( + fst.span(), + "first `{}` configuration argument here", + name + )) +} + +impl TryFrom for TraitDefinitionConfig { + type Error = syn::Error; + + fn try_from(args: ast::AttributeArgs) -> Result { + let mut namespace: Option<(syn::LitStr, ast::MetaNameValue)> = None; + for arg in args.into_iter() { + if arg.name.is_ident("namespace") { + if let Some((_, meta_name_value)) = namespace { + return Err(duplicate_config_err(meta_name_value, arg, "namespace")) + } + if let ast::PathOrLit::Lit(syn::Lit::Str(lit_str)) = &arg.value { + if syn::parse_str::(&lit_str.value()).is_err() { + return Err(format_err_spanned!( + lit_str, + "encountered invalid Rust identifier for the ink! namespace configuration parameter" + )) + } + namespace = Some((lit_str.clone(), arg)) + } else { + return Err(format_err_spanned!( + arg, + "expected a string literal for `namespace` ink! trait definition configuration argument", + )) + } + } else { + return Err(format_err_spanned!( + arg, + "encountered unknown or unsupported ink! trait definition configuration argument", + )) + } + } + Ok(TraitDefinitionConfig { + namespace: namespace.map(|(value, _)| value), + }) + } +} + +impl TraitDefinitionConfig { + /// Returns the namespace configuration argument if any as string. + pub fn namespace(&self) -> Option<&syn::LitStr> { + self.namespace.as_ref() + } +} diff --git a/crates/lang/ir/src/ir/trait_def/item/iter.rs b/crates/lang/ir/src/ir/trait_def/item/iter.rs new file mode 100644 index 00000000000..38a5acd2638 --- /dev/null +++ b/crates/lang/ir/src/ir/trait_def/item/iter.rs @@ -0,0 +1,93 @@ +// 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::{ + ir, + InkItemTrait, + InkTraitItem, + InkTraitMessage, + Selector, +}; +use std::collections::HashMap; + +/// Iterator over all the ink! trait items of an ink! trait definition. +pub struct IterInkTraitItemsRaw<'a> { + iter: core::slice::Iter<'a, syn::TraitItem>, +} + +impl<'a> IterInkTraitItemsRaw<'a> { + /// Creates a new iterator yielding ink! trait items over the raw Rust trait definition. + pub(super) fn from_raw(item_trait: &'a syn::ItemTrait) -> Self { + Self { + iter: item_trait.items.iter(), + } + } +} + +impl<'a> Iterator for IterInkTraitItemsRaw<'a> { + type Item = InkTraitItem<'a>; + + fn next(&mut self) -> Option { + 'outer: loop { + match self.iter.next() { + None => return None, + Some(syn::TraitItem::Method(method)) => { + let first_attr = ir::first_ink_attribute(&method.attrs) + .ok() + .flatten() + .expect("unexpected missing ink! attribute for trait method") + .first() + .kind() + .clone(); + match first_attr { + ir::AttributeArg::Message => { + return Some(InkTraitItem::Message(InkTraitMessage::new( + method, + ))) + } + _ => continue 'outer, + } + } + Some(_) => continue 'outer, + } + } + } +} + +/// Iterator over all the ink! trait items of an ink! trait definition. +pub struct IterInkTraitItems<'a> { + iter: IterInkTraitItemsRaw<'a>, + message_selectors: &'a HashMap, +} + +impl<'a> IterInkTraitItems<'a> { + /// Creates a new iterator yielding ink! trait items. + pub(super) fn new(item_trait: &'a InkItemTrait) -> Self { + Self { + iter: IterInkTraitItemsRaw::from_raw(&item_trait.item), + message_selectors: &item_trait.message_selectors, + } + } +} + +impl<'a> Iterator for IterInkTraitItems<'a> { + type Item = (InkTraitItem<'a>, Selector); + + fn next(&mut self) -> Option { + self.iter.next().map(|item| { + let selector = self.message_selectors[item.ident()]; + (item, selector) + }) + } +} diff --git a/crates/lang/ir/src/ir/trait_def/item/mod.rs b/crates/lang/ir/src/ir/trait_def/item/mod.rs new file mode 100644 index 00000000000..69cb677a383 --- /dev/null +++ b/crates/lang/ir/src/ir/trait_def/item/mod.rs @@ -0,0 +1,389 @@ +// 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. + +mod iter; +mod trait_item; + +use self::iter::IterInkTraitItemsRaw; +pub use self::{ + iter::IterInkTraitItems, + trait_item::{ + InkTraitItem, + InkTraitMessage, + }, +}; +use super::TraitDefinitionConfig; +use crate::{ + ir, + ir::idents_lint, + Selector, +}; +#[cfg(test)] +use core::convert::TryFrom; +use ir::TraitPrefix; +use proc_macro2::{ + Ident, + Span, +}; +use std::collections::HashMap; +use syn::{ + spanned::Spanned as _, + Result, +}; + +/// A checked ink! trait definition without its configuration. +#[derive(Debug, PartialEq, Eq)] +pub struct InkItemTrait { + item: syn::ItemTrait, + message_selectors: HashMap, +} + +#[cfg(test)] +impl TryFrom for InkItemTrait { + type Error = syn::Error; + + fn try_from(item_trait: syn::ItemTrait) -> core::result::Result { + let config = TraitDefinitionConfig::default(); + Self::new(&config, item_trait) + } +} + +impl InkItemTrait { + /// Creates a new ink! item trait from the given configuration and trait definition. + pub fn new( + config: &TraitDefinitionConfig, + item_trait: syn::ItemTrait, + ) -> Result { + idents_lint::ensure_no_ink_identifiers(&item_trait)?; + Self::analyse_properties(&item_trait)?; + Self::analyse_items(&item_trait)?; + let mut message_selectors = >::new(); + Self::extract_selectors(config, &item_trait, &mut message_selectors)?; + if message_selectors.is_empty() { + return Err(format_err!( + item_trait.span(), + "encountered invalid empty ink! trait definition" + )) + } + Ok(Self { + item: item_trait, + message_selectors, + }) + } +} + +impl InkItemTrait { + /// Returns span of the ink! trait definition. + pub fn span(&self) -> Span { + self.item.span() + } + + /// Returns the attributes of the ink! trait definition. + pub fn attrs(&self) -> &[syn::Attribute] { + &self.item.attrs + } + + /// Returns the identifier of the ink! trait definition. + pub fn ident(&self) -> &Ident { + &self.item.ident + } + + /// Returns an iterator yielding the ink! specific items of the ink! trait definition. + pub fn iter_items(&self) -> IterInkTraitItems { + IterInkTraitItems::new(self) + } + + /// Analyses the properties of the ink! trait definition. + /// + /// # Errors + /// + /// - If the trait has been defined as `unsafe`. + /// - If the trait is an automatically implemented trait (`auto trait`). + /// - If the trait is generic over some set of types. + /// - If the trait's visibility is not public (`pub`). + fn analyse_properties(item_trait: &syn::ItemTrait) -> Result<()> { + if let Some(unsafety) = &item_trait.unsafety { + return Err(format_err_spanned!( + unsafety, + "ink! trait definitions cannot be unsafe" + )) + } + if let Some(auto) = &item_trait.auto_token { + return Err(format_err_spanned!( + auto, + "ink! trait definitions cannot be automatically implemented traits" + )) + } + if !item_trait.generics.params.is_empty() { + return Err(format_err_spanned!( + item_trait.generics.params, + "ink! trait definitions must not be generic" + )) + } + if !matches!(item_trait.vis, syn::Visibility::Public(_)) { + return Err(format_err_spanned!( + item_trait.vis, + "ink! trait definitions must have public visibility" + )) + } + if !item_trait.supertraits.is_empty() { + return Err(format_err_spanned!( + item_trait.supertraits, + "ink! trait definitions with supertraits are not supported, yet" + )) + } + Ok(()) + } + + /// Returns `Ok` if all trait items respects the requirements for an ink! trait definition. + /// + /// # Errors + /// + /// - If the trait contains an unsupported trait item such as + /// - associated constants (`const`) + /// - associated types (`type`) + /// - macros definitions or usages + /// - unknown token sequences (verbatim) + /// - methods with default implementations + /// - If the trait contains methods which do not respect the ink! trait definition requirements: + /// - All trait methods need to be declared as either `#[ink(message)]` or `#[ink(constructor)]` + /// and need to respect their respective rules. + /// + /// # Note + /// + /// Associated types and constants might be allowed in the future. + fn analyse_items(item_trait: &syn::ItemTrait) -> Result<()> { + for trait_item in &item_trait.items { + match trait_item { + syn::TraitItem::Const(const_trait_item) => { + return Err(format_err_spanned!( + const_trait_item, + "associated constants in ink! trait definitions are not supported, yet" + )) + } + syn::TraitItem::Macro(macro_trait_item) => { + return Err(format_err_spanned!( + macro_trait_item, + "macros in ink! trait definitions are not supported" + )) + } + syn::TraitItem::Type(type_trait_item) => { + return Err(format_err_spanned!( + type_trait_item, + "associated types in ink! trait definitions are not supported, yet" + )) + } + syn::TraitItem::Verbatim(verbatim) => { + return Err(format_err_spanned!( + verbatim, + "encountered unsupported item in ink! trait definition" + )) + } + syn::TraitItem::Method(method_trait_item) => { + Self::analyse_trait_method(method_trait_item)?; + } + unknown => { + return Err(format_err_spanned!( + unknown, + "encountered unknown or unsupported item in ink! trait definition" + )) + } + } + } + Ok(()) + } + + /// Analyses an ink! method that can be either an ink! message or constructor. + /// + /// # Errors + /// + /// - If the method declared as `unsafe`, `const` or `async`. + /// - If the method has some explicit API. + /// - If the method is variadic or has generic parameters. + /// - If the method does not respect the properties of either an + /// ink! message or ink! constructor. + fn analyse_trait_method(method: &syn::TraitItemMethod) -> Result<()> { + if let Some(default_impl) = &method.default { + return Err(format_err_spanned!( + default_impl, + "ink! trait methods with default implementations are not supported" + )) + } + if let Some(constness) = &method.sig.constness { + return Err(format_err_spanned!( + constness, + "const ink! trait methods are not supported" + )) + } + if let Some(asyncness) = &method.sig.asyncness { + return Err(format_err_spanned!( + asyncness, + "async ink! trait methods are not supported" + )) + } + if let Some(unsafety) = &method.sig.unsafety { + return Err(format_err_spanned!( + unsafety, + "unsafe ink! trait methods are not supported" + )) + } + if let Some(abi) = &method.sig.abi { + return Err(format_err_spanned!( + abi, + "ink! trait methods with non default ABI are not supported" + )) + } + if let Some(variadic) = &method.sig.variadic { + return Err(format_err_spanned!( + variadic, + "variadic ink! trait methods are not supported" + )) + } + if !method.sig.generics.params.is_empty() { + return Err(format_err_spanned!( + method.sig.generics.params, + "generic ink! trait methods are not supported" + )) + } + match ir::first_ink_attribute(&method.attrs) { + Ok(Some(ink_attr)) => { + match ink_attr.first().kind() { + ir::AttributeArg::Message => { + Self::analyse_trait_message(method)?; + } + ir::AttributeArg::Constructor => { + Self::analyse_trait_constructor(method)?; + } + _unsupported => { + return Err(format_err_spanned!( + method, + "encountered unsupported ink! attribute for ink! trait method", + )) + } + } + } + Ok(None) => { + return Err(format_err_spanned!( + method, + "missing #[ink(message)] or #[ink(constructor)] flags on ink! trait method" + )) + } + Err(err) => return Err(err), + } + Ok(()) + } + + /// Constructors are generally not allowed in ink! trait definitions. + fn analyse_trait_constructor(constructor: &syn::TraitItemMethod) -> Result<()> { + return Err(format_err!( + constructor.span(), + "ink! trait definitions must not have constructors", + )) + } + + /// Analyses the properties of an ink! message. + /// + /// # Errors + /// + /// - If the message has no `&self` or `&mut self` receiver. + fn analyse_trait_message(message: &syn::TraitItemMethod) -> Result<()> { + InkTraitMessage::extract_attributes(message.span(), &message.attrs)?; + match message.sig.receiver() { + None | Some(syn::FnArg::Typed(_)) => { + return Err(format_err_spanned!( + message.sig, + "missing or malformed `&self` or `&mut self` receiver for ink! message", + )) + } + Some(syn::FnArg::Receiver(receiver)) => { + if receiver.reference.is_none() { + return Err(format_err_spanned!( + receiver, + "self receiver of ink! message must be `&self` or `&mut self`" + )) + } + } + } + Ok(()) + } + + /// Extract selectors for ink! trait constructors and messages. + /// + /// The composed or manually specified selectors are stored into the provided + /// hash tables for later look-up when querying ink! constructors or messages. + /// This way we are more flexible with regard to the underlying structures of the IR. + /// + /// In this step we assume that all sanitation checks have taken place prior so + /// instead of returning errors we simply panic upon failures. + /// + /// # Errors + /// + /// Returns an error if there are overlapping selectors for ink! constructors + /// or ink! messages. Note that overlaps between ink! constructor and message + /// selectors are allowed. + fn extract_selectors( + config: &TraitDefinitionConfig, + item_trait: &syn::ItemTrait, + message_selectors: &mut HashMap, + ) -> Result<()> { + let mut seen_message_selectors = >::new(); + let (_ink_attrs, _) = ir::sanitize_optional_attributes( + item_trait.span(), + item_trait.attrs.iter().cloned(), + |arg| { + match arg.kind() { + ir::AttributeArg::Namespace(_) => Ok(()), + _ => Err(None), + } + }, + ) + .expect("encountered unexpected invalid attributes on ink! trait definition"); + let namespace = config.namespace(); + let ident = &item_trait.ident; + let trait_prefix = TraitPrefix::new(ident, namespace); + for callable in IterInkTraitItemsRaw::from_raw(item_trait) { + let ident = callable.ident(); + let ink_attrs = callable.ink_attrs(); + let selector = match ink_attrs.selector() { + Some(manual_selector) => manual_selector, + None => Selector::compose(trait_prefix, ident), + }; + let (duplicate_selector, duplicate_ident) = match callable { + InkTraitItem::Message(_) => { + let duplicate_selector = + seen_message_selectors.insert(selector, ident.clone()); + let duplicate_ident = + message_selectors.insert(ident.clone(), selector); + (duplicate_selector, duplicate_ident) + } + }; + if let Some(duplicate_selector) = duplicate_selector { + use crate::error::ExtError as _; + return Err(format_err_spanned!( + ident, + "encountered duplicate selector ({:x?}) in the same ink! trait definition", + selector.to_bytes(), + ).into_combine(format_err_spanned!( + duplicate_selector, + "first ink! trait constructor or message with same selector found here", + ))) + } + assert!( + duplicate_ident.is_none(), + "encountered unexpected overlapping ink! trait constructor or message identifier", + ); + } + Ok(()) + } +} diff --git a/crates/lang/ir/src/ir/trait_def/item/trait_item.rs b/crates/lang/ir/src/ir/trait_def/item/trait_item.rs new file mode 100644 index 00000000000..9fddf5cbd84 --- /dev/null +++ b/crates/lang/ir/src/ir/trait_def/item/trait_item.rs @@ -0,0 +1,196 @@ +// 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 super::super::InkAttribute; +use crate::{ + ir::{ + self, + utils, + }, + InputsIter, + Receiver, +}; +use proc_macro2::Span; +use syn::{ + spanned::Spanned as _, + Result, +}; + +/// An ink! item within an ink! trait definition. +#[derive(Debug, Clone)] +pub enum InkTraitItem<'a> { + Message(InkTraitMessage<'a>), +} + +impl<'a> InkTraitItem<'a> { + /// Returns the Rust identifier of the ink! trait item. + pub fn ident(&self) -> &syn::Ident { + match self { + Self::Message(message) => message.ident(), + } + } + + /// Returns the ink! attributes of the ink! trait item. + pub fn ink_attrs(&self) -> InkAttribute { + match self { + Self::Message(message) => message.ink_attrs(), + } + } + + /// Returns `Some` if the ink! trait item is a message. + pub fn filter_map_message(self) -> Option> { + match self { + Self::Message(ink_trait_message) => Some(ink_trait_message), + } + } +} + +/// A checked ink! message of an ink! trait definition. +#[derive(Debug, Clone)] +pub struct InkTraitMessage<'a> { + item: &'a syn::TraitItemMethod, +} + +impl<'a> InkTraitMessage<'a> { + /// Panic message in case a user encounters invalid attributes. + const INVALID_ATTRIBUTES_ERRSTR: &'static str = + "encountered invalid attributes for ink! trait message"; + + /// Creates a new ink! trait definition message. + pub(super) fn new(item: &'a syn::TraitItemMethod) -> Self { + Self { item } + } + + /// Analyses and extracts the ink! and non-ink! attributes of an ink! trait message. + pub(super) fn extract_attributes( + span: Span, + attrs: &[syn::Attribute], + ) -> Result<(InkAttribute, Vec)> { + let (ink_attrs, non_ink_attrs) = ir::sanitize_attributes( + span, + attrs.iter().cloned(), + &ir::AttributeArgKind::Message, + |arg| { + match arg.kind() { + ir::AttributeArg::Message + | ir::AttributeArg::Payable + | ir::AttributeArg::Selector(_) => Ok(()), + _ => Err(None), + } + }, + )?; + Ok((ink_attrs, non_ink_attrs)) + } + + /// Returns all non-ink! attributes. + pub fn attrs(&self) -> Vec { + let (_, rust_attrs) = Self::extract_attributes(self.span(), &self.item.attrs) + .expect(Self::INVALID_ATTRIBUTES_ERRSTR); + rust_attrs + } + + /// Returns all ink! attributes. + pub fn ink_attrs(&self) -> InkAttribute { + let (ink_attrs, _) = Self::extract_attributes(self.span(), &self.item.attrs) + .expect(Self::INVALID_ATTRIBUTES_ERRSTR); + ink_attrs + } + + /// Returns the original signature of the ink! message. + pub fn sig(&self) -> &syn::Signature { + &self.item.sig + } + + /// Returns the `self` receiver of the ink! trait message. + /// + /// Returns `Ref` for `&self` messages and `RefMut` for `&mut self` messages. + pub fn receiver(&self) -> Receiver { + match self.item.sig.inputs.iter().next() { + Some(syn::FnArg::Receiver(receiver)) => { + debug_assert!(receiver.reference.is_some()); + if receiver.mutability.is_some() { + Receiver::RefMut + } else { + Receiver::Ref + } + } + _ => unreachable!("encountered invalid receiver argument for ink! message"), + } + } + + /// Returns an iterator over the inputs of the ink! trait message. + pub fn inputs(&self) -> InputsIter { + InputsIter::from(self) + } + + /// Returns the return type of the ink! message if any. + pub fn output(&self) -> Option<&syn::Type> { + match &self.item.sig.output { + syn::ReturnType::Default => None, + syn::ReturnType::Type(_, return_type) => Some(return_type), + } + } + + /// Returns the Rust identifier of the ink! message. + pub fn ident(&self) -> &syn::Ident { + &self.item.sig.ident + } + + /// Returns a local ID unique to the ink! trait definition of the ink! trait message. + /// + /// # Note + /// + /// It is a compile error if two ink! trait messages share the same local ID. + /// Although the above scenario is very unlikely since the local ID is computed + /// solely by the identifier of the ink! message. + pub fn local_id(&self) -> u32 { + utils::local_message_id(self.ident()) + } + + /// Returns the span of the ink! message. + pub fn span(&self) -> Span { + self.item.span() + } + + /// Returns `true` if the ink! message may mutate the contract storage. + pub fn mutates(&self) -> bool { + self.sig() + .receiver() + .map(|fn_arg| { + match fn_arg { + syn::FnArg::Receiver(receiver) if receiver.mutability.is_some() => { + true + } + syn::FnArg::Typed(pat_type) => { + match &*pat_type.ty { + syn::Type::Reference(reference) + if reference.mutability.is_some() => + { + true + } + _ => false, + } + } + _ => false, + } + }) + .expect("encountered missing receiver for ink! message") + } +} + +impl<'a> From<&'a InkTraitMessage<'a>> for InputsIter<'a> { + fn from(message: &'a InkTraitMessage) -> Self { + Self::new(&message.item.sig.inputs) + } +} diff --git a/crates/lang/ir/src/ir/trait_def/mod.rs b/crates/lang/ir/src/ir/trait_def/mod.rs new file mode 100644 index 00000000000..220038af0d1 --- /dev/null +++ b/crates/lang/ir/src/ir/trait_def/mod.rs @@ -0,0 +1,73 @@ +// 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. + +mod config; +mod item; + +#[cfg(test)] +mod tests; + +pub use self::{ + config::TraitDefinitionConfig, + item::{ + InkItemTrait, + InkTraitItem, + InkTraitMessage, + IterInkTraitItems, + }, +}; +use super::attrs::InkAttribute; +use core::convert::TryFrom; +use proc_macro2::TokenStream as TokenStream2; +use syn::Result; + +/// A checked ink! trait definition without its configuration. +#[derive(Debug, PartialEq, Eq)] +pub struct InkTraitDefinition { + config: TraitDefinitionConfig, + item: InkItemTrait, +} + +impl InkTraitDefinition { + /// Returns `Ok` if the input matches all requirements for an ink! trait definition. + pub fn new(config: TokenStream2, input: TokenStream2) -> Result { + let parsed_config = syn::parse2::(config)?; + let parsed_item = syn::parse2::(input)?; + let config = TraitDefinitionConfig::try_from(parsed_config)?; + let item = InkItemTrait::new(&config, parsed_item)?; + Ok(Self { config, item }) + } + + /// Constructs an ink! trait definition from its raw parts. + /// + /// # Note + /// + /// This is a test-only API. + #[cfg(test)] + pub fn from_raw_parts(config: TraitDefinitionConfig, item: InkItemTrait) -> Self { + Self { config, item } + } +} + +impl InkTraitDefinition { + /// Returns the ink! trait definition config. + pub fn config(&self) -> &TraitDefinitionConfig { + &self.config + } + + /// Returns the ink! trait item representing the ink! trait definition. + pub fn item(&self) -> &InkItemTrait { + &self.item + } +} diff --git a/crates/lang/ir/src/ir/trait_def/tests.rs b/crates/lang/ir/src/ir/trait_def/tests.rs new file mode 100644 index 00000000000..40caa5249b9 --- /dev/null +++ b/crates/lang/ir/src/ir/trait_def/tests.rs @@ -0,0 +1,446 @@ +// 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 super::*; + +/// Checks if the token stream in `$trait_def` results in the expected error message. +macro_rules! assert_ink_trait_eq_err { + ( error: $err_str:literal, $($trait_def:tt)* ) => { + assert_eq!( + >::try_from(syn::parse_quote! { + $( $trait_def )* + }) + .map_err(|err| err.to_string()), + Err( + $err_str.to_string() + ) + ) + }; +} + +#[test] +fn unsafe_trait_def_is_denied() { + assert_ink_trait_eq_err!( + error: "ink! trait definitions cannot be unsafe", + pub unsafe trait MyTrait {} + ); +} + +#[test] +fn auto_trait_def_is_denied() { + assert_ink_trait_eq_err!( + error: "ink! trait definitions cannot be automatically implemented traits", + pub auto trait MyTrait {} + ); +} + +#[test] +fn non_pub_trait_def_is_denied() { + assert_ink_trait_eq_err!( + error: "ink! trait definitions must have public visibility", + trait MyTrait {} + ); + assert_ink_trait_eq_err!( + error: "ink! trait definitions must have public visibility", + pub(crate) trait MyTrait {} + ); +} + +#[test] +fn generic_trait_def_is_denied() { + assert_ink_trait_eq_err!( + error: "ink! trait definitions must not be generic", + pub trait MyTrait {} + ); +} + +#[test] +fn trait_def_with_supertraits_is_denied() { + assert_ink_trait_eq_err!( + error: "ink! trait definitions with supertraits are not supported, yet", + pub trait MyTrait: SuperTrait {} + ); +} + +#[test] +fn trait_def_containing_const_item_is_denied() { + assert_ink_trait_eq_err!( + error: "associated constants in ink! trait definitions are not supported, yet", + pub trait MyTrait { + const T: i32; + } + ); +} + +#[test] +fn trait_def_containing_associated_type_is_denied() { + assert_ink_trait_eq_err!( + error: "associated types in ink! trait definitions are not supported, yet", + pub trait MyTrait { + type Type; + } + ); +} + +#[test] +fn trait_def_containing_macro_is_denied() { + assert_ink_trait_eq_err!( + error: "macros in ink! trait definitions are not supported", + pub trait MyTrait { + my_macro_call!(); + } + ); +} + +#[test] +fn trait_def_containing_non_flagged_method_is_denied() { + assert_ink_trait_eq_err!( + error: "missing #[ink(message)] or #[ink(constructor)] flags on ink! trait method", + pub trait MyTrait { + fn non_flagged_1(&self); + } + ); + assert_ink_trait_eq_err!( + error: "missing #[ink(message)] or #[ink(constructor)] flags on ink! trait method", + pub trait MyTrait { + fn non_flagged_2(&mut self); + } + ); + assert_ink_trait_eq_err!( + error: "missing #[ink(message)] or #[ink(constructor)] flags on ink! trait method", + pub trait MyTrait { + fn non_flagged_3() -> Self; + } + ); +} + +#[test] +fn trait_def_containing_default_implemented_methods_is_denied() { + assert_ink_trait_eq_err!( + error: "ink! trait methods with default implementations are not supported", + pub trait MyTrait { + #[ink(constructor)] + fn default_implemented() -> Self {} + } + ); + assert_ink_trait_eq_err!( + error: "ink! trait methods with default implementations are not supported", + pub trait MyTrait { + #[ink(message)] + fn default_implemented(&self) {} + } + ); +} + +#[test] +fn trait_def_containing_const_methods_is_denied() { + assert_ink_trait_eq_err!( + error: "const ink! trait methods are not supported", + pub trait MyTrait { + #[ink(constructor)] + const fn const_constructor() -> Self; + } + ); + assert_ink_trait_eq_err!( + error: "const ink! trait methods are not supported", + pub trait MyTrait { + #[ink(message)] + const fn const_message(&self); + } + ); +} + +#[test] +fn trait_def_containing_async_methods_is_denied() { + assert_ink_trait_eq_err!( + error: "async ink! trait methods are not supported", + pub trait MyTrait { + #[ink(constructor)] + async fn const_constructor() -> Self; + } + ); + assert_ink_trait_eq_err!( + error: "async ink! trait methods are not supported", + pub trait MyTrait { + #[ink(message)] + async fn const_message(&self); + } + ); +} + +#[test] +fn trait_def_containing_unsafe_methods_is_denied() { + assert_ink_trait_eq_err!( + error: "unsafe ink! trait methods are not supported", + pub trait MyTrait { + #[ink(constructor)] + unsafe fn const_constructor() -> Self; + } + ); + assert_ink_trait_eq_err!( + error: "unsafe ink! trait methods are not supported", + pub trait MyTrait { + #[ink(message)] + unsafe fn const_message(&self); + } + ); +} + +#[test] +fn trait_def_containing_methods_using_explicit_abi_is_denied() { + assert_ink_trait_eq_err!( + error: "ink! trait methods with non default ABI are not supported", + pub trait MyTrait { + #[ink(constructor)] + extern fn const_constructor() -> Self; + } + ); + assert_ink_trait_eq_err!( + error: "ink! trait methods with non default ABI are not supported", + pub trait MyTrait { + #[ink(message)] + extern fn const_message(&self); + } + ); +} + +#[test] +fn trait_def_containing_variadic_methods_is_denied() { + assert_ink_trait_eq_err!( + error: "variadic ink! trait methods are not supported", + pub trait MyTrait { + #[ink(constructor)] + fn const_constructor(...) -> Self; + } + ); + assert_ink_trait_eq_err!( + error: "variadic ink! trait methods are not supported", + pub trait MyTrait { + #[ink(message)] + fn const_message(&self, ...); + } + ); +} + +#[test] +fn trait_def_containing_generic_methods_is_denied() { + assert_ink_trait_eq_err!( + error: "generic ink! trait methods are not supported", + pub trait MyTrait { + #[ink(constructor)] + fn const_constructor() -> Self; + } + ); + assert_ink_trait_eq_err!( + error: "generic ink! trait methods are not supported", + pub trait MyTrait { + #[ink(message)] + fn const_message(&self); + } + ); +} + +#[test] +fn trait_def_containing_method_with_unsupported_ink_attribute_is_denied() { + assert_ink_trait_eq_err!( + error: "encountered unsupported ink! attribute for ink! trait method", + pub trait MyTrait { + #[ink(payable)] + fn unsupported_ink_attribute(&self); + } + ); + assert_ink_trait_eq_err!( + error: "unknown ink! attribute (path)", + pub trait MyTrait { + #[ink(unknown)] + fn unknown_ink_attribute(&self); + } + ); +} + +#[test] +fn trait_def_containing_invalid_message_is_denied() { + assert_ink_trait_eq_err!( + error: "missing or malformed `&self` or `&mut self` receiver for ink! message", + pub trait MyTrait { + #[ink(message)] + fn does_not_return_self(); + } + ); + assert_ink_trait_eq_err!( + error: "missing or malformed `&self` or `&mut self` receiver for ink! message", + pub trait MyTrait { + #[ink(message)] + fn does_not_return_self(self: &Self); + } + ); + assert_ink_trait_eq_err!( + error: "self receiver of ink! message must be `&self` or `&mut self`", + pub trait MyTrait { + #[ink(message)] + fn does_not_return_self(self); + } + ); +} + +#[test] +fn trait_def_containing_message_with_invalid_ink_attributes_is_denied() { + assert_ink_trait_eq_err!( + error: "encountered duplicate ink! attribute", + pub trait MyTrait { + #[ink(message)] + #[ink(message)] + fn does_not_return_self(&self); + } + ); + assert_ink_trait_eq_err!( + error: "encountered conflicting ink! attribute argument", + pub trait MyTrait { + #[ink(message)] + #[ink(constructor)] + fn does_not_return_self(&self); + } + ); + assert_ink_trait_eq_err!( + error: "encountered conflicting ink! attribute argument", + pub trait MyTrait { + #[ink(message)] + #[ink(anonymous)] + fn does_not_return_self(&self); + } + ); +} + +#[test] +fn trait_def_is_ok() { + assert!( + >::try_from(syn::parse_quote! { + pub trait MyTrait { + #[ink(message)] + fn my_message(&self); + #[ink(message)] + fn my_message_mut(&mut self); + } + }) + .is_ok() + ) +} + +#[test] +fn trait_def_with_namespace_is_ok() { + assert!( + >::try_from(syn::parse_quote! { + #[ink(namespace = "my_namespace")] + pub trait MyTrait { + #[ink(message)] + fn my_message(&self); + #[ink(message)] + fn my_message_mut(&mut self); + } + }) + .is_ok() + ) +} + +#[test] +fn trait_def_with_selectors_ok() { + assert!( + >::try_from(syn::parse_quote! { + pub trait MyTrait { + #[ink(message, selector = 0xDEADBEEF)] + fn my_message(&self); + #[ink(message, selector = 0xC0FEFEED)] + fn my_message_mut(&mut self); + } + }) + .is_ok() + ) +} + +#[test] +fn trait_def_with_payable_ok() { + assert!( + >::try_from(syn::parse_quote! { + pub trait MyTrait { + #[ink(message, payable)] + fn my_message(&self); + #[ink(message, payable)] + fn my_message_mut(&mut self); + } + }) + .is_ok() + ) +} + +#[test] +fn trait_def_with_everything_combined_ok() { + assert!( + >::try_from(syn::parse_quote! { + #[ink(namespace = "my_namespace")] + pub trait MyTrait { + #[ink(message)] + fn my_message_1(&self); + #[ink(message, payable)] + fn my_message_2(&self); + #[ink(message, payable, selector = 0xDEADBEEF)] + fn my_message_3(&self); + #[ink(message)] + fn my_message_mut_1(&mut self); + #[ink(message, payable)] + fn my_message_mut_2(&mut self); + #[ink(message, payable, selector = 0xC0DEBEEF)] + fn my_message_mut_3(&mut self); + } + }) + .is_ok() + ) +} + +#[test] +fn trait_def_with_overlapping_selectors() { + assert_ink_trait_eq_err!( + error: "encountered duplicate selector ([c0, de, ca, fe]) \ + in the same ink! trait definition", + pub trait MyTrait { + #[ink(message, selector = 0xC0DECAFE)] + fn my_message(&self); + #[ink(message, selector = 0xC0DECAFE)] + fn my_message_mut(&mut self); + } + ); +} + +#[test] +fn iter_messages_works() { + let ink_trait = + >::try_from(syn::parse_quote! { + pub trait MyTrait { + #[ink(message)] + fn message_1(&self); + #[ink(message)] + fn message_2(&mut self); + } + }) + .unwrap(); + let actual = ink_trait + .iter_items() + .map(|(item, _)| item) + .flat_map(|item| { + item.filter_map_message() + .map(|message| message.sig().ident.to_string()) + }) + .collect::>(); + let expected = vec!["message_1".to_string(), "message_2".to_string()]; + assert_eq!(actual, expected); +} diff --git a/crates/lang/ir/src/ir/utils.rs b/crates/lang/ir/src/ir/utils.rs index 51b2bd5b8d4..a075f9432c9 100644 --- a/crates/lang/ir/src/ir/utils.rs +++ b/crates/lang/ir/src/ir/utils.rs @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +use super::Selector; use crate::format_err; use proc_macro2::Span; use syn::spanned::Spanned as _; @@ -42,3 +43,15 @@ pub fn ensure_pub_visibility( } Ok(()) } + +/// Returns a local ID unique to the ink! trait definition for the identifier. +/// +/// # Note +/// +/// - The returned value is equal to the selector of the message identifier. +/// - Used from within ink! trait definitions as well as ink! trait implementation blocks. +pub fn local_message_id(ident: &syn::Ident) -> u32 { + let input = ident.to_string().into_bytes(); + let selector = Selector::compute(&input); + selector.into_be_u32() +} diff --git a/crates/lang/ir/src/lib.rs b/crates/lang/ir/src/lib.rs index 8482fd00f35..0be21bf5109 100644 --- a/crates/lang/ir/src/lib.rs +++ b/crates/lang/ir/src/lib.rs @@ -38,6 +38,7 @@ pub use self::{ ir::{ blake2b_256, marker, + utils, Blake2x256Macro, Callable, CallableKind, @@ -51,12 +52,13 @@ pub use self::{ ExtensionId, ImplItem, InkItem, + InkItemTrait, InkTest, - InkTrait, - InkTraitConstructor, + InkTraitDefinition, InkTraitItem, InkTraitMessage, InputsIter, + IsDocAttribute, Item, ItemImpl, ItemMod, diff --git a/crates/lang/macro/Cargo.toml b/crates/lang/macro/Cargo.toml index 89a5f8f651a..cbc182612e5 100644 --- a/crates/lang/macro/Cargo.toml +++ b/crates/lang/macro/Cargo.toml @@ -28,9 +28,6 @@ ink_metadata = { version = "3.0.0-rc6", path = "../../metadata/" } ink_env = { version = "3.0.0-rc6", path = "../../env/" } ink_storage = { version = "3.0.0-rc6", path = "../../storage/" } ink_lang = { version = "3.0.0-rc6", path = ".." } -ink_prelude = { version = "3.0.0-rc6", path = "../../prelude/" } - -trybuild = "1.0.24" scale-info = { version = "1.0", default-features = false, features = ["derive"] } [lib] @@ -44,10 +41,3 @@ std = [ "ink_lang_ir/std", "ink_primitives/std", ] - -# 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 = [] diff --git a/crates/lang/macro/src/lib.rs b/crates/lang/macro/src/lib.rs index ef5f8a590d4..ba8ef464d6e 100644 --- a/crates/lang/macro/src/lib.rs +++ b/crates/lang/macro/src/lib.rs @@ -597,18 +597,19 @@ pub fn contract(attr: TokenStream, item: TokenStream) -> TokenStream { /// ``` /// use ink_lang as ink; /// # type Balance = ::Balance; +/// # type AccountId = ::AccountId; /// /// #[ink::trait_definition] /// pub trait Erc20 { -/// /// Constructs a new ERC-20 compliant smart contract using the initial supply. -/// #[ink(constructor)] -/// fn new(initial_supply: Balance) -> Self; -/// /// /// Returns the total supply of the ERC-20 smart contract. /// #[ink(message)] /// fn total_supply(&self) -> Balance; /// -/// // etc. +/// /// Transfers balance from the caller to the given address. +/// #[ink(message)] +/// fn transfer(&mut self, amount: Balance, to: AccountId) -> bool; +/// +/// // etc ... /// } /// ``` /// @@ -624,34 +625,38 @@ pub fn contract(attr: TokenStream, item: TokenStream) -> TokenStream { /// # // We somehow cannot put the trait in the doc-test crate root due to bugs. /// # #[ink_lang::trait_definition] /// # pub trait Erc20 { -/// # /// Constructors a new ERC-20 compliant smart contract using the initial supply. -/// # #[ink(constructor)] -/// # fn new(initial_supply: Balance) -> Self; +/// # /// Returns the total supply of the ERC-20 smart contract. +/// # #[ink(message)] +/// # fn total_supply(&self) -> Balance; /// # -/// # /// Returns the total supply of the ERC-20 smart contract. -/// # #[ink(message)] -/// # fn total_supply(&self) -> Balance; +/// # /// Transfers balance from the caller to the given address. +/// # #[ink(message)] +/// # fn transfer(&mut self, amount: Balance, to: AccountId) -> bool; /// # } /// # /// #[ink(storage)] /// pub struct BaseErc20 { /// total_supply: Balance, -/// // etc .. /// } /// -/// impl Erc20 for BaseErc20 { +/// impl BaseErc20 { /// #[ink(constructor)] -/// fn new(initial_supply: Balance) -> Self { +/// pub fn new(initial_supply: Balance) -> Self { /// Self { total_supply: initial_supply } /// } +/// } /// +/// impl Erc20 for BaseErc20 { /// /// Returns the total supply of the ERC-20 smart contract. /// #[ink(message)] /// fn total_supply(&self) -> Balance { /// self.total_supply /// } /// -/// // etc .. +/// #[ink(message)] +/// fn transfer(&mut self, amount: Balance, to: AccountId) -> bool { +/// unimplemented!() +/// } /// } /// } /// ``` diff --git a/crates/lang/macro/src/trait_def.rs b/crates/lang/macro/src/trait_def.rs index d7f008c1fb4..71869348d18 100644 --- a/crates/lang/macro/src/trait_def.rs +++ b/crates/lang/macro/src/trait_def.rs @@ -16,14 +16,14 @@ use ink_lang_codegen::generate_code; use proc_macro2::TokenStream as TokenStream2; use syn::Result; -pub fn analyze(attr: TokenStream2, input: TokenStream2) -> TokenStream2 { - match analyze_or_err(attr, input) { +pub fn analyze(config: TokenStream2, input: TokenStream2) -> TokenStream2 { + match analyze_or_err(config, input) { Ok(tokens) => tokens, Err(err) => err.to_compile_error(), } } -pub fn analyze_or_err(attr: TokenStream2, input: TokenStream2) -> Result { - let trait_definition = ink_lang_ir::InkTrait::new(attr, input)?; +pub fn analyze_or_err(config: TokenStream2, input: TokenStream2) -> Result { + let trait_definition = ink_lang_ir::InkTraitDefinition::new(config, input)?; Ok(generate_code(&trait_definition)) } diff --git a/crates/lang/macro/tests/compile_tests.rs b/crates/lang/macro/tests/compile_tests.rs deleted file mode 100644 index 1ab2cab7881..00000000000 --- a/crates/lang/macro/tests/compile_tests.rs +++ /dev/null @@ -1,77 +0,0 @@ -// 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. - -#[test] -fn compile_tests() { - let t = trybuild::TestCases::new(); - - t.pass("tests/ui/blake2b/pass/*.rs"); - t.compile_fail("tests/ui/blake2b/fail/*.rs"); - - t.pass("tests/ui/selector_id/pass/*.rs"); - t.compile_fail("tests/ui/selector_id/fail/*.rs"); - - t.pass("tests/ui/selector_bytes/pass/*.rs"); - t.compile_fail("tests/ui/selector_bytes/fail/*.rs"); - - t.pass("tests/ui/contract/pass/01-noop-contract.rs"); - t.pass("tests/ui/contract/pass/02-flipper-contract.rs"); - t.pass("tests/ui/contract/pass/03-incrementer-contract.rs"); - t.pass("tests/ui/contract/pass/04-erc20-contract.rs"); - t.pass("tests/ui/contract/pass/05-erc721-contract.rs"); - t.pass("tests/ui/contract/pass/06-non-ink-items.rs"); - t.pass("tests/ui/contract/pass/07-flipper-as-dependency.rs"); - t.pass("tests/ui/contract/pass/08-flipper-as-dependency-trait.rs"); - t.pass("tests/ui/contract/pass/09-static-env.rs"); - t.pass("tests/ui/contract/pass/10-derive-for-storage.rs"); - t.pass("tests/ui/contract/pass/11-alias-storage-struct-impl.rs"); - - t.compile_fail("tests/ui/contract/fail/C-00-constructor-self-ref.rs"); - t.compile_fail("tests/ui/contract/fail/C-01-constructor-self-mut.rs"); - t.compile_fail("tests/ui/contract/fail/C-02-constructor-self-val.rs"); - t.compile_fail("tests/ui/contract/fail/C-03-constructor-missing-return.rs"); - t.compile_fail("tests/ui/contract/fail/C-04-missing-constructor.rs"); - t.compile_fail("tests/ui/contract/fail/C-10-async-constructor.rs"); - t.compile_fail("tests/ui/contract/fail/C-11-unsafe-constructor.rs"); - t.compile_fail("tests/ui/contract/fail/C-12-const-constructor.rs"); - t.compile_fail("tests/ui/contract/fail/C-13-abi-constructor.rs"); - t.compile_fail("tests/ui/contract/fail/C-14-payable-constructor.rs"); - t.compile_fail("tests/ui/contract/fail/C-15-payable-trait-constructor.rs"); - t.compile_fail("tests/ui/contract/fail/C-16-function-arg-struct-destructuring.rs"); - - t.compile_fail("tests/ui/contract/fail/H-01-invalid-dyn-alloc.rs"); - t.compile_fail("tests/ui/contract/fail/H-02-invalid-as-dependency.rs"); - t.compile_fail("tests/ui/contract/fail/H-03-use-forbidden-idents.rs"); - - t.compile_fail("tests/ui/contract/fail/M-01-missing-message.rs"); - t.compile_fail("tests/ui/contract/fail/M-02-message-missing-self-arg.rs"); - t.compile_fail("tests/ui/contract/fail/M-03-message-returns-self.rs"); - t.compile_fail("tests/ui/contract/fail/M-04-message-returns-non-codec.rs"); - t.compile_fail("tests/ui/contract/fail/M-05-message-invalid-selector.rs"); - t.compile_fail("tests/ui/contract/fail/M-06-message-invalid-selector-type.rs"); - t.compile_fail("tests/ui/contract/fail/M-10-method-unknown-ink-marker.rs"); - - t.compile_fail("tests/ui/contract/fail/S-01-missing-storage-struct.rs"); - t.compile_fail("tests/ui/contract/fail/S-02-multiple-storage-structs.rs"); - t.compile_fail("tests/ui/contract/fail/S-03-struct-unknown-ink-marker.rs"); - t.compile_fail("tests/ui/contract/fail/S-04-non-storage-ink-impls.rs"); - t.compile_fail("tests/ui/contract/fail/S-05-storage-as-event.rs"); - t.compile_fail("tests/ui/contract/fail/S-06-event-as-storage.rs"); - - t.compile_fail("tests/ui/contract/fail/N-01-namespace-invalid-identifier.rs"); - t.compile_fail("tests/ui/contract/fail/N-02-namespace-invalid-type.rs"); - t.compile_fail("tests/ui/contract/fail/N-03-namespace-missing-argument.rs"); - - t.pass("tests/ui/chain_extension/E-01-simple.rs"); -} diff --git a/crates/lang/macro/tests/ui/contract/fail/C-00-constructor-self-ref.rs b/crates/lang/macro/tests/ui/contract/fail/C-00-constructor-self-ref.rs deleted file mode 100644 index f6644cd4658..00000000000 --- a/crates/lang/macro/tests/ui/contract/fail/C-00-constructor-self-ref.rs +++ /dev/null @@ -1,19 +0,0 @@ -use ink_lang as ink; - -#[ink::contract] -mod noop { - #[ink(storage)] - pub struct Noop {} - - impl Noop { - #[ink(constructor)] - pub fn invalid_self_ref(&self) -> Self { - Self {} - } - - #[ink(message)] - pub fn noop(&self) {} - } -} - -fn main() {} diff --git a/crates/lang/macro/tests/ui/contract/fail/C-00-constructor-self-ref.stderr b/crates/lang/macro/tests/ui/contract/fail/C-00-constructor-self-ref.stderr deleted file mode 100644 index e31f0a668fe..00000000000 --- a/crates/lang/macro/tests/ui/contract/fail/C-00-constructor-self-ref.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: ink! constructors must have no `self` receiver - --> $DIR/C-00-constructor-self-ref.rs:10:33 - | -10 | pub fn invalid_self_ref(&self) -> Self { - | ^^^^^ diff --git a/crates/lang/macro/tests/ui/contract/fail/C-01-constructor-self-mut.rs b/crates/lang/macro/tests/ui/contract/fail/C-01-constructor-self-mut.rs deleted file mode 100644 index eb82fee6e5e..00000000000 --- a/crates/lang/macro/tests/ui/contract/fail/C-01-constructor-self-mut.rs +++ /dev/null @@ -1,19 +0,0 @@ -use ink_lang as ink; - -#[ink::contract] -mod noop { - #[ink(storage)] - pub struct Noop {} - - impl Noop { - #[ink(constructor)] - pub fn self_mut_arg(&mut self) -> Self { - Self {} - } - - #[ink(message)] - pub fn noop(&self) {} - } -} - -fn main() {} diff --git a/crates/lang/macro/tests/ui/contract/fail/C-01-constructor-self-mut.stderr b/crates/lang/macro/tests/ui/contract/fail/C-01-constructor-self-mut.stderr deleted file mode 100644 index eb2006c8510..00000000000 --- a/crates/lang/macro/tests/ui/contract/fail/C-01-constructor-self-mut.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: ink! constructors must have no `self` receiver - --> $DIR/C-01-constructor-self-mut.rs:10:29 - | -10 | pub fn self_mut_arg(&mut self) -> Self { - | ^^^^^^^^^ diff --git a/crates/lang/macro/tests/ui/contract/fail/C-02-constructor-self-val.stderr b/crates/lang/macro/tests/ui/contract/fail/C-02-constructor-self-val.stderr deleted file mode 100644 index 12fc09440b9..00000000000 --- a/crates/lang/macro/tests/ui/contract/fail/C-02-constructor-self-val.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: ink! constructors must have no `self` receiver - --> $DIR/C-02-constructor-self-val.rs:10:33 - | -10 | pub fn invalid_self_val(self) -> Self { - | ^^^^ diff --git a/crates/lang/macro/tests/ui/contract/fail/C-03-constructor-missing-return.stderr b/crates/lang/macro/tests/ui/contract/fail/C-03-constructor-missing-return.stderr deleted file mode 100644 index e37d10b07c2..00000000000 --- a/crates/lang/macro/tests/ui/contract/fail/C-03-constructor-missing-return.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: missing return for ink! constructor - --> $DIR/C-03-constructor-missing-return.rs:10:13 - | -10 | pub fn missing_return() {} - | ^^^^^^^^^^^^^^^^^^^ diff --git a/crates/lang/macro/tests/ui/contract/fail/C-04-missing-constructor.stderr b/crates/lang/macro/tests/ui/contract/fail/C-04-missing-constructor.stderr deleted file mode 100644 index ddcd3c1b980..00000000000 --- a/crates/lang/macro/tests/ui/contract/fail/C-04-missing-constructor.stderr +++ /dev/null @@ -1,11 +0,0 @@ -error: missing ink! constructor - --> $DIR/C-04-missing-constructor.rs:4:1 - | -4 | / mod noop { -5 | | #[ink(storage)] -6 | | pub struct Noop {} -7 | | -... | -11 | | } -12 | | } - | |_^ diff --git a/crates/lang/macro/tests/ui/contract/fail/C-10-async-constructor.stderr b/crates/lang/macro/tests/ui/contract/fail/C-10-async-constructor.stderr deleted file mode 100644 index 998945162b7..00000000000 --- a/crates/lang/macro/tests/ui/contract/fail/C-10-async-constructor.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: ink! constructors must not be async - --> $DIR/C-10-async-constructor.rs:10:13 - | -10 | pub async fn async_constructor() -> Self { - | ^^^^^ diff --git a/crates/lang/macro/tests/ui/contract/fail/C-11-unsafe-constructor.rs b/crates/lang/macro/tests/ui/contract/fail/C-11-unsafe-constructor.rs deleted file mode 100644 index 902da3ea8e8..00000000000 --- a/crates/lang/macro/tests/ui/contract/fail/C-11-unsafe-constructor.rs +++ /dev/null @@ -1,19 +0,0 @@ -use ink_lang as ink; - -#[ink::contract] -mod noop { - #[ink(storage)] - pub struct Noop {} - - impl Noop { - #[ink(constructor)] - pub unsafe fn unsafe_constructor() -> Self { - Self {} - } - - #[ink(message)] - pub fn noop(&self) {} - } -} - -fn main() {} diff --git a/crates/lang/macro/tests/ui/contract/fail/C-11-unsafe-constructor.stderr b/crates/lang/macro/tests/ui/contract/fail/C-11-unsafe-constructor.stderr deleted file mode 100644 index 5981cbc9bcb..00000000000 --- a/crates/lang/macro/tests/ui/contract/fail/C-11-unsafe-constructor.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: ink! constructors must not be unsafe - --> $DIR/C-11-unsafe-constructor.rs:10:13 - | -10 | pub unsafe fn unsafe_constructor() -> Self { - | ^^^^^^ diff --git a/crates/lang/macro/tests/ui/contract/fail/C-12-const-constructor.rs b/crates/lang/macro/tests/ui/contract/fail/C-12-const-constructor.rs deleted file mode 100644 index abd6d07fe52..00000000000 --- a/crates/lang/macro/tests/ui/contract/fail/C-12-const-constructor.rs +++ /dev/null @@ -1,19 +0,0 @@ -use ink_lang as ink; - -#[ink::contract] -mod noop { - #[ink(storage)] - pub struct Noop {} - - impl Noop { - #[ink(constructor)] - pub const fn const_constructor() -> Self { - Self {} - } - - #[ink(message)] - pub fn noop(&self) {} - } -} - -fn main() {} diff --git a/crates/lang/macro/tests/ui/contract/fail/C-12-const-constructor.stderr b/crates/lang/macro/tests/ui/contract/fail/C-12-const-constructor.stderr deleted file mode 100644 index a1a456b6961..00000000000 --- a/crates/lang/macro/tests/ui/contract/fail/C-12-const-constructor.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: ink! constructors must not be const - --> $DIR/C-12-const-constructor.rs:10:13 - | -10 | pub const fn const_constructor() -> Self { - | ^^^^^ diff --git a/crates/lang/macro/tests/ui/contract/fail/C-13-abi-constructor.rs b/crates/lang/macro/tests/ui/contract/fail/C-13-abi-constructor.rs deleted file mode 100644 index 84b06e52a41..00000000000 --- a/crates/lang/macro/tests/ui/contract/fail/C-13-abi-constructor.rs +++ /dev/null @@ -1,19 +0,0 @@ -use ink_lang as ink; - -#[ink::contract] -mod noop { - #[ink(storage)] - pub struct Noop {} - - impl Noop { - #[ink(constructor)] - pub extern "C" fn abi_constructor() -> Self { - Self {} - } - - #[ink(message)] - pub fn noop(&self) {} - } -} - -fn main() {} diff --git a/crates/lang/macro/tests/ui/contract/fail/C-13-abi-constructor.stderr b/crates/lang/macro/tests/ui/contract/fail/C-13-abi-constructor.stderr deleted file mode 100644 index 4eca2e5fe64..00000000000 --- a/crates/lang/macro/tests/ui/contract/fail/C-13-abi-constructor.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: ink! constructors must have explicit ABI - --> $DIR/C-13-abi-constructor.rs:10:13 - | -10 | pub extern "C" fn abi_constructor() -> Self { - | ^^^^^^^^^^ diff --git a/crates/lang/macro/tests/ui/contract/fail/C-15-payable-trait-constructor.rs b/crates/lang/macro/tests/ui/contract/fail/C-15-payable-trait-constructor.rs deleted file mode 100644 index 097c40d7b90..00000000000 --- a/crates/lang/macro/tests/ui/contract/fail/C-15-payable-trait-constructor.rs +++ /dev/null @@ -1,27 +0,0 @@ -use ink_lang as ink; - -#[ink::trait_definition] -pub trait Constructor { - #[ink(constructor)] - fn constructor() -> Self; -} - -#[ink::contract] -mod noop { - #[ink(storage)] - pub struct Noop {} - - impl Constructor for Noop { - #[ink(constructor, payable)] - fn constructor() -> Self { - Self {} - } - } - - impl Noop { - #[ink(message)] - pub fn noop(&self) {} - } -} - -fn main() {} diff --git a/crates/lang/macro/tests/ui/contract/fail/C-15-payable-trait-constructor.stderr b/crates/lang/macro/tests/ui/contract/fail/C-15-payable-trait-constructor.stderr deleted file mode 100644 index 1dda3879dc9..00000000000 --- a/crates/lang/macro/tests/ui/contract/fail/C-15-payable-trait-constructor.stderr +++ /dev/null @@ -1,11 +0,0 @@ -error: encountered conflicting ink! attribute argument - --> $DIR/C-15-payable-trait-constructor.rs:15:28 - | -15 | #[ink(constructor, payable)] - | ^^^^^^^ - -error: constructors are implicitly payable - --> $DIR/C-15-payable-trait-constructor.rs:15:28 - | -15 | #[ink(constructor, payable)] - | ^^^^^^^ diff --git a/crates/lang/macro/tests/ui/contract/fail/C-16-function-arg-struct-destructuring.rs b/crates/lang/macro/tests/ui/contract/fail/C-16-function-arg-struct-destructuring.rs deleted file mode 100644 index b811c1f2262..00000000000 --- a/crates/lang/macro/tests/ui/contract/fail/C-16-function-arg-struct-destructuring.rs +++ /dev/null @@ -1,24 +0,0 @@ -use ink_lang as ink; - -#[ink::contract] -mod noop { - - struct Args { - foo: (), - } - - #[ink(storage)] - pub struct Noop {} - - impl Noop { - #[ink(constructor)] - pub fn new() -> Self { - Self {} - } - - #[ink(message)] - pub fn noop(&self, Foo { foo }: Foo) {} - } -} - -fn main() {} diff --git a/crates/lang/macro/tests/ui/contract/fail/C-16-function-arg-struct-destructuring.stderr b/crates/lang/macro/tests/ui/contract/fail/C-16-function-arg-struct-destructuring.stderr deleted file mode 100644 index 33ada0340dc..00000000000 --- a/crates/lang/macro/tests/ui/contract/fail/C-16-function-arg-struct-destructuring.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: ink! message arguments must have an identifier - --> $DIR/C-16-function-arg-struct-destructuring.rs:20:28 - | -20 | pub fn noop(&self, Foo { foo }: Foo) {} - | ^^^^^^^^^^^^^^^^ diff --git a/crates/lang/macro/tests/ui/contract/fail/H-03-use-forbidden-idents.rs b/crates/lang/macro/tests/ui/contract/fail/H-03-use-forbidden-idents.rs deleted file mode 100644 index 6ae9c954822..00000000000 --- a/crates/lang/macro/tests/ui/contract/fail/H-03-use-forbidden-idents.rs +++ /dev/null @@ -1,23 +0,0 @@ -use ink_lang as ink; - -#[ink::contract] -mod forbidden_indents { - #[ink(storage)] - pub struct ForbiddenIndents {} - - impl ForbiddenIndents { - #[ink(constructor)] - pub fn constructor() -> Self { - Self {} - } - - /// An ink! message starting with __ink_ prefix. - #[ink(message)] - pub fn __ink_message(&self) { - // All identifiers starting with `__ink_` are forbidden to use in ink!. - let __ink_first = (); - } - } -} - -fn main() {} diff --git a/crates/lang/macro/tests/ui/contract/fail/H-03-use-forbidden-idents.stderr b/crates/lang/macro/tests/ui/contract/fail/H-03-use-forbidden-idents.stderr deleted file mode 100644 index 4dd20eb790e..00000000000 --- a/crates/lang/macro/tests/ui/contract/fail/H-03-use-forbidden-idents.stderr +++ /dev/null @@ -1,11 +0,0 @@ -error: encountered invalid identifier starting with __ink_ - --> $DIR/H-03-use-forbidden-idents.rs:16:16 - | -16 | pub fn __ink_message(&self) { - | ^^^^^^^^^^^^^ - -error: encountered invalid identifier starting with __ink_ - --> $DIR/H-03-use-forbidden-idents.rs:18:17 - | -18 | let __ink_first = (); - | ^^^^^^^^^^^ diff --git a/crates/lang/macro/tests/ui/contract/fail/M-01-missing-message.stderr b/crates/lang/macro/tests/ui/contract/fail/M-01-missing-message.stderr deleted file mode 100644 index 9072ceac84c..00000000000 --- a/crates/lang/macro/tests/ui/contract/fail/M-01-missing-message.stderr +++ /dev/null @@ -1,11 +0,0 @@ -error: missing ink! message - --> $DIR/M-01-missing-message.rs:4:1 - | -4 | / mod missing_message { -5 | | #[ink(storage)] -6 | | pub struct MissingMessage {} -7 | | -... | -13 | | } -14 | | } - | |_^ diff --git a/crates/lang/macro/tests/ui/contract/fail/M-02-message-missing-self-arg.stderr b/crates/lang/macro/tests/ui/contract/fail/M-02-message-missing-self-arg.stderr deleted file mode 100644 index 8f96d504a50..00000000000 --- a/crates/lang/macro/tests/ui/contract/fail/M-02-message-missing-self-arg.stderr +++ /dev/null @@ -1,6 +0,0 @@ -error: ink! messages must have `&self` or `&mut self` receiver - --> $DIR/M-02-message-missing-self-arg.rs:14:9 - | -14 | / #[ink(message)] -15 | | pub fn missing_self_arg() {} - | |____________________________________^ diff --git a/crates/lang/macro/tests/ui/contract/fail/M-04-message-returns-non-codec.stderr b/crates/lang/macro/tests/ui/contract/fail/M-04-message-returns-non-codec.stderr deleted file mode 100644 index 017f0cd2f00..00000000000 --- a/crates/lang/macro/tests/ui/contract/fail/M-04-message-returns-non-codec.stderr +++ /dev/null @@ -1,12 +0,0 @@ -error[E0277]: the trait bound `NonCodec: WrapperTypeEncode` is not satisfied - --> $DIR/M-04-message-returns-non-codec.rs:18:49 - | -18 | pub fn returns_non_codec_type(&self) -> NonCodec { - | ^^^^^^^^ the trait `WrapperTypeEncode` is not implemented for `NonCodec` - | - = note: required because of the requirements on the impl of `Encode` for `NonCodec` -note: required by a bound in `ink_lang::FnOutput::Output` - --> $DIR/traits.rs:79:18 - | -79 | type Output: scale::Encode + 'static; - | ^^^^^^^^^^^^^ required by this bound in `ink_lang::FnOutput::Output` diff --git a/crates/lang/macro/tests/ui/contract/fail/M-10-method-unknown-ink-marker.stderr b/crates/lang/macro/tests/ui/contract/fail/M-10-method-unknown-ink-marker.stderr deleted file mode 100644 index 7a05ae99fb5..00000000000 --- a/crates/lang/macro/tests/ui/contract/fail/M-10-method-unknown-ink-marker.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: unknown ink! attribute (path) - --> $DIR/M-10-method-unknown-ink-marker.rs:17:15 - | -17 | #[ink(unknown_marker)] - | ^^^^^^^^^^^^^^ diff --git a/crates/lang/macro/tests/ui/contract/fail/N-02-namespace-invalid-type.stderr b/crates/lang/macro/tests/ui/contract/fail/N-02-namespace-invalid-type.stderr deleted file mode 100644 index aed0beed386..00000000000 --- a/crates/lang/macro/tests/ui/contract/fail/N-02-namespace-invalid-type.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: expecteded string type for `namespace` argument, e.g. #[ink(namespace = "hello")] - --> $DIR/N-02-namespace-invalid-type.rs:8:11 - | -8 | #[ink(namespace = true)] - | ^^^^^^^^^^^^^^^^ diff --git a/crates/lang/macro/tests/ui/contract/fail/S-01-missing-storage-struct.stderr b/crates/lang/macro/tests/ui/contract/fail/S-01-missing-storage-struct.stderr deleted file mode 100644 index ac5146f2e1a..00000000000 --- a/crates/lang/macro/tests/ui/contract/fail/S-01-missing-storage-struct.stderr +++ /dev/null @@ -1,11 +0,0 @@ -error: missing ink! storage struct - --> $DIR/S-01-missing-storage-struct.rs:4:1 - | -4 | / mod missing_storage_struct { -5 | | // We are missing the #[ink(storage)] attribute here -6 | | pub struct MissingStorageStruct {} -7 | | -... | -14 | | } -15 | | } - | |_^ diff --git a/crates/lang/macro/tests/ui/contract/fail/S-02-multiple-storage-structs.rs b/crates/lang/macro/tests/ui/contract/fail/S-02-multiple-storage-structs.rs deleted file mode 100644 index 8bb6b0ea8c5..00000000000 --- a/crates/lang/macro/tests/ui/contract/fail/S-02-multiple-storage-structs.rs +++ /dev/null @@ -1,33 +0,0 @@ -use ink_lang as ink; - -#[ink::contract] -mod multiple_storage_structs { - #[ink(storage)] - pub struct FirstStorageStruct {} - - // ink! currently does not allow for multiple #[ink(storage)] structs - #[ink(storage)] - pub struct SecondStorageStruct {} - - impl FirstStorageStruct { - #[ink(constructor)] - pub fn constructor1() -> Self { - Self {} - } - - #[ink(message)] - pub fn message1(&self) {} - } - - impl SecondStorageStruct { - #[ink(constructor)] - pub fn constructor2() -> Self { - Self {} - } - - #[ink(message)] - pub fn message2(&self) {} - } -} - -fn main() {} diff --git a/crates/lang/macro/tests/ui/contract/fail/S-02-multiple-storage-structs.stderr b/crates/lang/macro/tests/ui/contract/fail/S-02-multiple-storage-structs.stderr deleted file mode 100644 index e521e457f1c..00000000000 --- a/crates/lang/macro/tests/ui/contract/fail/S-02-multiple-storage-structs.stderr +++ /dev/null @@ -1,23 +0,0 @@ -error: encountered multiple ink! storage structs, expected exactly one - --> $DIR/S-02-multiple-storage-structs.rs:4:1 - | -4 | / mod multiple_storage_structs { -5 | | #[ink(storage)] -6 | | pub struct FirstStorageStruct {} -7 | | -... | -30 | | } -31 | | } - | |_^ - -error: ink! storage struct here - --> $DIR/S-02-multiple-storage-structs.rs:6:5 - | -6 | pub struct FirstStorageStruct {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: ink! storage struct here - --> $DIR/S-02-multiple-storage-structs.rs:10:5 - | -10 | pub struct SecondStorageStruct {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/crates/lang/macro/tests/ui/contract/fail/S-03-struct-unknown-ink-marker.stderr b/crates/lang/macro/tests/ui/contract/fail/S-03-struct-unknown-ink-marker.stderr deleted file mode 100644 index eab64bedba9..00000000000 --- a/crates/lang/macro/tests/ui/contract/fail/S-03-struct-unknown-ink-marker.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: unknown ink! attribute (path) - --> $DIR/S-03-struct-unknown-ink-marker.rs:8:11 - | -8 | #[ink(unknown_or_unsupported)] - | ^^^^^^^^^^^^^^^^^^^^^^ diff --git a/crates/lang/macro/tests/ui/contract/fail/S-04-non-storage-ink-impls.rs b/crates/lang/macro/tests/ui/contract/fail/S-04-non-storage-ink-impls.rs deleted file mode 100644 index b3439f06de8..00000000000 --- a/crates/lang/macro/tests/ui/contract/fail/S-04-non-storage-ink-impls.rs +++ /dev/null @@ -1,39 +0,0 @@ -use ink_lang as ink; - -#[ink::contract] -mod non_storage_ink_impls { - // This test ensures that ink! `impl` blocks are always - // implemented on the only storage struct definition. - - #[ink(storage)] - pub struct StorageStruct {} - - // This ink! `impl` block is okay. - impl StorageStruct { - #[ink(constructor)] - pub fn constructor1() -> Self { - Self {} - } - - #[ink(message)] - pub fn message1(&self) {} - } - - // Missing the `#[ink(storage)]` attribute on purpose. - pub struct NonStorageStruct {} - - // This ink! `impl` block is invalid in that it implements - // the messages and constructors for a non-existing ink! - // storage struct. We expect a failure here. - impl NonStorageStruct { - #[ink(constructor)] - pub fn constructor2() -> Self { - Self {} - } - - #[ink(message)] - pub fn message2(&self) {} - } -} - -fn main() {} diff --git a/crates/lang/macro/tests/ui/contract/fail/S-04-non-storage-ink-impls.stderr b/crates/lang/macro/tests/ui/contract/fail/S-04-non-storage-ink-impls.stderr deleted file mode 100644 index 93e6fa7855b..00000000000 --- a/crates/lang/macro/tests/ui/contract/fail/S-04-non-storage-ink-impls.stderr +++ /dev/null @@ -1,39 +0,0 @@ -error[E0271]: type mismatch resolving `::This == StorageStruct` - --> $DIR/S-04-non-storage-ink-impls.rs:28:10 - | -28 | impl NonStorageStruct { - | ^^^^^^^^^^^^^^^^ expected struct `StorageStruct`, found struct `NonStorageStruct` - | -note: required by a bound in `non_storage_ink_impls::_::_::{closure#0}::assert_type_eq_all` - --> $DIR/S-04-non-storage-ink-impls.rs:28:10 - | -28 | impl NonStorageStruct { - | ^^^^^^^^^^^^^^^^ - | | - | required by a bound in this - | required by this bound in `non_storage_ink_impls::_::_::{closure#0}::assert_type_eq_all` - = note: this error originates in the macro `::ink_lang::static_assertions::assert_type_eq_all` (in Nightly builds, run with -Z macro-backtrace for more info) - -error[E0599]: no function or associated item named `message2` found for struct `StorageStruct` in the current scope - --> $DIR/S-04-non-storage-ink-impls.rs:35:16 - | -9 | pub struct StorageStruct {} - | ------------------------ function or associated item `message2` not found for this -... -35 | pub fn message2(&self) {} - | ^^^^^^^^ - | | - | function or associated item not found in `StorageStruct` - | help: there is an associated function with a similar name: `message1` - -error[E0599]: no function or associated item named `constructor2` found for struct `StorageStruct` in the current scope - --> $DIR/S-04-non-storage-ink-impls.rs:30:16 - | -9 | pub struct StorageStruct {} - | ------------------------ function or associated item `constructor2` not found for this -... -30 | pub fn constructor2() -> Self { - | ^^^^^^^^^^^^ - | | - | function or associated item not found in `StorageStruct` - | help: there is an associated function with a similar name: `constructor1` diff --git a/crates/lang/macro/tests/ui/contract/fail/S-05-storage-as-event.rs b/crates/lang/macro/tests/ui/contract/fail/S-05-storage-as-event.rs deleted file mode 100644 index 84cffdb25db..00000000000 --- a/crates/lang/macro/tests/ui/contract/fail/S-05-storage-as-event.rs +++ /dev/null @@ -1,20 +0,0 @@ -use ink_lang as ink; - -#[ink::contract] -mod storage_as_event { - #[ink(storage)] - #[ink(event)] // We cannot have #[ink(event)] if we already have #[ink(storage)] - pub struct StorageAsEvent {} - - impl StorageAsEvent { - #[ink(constructor)] - pub fn constructor() -> Self { - Self {} - } - - #[ink(message)] - pub fn message(&self) {} - } -} - -fn main() {} diff --git a/crates/lang/macro/tests/ui/contract/fail/S-05-storage-as-event.stderr b/crates/lang/macro/tests/ui/contract/fail/S-05-storage-as-event.stderr deleted file mode 100644 index 3c4ea660b7b..00000000000 --- a/crates/lang/macro/tests/ui/contract/fail/S-05-storage-as-event.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: encountered conflicting ink! attribute argument - --> $DIR/S-05-storage-as-event.rs:6:11 - | -6 | #[ink(event)] // We cannot have #[ink(event)] if we already have #[ink(storage)] - | ^^^^^ diff --git a/crates/lang/macro/tests/ui/contract/fail/S-06-event-as-storage.rs b/crates/lang/macro/tests/ui/contract/fail/S-06-event-as-storage.rs deleted file mode 100644 index 1a932386d3c..00000000000 --- a/crates/lang/macro/tests/ui/contract/fail/S-06-event-as-storage.rs +++ /dev/null @@ -1,20 +0,0 @@ -use ink_lang as ink; - -#[ink::contract] -mod event_as_storage { - #[ink(event)] - #[ink(storage)] // We cannot have #[ink(storage)] if we already have #[ink(event)] - pub struct EventAsStorage {} - - impl EventAsStorage { - #[ink(constructor)] - pub fn constructor() -> Self { - Self {} - } - - #[ink(message)] - pub fn message(&self) {} - } -} - -fn main() {} diff --git a/crates/lang/macro/tests/ui/contract/fail/S-06-event-as-storage.stderr b/crates/lang/macro/tests/ui/contract/fail/S-06-event-as-storage.stderr deleted file mode 100644 index 55a85d515b4..00000000000 --- a/crates/lang/macro/tests/ui/contract/fail/S-06-event-as-storage.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: encountered conflicting ink! attribute argument - --> $DIR/S-06-event-as-storage.rs:6:11 - | -6 | #[ink(storage)] // We cannot have #[ink(storage)] if we already have #[ink(event)] - | ^^^^^^^ diff --git a/crates/lang/macro/tests/ui/contract/pass/01-noop-contract.rs b/crates/lang/macro/tests/ui/contract/pass/01-noop-contract.rs deleted file mode 100644 index 130e7fb0c8f..00000000000 --- a/crates/lang/macro/tests/ui/contract/pass/01-noop-contract.rs +++ /dev/null @@ -1,19 +0,0 @@ -use ink_lang as ink; - -#[ink::contract] -mod noop { - #[ink(storage)] - pub struct Noop {} - - impl Noop { - #[ink(constructor)] - pub fn new() -> Self { - Self {} - } - - #[ink(message)] - pub fn noop(&self) {} - } -} - -fn main() {} diff --git a/crates/lang/macro/tests/ui/contract/pass/02-flipper-contract.rs b/crates/lang/macro/tests/ui/contract/pass/02-flipper-contract.rs deleted file mode 100644 index 89f173cf984..00000000000 --- a/crates/lang/macro/tests/ui/contract/pass/02-flipper-contract.rs +++ /dev/null @@ -1,47 +0,0 @@ -use ink_lang as ink; - -#[ink::contract] -mod flipper { - #[ink(storage)] - pub struct Flipper { - value: bool, - } - - impl Flipper { - #[ink(constructor)] - pub fn new(init_value: bool) -> Self { - Self { value: init_value } - } - - #[ink(constructor)] - pub fn default() -> Self { - Self::new(false) - } - - #[ink(message)] - pub fn flip(&mut self) { - self.value = !self.value; - } - - #[ink(message)] - pub fn get(&self) -> bool { - self.value - } - } - - #[cfg(test)] - mod tests { - use super::*; - use ink_lang as ink; - - #[ink::test] - fn it_works() { - let mut flipper = Flipper::new(false); - assert!(!flipper.get()); - flipper.flip(); - assert!(flipper.get()); - } - } -} - -fn main() {} diff --git a/crates/lang/macro/tests/ui/contract/pass/03-incrementer-contract.rs b/crates/lang/macro/tests/ui/contract/pass/03-incrementer-contract.rs deleted file mode 100644 index 612d1ffbba7..00000000000 --- a/crates/lang/macro/tests/ui/contract/pass/03-incrementer-contract.rs +++ /dev/null @@ -1,45 +0,0 @@ -use ink_lang as ink; - -#[ink::contract] -mod incrementer { - #[ink(storage)] - pub struct Incrementer { - value: i64, - } - - #[ink(event)] - pub struct Incremented { - #[ink(topic)] - caller: AccountId, - #[ink(topic)] - by: i32, - } - - impl Incrementer { - #[ink(constructor)] - pub fn new(init_value: i32) -> Self { - Self { - value: init_value as i64, - } - } - - #[ink(constructor)] - pub fn default() -> Self { - Self::new(0) - } - - #[ink(message)] - pub fn inc_by(&mut self, by: i32) { - let caller = self.env().caller(); - self.env().emit_event(Incremented { caller, by }); - self.value += by as i64; - } - - #[ink(message)] - pub fn get(&self) -> i64 { - self.value - } - } -} - -fn main() {} diff --git a/crates/lang/macro/tests/ui/contract/pass/06-non-ink-items.rs b/crates/lang/macro/tests/ui/contract/pass/06-non-ink-items.rs deleted file mode 100644 index b7d5d4e0718..00000000000 --- a/crates/lang/macro/tests/ui/contract/pass/06-non-ink-items.rs +++ /dev/null @@ -1,27 +0,0 @@ -use ink_lang as ink; - -#[ink::contract] -mod noop { - #[ink(storage)] - pub struct Noop {} - - impl Noop { - #[ink(constructor)] - pub fn new() -> Self { - Self {} - } - - #[ink(message)] - pub fn noop(&self) {} - } - - struct NonInkStruct {} - enum NonInkEnum {} - impl NonInkStruct { - fn do_nothing() {} - } - - type NonInkTypeAlias = u32; -} - -fn main() {} diff --git a/crates/lang/macro/tests/ui/contract/pass/08-flipper-as-dependency-trait.rs b/crates/lang/macro/tests/ui/contract/pass/08-flipper-as-dependency-trait.rs deleted file mode 100644 index 59514a4ea79..00000000000 --- a/crates/lang/macro/tests/ui/contract/pass/08-flipper-as-dependency-trait.rs +++ /dev/null @@ -1,40 +0,0 @@ -use ink_lang as ink; - -#[ink::contract(compile_as_dependency = true)] -mod flipper { - #[ink_lang::trait_definition] - pub trait FlipperTrait { - #[ink(constructor)] - fn new() -> Self; - - #[ink(message)] - fn flip(&mut self); - - #[ink(message)] - fn get(&self) -> bool; - } - - #[ink(storage)] - pub struct Flipper { - value: bool, - } - - impl FlipperTrait for Flipper { - #[ink(constructor)] - fn new() -> Self { - Self::default() - } - - #[ink(message)] - fn flip(&mut self) { - self.value = !self.value; - } - - #[ink(message)] - fn get(&self) -> bool { - self.value - } - } -} - -fn main() {} diff --git a/crates/lang/macro/tests/ui/contract/pass/09-static-env.rs b/crates/lang/macro/tests/ui/contract/pass/09-static-env.rs deleted file mode 100644 index 80e967db2d5..00000000000 --- a/crates/lang/macro/tests/ui/contract/pass/09-static-env.rs +++ /dev/null @@ -1,22 +0,0 @@ -use ink_lang as ink; - -#[ink::contract] -mod static_env { - #[ink(storage)] - pub struct UsesStaticEnv {} - - impl UsesStaticEnv { - #[ink(constructor)] - pub fn new() -> Self { - assert!(Self::env().balance() > 0); - Self {} - } - - #[ink(message)] - pub fn gas_left(&mut self) -> u64 { - Self::env().gas_left() - } - } -} - -fn main() {} diff --git a/crates/lang/macro/tests/ui/contract/pass/11-alias-storage-struct-impl.rs b/crates/lang/macro/tests/ui/contract/pass/11-alias-storage-struct-impl.rs deleted file mode 100644 index f287c2f7621..00000000000 --- a/crates/lang/macro/tests/ui/contract/pass/11-alias-storage-struct-impl.rs +++ /dev/null @@ -1,21 +0,0 @@ -use ink_lang as ink; - -#[ink::contract] -mod noop { - #[ink(storage)] - pub struct Noop {} - - pub type NoopAlias = Noop; - - impl NoopAlias { - #[ink(constructor)] - pub fn new() -> Self { - Self {} - } - - #[ink(message)] - pub fn noop(&self) {} - } -} - -fn main() {} diff --git a/crates/lang/src/codegen/dispatch/execution.rs b/crates/lang/src/codegen/dispatch/execution.rs new file mode 100644 index 00000000000..1ad6e6837c3 --- /dev/null +++ b/crates/lang/src/codegen/dispatch/execution.rs @@ -0,0 +1,171 @@ +// 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::reflect::{ + ContractEnv, + DispatchError, +}; +use core::{ + any::TypeId, + mem::ManuallyDrop, +}; +use ink_env::{ + Environment, + ReturnFlags, +}; +use ink_primitives::Key; +use ink_storage::{ + alloc, + alloc::ContractPhase, + traits::{ + pull_spread_root, + push_spread_root, + SpreadLayout, + }, +}; + +/// Returns `Ok` if the caller did not transfer additional value to the callee. +/// +/// # Errors +/// +/// If the caller did send some amount of transferred value to the callee. +#[inline] +pub fn deny_payment() -> Result<(), DispatchError> +where + E: Environment, +{ + let transferred = ink_env::transferred_balance::() + .expect("encountered error while querying transferred balance"); + if transferred != ::Balance::from(0u32) { + return Err(DispatchError::PaidUnpayableMessage) + } + Ok(()) +} + +/// Configuration for execution of ink! constructor. +#[derive(Debug, Copy, Clone)] +pub struct ExecuteConstructorConfig { + /// Yields `true` if the dynamic storage allocator has been enabled. + /// + /// # Note + /// + /// Authors can enable it via `#[ink::contract(dynamic_storage_allocator = true)]`. + pub dynamic_storage_alloc: bool, +} + +/// Executes the given ink! constructor. +/// +/// # Note +/// +/// The closure is supposed to already contain all the arguments that the real +/// constructor message requires and forwards them. +#[inline] +pub fn execute_constructor( + config: ExecuteConstructorConfig, + f: F, +) -> Result<(), DispatchError> +where + S: ink_storage::traits::SpreadLayout, + F: FnOnce() -> S, +{ + if config.dynamic_storage_alloc { + alloc::initialize(ContractPhase::Deploy); + } + let storage = ManuallyDrop::new(f()); + let root_key = Key::from([0x00; 32]); + push_spread_root::(&storage, &root_key); + if config.dynamic_storage_alloc { + alloc::finalize(); + } + Ok(()) +} + +/// Configuration for execution of ink! messages. +#[derive(Debug, Copy, Clone)] +pub struct ExecuteMessageConfig { + /// Yields `true` if the ink! message accepts payment. + /// + /// # Note + /// + /// If no ink! message within the same ink! smart contract + /// is payable then this flag will be `true` since the check + /// then is moved before the message dispatch as an optimization. + pub payable: bool, + /// Yields `true` if the ink! message might mutate contract storage. + /// + /// # Note + /// + /// This is usually true for `&mut self` ink! messages. + pub mutates: bool, + /// Yields `true` if the ink! message execution might revert execution. + /// + /// # Note + /// + /// In ink! this is usually `true` for root ink! smart contracts and + /// `false` for dependency ink! smart contracts. + pub may_revert: bool, + /// Yields `true` if the dynamic storage allocator has been enabled. + /// + /// # Note + /// + /// Authors can enable it via `#[ink::contract(dynamic_storage_allocator = true)]`. + pub dynamic_storage_alloc: bool, +} + +/// Executes the given `&mut self` message closure. +/// +/// # Note +/// +/// The closure is supposed to already contain all the arguments that the real +/// message requires and forwards them. +#[inline] +pub fn execute_message( + config: ExecuteMessageConfig, + f: F, +) -> Result<(), DispatchError> +where + Storage: SpreadLayout + ContractEnv, + Output: scale::Encode + 'static, + F: FnOnce(&mut Storage) -> Output, +{ + if !config.payable { + deny_payment::<::Env>()?; + } + if config.dynamic_storage_alloc { + alloc::initialize(ContractPhase::Call); + } + let root_key = Key::from([0x00; 32]); + let mut storage = ManuallyDrop::new(pull_spread_root::(&root_key)); + let result = f(&mut storage); + if config.mutates { + push_spread_root::(&storage, &root_key); + } + if config.dynamic_storage_alloc { + alloc::finalize(); + } + if TypeId::of::() != TypeId::of::<()>() { + // We include a check for `is_result_type!(Output)` despite the fact that this + // is indirectly covered by `is_result_err!(&result)` because the Rust compiler + // will have more opportunities to optimize the whole conditional away. This is + // due to the fact that `is_result_type!` relies on constant information whereas + // is_result_err!` requires `&self`. + let revert_state = + config.may_revert && is_result_type!(Output) && is_result_err!(&result); + ink_env::return_value::( + ReturnFlags::default().set_reverted(revert_state), + &result, + ) + } + Ok(()) +} diff --git a/crates/lang/src/codegen/dispatch/info.rs b/crates/lang/src/codegen/dispatch/info.rs new file mode 100644 index 00000000000..c7843d1ca3b --- /dev/null +++ b/crates/lang/src/codegen/dispatch/info.rs @@ -0,0 +1,22 @@ +// 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. + +/// Used to refer to the generated contract call builder. +/// +/// The generated contract call builder implements the long-hand calling API +/// for all inherent or trait ink! messages. +pub trait ContractCallBuilder { + /// The generated contract call builder type. + type Type; +} diff --git a/crates/lang/src/codegen/dispatch/mod.rs b/crates/lang/src/codegen/dispatch/mod.rs new file mode 100644 index 00000000000..d94871aa2a5 --- /dev/null +++ b/crates/lang/src/codegen/dispatch/mod.rs @@ -0,0 +1,32 @@ +// 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. + +mod execution; +mod info; +mod type_check; + +pub use self::{ + execution::{ + deny_payment, + execute_constructor, + execute_message, + ExecuteConstructorConfig, + ExecuteMessageConfig, + }, + info::ContractCallBuilder, + type_check::{ + DispatchInput, + DispatchOutput, + }, +}; diff --git a/crates/lang/src/codegen/dispatch/type_check.rs b/crates/lang/src/codegen/dispatch/type_check.rs new file mode 100644 index 00000000000..fe25341d504 --- /dev/null +++ b/crates/lang/src/codegen/dispatch/type_check.rs @@ -0,0 +1,69 @@ +// 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. + +/// Used to check if `T` is allowed as ink! input parameter type. +/// +/// # Note +/// +/// An ink! input parameter type must implement [`scale::Decode`] +/// and must have a `'static` lifetime. +/// +/// # Example +/// +/// This compiles since `i32` fulfills the requirements of an ink! input. +/// +/// ``` +/// # use ink_lang::codegen::DispatchInput; +/// const _: () = ink_lang::codegen::utils::consume_type::>(); +/// ``` +/// +/// This fails to compile since `Foo` does not fulfill all requirements. +/// +/// ```compile_fail +/// # use ink_lang::codegen::DispatchInput; +/// // Foo is missing scale codec implementations. +/// struct Foo {} +/// const _: () = ink_lang::codegen::utils::consume_type::>(); +/// ``` +pub struct DispatchInput(T) +where + T: scale::Decode + 'static; + +/// Used to check if `T` is allowed as ink! output parameter type. +/// +/// # Note +/// +/// An ink! input parameter type must implement [`scale::Encode`] +/// and must have a `'static` lifetime. +/// +/// # Example +/// +/// This compiles since `i32` fulfills the requirements of an ink! output. +/// +/// ``` +/// # use ink_lang::codegen::DispatchOutput; +/// const _: () = ink_lang::codegen::utils::consume_type::>(); +/// ``` +/// +/// This fails to compile since `Foo` does not fulfill all requirements. +/// +/// ```compile_fail +/// # use ink_lang::codegen::DispatchOutput; +/// // Foo is missing scale codec implementations. +/// struct Foo {} +/// const _: () = ink_lang::codegen::utils::consume_type::>(); +/// ``` +pub struct DispatchOutput(T) +where + T: scale::Encode + 'static; diff --git a/crates/lang/src/codegen/env.rs b/crates/lang/src/codegen/env.rs new file mode 100644 index 00000000000..0d34828c614 --- /dev/null +++ b/crates/lang/src/codegen/env.rs @@ -0,0 +1,43 @@ +// 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. + +/// Simplifies interaction with the host environment via `self`. +/// +/// # Note +/// +/// This is generally implemented for storage structs that include +/// their environment in order to allow the different dispatch functions +/// to use it for returning the contract's output. +pub trait Env { + /// The access wrapper. + type EnvAccess; + + /// Accesses the host environment with `self.env()` syntax. + fn env(self) -> Self::EnvAccess; +} + +/// Simplifies interaction with the host environment via `Self`. +/// +/// # Note +/// +/// This is generally implemented for storage structs that include +/// their environment in order to allow the different dispatch functions +/// to use it for returning the contract's output. +pub trait StaticEnv { + /// The access wrapper. + type EnvAccess; + + /// Accesses the host environment with `Self::env()` syntax. + fn env() -> Self::EnvAccess; +} diff --git a/crates/lang/src/events.rs b/crates/lang/src/codegen/event/emit.rs similarity index 57% rename from crates/lang/src/events.rs rename to crates/lang/src/codegen/event/emit.rs index 69ede2223f7..e47980e8b14 100644 --- a/crates/lang/src/events.rs +++ b/crates/lang/src/codegen/event/emit.rs @@ -12,28 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. -/// Implemented by contracts in order to override `env().emit_event(..)` -/// syntax for emitting of ink! contract events. -/// -/// # Developer Note -/// -/// Normally we'd try to define traits like these in the companion -/// `ink_lang` crate, however, due to Rust's orphan rules we must -/// define this trait here. +use crate::reflect::ContractEventBase; + +/// Allows for `self.env().emit_event(..)` syntax in ink! implementation blocks. pub trait EmitEvent where - C: BaseEvent, + C: ContractEventBase, { /// Emits an event that can be trivially converted into the base event. fn emit_event(self, event: E) where - E: Into<::Type>; -} - -/// Defines a base event type for the contract. -/// -/// This is usually the event enum that comprises all defined event types. -pub trait BaseEvent { - /// The generated base event enum. - type Type; + E: Into<::Type>; } diff --git a/crates/lang/src/codegen/event/mod.rs b/crates/lang/src/codegen/event/mod.rs new file mode 100644 index 00000000000..ec78bb811f5 --- /dev/null +++ b/crates/lang/src/codegen/event/mod.rs @@ -0,0 +1,26 @@ +// 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. + +mod emit; +mod topics; + +pub use self::{ + emit::EmitEvent, + topics::{ + EventLenTopics, + EventRespectsTopicLimit, + EventTopics, + RespectTopicLimit, + }, +}; diff --git a/crates/lang/src/codegen/event/topics.rs b/crates/lang/src/codegen/event/topics.rs new file mode 100644 index 00000000000..36bc36d9e1f --- /dev/null +++ b/crates/lang/src/codegen/event/topics.rs @@ -0,0 +1,91 @@ +// 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 core::marker::PhantomData; + +/// Guards that an ink! event definitions respects the topic limit. +/// +/// # Usage +/// +/// ``` +/// // #[ink(event)] +/// pub struct ExampleEvent {} +/// +/// /// The amount of the topics of the example event struct. +/// const LEN_TOPICS: usize = 3; +/// +/// /// The limit for the amount of topics per ink! event definition. +/// const TOPICS_LIMIT: usize = 4; +/// +/// impl ::ink_lang::codegen::EventLenTopics for ExampleEvent { +/// type LenTopics = ::ink_lang::codegen::EventTopics; +/// } +/// +/// // The below code only compiles successfully if the example ink! event +/// // definitions respects the topic limitation: it must have an amount of +/// // topics less than or equal to the topic limit. +/// const _: () = ::ink_lang::codegen::utils::consume_type::< +/// ::ink_lang::codegen::EventRespectsTopicLimit< +/// ExampleEvent, +/// TOPICS_LIMIT, +/// > +/// >(); +/// ``` +pub struct EventRespectsTopicLimit +where + Event: EventLenTopics, + ::LenTopics: RespectTopicLimit, +{ + marker: PhantomData Event>, +} + +/// Guards that an amount of event topics respects the event topic limit. +/// +/// # Note +/// +/// Implemented by `EventTopics` if M is less or equal to N. +/// Automatically implemented for up to 12 event topics. +pub trait RespectTopicLimit {} + +/// Represents an the amount of topics for an ink! event definition. +pub struct EventTopics; + +macro_rules! impl_is_smaller_or_equals { + ( $first:literal $( , $rest:literal )* $(,)? ) => { + impl RespectTopicLimit<$first> for EventTopics<$first> {} + $( + impl RespectTopicLimit<$rest> for EventTopics<$first> {} + )* + + impl_is_smaller_or_equals! { $( $rest ),* } + }; + ( ) => {}; +} +impl_is_smaller_or_equals! { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 +} + +/// Stores the number of event topics of the ink! event definition. +pub trait EventLenTopics { + /// Type denoting the number of event topics. + /// + /// # Note + /// + /// We use an associated type instead of an associated constant here + /// because Rust does not yet allow for generics in constant parameter + /// position which would be required in the `EventRespectsTopicLimit` + /// trait definition. + /// As soon as this is possible in Rust we might change this to a constant. + type LenTopics; +} diff --git a/crates/lang/src/codegen/implies_return.rs b/crates/lang/src/codegen/implies_return.rs new file mode 100644 index 00000000000..eacdbbc1394 --- /dev/null +++ b/crates/lang/src/codegen/implies_return.rs @@ -0,0 +1,66 @@ +// 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 ink_env::{ + call::{ + utils::{ + ReturnType, + Set, + }, + CallBuilder, + ExecutionInput, + }, + Environment, +}; + +/// Trait used as bound for the outputs of ink! trait definition messages. +/// +/// # Note +/// +/// Indicates that a value of type `T` or an equivalent `CallBuilder` +/// instantiation of an ink! message returning a value of type `T` are +/// returned by the respective ink! trait message. +/// +/// The bounds are automatically generated by the `#[ink::trait_definition]` +/// procedural macro. +pub trait ImpliesReturn {} + +impl ImpliesReturn for T {} +impl ImpliesReturn + for CallBuilder< + E, + Callee, + GasCost, + TransferredValue, + Set>, + Set>, + > +where + E: Environment, +{ +} + +impl ImpliesReturn<()> + for CallBuilder< + E, + Callee, + GasCost, + TransferredValue, + Set>, + Set<()>, + > +where + E: Environment, +{ +} diff --git a/crates/lang/src/codegen/is_same_type.rs b/crates/lang/src/codegen/is_same_type.rs new file mode 100644 index 00000000000..dedfcd4bd97 --- /dev/null +++ b/crates/lang/src/codegen/is_same_type.rs @@ -0,0 +1,45 @@ +// 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 core::marker::PhantomData; + +/// Can be used to check equality of types. +/// +/// # Example +/// +/// This code compiles: +/// +/// ``` +/// # use ink_lang::codegen::IsSameType; +/// const _: IsSameType = IsSameType::::new(); +/// ``` +/// +/// While this code does not: +/// +/// ```compile_fail +/// # use ink_lang::codegen::IsSameType; +/// const _: IsSameType = IsSameType::::new(); +/// ``` +pub struct IsSameType { + _marker: PhantomData, +} + +impl IsSameType { + /// Creates a new const instance. + pub const fn new() -> Self { + Self { + _marker: PhantomData, + } + } +} diff --git a/crates/lang/src/codegen/mod.rs b/crates/lang/src/codegen/mod.rs new file mode 100644 index 00000000000..a2965499096 --- /dev/null +++ b/crates/lang/src/codegen/mod.rs @@ -0,0 +1,54 @@ +// 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. + +//! Definitions and facilities mainly used by the ink! codegen. + +mod dispatch; +mod env; +mod event; +mod implies_return; +mod trait_def; +pub mod utils; + +pub use self::{ + dispatch::{ + deny_payment, + execute_constructor, + execute_message, + ContractCallBuilder, + DispatchInput, + DispatchOutput, + ExecuteConstructorConfig, + ExecuteMessageConfig, + }, + env::{ + Env, + StaticEnv, + }, + event::{ + EmitEvent, + EventLenTopics, + EventRespectsTopicLimit, + EventTopics, + RespectTopicLimit, + }, + implies_return::ImpliesReturn, + trait_def::{ + TraitCallBuilder, + TraitCallForwarder, + TraitCallForwarderFor, + TraitMessagePayable, + TraitMessageSelector, + }, +}; diff --git a/crates/lang/src/codegen/trait_def/call_builder.rs b/crates/lang/src/codegen/trait_def/call_builder.rs new file mode 100644 index 00000000000..846d2502a07 --- /dev/null +++ b/crates/lang/src/codegen/trait_def/call_builder.rs @@ -0,0 +1,81 @@ +// 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. + +/// The global call builder type for an ink! trait definition. +pub trait TraitCallBuilder { + /// The call builder type. + type Builder; + + /// Returns a shared reference to the global call builder type. + /// + /// This allows to call `&self` ink! trait messages. + fn call(&self) -> &Self::Builder; + + /// Returns an exclusive reference to the global call builder type. + /// + /// This allows to call any ink! trait message. + fn call_mut(&mut self) -> &mut Self::Builder; +} + +/// Implemented by the global trait info provider. +/// +/// It is used to query the global trait call forwarder. +/// There is one global trait call forwarder that implements +/// the call forwarding (short- and long-form) for all calls +/// to this trait in `ink-as-dependency` configuration. +pub trait TraitCallForwarder { + /// The call forwarder type. + type Forwarder: TraitCallBuilder; +} + +/// Implemented by call builders of smart contracts. +/// +/// These might be implementing multiple different ink! traits. +/// The codegen makes them implement this trait once for every +/// ink! trait they have to implement. +/// +/// While the trait is not necessary it encapsulates a lot of +/// utility and auxiliary code required for the actual ink! trait +/// implementations. +pub trait TraitCallForwarderFor { + type Forwarder: TraitCallBuilder; + + /// Forwards the `&self` call. + /// + /// # Note + /// + /// This is used for the short-hand calling syntax. + fn forward(&self) -> &Self::Forwarder; + + /// Forwards the `&mut self` call. + /// + /// # Note + /// + /// This is used for the short-hand calling syntax. + fn forward_mut(&mut self) -> &mut Self::Forwarder; + + /// Builds up the `&self` call. + /// + /// # Note + /// + /// This is used for the long-hand calling syntax. + fn build(&self) -> &::Builder; + + /// Builds up the `&mut self` call. + /// + /// # Note + /// + /// This is used for the long-hand calling syntax. + fn build_mut(&mut self) -> &mut ::Builder; +} diff --git a/crates/lang/src/codegen/trait_def/mod.rs b/crates/lang/src/codegen/trait_def/mod.rs new file mode 100644 index 00000000000..f3b18af8fbd --- /dev/null +++ b/crates/lang/src/codegen/trait_def/mod.rs @@ -0,0 +1,28 @@ +// 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. + +mod call_builder; +mod trait_message; + +pub use self::{ + call_builder::{ + TraitCallBuilder, + TraitCallForwarder, + TraitCallForwarderFor, + }, + trait_message::{ + TraitMessagePayable, + TraitMessageSelector, + }, +}; diff --git a/crates/lang/src/codegen/trait_def/trait_message.rs b/crates/lang/src/codegen/trait_def/trait_message.rs new file mode 100644 index 00000000000..c541e3122b1 --- /dev/null +++ b/crates/lang/src/codegen/trait_def/trait_message.rs @@ -0,0 +1,33 @@ +// 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. + +/// Used as `payable` property guard for ink! trait messages. +/// +/// # Note +/// +/// When an ink! trait message is annotated with `#[ink(payable)]` +/// a compile time check is generated by ink! to guard that the +/// payability of the ink! trait message matches the payability of +/// the same ink! message as defined by the ink! trait message. +pub struct TraitMessagePayable; + +/// Used as `selector` property guard for ink! trait messages. +/// +/// # Note +/// +/// When an ink! trait message is annotated with `#[ink(selector = ..)]` +/// a compile time check is generated by ink! to guard that the +/// selector of the ink! trait message matches the selector of +/// the same ink! message as defined by the ink! trait message. +pub struct TraitMessageSelector; diff --git a/crates/lang/src/codegen/utils/identity_type.rs b/crates/lang/src/codegen/utils/identity_type.rs new file mode 100644 index 00000000000..fffc5ce543e --- /dev/null +++ b/crates/lang/src/codegen/utils/identity_type.rs @@ -0,0 +1,47 @@ +// 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. + +/// Takes a generic type as input and just consumes it while doing nothing. +/// +/// # Note +/// +/// This can be used to trigger some compile time checks due to the fact +/// that the type consumed this way is type checked. We usually use this +/// to make the Rust compiler check the trait bounds in particular. +/// +/// # Usage: Compiles +/// +/// ``` +/// # use ink_lang::codegen::utils::consume_type; +/// # use core::marker::PhantomData; +/// # +/// pub struct RequiresCopy(PhantomData); +/// +/// // The following line of code works because `i32: Copy`. +/// let _: () = consume_type::>(); +/// ``` +/// +/// # Usage: Compile Error +/// +/// ```compile_fail +/// # use ink_lang::codegen::utils::consume_type; +/// # use core::marker::PhantomData; +/// # +/// pub struct RequiresCopy(PhantomData); +/// +/// // The following line of code fails to compile because +/// // `String` does not implement `Copy`. +/// let _: () = consume_type::>(); +/// ``` +pub const fn consume_type() {} diff --git a/crates/lang/src/codegen/utils/mod.rs b/crates/lang/src/codegen/utils/mod.rs new file mode 100644 index 00000000000..5ed73ba58ea --- /dev/null +++ b/crates/lang/src/codegen/utils/mod.rs @@ -0,0 +1,23 @@ +// 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. + +//! Utility types and definitions used by the ink! codegen. + +mod identity_type; +mod same_type; + +pub use self::{ + identity_type::consume_type, + same_type::IsSameType, +}; diff --git a/crates/lang/src/codegen/utils/same_type.rs b/crates/lang/src/codegen/utils/same_type.rs new file mode 100644 index 00000000000..44fbba54381 --- /dev/null +++ b/crates/lang/src/codegen/utils/same_type.rs @@ -0,0 +1,45 @@ +// 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 core::marker::PhantomData; + +/// Can be used to check equality of types. +/// +/// # Example +/// +/// This code compiles: +/// +/// ``` +/// # use ink_lang::codegen::utils::IsSameType; +/// const _: IsSameType = IsSameType::::new(); +/// ``` +/// +/// While this code does not: +/// +/// ```compile_fail +/// # use ink_lang::codegen::utils::IsSameType; +/// const _: IsSameType = IsSameType::::new(); +/// ``` +pub struct IsSameType { + _marker: PhantomData, +} + +impl IsSameType { + /// Creates a new const instance. + pub const fn new() -> Self { + Self { + _marker: PhantomData, + } + } +} diff --git a/crates/lang/src/contract.rs b/crates/lang/src/contract.rs deleted file mode 100644 index 79109122f64..00000000000 --- a/crates/lang/src/contract.rs +++ /dev/null @@ -1,36 +0,0 @@ -// 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::DispatchError; - -/// The contract dispatch mode. -/// -/// Tells the [`DispatchUsingMode`](`crate::DispatchUsingMode`) implementation for -/// an ink! smart contract how to dispatch for a call. -#[derive(Copy, Clone, PartialEq, Eq)] -#[doc(hidden)] -pub enum DispatchMode { - /// Mode for instantiating a contract. - Instantiate, - /// Mode for calling a contract. - Call, -} - -/// Trait implemented by contracts themselves in order to provide a clean -/// interface for the C-ABI specified `call` and `create` functions to forward -/// calls to. -#[doc(hidden)] -pub trait DispatchUsingMode { - fn dispatch_using_mode(mode: DispatchMode) -> Result<(), DispatchError>; -} diff --git a/crates/lang/src/contract_ref.rs b/crates/lang/src/contract_ref.rs new file mode 100644 index 00000000000..fa62e87375e --- /dev/null +++ b/crates/lang/src/contract_ref.rs @@ -0,0 +1,26 @@ +// 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 ink_env::Environment; + +/// Implemented by contracts that are compiled as dependencies. +/// +/// Allows them to return their underlying account identifier. +pub trait ToAccountId +where + T: Environment, +{ + /// Returns the underlying account identifier of the instantiated contract. + fn to_account_id(&self) -> ::AccountId; +} diff --git a/crates/lang/src/cross_calling.rs b/crates/lang/src/cross_calling.rs deleted file mode 100644 index 088fb2667e1..00000000000 --- a/crates/lang/src/cross_calling.rs +++ /dev/null @@ -1,56 +0,0 @@ -// 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 ink_env::Environment; - -/// The type that can never be returned because it is not possible to craft an instance of it. -#[doc(hidden)] -pub enum NeverReturns {} - -/// Implemented by contracts that are compiled as dependencies. -/// -/// This allows to forward `&self` calls to a call forwarder -/// that encodes and dispatches the calls to the chain. -#[doc(hidden)] -pub trait ForwardCall { - /// The call forwarder that handles `&self` messages. - type Forwarder; - - /// Instantiates a call forwarder to forward `&self` messages. - fn call(self) -> Self::Forwarder; -} - -/// Implemented by contracts that are compiled as dependencies. -/// -/// This allows to forward `&mut self` calls to a call forwarder -/// that encodes and dispatches the calls to the chain. -#[doc(hidden)] -pub trait ForwardCallMut { - /// The call forwarder that handles `&mut self` messages. - type Forwarder; - - /// Instantiates a call forwarder to forward `&mut self` messages. - fn call_mut(self) -> Self::Forwarder; -} - -/// Implemented by contracts that are compiled as dependencies. -/// -/// Allows them to return their underlying account identifier. -pub trait ToAccountId -where - T: Environment, -{ - /// Returns the underlying account identifier of the instantiated contract. - fn to_account_id(&self) -> ::AccountId; -} diff --git a/crates/lang/src/dispatcher.rs b/crates/lang/src/dispatcher.rs deleted file mode 100644 index aeedebb6037..00000000000 --- a/crates/lang/src/dispatcher.rs +++ /dev/null @@ -1,221 +0,0 @@ -// 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::{ - Constructor, - DispatchError, - FnOutput, - FnState, - MessageMut, - MessageRef, -}; -use core::{ - any::TypeId, - mem::ManuallyDrop, -}; -use ink_env::{ - Environment, - ReturnFlags, -}; -use ink_primitives::Key; -use ink_storage::{ - alloc, - alloc::ContractPhase, - traits::{ - pull_spread_root, - push_spread_root, - }, -}; - -/// Results of message handling operations. -#[doc(hidden)] -pub type Result = core::result::Result; - -/// Connector trait: Connects enum dispatcher for messages with the contract. -#[doc(hidden)] -pub trait MessageDispatcher { - /// The contract's message dispatcher type. - type Type; -} - -/// Connector trait: Connects enum dispatcher for constructors with the contract. -#[doc(hidden)] -pub trait ConstructorDispatcher { - /// The contract's constructors dispatcher type. - type Type; -} - -/// Connector trait used to start the execution of a smart contract. -/// -/// The generated message and constructor dispatch enums implement this trait -/// in order to forward their already decoded state to the selected messages -/// or constructors. -#[doc(hidden)] -pub trait Execute { - /// Starts the smart contract execution. - fn execute(self) -> Result<()>; -} - -/// Yields `true` if the message accepts payments. -#[derive(Copy, Clone)] -#[doc(hidden)] -pub struct AcceptsPayments(pub bool); - -impl From for bool { - #[inline] - fn from(accepts_payments: AcceptsPayments) -> Self { - accepts_payments.0 - } -} - -/// Yields `true` if the dynamic storage allocator is enabled for the given call. -#[derive(Copy, Clone)] -#[doc(hidden)] -pub struct EnablesDynamicStorageAllocator(pub bool); - -impl From for bool { - #[inline] - fn from(enables_dynamic_storage_allocator: EnablesDynamicStorageAllocator) -> Self { - enables_dynamic_storage_allocator.0 - } -} - -/// Executes the given `&self` message closure. -/// -/// # Note -/// -/// The closure is supposed to already contain all the arguments that the real -/// message requires and forwards them. -#[inline] -#[doc(hidden)] -pub fn execute_message( - accepts_payments: AcceptsPayments, - enables_dynamic_storage_allocator: EnablesDynamicStorageAllocator, - f: F, -) -> Result<()> -where - E: Environment, - M: MessageRef, - F: FnOnce(&::State) -> ::Output, -{ - let accepts_payments: bool = accepts_payments.into(); - let enables_dynamic_storage_allocator: bool = - enables_dynamic_storage_allocator.into(); - if !accepts_payments { - deny_payment::()?; - } - if enables_dynamic_storage_allocator { - alloc::initialize(ContractPhase::Call); - } - let root_key = Key::from([0x00; 32]); - let state = ManuallyDrop::new(pull_spread_root::<::State>(&root_key)); - let result = f(&state); - if enables_dynamic_storage_allocator { - alloc::finalize(); - } - if TypeId::of::<::Output>() != TypeId::of::<()>() { - ink_env::return_value::<::Output>(ReturnFlags::default(), &result) - } - Ok(()) -} - -/// Returns `Ok` if the caller did not transfer additional value to the callee. -/// -/// # Errors -/// -/// If the caller did send some amount of transferred value to the callee. -#[inline] -#[doc(hidden)] -pub fn deny_payment() -> Result<()> -where - E: Environment, -{ - let transferred = ink_env::transferred_balance::() - .expect("encountered error while querying transferred balance"); - if transferred != ::Balance::from(0u32) { - return Err(DispatchError::PaidUnpayableMessage) - } - Ok(()) -} - -/// Executes the given `&mut self` message closure. -/// -/// # Note -/// -/// The closure is supposed to already contain all the arguments that the real -/// message requires and forwards them. -#[inline] -#[doc(hidden)] -pub fn execute_message_mut( - accepts_payments: AcceptsPayments, - enables_dynamic_storage_allocator: EnablesDynamicStorageAllocator, - f: F, -) -> Result<()> -where - E: Environment, - M: MessageMut, - F: FnOnce(&mut ::State) -> ::Output, -{ - let accepts_payments: bool = accepts_payments.into(); - let enables_dynamic_storage_allocator: bool = - enables_dynamic_storage_allocator.into(); - if !accepts_payments { - deny_payment::()?; - } - if enables_dynamic_storage_allocator { - alloc::initialize(ContractPhase::Call); - } - let root_key = Key::from([0x00; 32]); - let mut state = - ManuallyDrop::new(pull_spread_root::<::State>(&root_key)); - let result = f(&mut state); - push_spread_root::<::State>(&state, &root_key); - if enables_dynamic_storage_allocator { - alloc::finalize(); - } - if TypeId::of::<::Output>() != TypeId::of::<()>() { - ink_env::return_value::<::Output>(ReturnFlags::default(), &result) - } - Ok(()) -} - -/// Executes the given constructor closure. -/// -/// # Note -/// -/// The closure is supposed to already contain all the arguments that the real -/// constructor message requires and forwards them. -#[inline] -#[doc(hidden)] -pub fn execute_constructor( - enables_dynamic_storage_allocator: EnablesDynamicStorageAllocator, - f: F, -) -> Result<()> -where - C: Constructor, - F: FnOnce() -> ::State, -{ - let enables_dynamic_storage_allocator: bool = - enables_dynamic_storage_allocator.into(); - if enables_dynamic_storage_allocator { - alloc::initialize(ContractPhase::Deploy); - } - let state = ManuallyDrop::new(f()); - let root_key = Key::from([0x00; 32]); - push_spread_root::<::State>(&state, &root_key); - if enables_dynamic_storage_allocator { - alloc::finalize(); - } - Ok(()) -} diff --git a/crates/lang/src/env_access.rs b/crates/lang/src/env_access.rs index 79f5b76af65..4d67f988bd5 100644 --- a/crates/lang/src/env_access.rs +++ b/crates/lang/src/env_access.rs @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +use crate::ChainExtensionInstance; use core::marker::PhantomData; use ink_env::{ call::{ @@ -29,48 +30,10 @@ use ink_env::{ RentStatus, Result, }; -use ink_primitives::Key; - -use crate::ChainExtensionInstance; use ink_eth_compatibility::ECDSAPublicKey; +use ink_primitives::Key; -/// The environment of the compiled ink! smart contract. -pub trait ContractEnv { - /// The environment type. - type Env: ::ink_env::Environment; -} - -/// Simplifies interaction with the host environment via `self`. -/// -/// # Note -/// -/// This is generally implemented for storage structs that include -/// their environment in order to allow the different dispatch functions -/// to use it for returning the contract's output. -pub trait Env { - /// The access wrapper. - type EnvAccess; - - /// Accesses the environment with predefined environmental types. - fn env(self) -> Self::EnvAccess; -} - -/// Simplifies interaction with the host environment via `Self`. -/// -/// # Note -/// -/// This is generally implemented for storage structs that include -/// their environment in order to allow the different dispatch functions -/// to use it for returning the contract's output. -pub trait StaticEnv { - /// The access wrapper. - type EnvAccess; - - /// Accesses the environment with predefined environmental types. - fn env() -> Self::EnvAccess; -} - -/// A typed accessor to the environment. +/// The API behind the `self.env()` and `Self::env()` syntax in ink!. /// /// This allows ink! messages to make use of the environment efficiently /// and user friendly while also maintaining access invariants. @@ -651,7 +614,7 @@ where /// DefaultEnvironment, /// call::{build_create, Selector, ExecutionInput} /// }; - /// use other_contract::OtherContract; + /// use other_contract::OtherContractRef; /// # /// # #[ink(storage)] /// # pub struct MyContract { } @@ -666,7 +629,7 @@ where /// /// Instantiates another contract. /// #[ink(message)] /// pub fn instantiate_contract(&self) -> AccountId { - /// let create_params = build_create::() + /// let create_params = build_create::() /// .code_hash(Hash::from([0x42; 32])) /// .gas_limit(4000) /// .endowment(25) diff --git a/crates/lang/src/error.rs b/crates/lang/src/error.rs deleted file mode 100644 index 6f336c382d8..00000000000 --- a/crates/lang/src/error.rs +++ /dev/null @@ -1,43 +0,0 @@ -// 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 derive_more::Display; - -/// A dispatch result. -#[doc(hidden)] -pub type DispatchResult = core::result::Result<(), DispatchError>; - -/// A dispatch error. -#[derive(Debug, Copy, Clone, Display)] -#[doc(hidden)] -pub enum DispatchError { - #[display(fmt = "unknown selector")] - UnknownSelector, - #[display(fmt = "unknown constructor selector")] - UnknownInstantiateSelector, - #[display(fmt = "unknown message selector")] - UnknownCallSelector, - - #[display(fmt = "unable to decoded input parameter bytes")] - InvalidParameters, - #[display(fmt = "unable to decoded input parameter bytes for constructor")] - InvalidInstantiateParameters, - #[display(fmt = "unable to decoded input parameter bytes for message")] - InvalidCallParameters, - - #[display(fmt = "could not read input parameters")] - CouldNotReadInput, - #[display(fmt = "paid an unpayable message")] - PaidUnpayableMessage, -} diff --git a/crates/lang/src/lib.rs b/crates/lang/src/lib.rs index 81c211f65a4..a833252b742 100644 --- a/crates/lang/src/lib.rs +++ b/crates/lang/src/lib.rs @@ -14,69 +14,27 @@ #![cfg_attr(not(feature = "std"), no_std)] +#[macro_use] +#[doc(hidden)] +mod result_info; + +#[cfg_attr(not(feature = "show-codegen-docs"), doc(hidden))] +pub mod codegen; + +pub mod reflect; + mod chain_extension; -mod contract; -mod cross_calling; -mod dispatcher; +mod contract_ref; mod env_access; -mod error; -mod events; -mod traits; pub use self::{ chain_extension::{ ChainExtensionInstance, IsResultType, }, - contract::{ - DispatchMode, - DispatchUsingMode, - }, - cross_calling::{ - ForwardCall, - ForwardCallMut, - NeverReturns, - ToAccountId, - }, - dispatcher::{ - deny_payment, - execute_constructor, - execute_message, - execute_message_mut, - AcceptsPayments, - ConstructorDispatcher, - EnablesDynamicStorageAllocator, - Execute, - MessageDispatcher, - }, - env_access::{ - ContractEnv, - Env, - EnvAccess, - StaticEnv, - }, - error::{ - DispatchError, - DispatchResult, - }, - events::{ - BaseEvent, - EmitEvent, - }, - traits::{ - CheckedInkTrait, - Constructor, - FnInput, - FnOutput, - FnSelector, - FnState, - ImpliesReturn, - MessageMut, - MessageRef, - True, - }, + contract_ref::ToAccountId, + env_access::EnvAccess, }; -pub use ::static_assertions; pub use ink_lang_macro::{ blake2x256, chain_extension, diff --git a/crates/lang/src/reflect/contract.rs b/crates/lang/src/reflect/contract.rs new file mode 100644 index 00000000000..7083f56f8b5 --- /dev/null +++ b/crates/lang/src/reflect/contract.rs @@ -0,0 +1,190 @@ +// 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. + +/// Stores the name of the ink! smart contract. +/// +/// # Note +/// +/// The name is the identifier of the `#[ink(storage)]` annotated `struct`. +/// +/// # Usage +/// +/// ``` +/// use ink_lang as ink; +/// +/// #[ink::contract] +/// pub mod contract { +/// #[ink(storage)] +/// pub struct Contract {} +/// +/// impl Contract { +/// #[ink(constructor)] +/// pub fn constructor() -> Self { Self {} } +/// +/// #[ink(message)] +/// pub fn message(&self) {} +/// } +/// } +/// +/// use contract::Contract; +/// +/// # use ink_lang::reflect::ContractName; +/// assert_eq!( +/// ::NAME, +/// "Contract", +/// ); +/// ``` +pub trait ContractName { + /// The name of the ink! smart contract. + const NAME: &'static str; +} + +/// Stores the used host environment type of the ink! smart contract. +/// +/// # Note +/// +/// The used host environment can be altered using the `env` configuration +/// parameter in the `#[ink::contract]` parameters. For example if the user +/// wanted to use an environment type definition called `MyEnvironment` they +/// issue the ink! smart contract as follows: +/// +/// ```no_compile +/// #[ink::contract(env = MyEnvironment)] +/// ``` +/// +/// # Usage: Default Environment +/// +/// ``` +/// use ink_lang as ink; +/// +/// #[ink::contract] +/// pub mod contract { +/// #[ink(storage)] +/// pub struct Contract {} +/// +/// impl Contract { +/// #[ink(constructor)] +/// pub fn constructor() -> Self { Self {} } +/// +/// #[ink(message)] +/// pub fn message(&self) {} +/// } +/// } +/// +/// use contract::Contract; +/// +/// # use ink_lang::reflect::ContractEnv; +/// # use ink_lang::codegen::utils::IsSameType; +/// +/// // The following line only compiles successfully if both +/// // `ink_env::DefaultEnvironment` and `::Env` +/// // are of the same type. +/// const _: IsSameType<::Env> = +/// >::new(); +/// ``` +/// +/// # Usage: Custom Environment +/// +/// ``` +/// use ink_lang as ink; +/// # use ink_env::{Environment, DefaultEnvironment}; +/// +/// pub struct CustomEnvironment {} +/// +/// impl Environment for CustomEnvironment { +/// const MAX_EVENT_TOPICS: usize = 4; +/// +/// type AccountId = ::AccountId; +/// type Balance = u64; +/// type Hash = ::Hash; +/// type BlockNumber = u32; +/// type Timestamp = u64; +/// type RentFraction = ::RentFraction; +/// type ChainExtension = ::ChainExtension; +/// } +/// +/// #[ink::contract(env = super::CustomEnvironment)] +/// pub mod contract { +/// #[ink(storage)] +/// pub struct Contract {} +/// +/// impl Contract { +/// #[ink(constructor)] +/// pub fn constructor() -> Self { Self {} } +/// +/// #[ink(message)] +/// pub fn message(&self) {} +/// } +/// } +/// +/// use contract::Contract; +/// # use ink_lang::reflect::ContractEnv; +/// # use ink_lang::codegen::utils::IsSameType; +/// +/// // The following line only compiles successfully if both +/// // `CustomEnvironment` and `::Env` +/// // are of the same type. +/// const _: IsSameType<::Env> = +/// >::new(); +/// +/// fn main() {} +/// ``` +pub trait ContractEnv { + /// The environment type. + type Env: ::ink_env::Environment; +} + +/// Refers to the generated ink! smart contract reference type. +/// +/// # Note +/// +/// Given an ink! storage struct with identifier `Contract` the ink! codegen produces +/// the ink! root type `Contract` and the ink! reference type `ContractRef`. +/// +/// This trait exists so that users can avoid using a generated identifier to refer to +/// the generated reference type of the ink! smart contract. +/// +/// # Usage +/// +/// ``` +/// use ink_lang as ink; +/// +/// #[ink::contract] +/// pub mod contract { +/// #[ink(storage)] +/// pub struct Contract {} +/// +/// impl Contract { +/// #[ink(constructor)] +/// pub fn constructor() -> Self { Self {} } +/// +/// #[ink(message)] +/// pub fn message(&self) {} +/// } +/// } +/// +/// use contract::{Contract, ContractRef}; +/// # use ink_lang::codegen::utils::IsSameType; +/// # use ink_lang::reflect::ContractReference; +/// +/// // The following line only compiles successfully if both +/// // `ContractReference` and `::Type` +/// // are of the same type. +/// const _: IsSameType<::Type> = +/// >::new(); +/// ``` +pub trait ContractReference { + /// The generated contract reference type. + type Type; +} diff --git a/crates/lang/src/reflect/dispatch.rs b/crates/lang/src/reflect/dispatch.rs new file mode 100644 index 00000000000..4a396f631f5 --- /dev/null +++ b/crates/lang/src/reflect/dispatch.rs @@ -0,0 +1,651 @@ +// 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 core::fmt::Display; + +/// Reflects the number of dispatchable ink! messages and constructors respectively. +/// +/// # Note +/// +/// - This is automatically implemented by all ink! smart contracts. +/// - All ink! constructors and ink! messages of an ink! smart contract are dispatchables. +/// This explicitly includes ink! messages from ink! trait implementations. +/// +/// # Usage +/// +/// ``` +/// use ink_lang as ink; +/// # use ink_lang::reflect::ContractAmountDispatchables; +/// +/// #[ink::contract] +/// pub mod contract { +/// #[ink(storage)] +/// pub struct Contract {} +/// +/// impl Contract { +/// #[ink(constructor)] +/// pub fn constructor1() -> Self { Contract {} } +/// +/// #[ink(constructor)] +/// pub fn constructor2() -> Self { Contract {} } +/// +/// #[ink(message)] +/// pub fn message1(&self) {} +/// +/// #[ink(message)] +/// pub fn message2(&self) {} +/// +/// #[ink(message)] +/// pub fn message3(&self) {} +/// } +/// } +/// +/// use contract::Contract; +/// +/// fn main() { +/// assert_eq!(::CONSTRUCTORS, 2); +/// assert_eq!(::MESSAGES, 3); +/// } +/// ``` +pub trait ContractAmountDispatchables { + /// The number of dispatchable ink! messages. + const MESSAGES: usize; + /// The number of dispatchable ink! constructors. + const CONSTRUCTORS: usize; +} + +/// Reflects the sequence of all dispatchable ink! messages of the ink! smart contract. +/// +/// # Note +/// +/// This is automatically implemented by all ink! smart contracts. +/// +/// # Usage +/// +/// ``` +/// use ink_lang as ink; +/// # use ink_lang::reflect::ContractAmountDispatchables; +/// # use ink_lang::reflect::ContractDispatchableMessages; +/// # use ink_lang::selector_id; +/// +/// #[ink::contract] +/// pub mod contract { +/// #[ink(storage)] +/// pub struct Contract {} +/// +/// impl Contract { +/// #[ink(constructor)] +/// pub fn constructor1() -> Self { Contract {} } +/// +/// #[ink(message, selector = 1234)] +/// pub fn message1(&self) {} +/// +/// #[ink(message, selector = 0xC0DECAFE)] +/// pub fn message2(&self) {} +/// +/// #[ink(message)] +/// pub fn message3(&self) {} +/// } +/// } +/// +/// use contract::Contract; +/// +/// fn main() { +/// assert_eq!( +/// ::MESSAGES +/// }>>::IDS, +/// [1234, 0xC0DECAFE, selector_id!("message3")], +/// ); +/// } +/// ``` +pub trait ContractDispatchableMessages { + /// The sequence stores selector IDs of all ink! messages dispatchable by the ink! smart contract. + const IDS: [u32; AMOUNT]; +} + +/// Reflects the sequence of all dispatchable ink! constructors of the ink! smart contract. +/// +/// # Note +/// +/// This is automatically implemented by all ink! smart contracts. +/// +/// # Usage +/// +/// ``` +/// use ink_lang as ink; +/// # use ink_lang::reflect::ContractAmountDispatchables; +/// # use ink_lang::reflect::ContractDispatchableConstructors; +/// # use ink_lang::selector_id; +/// +/// #[ink::contract] +/// pub mod contract { +/// #[ink(storage)] +/// pub struct Contract {} +/// +/// impl Contract { +/// #[ink(constructor, selector = 1234)] +/// pub fn constructor1() -> Self { Contract {} } +/// +/// #[ink(constructor, selector = 0xC0DECAFE)] +/// pub fn constructor2() -> Self { Contract {} } +/// +/// #[ink(constructor)] +/// pub fn constructor3() -> Self { Contract {} } +/// +/// #[ink(message)] +/// pub fn message1(&self) {} +/// } +/// } +/// +/// use contract::Contract; +/// +/// fn main() { +/// assert_eq!( +/// ::CONSTRUCTORS +/// }>>::IDS, +/// [1234, 0xC0DECAFE, selector_id!("constructor3")], +/// ); +/// } +/// ``` +pub trait ContractDispatchableConstructors { + /// The sequence stores selector IDs of all ink! constructors dispatchable by the ink! smart contract. + const IDS: [u32; AMOUNT]; +} + +/// Stores various information of the respective dispatchable ink! message. +/// +/// # Note +/// +/// This trait is implemented by ink! for every dispatchable ink! message +/// of the root ink! smart contract. The `ID` used in the trait reflects the +/// chosen or derived selector of the dispatchable ink! message. +/// +/// # Usage +/// +/// ``` +/// use ink_lang as ink; +/// # use ink_lang::reflect::DispatchableMessageInfo; +/// # use ink_lang::{selector_id, selector_bytes}; +/// +/// #[ink::contract] +/// pub mod contract { +/// #[ink(storage)] +/// pub struct Contract {} +/// +/// impl Contract { +/// #[ink(constructor)] +/// pub fn constructor() -> Self { Contract {} } +/// +/// #[ink(message)] +/// pub fn message1(&self) {} +/// +/// #[ink(message, payable, selector = 0xC0DECAFE)] +/// pub fn message2(&mut self, input1: i32, input2: i64) -> (bool, i32) { +/// unimplemented!() +/// } +/// } +/// } +/// +/// use contract::Contract; +/// +/// /// Asserts that the message with the selector `ID` has the following properties. +/// /// +/// /// # Note +/// /// +/// /// The `In` and `Out` generic parameters describe the input and output types. +/// fn assert_message_info( +/// mutates: bool, +/// payable: bool, +/// selector: [u8; 4], +/// label: &str, +/// ) +/// where +/// Contract: DispatchableMessageInfo<{ID}, Input = In, Output = Out>, +/// { +/// assert_eq!(>::MUTATES, mutates); +/// assert_eq!(>::PAYABLE, payable); +/// assert_eq!( +/// >::SELECTOR, +/// selector, +/// ); +/// assert_eq!( +/// >::LABEL, +/// label, +/// ); +/// } +/// +/// fn main() { +/// assert_message_info::<(), (), {selector_id!("message1")}>( +/// false, false, selector_bytes!("message1"), "message1" +/// ); +/// assert_message_info::<(i32, i64), (bool, i32), 0xC0DECAFE_u32>( +/// true, true, [0xC0, 0xDE, 0xCA, 0xFE], "message2" +/// ); +/// } +/// ``` +pub trait DispatchableMessageInfo { + /// Reflects the input types of the dispatchable ink! message. + type Input; + /// Reflects the output type of the dispatchable ink! message. + type Output; + /// The ink! storage struct type. + type Storage; + + /// The closure that can be used to dispatch into the dispatchable ink! message. + /// + /// # Note + /// + /// We unify `&self` and `&mut self` ink! messages here and always take a `&mut self`. + /// This is mainly done for simplification but also because we can easily convert from + /// `&mut self` to `&self` with our current dispatch codegen architecture. + const CALLABLE: fn(&mut Self::Storage, Self::Input) -> Self::Output; + + /// Yields `true` if the dispatchable ink! message mutates the ink! storage. + const MUTATES: bool; + /// Yields `true` if the dispatchable ink! message is payable. + const PAYABLE: bool; + /// The selectors of the dispatchable ink! message. + const SELECTOR: [u8; 4]; + /// The label of the dispatchable ink! message. + const LABEL: &'static str; +} + +/// Stores various information of the respective dispatchable ink! constructor. +/// +/// # Note +/// +/// This trait is implemented by ink! for every dispatchable ink! constructor +/// of the root ink! smart contract. The `ID` used in the trait reflects the +/// chosen or derived selector of the dispatchable ink! constructor. +/// +/// # Usage +/// +/// ``` +/// use ink_lang as ink; +/// # use ink_lang::reflect::DispatchableConstructorInfo; +/// # use ink_lang::{selector_id, selector_bytes}; +/// +/// #[ink::contract] +/// pub mod contract { +/// #[ink(storage)] +/// pub struct Contract {} +/// +/// impl Contract { +/// #[ink(constructor)] +/// pub fn constructor1() -> Self { Contract {} } +/// +/// #[ink(constructor, selector = 0xC0DECAFE)] +/// pub fn constructor2(input1: i32, input2: i64) -> Self { +/// Contract {} +/// } +/// +/// #[ink(message)] +/// pub fn message(&self) {} +/// } +/// } +/// +/// use contract::Contract; +/// +/// /// Asserts that the constructor with the selector `ID` has the following properties. +/// /// +/// /// # Note +/// /// +/// /// The `In` and `Out` generic parameters describe the input and output types. +/// fn assert_constructor_info( +/// selector: [u8; 4], +/// label: &str, +/// ) +/// where +/// Contract: DispatchableConstructorInfo<{ID}, Input = In>, +/// { +/// assert_eq!( +/// >::SELECTOR, +/// selector, +/// ); +/// assert_eq!( +/// >::LABEL, +/// label, +/// ); +/// } +/// +/// fn main() { +/// assert_constructor_info::<(), {selector_id!("constructor1")}>( +/// selector_bytes!("constructor1"), "constructor1" +/// ); +/// assert_constructor_info::<(i32, i64), 0xC0DECAFE_u32>( +/// [0xC0, 0xDE, 0xCA, 0xFE], "constructor2" +/// ); +/// } +/// ``` +pub trait DispatchableConstructorInfo { + /// Reflects the input types of the dispatchable ink! constructor. + type Input; + /// The ink! storage struct type. + type Storage; + + /// The closure that can be used to dispatch into the dispatchable ink! constructor. + const CALLABLE: fn(Self::Input) -> Self::Storage; + + /// The selectors of the dispatchable ink! constructor. + const SELECTOR: [u8; 4]; + /// The label of the dispatchable ink! constructor. + const LABEL: &'static str; +} + +/// Generated type used to decode all dispatchable ink! messages of the ink! smart contract. +/// +/// # Note +/// +/// The decoder follows the ink! calling ABI where all ink! message calls start with +/// 4 bytes dedicated to the ink! message selector followed by the SCALE encoded parameters. +/// +/// # Usage +/// +/// ``` +/// use ink_lang as ink; +/// # use ink_lang::reflect::ContractMessageDecoder; +/// # use ink_lang::selector_bytes; +/// # use scale::{Encode, Decode}; +/// +/// #[ink::contract] +/// pub mod contract { +/// #[ink(storage)] +/// pub struct Contract {} +/// +/// impl Contract { +/// #[ink(constructor)] +/// pub fn constructor() -> Self { Self {} } +/// +/// #[ink(message)] +/// pub fn message1(&self) {} +/// +/// #[ink(message)] +/// pub fn message2(&self, input1: bool, input2: i32) {} +/// } +/// } +/// +/// use contract::Contract; +/// +/// fn main() { +/// // Call to `message1` without input parameters. +/// { +/// let mut input_bytes = Vec::new(); +/// input_bytes.extend(selector_bytes!("message1")); +/// assert!( +/// <::Type as Decode>::decode( +/// &mut &input_bytes[..]).is_ok() +/// ); +/// } +/// // Call to `message2` with 2 parameters. +/// { +/// let mut input_bytes = Vec::new(); +/// input_bytes.extend(selector_bytes!("message2")); +/// input_bytes.extend(true.encode()); +/// input_bytes.extend(42i32.encode()); +/// assert!( +/// <::Type as Decode>::decode( +/// &mut &input_bytes[..]).is_ok() +/// ); +/// } +/// // Call with invalid ink! message selector. +/// { +/// let mut input_bytes = Vec::new(); +/// input_bytes.extend(selector_bytes!("non_existing_message")); +/// assert!( +/// <::Type as Decode>::decode( +/// &mut &input_bytes[..]).is_err() +/// ); +/// } +/// // Call with invalid ink! message parameters. +/// { +/// let mut input_bytes = Vec::new(); +/// input_bytes.extend(selector_bytes!("message2")); +/// assert!( +/// <::Type as Decode>::decode( +/// &mut &input_bytes[..]).is_err() +/// ); +/// } +/// } +/// ``` +pub trait ContractMessageDecoder { + /// The ink! smart contract message decoder type. + type Type: scale::Decode + ExecuteDispatchable; +} + +/// Generated type used to decode all dispatchable ink! constructors of the ink! smart contract. +/// +/// # Note +/// +/// The decoder follows the ink! calling ABI where all ink! constructor calls start with +/// 4 bytes dedicated to the ink! constructor selector followed by the SCALE encoded parameters. +/// +/// # Usage +/// +/// ``` +/// use ink_lang as ink; +/// # use ink_lang::reflect::ContractConstructorDecoder; +/// # use ink_lang::selector_bytes; +/// # use scale::{Encode, Decode}; +/// +/// #[ink::contract] +/// pub mod contract { +/// #[ink(storage)] +/// pub struct Contract {} +/// +/// impl Contract { +/// #[ink(constructor)] +/// pub fn constructor1() -> Self { Self {} } +/// +/// #[ink(constructor)] +/// pub fn constructor2(input1: bool, input2: i32) -> Self { Self {} } +/// +/// #[ink(message)] +/// pub fn message(&self) {} +/// } +/// } +/// +/// use contract::Contract; +/// +/// fn main() { +/// // Call to `constructor1` without input parameters. +/// { +/// let mut input_bytes = Vec::new(); +/// input_bytes.extend(selector_bytes!("constructor1")); +/// assert!( +/// <::Type as Decode>::decode( +/// &mut &input_bytes[..]).is_ok() +/// ); +/// } +/// // Call to `constructor2` with 2 parameters. +/// { +/// let mut input_bytes = Vec::new(); +/// input_bytes.extend(selector_bytes!("constructor2")); +/// input_bytes.extend(true.encode()); +/// input_bytes.extend(42i32.encode()); +/// assert!( +/// <::Type as Decode>::decode( +/// &mut &input_bytes[..]).is_ok() +/// ); +/// } +/// // Call with invalid ink! constructor selector. +/// { +/// let mut input_bytes = Vec::new(); +/// input_bytes.extend(selector_bytes!("non_existing_constructor")); +/// assert!( +/// <::Type as Decode>::decode( +/// &mut &input_bytes[..]).is_err() +/// ); +/// } +/// // Call with invalid ink! constructor parameters. +/// { +/// let mut input_bytes = Vec::new(); +/// input_bytes.extend(selector_bytes!("constructor2")); +/// assert!( +/// <::Type as Decode>::decode( +/// &mut &input_bytes[..]).is_err() +/// ); +/// } +/// } +/// ``` +pub trait ContractConstructorDecoder { + /// The ink! smart contract constructor decoder type. + type Type: DecodeDispatch + ExecuteDispatchable; +} + +/// Starts the execution of the respective ink! message or constructor call. +/// +/// # Note +/// +/// Implemented by the ink! smart contract message or constructor decoder. +pub trait ExecuteDispatchable { + /// Executes the ink! smart contract message or constructor. + fn execute_dispatchable(self) -> Result<(), DispatchError>; +} + +/// An error that can occur during dispatch of ink! dispatchables. +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum DispatchError { + /// Failed to decode into a valid dispatch selector. + InvalidSelector, + /// The decoded selector is not known to the dispatch decoder. + UnknownSelector, + /// Failed to decode the parameters for the selected dispatchable. + InvalidParameters, + /// Failed to read execution input for the dispatchable. + CouldNotReadInput, + /// Invalidly paid an unpayable dispatchable. + PaidUnpayableMessage, +} + +impl Display for DispatchError { + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + write!(f, "{}", self.as_str()) + } +} + +impl DispatchError { + /// Returns a string representation of the error. + #[inline] + fn as_str(&self) -> &'static str { + match self { + Self::InvalidSelector => "unable to decode selector", + Self::UnknownSelector => "encountered unknown selector", + Self::InvalidParameters => "unable to decode input", + Self::CouldNotReadInput => "could not read input", + Self::PaidUnpayableMessage => "paid an unpayable message", + } + } +} + +impl From for scale::Error { + #[inline] + fn from(error: DispatchError) -> Self { + Self::from(error.as_str()) + } +} + +/// Decodes an ink! dispatch input into a known selector and its expected parameters. +/// +/// # Note +/// +/// This trait is automatically implemented for ink! message and constructor decoders. +/// +/// # Errors +/// +/// Returns an error if any of the decode steps failed: +/// +/// - `InvalidSelector`: The first four bytes could not properly decoded into the selector. +/// - `UnknownSelector`: The decoded selector did not match any of the expected ones. +/// - `InvalidParameters`: Failed to decoded the parameters for the selected dispatchable. +/// +/// The other dispatch errors are handled by other structures usually. +/// +/// # Usage +/// +/// ``` +/// use ink_lang as ink; +/// # use ink_lang::reflect::{ContractMessageDecoder, DecodeDispatch, DispatchError}; +/// # use ink_lang::selector_bytes; +/// # use scale::Encode; +/// +/// #[ink::contract] +/// pub mod contract { +/// #[ink(storage)] +/// pub struct Contract {} +/// +/// impl Contract { +/// #[ink(constructor)] +/// pub fn constructor() -> Self { Self {} } +/// +/// #[ink(message)] +/// pub fn message(&self, input_1: bool, input_2: i32) {} +/// } +/// } +/// +/// use contract::Contract; +/// +/// fn main() { +/// // Valid call to `message`: +/// { +/// let mut input_bytes = Vec::new(); +/// input_bytes.extend(selector_bytes!("message")); +/// input_bytes.extend(true.encode()); +/// input_bytes.extend(42i32.encode()); +/// assert!( +/// <::Type as DecodeDispatch>::decode_dispatch( +/// &mut &input_bytes[..]).is_ok() +/// ); +/// } +/// // Invalid call with invalid selector (or empty input). +/// { +/// let mut input_bytes = Vec::new(); +/// assert_eq!( +/// <::Type +/// as DecodeDispatch>::decode_dispatch(&mut &input_bytes[..]) +/// # .map(|_| ()) +/// .unwrap_err(), +/// DispatchError::InvalidSelector, +/// ); +/// } +/// // Invalid call to `message` with unknown selector. +/// { +/// let mut input_bytes = Vec::new(); +/// input_bytes.extend(selector_bytes!("unknown_selector")); +/// assert_eq!( +/// <::Type +/// as DecodeDispatch>::decode_dispatch(&mut &input_bytes[..]) +/// # .map(|_| ()) +/// .unwrap_err(), +/// DispatchError::UnknownSelector, +/// ); +/// } +/// // Invalid call to `message` with invalid (or missing) parameters. +/// { +/// let mut input_bytes = Vec::new(); +/// input_bytes.extend(selector_bytes!("message")); +/// assert_eq!( +/// <::Type +/// as DecodeDispatch>::decode_dispatch(&mut &input_bytes[..]) +/// # .map(|_| ()) +/// .unwrap_err(), +/// DispatchError::InvalidParameters, +/// ); +/// } +/// } +/// ``` +pub trait DecodeDispatch: scale::Decode { + fn decode_dispatch(input: &mut I) -> Result; +} diff --git a/crates/lang/src/reflect/event.rs b/crates/lang/src/reflect/event.rs new file mode 100644 index 00000000000..f3befcf704d --- /dev/null +++ b/crates/lang/src/reflect/event.rs @@ -0,0 +1,52 @@ +// 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. + +/// Defines a base event type for the contract. +/// +/// This is usually the event enum that comprises all defined event types. +/// +/// # Usage +/// +/// ``` +/// use ink_lang as ink; +/// +/// #[ink::contract] +/// pub mod contract { +/// #[ink(storage)] +/// pub struct Contract {} +/// +/// #[ink(event)] +/// pub struct Event1 {} +/// +/// #[ink(event)] +/// pub struct Event2 {} +/// +/// impl Contract { +/// #[ink(constructor)] +/// pub fn constructor() -> Self { Self {} } +/// +/// #[ink(message)] +/// pub fn message(&self) {} +/// } +/// } +/// +/// use contract::Contract; +/// # use ink_lang::reflect::ContractEventBase; +/// +/// type BaseEvent = ::Type; +/// ``` +pub trait ContractEventBase { + /// The generated base event enum. + type Type; +} diff --git a/crates/lang/src/reflect/mod.rs b/crates/lang/src/reflect/mod.rs new file mode 100644 index 00000000000..3d31dba0211 --- /dev/null +++ b/crates/lang/src/reflect/mod.rs @@ -0,0 +1,55 @@ +// 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. + +//! Definitions and utilities for ink! smart contract static reflection. +//! +//! # Note +//! +//! The ink! smart contract codegen uses these reflections in order to +//! structure, solidify and manage the generated code. +//! +//! However, the definitions in this module might be useful to ink! smart +//! contract authors as well as they allow to inspect compile time information +//! about the ink! smart contract at hand. + +mod contract; +mod dispatch; +mod event; +mod trait_def; + +pub use self::{ + contract::{ + ContractEnv, + ContractName, + ContractReference, + }, + dispatch::{ + ContractAmountDispatchables, + ContractConstructorDecoder, + ContractDispatchableConstructors, + ContractDispatchableMessages, + ContractMessageDecoder, + DecodeDispatch, + DispatchError, + DispatchableConstructorInfo, + DispatchableMessageInfo, + ExecuteDispatchable, + }, + event::ContractEventBase, + trait_def::{ + TraitDefinitionRegistry, + TraitMessageInfo, + TraitModulePath, + }, +}; diff --git a/crates/lang/src/reflect/trait_def/info.rs b/crates/lang/src/reflect/trait_def/info.rs new file mode 100644 index 00000000000..8745c1b2daf --- /dev/null +++ b/crates/lang/src/reflect/trait_def/info.rs @@ -0,0 +1,154 @@ +// 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. + +/// Stores information for every ink! trait message of an ink! trait definition. +/// +/// This information includes if the ink! trait message is payable +/// as well as its derived or manually specified selector. +/// +/// In the future this info trait might be extended to contain +/// more information about a single ink! trait message. +/// +/// The information provided through this trait can be used on the +/// implementer side of an ink! trait to check and guard certain +/// properties on a Rust type system level. This is important since +/// ink! cannot be guaranteed to have both the ink! trait definition +/// and all of its implementers under its scope and radar. +/// +/// # Note +/// +/// - The `TraitMessageInfo` is implemented by the +/// automatically generated ink! trait definition information object +/// associated to the ink! trait definition at hand. +/// - For every ink! trait message defined by the ink! trait definition +/// the associated ink! trait definition information object implements +/// this trait given the `TRAIT_LOCAL_MESSAGE_ID` of each ink! trait +/// message respectively. +/// - The local IDs uniquely identifying all the ink! trait messages +/// of the ink! trait definition are computed solely using the Rust +/// identifier of the ink! trait message which can be derived from +/// ink! implementation blocks in order to query the information +/// stored by this ink! trait information object trait implementation. +/// +/// # Usage +/// +/// ``` +/// use ink_lang as ink; +/// +/// #[ink::trait_definition] +/// pub trait InkTrait { +/// #[ink(message)] +/// fn trait_message_1(&self); +/// +/// #[ink(message, payable, selector = 0xC0DECAFE)] +/// fn trait_message_2(&mut self); +/// } +/// +/// #[ink::trait_definition(namespace = "foo")] +/// pub trait InkTrait2 { +/// #[ink(message)] +/// fn trait_message(&self); +/// } +/// +/// #[ink::contract] +/// pub mod contract { +/// use super::{InkTrait, InkTrait2}; +/// +/// #[ink(storage)] +/// pub struct Contract {} +/// +/// impl Contract { +/// #[ink(constructor)] +/// pub fn constructor() -> Self { Contract {} } +/// } +/// +/// impl InkTrait for Contract { +/// #[ink(message)] +/// fn trait_message_1(&self) {} +/// +/// #[ink(message)] +/// fn trait_message_2(&mut self) {} +/// } +/// +/// impl InkTrait2 for Contract { +/// #[ink(message)] +/// fn trait_message(&self) {} +/// } +/// } +/// +/// # use ink_lang::reflect::TraitDefinitionRegistry; +/// # use ink_lang::reflect::TraitMessageInfo; +/// # use ink_env::DefaultEnvironment; +/// # use ink_lang::{selector_id, selector_bytes}; +/// +/// fn main() { +/// assert_eq!( +/// < as InkTrait>::__ink_TraitInfo +/// as TraitMessageInfo<{selector_id!("trait_message_1")}>>::PAYABLE, +/// false, +/// ); +/// assert_eq!( +/// < as InkTrait>::__ink_TraitInfo +/// as TraitMessageInfo<{selector_id!("trait_message_2")}>>::PAYABLE, +/// true, +/// ); +/// assert_eq!( +/// < as InkTrait2>::__ink_TraitInfo +/// as TraitMessageInfo<{selector_id!("trait_message")}>>::PAYABLE, +/// false, +/// ); +/// assert_eq!( +/// < as InkTrait>::__ink_TraitInfo +/// as TraitMessageInfo<{selector_id!("trait_message_1")}>>::SELECTOR, +/// selector_bytes!("InkTrait::trait_message_1") +/// ); +/// assert_eq!( +/// < as InkTrait>::__ink_TraitInfo +/// as TraitMessageInfo<{selector_id!("trait_message_2")}>>::SELECTOR, +/// [0xC0, 0xDE, 0xCA, 0xFE] +/// ); +/// assert_eq!( +/// < as InkTrait2>::__ink_TraitInfo +/// as TraitMessageInfo<{selector_id!("trait_message")}>>::SELECTOR, +/// selector_bytes!("foo::InkTrait2::trait_message") +/// ); +/// } +/// ``` +pub trait TraitMessageInfo { + /// Is `true` if the ink! trait message has been annotated with `#[ink(payable)]`. + const PAYABLE: bool; + + /// The unique selector of the ink! trait message. + /// + /// This might have been adjusted using `#[ink(selector = N:u32)]` at the + /// ink! trait definition site. + const SELECTOR: [u8; 4]; +} + +/// Captures the module path of the ink! trait definition. +/// +/// This can be used to differentiate between two equally named +/// ink! trait definitions and also for metadata. +pub trait TraitModulePath { + /// The module path of the ink! trait definition. + /// + /// This is equivalent to Rust's builtin `module_path!` macro + /// invocation at the definition site of the ink! trait. + const PATH: &'static str; + + /// The name of the ink! trait. + /// + /// This is just for convenience. + const NAME: &'static str; +} diff --git a/crates/lang/src/reflect/trait_def/mod.rs b/crates/lang/src/reflect/trait_def/mod.rs new file mode 100644 index 00000000000..f28cf9be170 --- /dev/null +++ b/crates/lang/src/reflect/trait_def/mod.rs @@ -0,0 +1,24 @@ +// 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. + +mod info; +mod registry; + +pub use self::{ + info::{ + TraitMessageInfo, + TraitModulePath, + }, + registry::TraitDefinitionRegistry, +}; diff --git a/crates/lang/src/reflect/trait_def/registry.rs b/crates/lang/src/reflect/trait_def/registry.rs new file mode 100644 index 00000000000..5f33d2323eb --- /dev/null +++ b/crates/lang/src/reflect/trait_def/registry.rs @@ -0,0 +1,63 @@ +// 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::reflect::ContractEnv; +use core::marker::PhantomData; + +/// Type that is guaranteed by ink! to implement all ink! trait definitions. +/// +/// This guarantee is used by ink! itself and can be used by ink! smart contract +/// authors to query static information about known ink! trait definitions. +/// +/// # Codegen +/// +/// - The `#[ink::trait_definition]` procedural macro generates an associated type +/// called `__ink_TraitInfo` for each ink! trait definition. +/// - Furthermore the ink! codegen implements the ink! trait definition for the +/// `TraitDefinitionRegistry` with stub implementations for all methods that +/// guarantee that they are never called. +/// - For every implemented ink! trait definition an ink! trait info object type +/// is generated that is linked to the global `TraitDefinitionRegistry` through +/// the aforementioned `__ink_TraitInfo` associated type. +/// - This trait info object type itself implements various traits each providing +/// useful static reflection information to the rest of the codegen about the ink! +/// trait definition. +/// +/// # Usage +/// +/// ``` +/// # use ink_lang as ink; +/// # use ink_lang::reflect::TraitDefinitionRegistry; +/// use ink_env::DefaultEnvironment; +/// +/// #[ink::trait_definition] +/// pub trait TraitDefinition { +/// #[ink(message)] +/// fn message(&self); +/// } +/// +/// /// Access the generated ink! trait info object type like this: +/// type TraitInfo = +/// as TraitDefinition>::__ink_TraitInfo; +/// ``` +pub struct TraitDefinitionRegistry { + marker: PhantomData E>, +} + +impl ContractEnv for TraitDefinitionRegistry +where + E: ink_env::Environment, +{ + type Env = E; +} diff --git a/crates/lang/src/result_info.rs b/crates/lang/src/result_info.rs new file mode 100644 index 00000000000..12632d6aa3e --- /dev/null +++ b/crates/lang/src/result_info.rs @@ -0,0 +1,119 @@ +// 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. + +pub struct IsResultType { + marker: core::marker::PhantomData T>, +} + +impl IsResultType<::core::result::Result> { + // We need to allow for dead code at this point because + // the Rust compiler thinks this function is unused even + // though it acts as the specialized case for detection. + #[allow(dead_code)] + pub const VALUE: bool = true; +} + +pub trait IsResultTypeFallback { + const VALUE: bool = false; +} +impl IsResultTypeFallback for IsResultType {} + +/// Returns `true` if the given type is a `Result` type. +macro_rules! is_result_type { + ( $T:ty $(,)? ) => {{ + #[allow(unused_imports)] + use $crate::result_info::IsResultTypeFallback as _; + + $crate::result_info::IsResultType::<$T>::VALUE + }}; +} + +pub struct IsResultErr<'lt, T>(pub &'lt T); + +impl IsResultErr<'_, ::core::result::Result> { + #[inline(always)] + // We need to allow for dead code at this point because + // the Rust compiler thinks this function is unused even + // though it acts as the specialized case for detection. + #[allow(dead_code)] + pub fn value(&self) -> bool { + self.0.is_err() + } +} + +pub trait IsResultErrFallback { + #[inline(always)] + fn value(&self) -> bool { + false + } +} +impl IsResultErrFallback for IsResultErr<'_, T> {} + +/// Evaluates to `true` if the given expression is a `Result::Err(_)`. +/// +/// # Note +/// +/// This given expression is not required to be of type `Result`. +macro_rules! is_result_err { + ( $e:expr $(,)? ) => {{ + #[allow(unused_imports)] + use $crate::result_info::IsResultErrFallback as _; + $crate::result_info::IsResultErr(&$e).value() + }}; +} + +#[cfg(test)] +mod tests { + #[test] + fn is_result_type_works() { + assert!(!is_result_type!(bool)); + assert!(!is_result_type!(String)); + assert!(!is_result_type!(Option)); + + assert!(is_result_type!(Result<(), ()>)); + assert!(is_result_type!(Result)); + assert!(is_result_type!(Result<(), String>)); + assert!(is_result_type!(Result)); + + assert!(is_result_type!(Result, ()>)); + assert!(is_result_type!(Result<(), Result<(), ()>>)); + assert!(is_result_type!(Result, Result<(), ()>>)); + + // Check that type aliases work, too. + type MyResult = Result<(), ()>; + assert!(is_result_type!(MyResult)); + } + + #[test] + fn is_result_err_works() { + assert!(!is_result_err!(true)); + assert!(!is_result_err!(42)); + assert!(!is_result_err!("Hello, World!")); + + assert!(!is_result_err!(Ok::<(), ()>(()))); + assert!(!is_result_err!(Ok::(5))); + assert!(!is_result_err!(Ok::(true))); + + assert!(is_result_err!(Err::<(), ()>(()))); + assert!(is_result_err!(Err::<(), i32>(5))); + assert!(is_result_err!(Err::(false))); + assert!(is_result_err!(Err::>(Ok(42)))); + + { + // Check that we do not simply check against `Result` as identifier. + type Result = Option<()>; + assert!(!is_result_type!(Result)); + } + } +} diff --git a/crates/lang/src/traits.rs b/crates/lang/src/traits.rs deleted file mode 100644 index 2f8f31d918f..00000000000 --- a/crates/lang/src/traits.rs +++ /dev/null @@ -1,122 +0,0 @@ -// 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 ink_env::{ - call::{ - utils::{ - ReturnType, - Set, - }, - CallBuilder, - ExecutionInput, - Selector, - }, - Environment, -}; -use ink_storage::traits::SpreadLayout; - -/// Trait used to indicate that an ink! trait definition has been checked -/// by the `#[ink::trait_definition]` procedural macro. -#[doc(hidden)] -pub unsafe trait CheckedInkTrait {} - -/// Trait used by `#[ink::trait_definition]` to ensure that the associated -/// return type for each trait message is correct. -#[doc(hidden)] -pub trait ImpliesReturn {} - -impl ImpliesReturn for T {} -impl ImpliesReturn - for CallBuilder< - E, - Callee, - GasCost, - TransferredValue, - Set>, - Set>, - > -where - E: Environment, -{ -} - -impl ImpliesReturn<()> - for CallBuilder< - E, - Callee, - GasCost, - TransferredValue, - Set>, - Set<()>, - > -where - E: Environment, -{ -} - -/// Dispatchable functions that have inputs. -#[doc(hidden)] -pub trait FnInput { - /// The tuple-type of all inputs. - type Input: scale::Decode + 'static; -} - -/// Dispatchable functions that have an output. -#[doc(hidden)] -pub trait FnOutput { - /// The output type. - type Output: scale::Encode + 'static; -} - -/// The selector of dispatchable functions. -#[doc(hidden)] -pub trait FnSelector { - /// The selector. - const SELECTOR: Selector; -} - -/// The storage state that the dispatchable function acts on. -#[doc(hidden)] -pub trait FnState { - /// The storage state. - type State: SpreadLayout + Sized; -} - -/// A dispatchable contract constructor message. -#[doc(hidden)] -pub trait Constructor: FnInput + FnSelector + FnState { - const CALLABLE: fn(::Input) -> ::State; -} - -/// A `&self` dispatchable contract message. -#[doc(hidden)] -pub trait MessageRef: FnInput + FnOutput + FnSelector + FnState { - const CALLABLE: fn( - &::State, - ::Input, - ) -> ::Output; -} - -/// A `&mut self` dispatchable contract message. -#[doc(hidden)] -pub trait MessageMut: FnInput + FnOutput + FnSelector + FnState { - const CALLABLE: fn( - &mut ::State, - ::Input, - ) -> ::Output; -} - -/// Indicates that some compile time expression is expected to be `true`. -#[doc(hidden)] -pub trait True {} diff --git a/crates/lang/tests/compile_tests.rs b/crates/lang/tests/compile_tests.rs new file mode 100644 index 00000000000..88769efbb1d --- /dev/null +++ b/crates/lang/tests/compile_tests.rs @@ -0,0 +1,35 @@ +// 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. + +#[test] +fn ui_tests() { + let t = trybuild::TestCases::new(); + + t.pass("tests/ui/blake2b/pass/*.rs"); + t.compile_fail("tests/ui/blake2b/fail/*.rs"); + + t.pass("tests/ui/selector_id/pass/*.rs"); + t.compile_fail("tests/ui/selector_id/fail/*.rs"); + + t.pass("tests/ui/selector_bytes/pass/*.rs"); + t.compile_fail("tests/ui/selector_bytes/fail/*.rs"); + + t.pass("tests/ui/contract/pass/*.rs"); + t.compile_fail("tests/ui/contract/fail/*.rs"); + + t.pass("tests/ui/trait_def/pass/*.rs"); + t.compile_fail("tests/ui/trait_def/fail/*.rs"); + + t.pass("tests/ui/chain_extension/E-01-simple.rs"); +} diff --git a/crates/lang/macro/tests/ui/blake2b/fail/invalid_parameter_type_01.rs b/crates/lang/tests/ui/blake2b/fail/invalid_parameter_type_01.rs similarity index 100% rename from crates/lang/macro/tests/ui/blake2b/fail/invalid_parameter_type_01.rs rename to crates/lang/tests/ui/blake2b/fail/invalid_parameter_type_01.rs diff --git a/crates/lang/macro/tests/ui/blake2b/fail/invalid_parameter_type_01.stderr b/crates/lang/tests/ui/blake2b/fail/invalid_parameter_type_01.stderr similarity index 75% rename from crates/lang/macro/tests/ui/blake2b/fail/invalid_parameter_type_01.stderr rename to crates/lang/tests/ui/blake2b/fail/invalid_parameter_type_01.stderr index da36a72ec48..4d1451ec437 100644 --- a/crates/lang/macro/tests/ui/blake2b/fail/invalid_parameter_type_01.stderr +++ b/crates/lang/tests/ui/blake2b/fail/invalid_parameter_type_01.stderr @@ -1,5 +1,5 @@ error: expected string or byte string literal as input. found Bool(LitBool { value: true }) - --> $DIR/invalid_parameter_type_01.rs:3:38 + --> tests/ui/blake2b/fail/invalid_parameter_type_01.rs:3:38 | 3 | const _: [u8; 32] = ink::blake2x256!(true); | ^^^^ diff --git a/crates/lang/macro/tests/ui/blake2b/fail/invalid_parameter_type_02.rs b/crates/lang/tests/ui/blake2b/fail/invalid_parameter_type_02.rs similarity index 100% rename from crates/lang/macro/tests/ui/blake2b/fail/invalid_parameter_type_02.rs rename to crates/lang/tests/ui/blake2b/fail/invalid_parameter_type_02.rs diff --git a/crates/lang/macro/tests/ui/blake2b/fail/invalid_parameter_type_02.stderr b/crates/lang/tests/ui/blake2b/fail/invalid_parameter_type_02.stderr similarity index 74% rename from crates/lang/macro/tests/ui/blake2b/fail/invalid_parameter_type_02.stderr rename to crates/lang/tests/ui/blake2b/fail/invalid_parameter_type_02.stderr index 96d1399302a..19c784fa440 100644 --- a/crates/lang/macro/tests/ui/blake2b/fail/invalid_parameter_type_02.stderr +++ b/crates/lang/tests/ui/blake2b/fail/invalid_parameter_type_02.stderr @@ -1,5 +1,5 @@ error: expected string or byte string literal as input. found Int(LitInt { token: 42 }) - --> $DIR/invalid_parameter_type_02.rs:3:38 + --> tests/ui/blake2b/fail/invalid_parameter_type_02.rs:3:38 | 3 | const _: [u8; 32] = ink::blake2x256!(42); | ^^ diff --git a/crates/lang/macro/tests/ui/blake2b/fail/missing_parameter.rs b/crates/lang/tests/ui/blake2b/fail/missing_parameter.rs similarity index 100% rename from crates/lang/macro/tests/ui/blake2b/fail/missing_parameter.rs rename to crates/lang/tests/ui/blake2b/fail/missing_parameter.rs diff --git a/crates/lang/macro/tests/ui/blake2b/fail/missing_parameter.stderr b/crates/lang/tests/ui/blake2b/fail/missing_parameter.stderr similarity index 85% rename from crates/lang/macro/tests/ui/blake2b/fail/missing_parameter.stderr rename to crates/lang/tests/ui/blake2b/fail/missing_parameter.stderr index 172fbee0df6..952d1515ba8 100644 --- a/crates/lang/macro/tests/ui/blake2b/fail/missing_parameter.stderr +++ b/crates/lang/tests/ui/blake2b/fail/missing_parameter.stderr @@ -1,5 +1,5 @@ error: expected string or byte string literal as input: unexpected end of input, expected literal - --> $DIR/missing_parameter.rs:3:21 + --> tests/ui/blake2b/fail/missing_parameter.rs:3:21 | 3 | const _: [u8; 32] = ink::blake2x256!(); | ^^^^^^^^^^^^^^^^^^ diff --git a/crates/lang/macro/tests/ui/blake2b/fail/non_literal_parameter.rs b/crates/lang/tests/ui/blake2b/fail/non_literal_parameter.rs similarity index 100% rename from crates/lang/macro/tests/ui/blake2b/fail/non_literal_parameter.rs rename to crates/lang/tests/ui/blake2b/fail/non_literal_parameter.rs diff --git a/crates/lang/macro/tests/ui/blake2b/fail/non_literal_parameter.stderr b/crates/lang/tests/ui/blake2b/fail/non_literal_parameter.stderr similarity index 75% rename from crates/lang/macro/tests/ui/blake2b/fail/non_literal_parameter.stderr rename to crates/lang/tests/ui/blake2b/fail/non_literal_parameter.stderr index 95f6bf75b28..0c6f18647ed 100644 --- a/crates/lang/macro/tests/ui/blake2b/fail/non_literal_parameter.stderr +++ b/crates/lang/tests/ui/blake2b/fail/non_literal_parameter.stderr @@ -1,5 +1,5 @@ error: expected string or byte string literal as input: expected literal - --> $DIR/non_literal_parameter.rs:4:38 + --> tests/ui/blake2b/fail/non_literal_parameter.rs:4:38 | 4 | const _: [u8; 32] = ink::blake2x256!(INPUT); | ^^^^^ diff --git a/crates/lang/macro/tests/ui/blake2b/pass/bytestring_input.rs b/crates/lang/tests/ui/blake2b/pass/bytestring_input.rs similarity index 100% rename from crates/lang/macro/tests/ui/blake2b/pass/bytestring_input.rs rename to crates/lang/tests/ui/blake2b/pass/bytestring_input.rs diff --git a/crates/lang/macro/tests/ui/blake2b/pass/no_implicit_prelude.rs b/crates/lang/tests/ui/blake2b/pass/no_implicit_prelude.rs similarity index 100% rename from crates/lang/macro/tests/ui/blake2b/pass/no_implicit_prelude.rs rename to crates/lang/tests/ui/blake2b/pass/no_implicit_prelude.rs diff --git a/crates/lang/macro/tests/ui/blake2b/pass/string_input.rs b/crates/lang/tests/ui/blake2b/pass/string_input.rs similarity index 100% rename from crates/lang/macro/tests/ui/blake2b/pass/string_input.rs rename to crates/lang/tests/ui/blake2b/pass/string_input.rs diff --git a/crates/lang/macro/tests/ui/chain_extension/E-01-simple.rs b/crates/lang/tests/ui/chain_extension/E-01-simple.rs similarity index 100% rename from crates/lang/macro/tests/ui/chain_extension/E-01-simple.rs rename to crates/lang/tests/ui/chain_extension/E-01-simple.rs diff --git a/crates/lang/macro/tests/ui/contract/fail/H-02-invalid-as-dependency.rs b/crates/lang/tests/ui/contract/fail/config-compile-as-dependency-invalid-type-01.rs similarity index 73% rename from crates/lang/macro/tests/ui/contract/fail/H-02-invalid-as-dependency.rs rename to crates/lang/tests/ui/contract/fail/config-compile-as-dependency-invalid-type-01.rs index 8604f93a577..479bab49dc2 100644 --- a/crates/lang/macro/tests/ui/contract/fail/H-02-invalid-as-dependency.rs +++ b/crates/lang/tests/ui/contract/fail/config-compile-as-dependency-invalid-type-01.rs @@ -1,11 +1,11 @@ use ink_lang as ink; #[ink::contract(compile_as_dependency = "yes")] -mod invalid_as_dependency { +mod contract { #[ink(storage)] - pub struct InvalidAsDependency {} + pub struct Contract {} - impl InvalidAsDependency { + impl Contract { #[ink(constructor)] pub fn constructor() -> Self { Self {} diff --git a/crates/lang/macro/tests/ui/contract/fail/H-02-invalid-as-dependency.stderr b/crates/lang/tests/ui/contract/fail/config-compile-as-dependency-invalid-type-01.stderr similarity index 70% rename from crates/lang/macro/tests/ui/contract/fail/H-02-invalid-as-dependency.stderr rename to crates/lang/tests/ui/contract/fail/config-compile-as-dependency-invalid-type-01.stderr index 9ea0cdc5b21..6aa45e18c32 100644 --- a/crates/lang/macro/tests/ui/contract/fail/H-02-invalid-as-dependency.stderr +++ b/crates/lang/tests/ui/contract/fail/config-compile-as-dependency-invalid-type-01.stderr @@ -1,5 +1,5 @@ error: expected a bool literal for `compile_as_dependency` ink! configuration argument - --> $DIR/H-02-invalid-as-dependency.rs:3:17 + --> tests/ui/contract/fail/config-compile-as-dependency-invalid-type-01.rs:3:17 | 3 | #[ink::contract(compile_as_dependency = "yes")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/crates/lang/tests/ui/contract/fail/config-compile-as-dependency-invalid-type-02.rs b/crates/lang/tests/ui/contract/fail/config-compile-as-dependency-invalid-type-02.rs new file mode 100644 index 00000000000..e057aab4750 --- /dev/null +++ b/crates/lang/tests/ui/contract/fail/config-compile-as-dependency-invalid-type-02.rs @@ -0,0 +1,19 @@ +use ink_lang as ink; + +#[ink::contract(compile_as_dependency = 42)] +mod contract { + #[ink(storage)] + pub struct Contract {} + + impl Contract { + #[ink(constructor)] + pub fn constructor() -> Self { + Self {} + } + + #[ink(message)] + pub fn message(&self) {} + } +} + +fn main() {} diff --git a/crates/lang/tests/ui/contract/fail/config-compile-as-dependency-invalid-type-02.stderr b/crates/lang/tests/ui/contract/fail/config-compile-as-dependency-invalid-type-02.stderr new file mode 100644 index 00000000000..270aee26990 --- /dev/null +++ b/crates/lang/tests/ui/contract/fail/config-compile-as-dependency-invalid-type-02.stderr @@ -0,0 +1,5 @@ +error: expected a bool literal for `compile_as_dependency` ink! configuration argument + --> tests/ui/contract/fail/config-compile-as-dependency-invalid-type-02.rs:3:17 + | +3 | #[ink::contract(compile_as_dependency = 42)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/crates/lang/tests/ui/contract/fail/config-compile-as-dependency-missing-arg.rs b/crates/lang/tests/ui/contract/fail/config-compile-as-dependency-missing-arg.rs new file mode 100644 index 00000000000..179a18340a0 --- /dev/null +++ b/crates/lang/tests/ui/contract/fail/config-compile-as-dependency-missing-arg.rs @@ -0,0 +1,19 @@ +use ink_lang as ink; + +#[ink::contract(compile_as_dependency)] +mod contract { + #[ink(storage)] + pub struct Contract {} + + impl Contract { + #[ink(constructor)] + pub fn constructor() -> Self { + Self {} + } + + #[ink(message)] + pub fn message(&self) {} + } +} + +fn main() {} diff --git a/crates/lang/tests/ui/contract/fail/config-compile-as-dependency-missing-arg.stderr b/crates/lang/tests/ui/contract/fail/config-compile-as-dependency-missing-arg.stderr new file mode 100644 index 00000000000..8a0176d6d90 --- /dev/null +++ b/crates/lang/tests/ui/contract/fail/config-compile-as-dependency-missing-arg.stderr @@ -0,0 +1,5 @@ +error: ink! config options require an argument separated by '=' + --> tests/ui/contract/fail/config-compile-as-dependency-missing-arg.rs:3:17 + | +3 | #[ink::contract(compile_as_dependency)] + | ^^^^^^^^^^^^^^^^^^^^^ diff --git a/crates/lang/macro/tests/ui/contract/fail/H-01-invalid-dyn-alloc.rs b/crates/lang/tests/ui/contract/fail/config-dynamic-storage-allocator-invalid-type-01.rs similarity index 77% rename from crates/lang/macro/tests/ui/contract/fail/H-01-invalid-dyn-alloc.rs rename to crates/lang/tests/ui/contract/fail/config-dynamic-storage-allocator-invalid-type-01.rs index 0efde049b09..99217339287 100644 --- a/crates/lang/macro/tests/ui/contract/fail/H-01-invalid-dyn-alloc.rs +++ b/crates/lang/tests/ui/contract/fail/config-dynamic-storage-allocator-invalid-type-01.rs @@ -1,11 +1,11 @@ use ink_lang as ink; #[ink::contract(dynamic_storage_allocator = "foo")] -mod invalid_version { +mod contract { #[ink(storage)] - pub struct InvalidVersion {} + pub struct Contract {} - impl InvalidVersion { + impl Contract { #[ink(constructor)] pub fn constructor() -> Self { Self {} diff --git a/crates/lang/macro/tests/ui/contract/fail/H-01-invalid-dyn-alloc.stderr b/crates/lang/tests/ui/contract/fail/config-dynamic-storage-allocator-invalid-type-01.stderr similarity index 70% rename from crates/lang/macro/tests/ui/contract/fail/H-01-invalid-dyn-alloc.stderr rename to crates/lang/tests/ui/contract/fail/config-dynamic-storage-allocator-invalid-type-01.stderr index 1875fce7be0..51071b94c35 100644 --- a/crates/lang/macro/tests/ui/contract/fail/H-01-invalid-dyn-alloc.stderr +++ b/crates/lang/tests/ui/contract/fail/config-dynamic-storage-allocator-invalid-type-01.stderr @@ -1,5 +1,5 @@ error: expected a bool literal for `dynamic_storage_allocator` ink! configuration argument - --> $DIR/H-01-invalid-dyn-alloc.rs:3:17 + --> tests/ui/contract/fail/config-dynamic-storage-allocator-invalid-type-01.rs:3:17 | 3 | #[ink::contract(dynamic_storage_allocator = "foo")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/crates/lang/tests/ui/contract/fail/config-dynamic-storage-allocator-invalid-type-02.rs b/crates/lang/tests/ui/contract/fail/config-dynamic-storage-allocator-invalid-type-02.rs new file mode 100644 index 00000000000..7a05d13161d --- /dev/null +++ b/crates/lang/tests/ui/contract/fail/config-dynamic-storage-allocator-invalid-type-02.rs @@ -0,0 +1,19 @@ +use ink_lang as ink; + +#[ink::contract(dynamic_storage_allocator = 42)] +mod contract { + #[ink(storage)] + pub struct Contract {} + + impl Contract { + #[ink(constructor)] + pub fn constructor() -> Self { + Self {} + } + + #[ink(message)] + pub fn message(&self) {} + } +} + +fn main() {} diff --git a/crates/lang/tests/ui/contract/fail/config-dynamic-storage-allocator-invalid-type-02.stderr b/crates/lang/tests/ui/contract/fail/config-dynamic-storage-allocator-invalid-type-02.stderr new file mode 100644 index 00000000000..63b1373fb1d --- /dev/null +++ b/crates/lang/tests/ui/contract/fail/config-dynamic-storage-allocator-invalid-type-02.stderr @@ -0,0 +1,5 @@ +error: expected a bool literal for `dynamic_storage_allocator` ink! configuration argument + --> tests/ui/contract/fail/config-dynamic-storage-allocator-invalid-type-02.rs:3:17 + | +3 | #[ink::contract(dynamic_storage_allocator = 42)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/crates/lang/tests/ui/contract/fail/config-dynamic-storage-allocator-missing-arg.rs b/crates/lang/tests/ui/contract/fail/config-dynamic-storage-allocator-missing-arg.rs new file mode 100644 index 00000000000..c30ebd1c9e8 --- /dev/null +++ b/crates/lang/tests/ui/contract/fail/config-dynamic-storage-allocator-missing-arg.rs @@ -0,0 +1,19 @@ +use ink_lang as ink; + +#[ink::contract(dynamic_storage_allocator)] +mod contract { + #[ink(storage)] + pub struct Contract {} + + impl Contract { + #[ink(constructor)] + pub fn constructor() -> Self { + Self {} + } + + #[ink(message)] + pub fn message(&self) {} + } +} + +fn main() {} diff --git a/crates/lang/tests/ui/contract/fail/config-dynamic-storage-allocator-missing-arg.stderr b/crates/lang/tests/ui/contract/fail/config-dynamic-storage-allocator-missing-arg.stderr new file mode 100644 index 00000000000..b19ec2b4275 --- /dev/null +++ b/crates/lang/tests/ui/contract/fail/config-dynamic-storage-allocator-missing-arg.stderr @@ -0,0 +1,5 @@ +error: ink! config options require an argument separated by '=' + --> tests/ui/contract/fail/config-dynamic-storage-allocator-missing-arg.rs:3:17 + | +3 | #[ink::contract(dynamic_storage_allocator)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/crates/lang/tests/ui/contract/fail/constructor-abi.rs b/crates/lang/tests/ui/contract/fail/constructor-abi.rs new file mode 100644 index 00000000000..749c41e30ec --- /dev/null +++ b/crates/lang/tests/ui/contract/fail/constructor-abi.rs @@ -0,0 +1,19 @@ +use ink_lang as ink; + +#[ink::contract] +mod contract { + #[ink(storage)] + pub struct Contract {} + + impl Contract { + #[ink(constructor)] + pub extern "C" fn constructor() -> Self { + Self {} + } + + #[ink(message)] + pub fn message(&self) {} + } +} + +fn main() {} diff --git a/crates/lang/tests/ui/contract/fail/constructor-abi.stderr b/crates/lang/tests/ui/contract/fail/constructor-abi.stderr new file mode 100644 index 00000000000..ac69b6edc50 --- /dev/null +++ b/crates/lang/tests/ui/contract/fail/constructor-abi.stderr @@ -0,0 +1,5 @@ +error: ink! constructors must not have explicit ABI + --> tests/ui/contract/fail/constructor-abi.rs:10:13 + | +10 | pub extern "C" fn constructor() -> Self { + | ^^^^^^^^^^ diff --git a/crates/lang/tests/ui/contract/fail/constructor-async.rs b/crates/lang/tests/ui/contract/fail/constructor-async.rs new file mode 100644 index 00000000000..4acfb0f1386 --- /dev/null +++ b/crates/lang/tests/ui/contract/fail/constructor-async.rs @@ -0,0 +1,19 @@ +use ink_lang as ink; + +#[ink::contract] +mod contract { + #[ink(storage)] + pub struct Contract {} + + impl Contract { + #[ink(constructor)] + pub async fn constructor() -> Self { + Self {} + } + + #[ink(message)] + pub fn message(&self) {} + } +} + +fn main() {} diff --git a/crates/lang/tests/ui/contract/fail/constructor-async.stderr b/crates/lang/tests/ui/contract/fail/constructor-async.stderr new file mode 100644 index 00000000000..86a797e0c13 --- /dev/null +++ b/crates/lang/tests/ui/contract/fail/constructor-async.stderr @@ -0,0 +1,5 @@ +error: ink! constructors must not be async + --> tests/ui/contract/fail/constructor-async.rs:10:13 + | +10 | pub async fn constructor() -> Self { + | ^^^^^ diff --git a/crates/lang/tests/ui/contract/fail/constructor-const.rs b/crates/lang/tests/ui/contract/fail/constructor-const.rs new file mode 100644 index 00000000000..dae9d73c0aa --- /dev/null +++ b/crates/lang/tests/ui/contract/fail/constructor-const.rs @@ -0,0 +1,19 @@ +use ink_lang as ink; + +#[ink::contract] +mod contract { + #[ink(storage)] + pub struct Contract {} + + impl Contract { + #[ink(constructor)] + pub const fn constructor() -> Self { + Self {} + } + + #[ink(message)] + pub fn message(&self) {} + } +} + +fn main() {} diff --git a/crates/lang/tests/ui/contract/fail/constructor-const.stderr b/crates/lang/tests/ui/contract/fail/constructor-const.stderr new file mode 100644 index 00000000000..4a4b2399fd3 --- /dev/null +++ b/crates/lang/tests/ui/contract/fail/constructor-const.stderr @@ -0,0 +1,5 @@ +error: ink! constructors must not be const + --> tests/ui/contract/fail/constructor-const.rs:10:13 + | +10 | pub const fn constructor() -> Self { + | ^^^^^ diff --git a/crates/lang/tests/ui/contract/fail/constructor-input-non-codec.rs b/crates/lang/tests/ui/contract/fail/constructor-input-non-codec.rs new file mode 100644 index 00000000000..c12317cfd38 --- /dev/null +++ b/crates/lang/tests/ui/contract/fail/constructor-input-non-codec.rs @@ -0,0 +1,22 @@ +use ink_lang as ink; + +#[ink::contract] +mod contract { + #[derive(scale_info::TypeInfo)] + pub struct NonCodecType; + + #[ink(storage)] + pub struct Contract {} + + impl Contract { + #[ink(constructor)] + pub fn constructor(_input: NonCodecType) -> Self { + Self {} + } + + #[ink(message)] + pub fn message(&self) {} + } +} + +fn main() {} diff --git a/crates/lang/tests/ui/contract/fail/constructor-input-non-codec.stderr b/crates/lang/tests/ui/contract/fail/constructor-input-non-codec.stderr new file mode 100644 index 00000000000..c5e6bacd40a --- /dev/null +++ b/crates/lang/tests/ui/contract/fail/constructor-input-non-codec.stderr @@ -0,0 +1,41 @@ +error[E0277]: the trait bound `NonCodecType: WrapperTypeDecode` is not satisfied + --> tests/ui/contract/fail/constructor-input-non-codec.rs:13:28 + | +13 | pub fn constructor(_input: NonCodecType) -> Self { + | ^^^^^^^^^^^^^^^^^^^^ the trait `WrapperTypeDecode` is not implemented for `NonCodecType` + | + = note: required because of the requirements on the impl of `parity_scale_codec::Decode` for `NonCodecType` +note: required by a bound in `DispatchInput` + --> src/codegen/dispatch/type_check.rs:41:8 + | +41 | T: scale::Decode + 'static; + | ^^^^^^^^^^^^^ required by this bound in `DispatchInput` + +error[E0277]: the trait bound `NonCodecType: WrapperTypeDecode` is not satisfied + --> tests/ui/contract/fail/constructor-input-non-codec.rs:13:9 + | +13 | / pub fn constructor(_input: NonCodecType) -> Self { +14 | | Self {} +15 | | } + | |_________^ the trait `WrapperTypeDecode` is not implemented for `NonCodecType` + | + = note: required because of the requirements on the impl of `parity_scale_codec::Decode` for `NonCodecType` +note: required by a bound in `parity_scale_codec::Decode::decode` + --> $CARGO/parity-scale-codec-2.3.1/src/codec.rs + | + | fn decode(input: &mut I) -> Result; + | ^ required by this bound in `parity_scale_codec::Decode::decode` + +error[E0277]: the trait bound `NonCodecType: WrapperTypeEncode` is not satisfied + --> tests/ui/contract/fail/constructor-input-non-codec.rs:3:1 + | +3 | #[ink::contract] + | ^^^^^^^^^^^^^^^^ the trait `WrapperTypeEncode` is not implemented for `NonCodecType` +... +13 | / pub fn constructor(_input: NonCodecType) -> Self { +14 | | Self {} +15 | | } + | |_________- required by a bound introduced by this call + | + = note: required because of the requirements on the impl of `Encode` for `NonCodecType` + = note: this error originates in the attribute macro `ink::contract` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/crates/lang/tests/ui/contract/fail/constructor-input-pattern.rs b/crates/lang/tests/ui/contract/fail/constructor-input-pattern.rs new file mode 100644 index 00000000000..d4ba3cf098d --- /dev/null +++ b/crates/lang/tests/ui/contract/fail/constructor-input-pattern.rs @@ -0,0 +1,19 @@ +use ink_lang as ink; + +#[ink::contract] +mod contract { + #[ink(storage)] + pub struct Contract {} + + impl Contract { + #[ink(constructor)] + pub fn constructor((_a, _b): (i32, i32)) -> Self { + Self {} + } + + #[ink(message)] + pub fn message(&self) {} + } +} + +fn main() {} diff --git a/crates/lang/tests/ui/contract/fail/constructor-input-pattern.stderr b/crates/lang/tests/ui/contract/fail/constructor-input-pattern.stderr new file mode 100644 index 00000000000..59def8bc720 --- /dev/null +++ b/crates/lang/tests/ui/contract/fail/constructor-input-pattern.stderr @@ -0,0 +1,5 @@ +error: ink! constructor arguments must have an identifier + --> tests/ui/contract/fail/constructor-input-pattern.rs:10:28 + | +10 | pub fn constructor((_a, _b): (i32, i32)) -> Self { + | ^^^^^^^^^^^^^^^^^^^^ diff --git a/crates/lang/macro/tests/ui/contract/fail/C-03-constructor-missing-return.rs b/crates/lang/tests/ui/contract/fail/constructor-missing-return.rs similarity index 51% rename from crates/lang/macro/tests/ui/contract/fail/C-03-constructor-missing-return.rs rename to crates/lang/tests/ui/contract/fail/constructor-missing-return.rs index eeb4a12b431..48ac98ae446 100644 --- a/crates/lang/macro/tests/ui/contract/fail/C-03-constructor-missing-return.rs +++ b/crates/lang/tests/ui/contract/fail/constructor-missing-return.rs @@ -1,16 +1,16 @@ use ink_lang as ink; #[ink::contract] -mod noop { +mod contract { #[ink(storage)] - pub struct Noop {} + pub struct Contract {} - impl Noop { + impl Contract { #[ink(constructor)] - pub fn missing_return() {} + pub fn constructor() {} #[ink(message)] - pub fn noop(&self) {} + pub fn message(&self) {} } } diff --git a/crates/lang/tests/ui/contract/fail/constructor-missing-return.stderr b/crates/lang/tests/ui/contract/fail/constructor-missing-return.stderr new file mode 100644 index 00000000000..fda3fa61b5a --- /dev/null +++ b/crates/lang/tests/ui/contract/fail/constructor-missing-return.stderr @@ -0,0 +1,5 @@ +error: missing return for ink! constructor + --> tests/ui/contract/fail/constructor-missing-return.rs:10:13 + | +10 | pub fn constructor() {} + | ^^^^^^^^^^^^^^^^ diff --git a/crates/lang/macro/tests/ui/contract/fail/C-14-payable-constructor.rs b/crates/lang/tests/ui/contract/fail/constructor-payable.rs similarity index 56% rename from crates/lang/macro/tests/ui/contract/fail/C-14-payable-constructor.rs rename to crates/lang/tests/ui/contract/fail/constructor-payable.rs index dcf230180da..b1a912857a9 100644 --- a/crates/lang/macro/tests/ui/contract/fail/C-14-payable-constructor.rs +++ b/crates/lang/tests/ui/contract/fail/constructor-payable.rs @@ -1,18 +1,18 @@ use ink_lang as ink; #[ink::contract] -mod noop { +mod contract { #[ink(storage)] - pub struct Noop {} + pub struct Contract {} - impl Noop { + impl Contract { #[ink(constructor, payable)] - pub fn abi_constructor() -> Self { + pub fn constructor() -> Self { Self {} } #[ink(message)] - pub fn noop(&self) {} + pub fn message(&self) {} } } diff --git a/crates/lang/macro/tests/ui/contract/fail/C-14-payable-constructor.stderr b/crates/lang/tests/ui/contract/fail/constructor-payable.stderr similarity index 70% rename from crates/lang/macro/tests/ui/contract/fail/C-14-payable-constructor.stderr rename to crates/lang/tests/ui/contract/fail/constructor-payable.stderr index 159540e092b..e2c7f4ed6af 100644 --- a/crates/lang/macro/tests/ui/contract/fail/C-14-payable-constructor.stderr +++ b/crates/lang/tests/ui/contract/fail/constructor-payable.stderr @@ -1,11 +1,11 @@ error: encountered conflicting ink! attribute argument - --> $DIR/C-14-payable-constructor.rs:9:28 + --> tests/ui/contract/fail/constructor-payable.rs:9:28 | 9 | #[ink(constructor, payable)] | ^^^^^^^ error: constructors are implicitly payable - --> $DIR/C-14-payable-constructor.rs:9:28 + --> tests/ui/contract/fail/constructor-payable.rs:9:28 | 9 | #[ink(constructor, payable)] | ^^^^^^^ diff --git a/crates/lang/macro/tests/ui/contract/fail/C-10-async-constructor.rs b/crates/lang/tests/ui/contract/fail/constructor-self-receiver-01.rs similarity index 54% rename from crates/lang/macro/tests/ui/contract/fail/C-10-async-constructor.rs rename to crates/lang/tests/ui/contract/fail/constructor-self-receiver-01.rs index fa965eb0f78..f1220614067 100644 --- a/crates/lang/macro/tests/ui/contract/fail/C-10-async-constructor.rs +++ b/crates/lang/tests/ui/contract/fail/constructor-self-receiver-01.rs @@ -1,18 +1,18 @@ use ink_lang as ink; #[ink::contract] -mod noop { +mod contract { #[ink(storage)] - pub struct Noop {} + pub struct Contract {} - impl Noop { + impl Contract { #[ink(constructor)] - pub async fn async_constructor() -> Self { + pub fn constructor(&self) -> Self { Self {} } #[ink(message)] - pub fn noop(&self) {} + pub fn message(&self) {} } } diff --git a/crates/lang/tests/ui/contract/fail/constructor-self-receiver-01.stderr b/crates/lang/tests/ui/contract/fail/constructor-self-receiver-01.stderr new file mode 100644 index 00000000000..b10addb66d3 --- /dev/null +++ b/crates/lang/tests/ui/contract/fail/constructor-self-receiver-01.stderr @@ -0,0 +1,5 @@ +error: ink! constructors must have no `self` receiver + --> tests/ui/contract/fail/constructor-self-receiver-01.rs:10:28 + | +10 | pub fn constructor(&self) -> Self { + | ^^^^^ diff --git a/crates/lang/tests/ui/contract/fail/constructor-self-receiver-02.rs b/crates/lang/tests/ui/contract/fail/constructor-self-receiver-02.rs new file mode 100644 index 00000000000..8e9701c69d7 --- /dev/null +++ b/crates/lang/tests/ui/contract/fail/constructor-self-receiver-02.rs @@ -0,0 +1,19 @@ +use ink_lang as ink; + +#[ink::contract] +mod contract { + #[ink(storage)] + pub struct Contract {} + + impl Contract { + #[ink(constructor)] + pub fn constructor(&mut self) -> Self { + Self {} + } + + #[ink(message)] + pub fn message(&self) {} + } +} + +fn main() {} diff --git a/crates/lang/tests/ui/contract/fail/constructor-self-receiver-02.stderr b/crates/lang/tests/ui/contract/fail/constructor-self-receiver-02.stderr new file mode 100644 index 00000000000..f217562f530 --- /dev/null +++ b/crates/lang/tests/ui/contract/fail/constructor-self-receiver-02.stderr @@ -0,0 +1,5 @@ +error: ink! constructors must have no `self` receiver + --> tests/ui/contract/fail/constructor-self-receiver-02.rs:10:28 + | +10 | pub fn constructor(&mut self) -> Self { + | ^^^^^^^^^ diff --git a/crates/lang/tests/ui/contract/fail/constructor-self-receiver-03.rs b/crates/lang/tests/ui/contract/fail/constructor-self-receiver-03.rs new file mode 100644 index 00000000000..1f7dd5890b0 --- /dev/null +++ b/crates/lang/tests/ui/contract/fail/constructor-self-receiver-03.rs @@ -0,0 +1,19 @@ +use ink_lang as ink; + +#[ink::contract] +mod contract { + #[ink(storage)] + pub struct Contract {} + + impl Contract { + #[ink(constructor)] + pub fn constructor(this: &Self) -> Self { + Self {} + } + + #[ink(message)] + pub fn message(&self) {} + } +} + +fn main() {} diff --git a/crates/lang/tests/ui/contract/fail/constructor-self-receiver-03.stderr b/crates/lang/tests/ui/contract/fail/constructor-self-receiver-03.stderr new file mode 100644 index 00000000000..30cdf70f020 --- /dev/null +++ b/crates/lang/tests/ui/contract/fail/constructor-self-receiver-03.stderr @@ -0,0 +1,18 @@ +error[E0411]: cannot find type `Self` in this scope + --> tests/ui/contract/fail/constructor-self-receiver-03.rs:10:35 + | +10 | pub fn constructor(this: &Self) -> Self { + | ^^^^ `Self` is only available in impls, traits, and type definitions + +error[E0106]: missing lifetime specifier + --> tests/ui/contract/fail/constructor-self-receiver-03.rs:10:34 + | +10 | pub fn constructor(this: &Self) -> Self { + | ^ expected named lifetime parameter + | +help: consider introducing a named lifetime parameter + | +10 ~ pub fn constructor(this: &'a Self) -> Self { +11 | Self {} +12 ~ }<'a> + | diff --git a/crates/lang/macro/tests/ui/contract/fail/C-02-constructor-self-val.rs b/crates/lang/tests/ui/contract/fail/constructor-self-receiver-04.rs similarity index 54% rename from crates/lang/macro/tests/ui/contract/fail/C-02-constructor-self-val.rs rename to crates/lang/tests/ui/contract/fail/constructor-self-receiver-04.rs index 30b661d309d..794be481142 100644 --- a/crates/lang/macro/tests/ui/contract/fail/C-02-constructor-self-val.rs +++ b/crates/lang/tests/ui/contract/fail/constructor-self-receiver-04.rs @@ -1,18 +1,18 @@ use ink_lang as ink; #[ink::contract] -mod noop { +mod contract { #[ink(storage)] - pub struct Noop {} + pub struct Contract {} - impl Noop { + impl Contract { #[ink(constructor)] - pub fn invalid_self_val(self) -> Self { + pub fn constructor(self) -> Self { Self {} } #[ink(message)] - pub fn noop(&self) {} + pub fn message(&self) {} } } diff --git a/crates/lang/tests/ui/contract/fail/constructor-self-receiver-04.stderr b/crates/lang/tests/ui/contract/fail/constructor-self-receiver-04.stderr new file mode 100644 index 00000000000..c5259d8f9bd --- /dev/null +++ b/crates/lang/tests/ui/contract/fail/constructor-self-receiver-04.stderr @@ -0,0 +1,5 @@ +error: ink! constructors must have no `self` receiver + --> tests/ui/contract/fail/constructor-self-receiver-04.rs:10:28 + | +10 | pub fn constructor(self) -> Self { + | ^^^^ diff --git a/crates/lang/tests/ui/contract/fail/constructor-unsafe.rs b/crates/lang/tests/ui/contract/fail/constructor-unsafe.rs new file mode 100644 index 00000000000..c5c770520ca --- /dev/null +++ b/crates/lang/tests/ui/contract/fail/constructor-unsafe.rs @@ -0,0 +1,19 @@ +use ink_lang as ink; + +#[ink::contract] +mod contract { + #[ink(storage)] + pub struct Contract {} + + impl Contract { + #[ink(constructor)] + pub unsafe fn constructor() -> Self { + Self {} + } + + #[ink(message)] + pub fn message(&self) {} + } +} + +fn main() {} diff --git a/crates/lang/tests/ui/contract/fail/constructor-unsafe.stderr b/crates/lang/tests/ui/contract/fail/constructor-unsafe.stderr new file mode 100644 index 00000000000..d340ce7205e --- /dev/null +++ b/crates/lang/tests/ui/contract/fail/constructor-unsafe.stderr @@ -0,0 +1,5 @@ +error: ink! constructors must not be unsafe + --> tests/ui/contract/fail/constructor-unsafe.rs:10:13 + | +10 | pub unsafe fn constructor() -> Self { + | ^^^^^^ diff --git a/crates/lang/tests/ui/contract/fail/event-conflicting-storage.rs b/crates/lang/tests/ui/contract/fail/event-conflicting-storage.rs new file mode 100644 index 00000000000..4e126ecda3f --- /dev/null +++ b/crates/lang/tests/ui/contract/fail/event-conflicting-storage.rs @@ -0,0 +1,23 @@ +use ink_lang as ink; + +#[ink::contract] +mod contract { + #[ink(storage)] + pub struct Contract {} + + #[ink(event)] + #[ink(storage)] + pub struct Event {} + + impl Contract { + #[ink(constructor)] + pub fn constructor() -> Self { + Self {} + } + + #[ink(message)] + pub fn message(&self) {} + } +} + +fn main() {} diff --git a/crates/lang/tests/ui/contract/fail/event-conflicting-storage.stderr b/crates/lang/tests/ui/contract/fail/event-conflicting-storage.stderr new file mode 100644 index 00000000000..42ea9e9f95e --- /dev/null +++ b/crates/lang/tests/ui/contract/fail/event-conflicting-storage.stderr @@ -0,0 +1,5 @@ +error: encountered conflicting ink! attribute argument + --> tests/ui/contract/fail/event-conflicting-storage.rs:9:11 + | +9 | #[ink(storage)] + | ^^^^^^^ diff --git a/crates/lang/tests/ui/contract/fail/event-too-many-topics-anonymous.rs b/crates/lang/tests/ui/contract/fail/event-too-many-topics-anonymous.rs new file mode 100644 index 00000000000..7ef6b7499e0 --- /dev/null +++ b/crates/lang/tests/ui/contract/fail/event-too-many-topics-anonymous.rs @@ -0,0 +1,62 @@ +use ink_env::{ + DefaultEnvironment, + Environment, +}; +use ink_lang as ink; + +pub struct EnvironmentMoreTopics; + +impl ink_env::Environment for EnvironmentMoreTopics { + const MAX_EVENT_TOPICS: usize = 2; + + type AccountId = ::AccountId; + type Balance = ::Balance; + type Hash = ::Hash; + type Timestamp = ::Timestamp; + type BlockNumber = ::BlockNumber; + type ChainExtension = (); + type RentFraction = ::RentFraction; +} + +#[ink::contract(env = super::EnvironmentMoreTopics)] +mod contract { + #[ink(storage)] + pub struct Contract {} + + #[ink(event, anonymous)] + pub struct Event { + #[ink(topic)] + arg_1: i8, + #[ink(topic)] + arg_2: i16, + #[ink(topic)] + arg_3: i32, + #[ink(topic)] + arg_4: i32, + } + + impl Contract { + #[ink(constructor)] + pub fn constructor() -> Self { + Self::env().emit_event(Event { + arg_1: 1, + arg_2: 2, + arg_3: 3, + arg_4: 4, + }); + Self {} + } + + #[ink(message)] + pub fn message(&self) { + self.env().emit_event(Event { + arg_1: 1, + arg_2: 2, + arg_3: 3, + arg_4: 4, + }); + } + } +} + +fn main() {} diff --git a/crates/lang/tests/ui/contract/fail/event-too-many-topics-anonymous.stderr b/crates/lang/tests/ui/contract/fail/event-too-many-topics-anonymous.stderr new file mode 100644 index 00000000000..026ef1c4dd6 --- /dev/null +++ b/crates/lang/tests/ui/contract/fail/event-too-many-topics-anonymous.stderr @@ -0,0 +1,23 @@ +error[E0277]: the trait bound `EventTopics<4_usize>: RespectTopicLimit<2_usize>` is not satisfied + --> tests/ui/contract/fail/event-too-many-topics-anonymous.rs:27:5 + | +27 | / pub struct Event { +28 | | #[ink(topic)] +29 | | arg_1: i8, +30 | | #[ink(topic)] +... | +35 | | arg_4: i32, +36 | | } + | |_____^ the trait `RespectTopicLimit<2_usize>` is not implemented for `EventTopics<4_usize>` + | + = help: the following implementations were found: + as RespectTopicLimit<0_usize>> + as RespectTopicLimit<10_usize>> + as RespectTopicLimit<11_usize>> + as RespectTopicLimit<12_usize>> + and 87 others +note: required by a bound in `EventRespectsTopicLimit` + --> src/codegen/event/topics.rs:48:43 + | +48 | ::LenTopics: RespectTopicLimit, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `EventRespectsTopicLimit` diff --git a/crates/lang/tests/ui/contract/fail/event-too-many-topics.rs b/crates/lang/tests/ui/contract/fail/event-too-many-topics.rs new file mode 100644 index 00000000000..c3834db8d92 --- /dev/null +++ b/crates/lang/tests/ui/contract/fail/event-too-many-topics.rs @@ -0,0 +1,58 @@ +use ink_env::{ + DefaultEnvironment, + Environment, +}; +use ink_lang as ink; + +pub struct EnvironmentMoreTopics; + +impl ink_env::Environment for EnvironmentMoreTopics { + const MAX_EVENT_TOPICS: usize = 2; + + type AccountId = ::AccountId; + type Balance = ::Balance; + type Hash = ::Hash; + type Timestamp = ::Timestamp; + type BlockNumber = ::BlockNumber; + type ChainExtension = (); + type RentFraction = ::RentFraction; +} + +#[ink::contract(env = super::EnvironmentMoreTopics)] +mod contract { + #[ink(storage)] + pub struct Contract {} + + #[ink(event)] + pub struct Event { + #[ink(topic)] + arg_1: i8, + #[ink(topic)] + arg_2: i16, + #[ink(topic)] + arg_3: i32, + } + + impl Contract { + #[ink(constructor)] + pub fn constructor() -> Self { + Self::env().emit_event(Event { + arg_1: 1, + arg_2: 2, + arg_3: 3, + }); + Self {} + } + + #[ink(message)] + pub fn message(&self) { + self.env().emit_event(Event { + arg_1: 1, + arg_2: 2, + arg_3: 3, + }); + } + } +} + +fn main() {} diff --git a/crates/lang/tests/ui/contract/fail/event-too-many-topics.stderr b/crates/lang/tests/ui/contract/fail/event-too-many-topics.stderr new file mode 100644 index 00000000000..4ae2bbb32cd --- /dev/null +++ b/crates/lang/tests/ui/contract/fail/event-too-many-topics.stderr @@ -0,0 +1,23 @@ +error[E0277]: the trait bound `EventTopics<3_usize>: RespectTopicLimit<2_usize>` is not satisfied + --> tests/ui/contract/fail/event-too-many-topics.rs:27:5 + | +27 | / pub struct Event { +28 | | #[ink(topic)] +29 | | arg_1: i8, +30 | | #[ink(topic)] +... | +33 | | arg_3: i32, +34 | | } + | |_____^ the trait `RespectTopicLimit<2_usize>` is not implemented for `EventTopics<3_usize>` + | + = help: the following implementations were found: + as RespectTopicLimit<0_usize>> + as RespectTopicLimit<10_usize>> + as RespectTopicLimit<11_usize>> + as RespectTopicLimit<12_usize>> + and 87 others +note: required by a bound in `EventRespectsTopicLimit` + --> src/codegen/event/topics.rs:48:43 + | +48 | ::LenTopics: RespectTopicLimit, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `EventRespectsTopicLimit` diff --git a/crates/lang/tests/ui/contract/fail/impl-block-for-non-storage-01.rs b/crates/lang/tests/ui/contract/fail/impl-block-for-non-storage-01.rs new file mode 100644 index 00000000000..1e648da4fa0 --- /dev/null +++ b/crates/lang/tests/ui/contract/fail/impl-block-for-non-storage-01.rs @@ -0,0 +1,31 @@ +use ink_lang as ink; + +#[ink::contract] +mod contract { + #[ink(storage)] + pub struct Contract {} + + impl Contract { + #[ink(constructor)] + pub fn constructor_1() -> Self { + Self {} + } + + #[ink(message)] + pub fn message_1(&self) {} + } + + pub struct NonContract {} + + impl NonContract { + #[ink(constructor)] + pub fn constructor_2() -> Self { + Self {} + } + + #[ink(message)] + pub fn message_2(&self) {} + } +} + +fn main() {} diff --git a/crates/lang/tests/ui/contract/fail/impl-block-for-non-storage-01.stderr b/crates/lang/tests/ui/contract/fail/impl-block-for-non-storage-01.stderr new file mode 100644 index 00000000000..50d787d6130 --- /dev/null +++ b/crates/lang/tests/ui/contract/fail/impl-block-for-non-storage-01.stderr @@ -0,0 +1,32 @@ +error[E0308]: mismatched types + --> tests/ui/contract/fail/impl-block-for-non-storage-01.rs:20:10 + | +20 | impl NonContract { + | ^^^^^^^^^^^ expected struct `Contract`, found struct `NonContract` + | + = note: expected struct `IsSameType` + found struct `IsSameType` + +error[E0599]: no function or associated item named `constructor_2` found for struct `Contract` in the current scope + --> tests/ui/contract/fail/impl-block-for-non-storage-01.rs:22:16 + | +6 | pub struct Contract {} + | ------------------- function or associated item `constructor_2` not found for this +... +22 | pub fn constructor_2() -> Self { + | ^^^^^^^^^^^^^ + | | + | function or associated item not found in `Contract` + | help: there is an associated function with a similar name: `constructor_1` + +error[E0599]: no function or associated item named `message_2` found for struct `Contract` in the current scope + --> tests/ui/contract/fail/impl-block-for-non-storage-01.rs:27:16 + | +6 | pub struct Contract {} + | ------------------- function or associated item `message_2` not found for this +... +27 | pub fn message_2(&self) {} + | ^^^^^^^^^ + | | + | function or associated item not found in `Contract` + | help: there is an associated function with a similar name: `message_1` diff --git a/crates/lang/tests/ui/contract/fail/impl-block-for-non-storage-02.rs b/crates/lang/tests/ui/contract/fail/impl-block-for-non-storage-02.rs new file mode 100644 index 00000000000..779a431bb1a --- /dev/null +++ b/crates/lang/tests/ui/contract/fail/impl-block-for-non-storage-02.rs @@ -0,0 +1,24 @@ +use ink_lang as ink; + +#[ink::contract] +mod contract { + #[ink(storage)] + pub struct Contract {} + + impl Contract { + #[ink(constructor)] + pub fn constructor() -> Self { + Self {} + } + + #[ink(message)] + pub fn message(&self) {} + } + + pub struct NonContract {} + + #[ink(impl)] + impl NonContract {} +} + +fn main() {} diff --git a/crates/lang/tests/ui/contract/fail/impl-block-for-non-storage-02.stderr b/crates/lang/tests/ui/contract/fail/impl-block-for-non-storage-02.stderr new file mode 100644 index 00000000000..3aa844764b8 --- /dev/null +++ b/crates/lang/tests/ui/contract/fail/impl-block-for-non-storage-02.stderr @@ -0,0 +1,8 @@ +error[E0308]: mismatched types + --> tests/ui/contract/fail/impl-block-for-non-storage-02.rs:21:10 + | +21 | impl NonContract {} + | ^^^^^^^^^^^ expected struct `Contract`, found struct `NonContract` + | + = note: expected struct `IsSameType` + found struct `IsSameType` diff --git a/crates/lang/macro/tests/ui/contract/fail/N-01-namespace-invalid-identifier.rs b/crates/lang/tests/ui/contract/fail/impl-block-namespace-invalid-identifier.rs similarity index 77% rename from crates/lang/macro/tests/ui/contract/fail/N-01-namespace-invalid-identifier.rs rename to crates/lang/tests/ui/contract/fail/impl-block-namespace-invalid-identifier.rs index 21d08c2d3d8..1151ef70f1d 100644 --- a/crates/lang/macro/tests/ui/contract/fail/N-01-namespace-invalid-identifier.rs +++ b/crates/lang/tests/ui/contract/fail/impl-block-namespace-invalid-identifier.rs @@ -1,12 +1,12 @@ use ink_lang as ink; #[ink::contract] -mod invalid_namespace_identifier { +mod contract { #[ink(storage)] - pub struct MyStorage {} + pub struct Contract {} #[ink(namespace = "::invalid_identifier")] - impl MyStorage { + impl Contract { #[ink(constructor)] pub fn constructor() -> Self { Self {} diff --git a/crates/lang/macro/tests/ui/contract/fail/N-01-namespace-invalid-identifier.stderr b/crates/lang/tests/ui/contract/fail/impl-block-namespace-invalid-identifier.stderr similarity index 69% rename from crates/lang/macro/tests/ui/contract/fail/N-01-namespace-invalid-identifier.stderr rename to crates/lang/tests/ui/contract/fail/impl-block-namespace-invalid-identifier.stderr index 8e3bacd08e3..d4e5efa7e46 100644 --- a/crates/lang/macro/tests/ui/contract/fail/N-01-namespace-invalid-identifier.stderr +++ b/crates/lang/tests/ui/contract/fail/impl-block-namespace-invalid-identifier.stderr @@ -1,5 +1,5 @@ error: encountered invalid Rust identifier for namespace argument - --> $DIR/N-01-namespace-invalid-identifier.rs:8:23 + --> tests/ui/contract/fail/impl-block-namespace-invalid-identifier.rs:8:23 | 8 | #[ink(namespace = "::invalid_identifier")] | ^^^^^^^^^^^^^^^^^^^^^^ diff --git a/crates/lang/macro/tests/ui/contract/fail/N-02-namespace-invalid-type.rs b/crates/lang/tests/ui/contract/fail/impl-block-namespace-invalid-type.rs similarity index 76% rename from crates/lang/macro/tests/ui/contract/fail/N-02-namespace-invalid-type.rs rename to crates/lang/tests/ui/contract/fail/impl-block-namespace-invalid-type.rs index 60871df1e53..4a4decd71f1 100644 --- a/crates/lang/macro/tests/ui/contract/fail/N-02-namespace-invalid-type.rs +++ b/crates/lang/tests/ui/contract/fail/impl-block-namespace-invalid-type.rs @@ -1,12 +1,12 @@ use ink_lang as ink; #[ink::contract] -mod invalid_namespace_identifier { +mod contract { #[ink(storage)] - pub struct MyStorage {} + pub struct Contract {} #[ink(namespace = true)] - impl MyStorage { + impl Contract { #[ink(constructor)] pub fn constructor() -> Self { Self {} diff --git a/crates/lang/tests/ui/contract/fail/impl-block-namespace-invalid-type.stderr b/crates/lang/tests/ui/contract/fail/impl-block-namespace-invalid-type.stderr new file mode 100644 index 00000000000..a6014a9e97b --- /dev/null +++ b/crates/lang/tests/ui/contract/fail/impl-block-namespace-invalid-type.stderr @@ -0,0 +1,5 @@ +error: expected string type for `namespace` argument, e.g. #[ink(namespace = "hello")] + --> tests/ui/contract/fail/impl-block-namespace-invalid-type.rs:8:11 + | +8 | #[ink(namespace = true)] + | ^^^^^^^^^^^^^^^^ diff --git a/crates/lang/macro/tests/ui/contract/fail/N-03-namespace-missing-argument.rs b/crates/lang/tests/ui/contract/fail/impl-block-namespace-missing-argument.rs similarity index 75% rename from crates/lang/macro/tests/ui/contract/fail/N-03-namespace-missing-argument.rs rename to crates/lang/tests/ui/contract/fail/impl-block-namespace-missing-argument.rs index a4af79c249d..94bbf98b5ba 100644 --- a/crates/lang/macro/tests/ui/contract/fail/N-03-namespace-missing-argument.rs +++ b/crates/lang/tests/ui/contract/fail/impl-block-namespace-missing-argument.rs @@ -1,12 +1,12 @@ use ink_lang as ink; #[ink::contract] -mod invalid_namespace_identifier { +mod contract { #[ink(storage)] - pub struct MyStorage {} + pub struct Contract {} #[ink(namespace)] - impl MyStorage { + impl Contract { #[ink(constructor)] pub fn constructor() -> Self { Self {} diff --git a/crates/lang/macro/tests/ui/contract/fail/N-03-namespace-missing-argument.stderr b/crates/lang/tests/ui/contract/fail/impl-block-namespace-missing-argument.stderr similarity index 70% rename from crates/lang/macro/tests/ui/contract/fail/N-03-namespace-missing-argument.stderr rename to crates/lang/tests/ui/contract/fail/impl-block-namespace-missing-argument.stderr index e5298677d27..b09ed8ec0cd 100644 --- a/crates/lang/macro/tests/ui/contract/fail/N-03-namespace-missing-argument.stderr +++ b/crates/lang/tests/ui/contract/fail/impl-block-namespace-missing-argument.stderr @@ -1,5 +1,5 @@ error: encountered #[ink(namespace)] that is missing its string parameter. Did you mean #[ink(namespace = name: str)] ? - --> $DIR/N-03-namespace-missing-argument.rs:8:11 + --> tests/ui/contract/fail/impl-block-namespace-missing-argument.rs:8:11 | 8 | #[ink(namespace)] | ^^^^^^^^^ diff --git a/crates/lang/tests/ui/contract/fail/impl-block-using-env-no-marker.rs b/crates/lang/tests/ui/contract/fail/impl-block-using-env-no-marker.rs new file mode 100644 index 00000000000..0b51814af74 --- /dev/null +++ b/crates/lang/tests/ui/contract/fail/impl-block-using-env-no-marker.rs @@ -0,0 +1,29 @@ +use ink_lang as ink; + +#[ink::contract] +mod contract { + #[ink(storage)] + pub struct Contract {} + + impl Contract { + #[ink(constructor)] + pub fn constructor() -> Self { + Self {} + } + } + + impl Contract { + #[ink(message)] + pub fn message(&self) { + self.message_impl(); + } + } + + impl Contract { + fn message_impl(&self) { + let _ = self.env().caller(); + } + } +} + +fn main() {} diff --git a/crates/lang/tests/ui/contract/fail/impl-block-using-env-no-marker.stderr b/crates/lang/tests/ui/contract/fail/impl-block-using-env-no-marker.stderr new file mode 100644 index 00000000000..55c4462a04f --- /dev/null +++ b/crates/lang/tests/ui/contract/fail/impl-block-using-env-no-marker.stderr @@ -0,0 +1,11 @@ +error[E0599]: no method named `env` found for reference `&Contract` in the current scope + --> tests/ui/contract/fail/impl-block-using-env-no-marker.rs:24:26 + | +24 | let _ = self.env().caller(); + | ^^^ method not found in `&Contract` + | + = help: items from traits can only be used if the trait is in scope +help: the following trait is implemented but not in scope; perhaps add a `use` for it: + | +6 | use ink_lang::codegen::Env; + | diff --git a/crates/lang/tests/ui/contract/fail/impl-block-using-static-env-no-marker.rs b/crates/lang/tests/ui/contract/fail/impl-block-using-static-env-no-marker.rs new file mode 100644 index 00000000000..0aace2aac9a --- /dev/null +++ b/crates/lang/tests/ui/contract/fail/impl-block-using-static-env-no-marker.rs @@ -0,0 +1,28 @@ +use ink_lang as ink; + +#[ink::contract] +mod contract { + #[ink(storage)] + pub struct Contract {} + + impl Contract { + #[ink(constructor)] + pub fn constructor() -> Self { + Self::constructor_impl() + } + } + + impl Contract { + #[ink(message)] + pub fn message(&self) {} + } + + impl Contract { + fn constructor_impl() -> Self { + let _ = Self::env().caller(); + Self {} + } + } +} + +fn main() {} diff --git a/crates/lang/tests/ui/contract/fail/impl-block-using-static-env-no-marker.stderr b/crates/lang/tests/ui/contract/fail/impl-block-using-static-env-no-marker.stderr new file mode 100644 index 00000000000..45d8ceab93d --- /dev/null +++ b/crates/lang/tests/ui/contract/fail/impl-block-using-static-env-no-marker.stderr @@ -0,0 +1,14 @@ +error[E0599]: no function or associated item named `env` found for struct `Contract` in the current scope + --> tests/ui/contract/fail/impl-block-using-static-env-no-marker.rs:22:27 + | +6 | pub struct Contract {} + | ------------------- function or associated item `env` not found for this +... +22 | let _ = Self::env().caller(); + | ^^^ function or associated item not found in `Contract` + | + = help: items from traits can only be used if the trait is in scope +help: the following trait is implemented but not in scope; perhaps add a `use` for it: + | +6 | use ink_lang::codegen::StaticEnv; + | diff --git a/crates/lang/tests/ui/contract/fail/message-input-non-codec.rs b/crates/lang/tests/ui/contract/fail/message-input-non-codec.rs new file mode 100644 index 00000000000..575dd04719a --- /dev/null +++ b/crates/lang/tests/ui/contract/fail/message-input-non-codec.rs @@ -0,0 +1,22 @@ +use ink_lang as ink; + +#[ink::contract] +mod contract { + #[derive(scale_info::TypeInfo)] + pub struct NonCodecType; + + #[ink(storage)] + pub struct Contract {} + + impl Contract { + #[ink(constructor)] + pub fn constructor() -> Self { + Self {} + } + + #[ink(message)] + pub fn message(&self, _input: NonCodecType) {} + } +} + +fn main() {} diff --git a/crates/lang/tests/ui/contract/fail/message-input-non-codec.stderr b/crates/lang/tests/ui/contract/fail/message-input-non-codec.stderr new file mode 100644 index 00000000000..9b470de454b --- /dev/null +++ b/crates/lang/tests/ui/contract/fail/message-input-non-codec.stderr @@ -0,0 +1,51 @@ +error[E0277]: the trait bound `NonCodecType: WrapperTypeDecode` is not satisfied + --> tests/ui/contract/fail/message-input-non-codec.rs:18:31 + | +18 | pub fn message(&self, _input: NonCodecType) {} + | ^^^^^^^^^^^^^^^^^^^^ the trait `WrapperTypeDecode` is not implemented for `NonCodecType` + | + = note: required because of the requirements on the impl of `parity_scale_codec::Decode` for `NonCodecType` +note: required by a bound in `DispatchInput` + --> src/codegen/dispatch/type_check.rs:41:8 + | +41 | T: scale::Decode + 'static; + | ^^^^^^^^^^^^^ required by this bound in `DispatchInput` + +error[E0277]: the trait bound `NonCodecType: WrapperTypeDecode` is not satisfied + --> tests/ui/contract/fail/message-input-non-codec.rs:18:9 + | +18 | pub fn message(&self, _input: NonCodecType) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `WrapperTypeDecode` is not implemented for `NonCodecType` + | + = note: required because of the requirements on the impl of `parity_scale_codec::Decode` for `NonCodecType` +note: required by a bound in `parity_scale_codec::Decode::decode` + --> $CARGO/parity-scale-codec-2.3.1/src/codec.rs + | + | fn decode(input: &mut I) -> Result; + | ^ required by this bound in `parity_scale_codec::Decode::decode` + +error[E0277]: the trait bound `NonCodecType: WrapperTypeEncode` is not satisfied + --> tests/ui/contract/fail/message-input-non-codec.rs:3:1 + | +3 | #[ink::contract] + | ^^^^^^^^^^^^^^^^ the trait `WrapperTypeEncode` is not implemented for `NonCodecType` +... +18 | pub fn message(&self, _input: NonCodecType) {} + | ---------------------------------------------- required by a bound introduced by this call + | + = note: required because of the requirements on the impl of `Encode` for `NonCodecType` + = note: this error originates in the attribute macro `ink::contract` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0599]: the method `fire` exists for struct `ink_env::call::CallBuilder, Unset, Unset, Set, ArgumentList>>>, Set<()>>`, but its trait bounds were not satisfied + --> tests/ui/contract/fail/message-input-non-codec.rs:18:9 + | +18 | pub fn message(&self, _input: NonCodecType) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ method cannot be called on `ink_env::call::CallBuilder, Unset, Unset, Set, ArgumentList>>>, Set<()>>` due to unsatisfied trait bounds + | + ::: $WORKSPACE/crates/env/src/call/execution_input.rs + | + | pub struct ArgumentList { + | ----------------------------------- doesn't satisfy `_: Encode` + | + = note: the following trait bounds were not satisfied: + `ArgumentList, ArgumentList>: Encode` diff --git a/crates/lang/macro/tests/ui/contract/fail/M-02-message-missing-self-arg.rs b/crates/lang/tests/ui/contract/fail/message-input-pattern.rs similarity index 60% rename from crates/lang/macro/tests/ui/contract/fail/M-02-message-missing-self-arg.rs rename to crates/lang/tests/ui/contract/fail/message-input-pattern.rs index d60402a0b62..20d3863ead9 100644 --- a/crates/lang/macro/tests/ui/contract/fail/M-02-message-missing-self-arg.rs +++ b/crates/lang/tests/ui/contract/fail/message-input-pattern.rs @@ -1,18 +1,18 @@ use ink_lang as ink; #[ink::contract] -mod missing_message_self_arg { +mod contract { #[ink(storage)] - pub struct MissingMessageSelfArg {} + pub struct Contract {} - impl MissingMessage { + impl Contract { #[ink(constructor)] pub fn constructor() -> Self { Self {} } #[ink(message)] - pub fn missing_self_arg() {} + pub fn message(&self, (_a, _b): (i32, i32)) {} } } diff --git a/crates/lang/tests/ui/contract/fail/message-input-pattern.stderr b/crates/lang/tests/ui/contract/fail/message-input-pattern.stderr new file mode 100644 index 00000000000..f74e49855d4 --- /dev/null +++ b/crates/lang/tests/ui/contract/fail/message-input-pattern.stderr @@ -0,0 +1,5 @@ +error: ink! message arguments must have an identifier + --> tests/ui/contract/fail/message-input-pattern.rs:15:31 + | +15 | pub fn message(&self, (_a, _b): (i32, i32)) {} + | ^^^^^^^^^^^^^^^^^^^^ diff --git a/crates/lang/macro/tests/ui/contract/fail/M-04-message-returns-non-codec.rs b/crates/lang/tests/ui/contract/fail/message-returns-non-codec.rs similarity index 53% rename from crates/lang/macro/tests/ui/contract/fail/M-04-message-returns-non-codec.rs rename to crates/lang/tests/ui/contract/fail/message-returns-non-codec.rs index 9ac92b4691b..cc0295f187a 100644 --- a/crates/lang/macro/tests/ui/contract/fail/M-04-message-returns-non-codec.rs +++ b/crates/lang/tests/ui/contract/fail/message-returns-non-codec.rs @@ -1,22 +1,22 @@ use ink_lang as ink; #[ink::contract] -mod message_returns_non_codec { +mod contract { #[derive(scale_info::TypeInfo)] - pub struct NonCodec; + pub struct NonCodecType; #[ink(storage)] - pub struct MessageReturnsNonCodecType {} + pub struct Contract {} - impl MessageReturnsNonCodecType { + impl Contract { #[ink(constructor)] pub fn constructor() -> Self { Self {} } #[ink(message)] - pub fn returns_non_codec_type(&self) -> NonCodec { - NonCodec + pub fn message(&self) -> NonCodecType { + NonCodecType } } } diff --git a/crates/lang/tests/ui/contract/fail/message-returns-non-codec.stderr b/crates/lang/tests/ui/contract/fail/message-returns-non-codec.stderr new file mode 100644 index 00000000000..2dbf27249ad --- /dev/null +++ b/crates/lang/tests/ui/contract/fail/message-returns-non-codec.stderr @@ -0,0 +1,52 @@ +error[E0277]: the trait bound `NonCodecType: WrapperTypeEncode` is not satisfied + --> tests/ui/contract/fail/message-returns-non-codec.rs:18:34 + | +18 | pub fn message(&self) -> NonCodecType { + | ^^^^^^^^^^^^ the trait `WrapperTypeEncode` is not implemented for `NonCodecType` + | + = note: required because of the requirements on the impl of `Encode` for `NonCodecType` +note: required by a bound in `DispatchOutput` + --> src/codegen/dispatch/type_check.rs:69:8 + | +69 | T: scale::Encode + 'static; + | ^^^^^^^^^^^^^ required by this bound in `DispatchOutput` + +error[E0277]: the trait bound `NonCodecType: WrapperTypeEncode` is not satisfied + --> tests/ui/contract/fail/message-returns-non-codec.rs:18:9 + | +18 | / pub fn message(&self) -> NonCodecType { +19 | | NonCodecType +20 | | } + | |_________^ the trait `WrapperTypeEncode` is not implemented for `NonCodecType` + | + = note: required because of the requirements on the impl of `Encode` for `NonCodecType` +note: required by a bound in `execute_message` + --> src/codegen/dispatch/execution.rs:139:13 + | +139 | Output: scale::Encode + 'static, + | ^^^^^^^^^^^^^ required by this bound in `execute_message` + +error[E0599]: the method `fire` exists for struct `ink_env::call::CallBuilder, Unset, Unset, Set>>, Set>>`, but its trait bounds were not satisfied + --> tests/ui/contract/fail/message-returns-non-codec.rs:18:9 + | +6 | pub struct NonCodecType; + | ------------------------ doesn't satisfy `NonCodecType: parity_scale_codec::Decode` +... +18 | / pub fn message(&self) -> NonCodecType { +19 | | NonCodecType +20 | | } + | |_________^ method cannot be called on `ink_env::call::CallBuilder, Unset, Unset, Set>>, Set>>` due to unsatisfied trait bounds + | + = note: the following trait bounds were not satisfied: + `NonCodecType: parity_scale_codec::Decode` +note: the following trait must be implemented + --> $CARGO/parity-scale-codec-2.3.1/src/codec.rs + | + | / pub trait Decode: Sized { + | | // !INTERNAL USE ONLY! + | | // This const helps SCALE to optimize the encoding/decoding by doing fake specialization. + | | #[doc(hidden)] +... | + | | } + | | } + | |_^ diff --git a/crates/lang/macro/tests/ui/contract/fail/M-03-message-returns-self.rs b/crates/lang/tests/ui/contract/fail/message-returns-self.rs similarity index 72% rename from crates/lang/macro/tests/ui/contract/fail/M-03-message-returns-self.rs rename to crates/lang/tests/ui/contract/fail/message-returns-self.rs index 5361751ef3a..a5f04da7854 100644 --- a/crates/lang/macro/tests/ui/contract/fail/M-03-message-returns-self.rs +++ b/crates/lang/tests/ui/contract/fail/message-returns-self.rs @@ -1,11 +1,11 @@ use ink_lang as ink; #[ink::contract] -mod message_returns_self { +mod contract { #[ink(storage)] - pub struct MessageReturnsSelf {} + pub struct Contract {} - impl MessageReturnsSelf { + impl Contract { #[ink(constructor)] pub fn constructor() -> Self { Self {} diff --git a/crates/lang/macro/tests/ui/contract/fail/M-03-message-returns-self.stderr b/crates/lang/tests/ui/contract/fail/message-returns-self.stderr similarity index 71% rename from crates/lang/macro/tests/ui/contract/fail/M-03-message-returns-self.stderr rename to crates/lang/tests/ui/contract/fail/message-returns-self.stderr index 1ce8a7adc75..7dea02f224d 100644 --- a/crates/lang/macro/tests/ui/contract/fail/M-03-message-returns-self.stderr +++ b/crates/lang/tests/ui/contract/fail/message-returns-self.stderr @@ -1,5 +1,5 @@ error: ink! messages must not return `Self` - --> $DIR/M-03-message-returns-self.rs:15:39 + --> tests/ui/contract/fail/message-returns-self.rs:15:39 | 15 | pub fn returns_self(&self) -> Self {} | ^^^^ diff --git a/crates/lang/macro/tests/ui/contract/fail/M-05-message-invalid-selector.rs b/crates/lang/tests/ui/contract/fail/message-selector-invalid-type-01.rs similarity index 60% rename from crates/lang/macro/tests/ui/contract/fail/M-05-message-invalid-selector.rs rename to crates/lang/tests/ui/contract/fail/message-selector-invalid-type-01.rs index ef0c35fefed..405486ce243 100644 --- a/crates/lang/macro/tests/ui/contract/fail/M-05-message-invalid-selector.rs +++ b/crates/lang/tests/ui/contract/fail/message-selector-invalid-type-01.rs @@ -1,18 +1,18 @@ use ink_lang as ink; #[ink::contract] -mod message_invalid_selector { +mod contract { #[ink(storage)] - pub struct MessageInvalidSelector {} + pub struct Contract {} - impl MessageInvalidSelector { + impl Contract { #[ink(constructor)] pub fn constructor() -> Self { Self {} } #[ink(message, selector = "0xC0DECAFE")] - pub fn invalid_selector(&self) {} + pub fn message(&self) {} } } diff --git a/crates/lang/macro/tests/ui/contract/fail/M-05-message-invalid-selector.stderr b/crates/lang/tests/ui/contract/fail/message-selector-invalid-type-01.stderr similarity index 79% rename from crates/lang/macro/tests/ui/contract/fail/M-05-message-invalid-selector.stderr rename to crates/lang/tests/ui/contract/fail/message-selector-invalid-type-01.stderr index ef29bddaccc..91585edc1b0 100644 --- a/crates/lang/macro/tests/ui/contract/fail/M-05-message-invalid-selector.stderr +++ b/crates/lang/tests/ui/contract/fail/message-selector-invalid-type-01.stderr @@ -1,5 +1,5 @@ error: #[ink(selector = ..)] attributes with string inputs are deprecated. use an integer instead, e.g. #[ink(selector = 1)] or #[ink(selector = 0xC0DECAFE)]. - --> $DIR/M-05-message-invalid-selector.rs:14:24 + --> tests/ui/contract/fail/message-selector-invalid-type-01.rs:14:24 | 14 | #[ink(message, selector = "0xC0DECAFE")] | ^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/crates/lang/macro/tests/ui/contract/fail/M-06-message-invalid-selector-type.rs b/crates/lang/tests/ui/contract/fail/message-selector-invalid-type-02.rs similarity index 59% rename from crates/lang/macro/tests/ui/contract/fail/M-06-message-invalid-selector-type.rs rename to crates/lang/tests/ui/contract/fail/message-selector-invalid-type-02.rs index 88348aa4714..2c9c8a3dc5d 100644 --- a/crates/lang/macro/tests/ui/contract/fail/M-06-message-invalid-selector-type.rs +++ b/crates/lang/tests/ui/contract/fail/message-selector-invalid-type-02.rs @@ -1,18 +1,18 @@ use ink_lang as ink; #[ink::contract] -mod message_invalid_selector { +mod contract { #[ink(storage)] - pub struct MessageInvalidSelector {} + pub struct Contract {} - impl MessageInvalidSelector { + impl Contract { #[ink(constructor)] pub fn constructor() -> Self { Self {} } #[ink(message, selector = true)] - pub fn invalid_selector(&self) {} + pub fn message(&self) {} } } diff --git a/crates/lang/macro/tests/ui/contract/fail/M-06-message-invalid-selector-type.stderr b/crates/lang/tests/ui/contract/fail/message-selector-invalid-type-02.stderr similarity index 72% rename from crates/lang/macro/tests/ui/contract/fail/M-06-message-invalid-selector-type.stderr rename to crates/lang/tests/ui/contract/fail/message-selector-invalid-type-02.stderr index 45844a68a6e..f261b80a9b9 100644 --- a/crates/lang/macro/tests/ui/contract/fail/M-06-message-invalid-selector-type.stderr +++ b/crates/lang/tests/ui/contract/fail/message-selector-invalid-type-02.stderr @@ -1,5 +1,5 @@ error: expecteded 4-digit hexcode for `selector` argument, e.g. #[ink(selector = 0xC0FEBABE] - --> $DIR/M-06-message-invalid-selector-type.rs:14:24 + --> tests/ui/contract/fail/message-selector-invalid-type-02.rs:14:24 | 14 | #[ink(message, selector = true)] | ^^^^^^^^^^^^^^^ diff --git a/crates/lang/tests/ui/contract/fail/message-selector-missing-arg.rs b/crates/lang/tests/ui/contract/fail/message-selector-missing-arg.rs new file mode 100644 index 00000000000..e7c9697ae82 --- /dev/null +++ b/crates/lang/tests/ui/contract/fail/message-selector-missing-arg.rs @@ -0,0 +1,19 @@ +use ink_lang as ink; + +#[ink::contract] +mod contract { + #[ink(storage)] + pub struct Contract {} + + impl Contract { + #[ink(constructor)] + pub fn constructor() -> Self { + Self {} + } + + #[ink(message, selector)] + pub fn message(&self) {} + } +} + +fn main() {} diff --git a/crates/lang/tests/ui/contract/fail/message-selector-missing-arg.stderr b/crates/lang/tests/ui/contract/fail/message-selector-missing-arg.stderr new file mode 100644 index 00000000000..2e5d84a55c8 --- /dev/null +++ b/crates/lang/tests/ui/contract/fail/message-selector-missing-arg.stderr @@ -0,0 +1,5 @@ +error: encountered #[ink(selector)] that is missing its u32 parameter. Did you mean #[ink(selector = value: u32)] ? + --> tests/ui/contract/fail/message-selector-missing-arg.rs:14:24 + | +14 | #[ink(message, selector)] + | ^^^^^^^^ diff --git a/crates/lang/tests/ui/contract/fail/message-self-receiver-invalid-01.rs b/crates/lang/tests/ui/contract/fail/message-self-receiver-invalid-01.rs new file mode 100644 index 00000000000..518f66e75ba --- /dev/null +++ b/crates/lang/tests/ui/contract/fail/message-self-receiver-invalid-01.rs @@ -0,0 +1,19 @@ +use ink_lang as ink; + +#[ink::contract] +mod contract { + #[ink(storage)] + pub struct Contract {} + + impl Contract { + #[ink(constructor)] + pub fn constructor() -> Self { + Self {} + } + + #[ink(message)] + pub fn message(this: &Self) {} + } +} + +fn main() {} diff --git a/crates/lang/tests/ui/contract/fail/message-self-receiver-invalid-01.stderr b/crates/lang/tests/ui/contract/fail/message-self-receiver-invalid-01.stderr new file mode 100644 index 00000000000..2f8df8130e3 --- /dev/null +++ b/crates/lang/tests/ui/contract/fail/message-self-receiver-invalid-01.stderr @@ -0,0 +1,5 @@ +error: ink! messages must have `&self` or `&mut self` receiver + --> tests/ui/contract/fail/message-self-receiver-invalid-01.rs:15:24 + | +15 | pub fn message(this: &Self) {} + | ^^^^^^^^^^^ diff --git a/crates/lang/tests/ui/contract/fail/message-self-receiver-invalid-02.rs b/crates/lang/tests/ui/contract/fail/message-self-receiver-invalid-02.rs new file mode 100644 index 00000000000..94ec0c3fb55 --- /dev/null +++ b/crates/lang/tests/ui/contract/fail/message-self-receiver-invalid-02.rs @@ -0,0 +1,19 @@ +use ink_lang as ink; + +#[ink::contract] +mod contract { + #[ink(storage)] + pub struct Contract {} + + impl Contract { + #[ink(constructor)] + pub fn constructor() -> Self { + Self {} + } + + #[ink(message)] + pub fn message(this: &mut Self) {} + } +} + +fn main() {} diff --git a/crates/lang/tests/ui/contract/fail/message-self-receiver-invalid-02.stderr b/crates/lang/tests/ui/contract/fail/message-self-receiver-invalid-02.stderr new file mode 100644 index 00000000000..3caaa0e6a71 --- /dev/null +++ b/crates/lang/tests/ui/contract/fail/message-self-receiver-invalid-02.stderr @@ -0,0 +1,5 @@ +error: ink! messages must have `&self` or `&mut self` receiver + --> tests/ui/contract/fail/message-self-receiver-invalid-02.rs:15:24 + | +15 | pub fn message(this: &mut Self) {} + | ^^^^^^^^^^^^^^^ diff --git a/crates/lang/tests/ui/contract/fail/message-self-receiver-invalid-03.rs b/crates/lang/tests/ui/contract/fail/message-self-receiver-invalid-03.rs new file mode 100644 index 00000000000..c30aa771cc9 --- /dev/null +++ b/crates/lang/tests/ui/contract/fail/message-self-receiver-invalid-03.rs @@ -0,0 +1,19 @@ +use ink_lang as ink; + +#[ink::contract] +mod contract { + #[ink(storage)] + pub struct Contract {} + + impl Contract { + #[ink(constructor)] + pub fn constructor() -> Self { + Self {} + } + + #[ink(message)] + pub fn message(self) {} + } +} + +fn main() {} diff --git a/crates/lang/tests/ui/contract/fail/message-self-receiver-invalid-03.stderr b/crates/lang/tests/ui/contract/fail/message-self-receiver-invalid-03.stderr new file mode 100644 index 00000000000..0c95e3170d5 --- /dev/null +++ b/crates/lang/tests/ui/contract/fail/message-self-receiver-invalid-03.stderr @@ -0,0 +1,5 @@ +error: ink! messages must have `&self` or `&mut self` receiver + --> tests/ui/contract/fail/message-self-receiver-invalid-03.rs:15:24 + | +15 | pub fn message(self) {} + | ^^^^ diff --git a/crates/lang/tests/ui/contract/fail/message-self-receiver-missing.rs b/crates/lang/tests/ui/contract/fail/message-self-receiver-missing.rs new file mode 100644 index 00000000000..552c91c143f --- /dev/null +++ b/crates/lang/tests/ui/contract/fail/message-self-receiver-missing.rs @@ -0,0 +1,19 @@ +use ink_lang as ink; + +#[ink::contract] +mod contract { + #[ink(storage)] + pub struct Contract {} + + impl Contract { + #[ink(constructor)] + pub fn constructor() -> Self { + Self {} + } + + #[ink(message)] + pub fn message() {} + } +} + +fn main() {} diff --git a/crates/lang/tests/ui/contract/fail/message-self-receiver-missing.stderr b/crates/lang/tests/ui/contract/fail/message-self-receiver-missing.stderr new file mode 100644 index 00000000000..5a145a27650 --- /dev/null +++ b/crates/lang/tests/ui/contract/fail/message-self-receiver-missing.stderr @@ -0,0 +1,6 @@ +error: ink! messages must have `&self` or `&mut self` receiver + --> tests/ui/contract/fail/message-self-receiver-missing.rs:14:9 + | +14 | / #[ink(message)] +15 | | pub fn message() {} + | |___________________________^ diff --git a/crates/lang/macro/tests/ui/contract/fail/M-10-method-unknown-ink-marker.rs b/crates/lang/tests/ui/contract/fail/message-unknown-property.rs similarity index 65% rename from crates/lang/macro/tests/ui/contract/fail/M-10-method-unknown-ink-marker.rs rename to crates/lang/tests/ui/contract/fail/message-unknown-property.rs index 22b188429a3..e8e51eb8e47 100644 --- a/crates/lang/macro/tests/ui/contract/fail/M-10-method-unknown-ink-marker.rs +++ b/crates/lang/tests/ui/contract/fail/message-unknown-property.rs @@ -1,21 +1,19 @@ use ink_lang as ink; #[ink::contract] -mod unknown_method_ink_marker { +mod contract { #[ink(storage)] - pub struct UnknownMethodInkMarker {} + pub struct Contract {} - impl UnknownMethodInkMarker { + impl Contract { #[ink(constructor)] pub fn constructor() -> Self { Self {} } #[ink(message)] - pub fn message(&self) {} - #[ink(unknown_marker)] - pub fn method(&self) {} + pub fn message(&self) {} } } diff --git a/crates/lang/tests/ui/contract/fail/message-unknown-property.stderr b/crates/lang/tests/ui/contract/fail/message-unknown-property.stderr new file mode 100644 index 00000000000..0f27d2dc911 --- /dev/null +++ b/crates/lang/tests/ui/contract/fail/message-unknown-property.stderr @@ -0,0 +1,5 @@ +error: unknown ink! attribute (path) + --> tests/ui/contract/fail/message-unknown-property.rs:15:15 + | +15 | #[ink(unknown_marker)] + | ^^^^^^^^^^^^^^ diff --git a/crates/lang/macro/tests/ui/contract/fail/C-04-missing-constructor.rs b/crates/lang/tests/ui/contract/fail/module-missing-constructor.rs similarity index 52% rename from crates/lang/macro/tests/ui/contract/fail/C-04-missing-constructor.rs rename to crates/lang/tests/ui/contract/fail/module-missing-constructor.rs index 89d09bce046..e259a4d1420 100644 --- a/crates/lang/macro/tests/ui/contract/fail/C-04-missing-constructor.rs +++ b/crates/lang/tests/ui/contract/fail/module-missing-constructor.rs @@ -1,13 +1,13 @@ use ink_lang as ink; #[ink::contract] -mod noop { +mod contract { #[ink(storage)] - pub struct Noop {} + pub struct Contract {} - impl Noop { + impl Contract { #[ink(message)] - pub fn noop(&self) {} + pub fn message(&self) {} } } diff --git a/crates/lang/tests/ui/contract/fail/module-missing-constructor.stderr b/crates/lang/tests/ui/contract/fail/module-missing-constructor.stderr new file mode 100644 index 00000000000..fa0735e4b7c --- /dev/null +++ b/crates/lang/tests/ui/contract/fail/module-missing-constructor.stderr @@ -0,0 +1,11 @@ +error: missing ink! constructor + --> tests/ui/contract/fail/module-missing-constructor.rs:4:1 + | +4 | / mod contract { +5 | | #[ink(storage)] +6 | | pub struct Contract {} +7 | | +... | +11 | | } +12 | | } + | |_^ diff --git a/crates/lang/macro/tests/ui/contract/fail/M-01-missing-message.rs b/crates/lang/tests/ui/contract/fail/module-missing-message.rs similarity index 68% rename from crates/lang/macro/tests/ui/contract/fail/M-01-missing-message.rs rename to crates/lang/tests/ui/contract/fail/module-missing-message.rs index 2a3d5f0b2a0..6c0ad2a14ec 100644 --- a/crates/lang/macro/tests/ui/contract/fail/M-01-missing-message.rs +++ b/crates/lang/tests/ui/contract/fail/module-missing-message.rs @@ -1,11 +1,11 @@ use ink_lang as ink; #[ink::contract] -mod missing_message { +mod contract { #[ink(storage)] - pub struct MissingMessage {} + pub struct Contract {} - impl MissingMessage { + impl Contract { #[ink(constructor)] pub fn constructor() -> Self { Self {} diff --git a/crates/lang/tests/ui/contract/fail/module-missing-message.stderr b/crates/lang/tests/ui/contract/fail/module-missing-message.stderr new file mode 100644 index 00000000000..b71b1d5bd4b --- /dev/null +++ b/crates/lang/tests/ui/contract/fail/module-missing-message.stderr @@ -0,0 +1,11 @@ +error: missing ink! message + --> tests/ui/contract/fail/module-missing-message.rs:4:1 + | +4 | / mod contract { +5 | | #[ink(storage)] +6 | | pub struct Contract {} +7 | | +... | +13 | | } +14 | | } + | |_^ diff --git a/crates/lang/macro/tests/ui/contract/fail/S-01-missing-storage-struct.rs b/crates/lang/tests/ui/contract/fail/module-missing-storage.rs similarity index 54% rename from crates/lang/macro/tests/ui/contract/fail/S-01-missing-storage-struct.rs rename to crates/lang/tests/ui/contract/fail/module-missing-storage.rs index b951ed06f69..02f0990249d 100644 --- a/crates/lang/macro/tests/ui/contract/fail/S-01-missing-storage-struct.rs +++ b/crates/lang/tests/ui/contract/fail/module-missing-storage.rs @@ -1,11 +1,11 @@ use ink_lang as ink; #[ink::contract] -mod missing_storage_struct { - // We are missing the #[ink(storage)] attribute here - pub struct MissingStorageStruct {} +mod contract { + // #[ink(storage)] + pub struct Contract {} - impl MissingStorageStruct { + impl Contract { #[ink(constructor)] pub fn constructor() -> Self {} diff --git a/crates/lang/tests/ui/contract/fail/module-missing-storage.stderr b/crates/lang/tests/ui/contract/fail/module-missing-storage.stderr new file mode 100644 index 00000000000..2559d3726bb --- /dev/null +++ b/crates/lang/tests/ui/contract/fail/module-missing-storage.stderr @@ -0,0 +1,11 @@ +error: missing ink! storage struct + --> tests/ui/contract/fail/module-missing-storage.rs:4:1 + | +4 | / mod contract { +5 | | // #[ink(storage)] +6 | | pub struct Contract {} +7 | | +... | +14 | | } +15 | | } + | |_^ diff --git a/crates/lang/tests/ui/contract/fail/module-multiple-storages.rs b/crates/lang/tests/ui/contract/fail/module-multiple-storages.rs new file mode 100644 index 00000000000..4a5f2ad816c --- /dev/null +++ b/crates/lang/tests/ui/contract/fail/module-multiple-storages.rs @@ -0,0 +1,32 @@ +use ink_lang as ink; + +#[ink::contract] +mod contract { + #[ink(storage)] + pub struct Contract {} + + impl Contract { + #[ink(constructor)] + pub fn constructor() -> Self { + Self {} + } + + #[ink(message)] + pub fn message(&self) {} + } + + #[ink(storage)] + pub struct Contract2 {} + + impl Contract2 { + #[ink(constructor)] + pub fn constructor() -> Self { + Self {} + } + + #[ink(message)] + pub fn message(&self) {} + } +} + +fn main() {} diff --git a/crates/lang/tests/ui/contract/fail/module-multiple-storages.stderr b/crates/lang/tests/ui/contract/fail/module-multiple-storages.stderr new file mode 100644 index 00000000000..98faa7ab857 --- /dev/null +++ b/crates/lang/tests/ui/contract/fail/module-multiple-storages.stderr @@ -0,0 +1,23 @@ +error: encountered multiple ink! storage structs, expected exactly one + --> tests/ui/contract/fail/module-multiple-storages.rs:4:1 + | +4 | / mod contract { +5 | | #[ink(storage)] +6 | | pub struct Contract {} +7 | | +... | +29 | | } +30 | | } + | |_^ + +error: ink! storage struct here + --> tests/ui/contract/fail/module-multiple-storages.rs:6:5 + | +6 | pub struct Contract {} + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: ink! storage struct here + --> tests/ui/contract/fail/module-multiple-storages.rs:19:5 + | +19 | pub struct Contract2 {} + | ^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/crates/lang/tests/ui/contract/fail/module-use-forbidden-idents.rs b/crates/lang/tests/ui/contract/fail/module-use-forbidden-idents.rs new file mode 100644 index 00000000000..61416bf8776 --- /dev/null +++ b/crates/lang/tests/ui/contract/fail/module-use-forbidden-idents.rs @@ -0,0 +1,25 @@ +use ink_lang as ink; + +#[ink::contract] +mod __ink_contract { + #[ink(storage)] + pub struct __ink_Contract {} + + const __ink_CONST: () = (); + static __ink_STATIC: () = (); + type __ink_Type = (); + + impl __ink_Contract { + #[ink(constructor)] + pub fn __ink_constructor(__ink_input: __ink_Type) -> Self { + Self {} + } + + #[ink(message)] + pub fn __ink_message(&self, __ink_input: __ink_Type) -> __ink_Type { + let __ink_first = (); + } + } +} + +fn main() {} diff --git a/crates/lang/tests/ui/contract/fail/module-use-forbidden-idents.stderr b/crates/lang/tests/ui/contract/fail/module-use-forbidden-idents.stderr new file mode 100644 index 00000000000..2a326233af2 --- /dev/null +++ b/crates/lang/tests/ui/contract/fail/module-use-forbidden-idents.stderr @@ -0,0 +1,83 @@ +error: encountered invalid identifier starting with __ink_ + --> tests/ui/contract/fail/module-use-forbidden-idents.rs:4:5 + | +4 | mod __ink_contract { + | ^^^^^^^^^^^^^^ + +error: encountered invalid identifier starting with __ink_ + --> tests/ui/contract/fail/module-use-forbidden-idents.rs:6:16 + | +6 | pub struct __ink_Contract {} + | ^^^^^^^^^^^^^^ + +error: encountered invalid identifier starting with __ink_ + --> tests/ui/contract/fail/module-use-forbidden-idents.rs:8:11 + | +8 | const __ink_CONST: () = (); + | ^^^^^^^^^^^ + +error: encountered invalid identifier starting with __ink_ + --> tests/ui/contract/fail/module-use-forbidden-idents.rs:9:12 + | +9 | static __ink_STATIC: () = (); + | ^^^^^^^^^^^^ + +error: encountered invalid identifier starting with __ink_ + --> tests/ui/contract/fail/module-use-forbidden-idents.rs:10:10 + | +10 | type __ink_Type = (); + | ^^^^^^^^^^ + +error: encountered invalid identifier starting with __ink_ + --> tests/ui/contract/fail/module-use-forbidden-idents.rs:12:10 + | +12 | impl __ink_Contract { + | ^^^^^^^^^^^^^^ + +error: encountered invalid identifier starting with __ink_ + --> tests/ui/contract/fail/module-use-forbidden-idents.rs:14:16 + | +14 | pub fn __ink_constructor(__ink_input: __ink_Type) -> Self { + | ^^^^^^^^^^^^^^^^^ + +error: encountered invalid identifier starting with __ink_ + --> tests/ui/contract/fail/module-use-forbidden-idents.rs:14:34 + | +14 | pub fn __ink_constructor(__ink_input: __ink_Type) -> Self { + | ^^^^^^^^^^^ + +error: encountered invalid identifier starting with __ink_ + --> tests/ui/contract/fail/module-use-forbidden-idents.rs:14:47 + | +14 | pub fn __ink_constructor(__ink_input: __ink_Type) -> Self { + | ^^^^^^^^^^ + +error: encountered invalid identifier starting with __ink_ + --> tests/ui/contract/fail/module-use-forbidden-idents.rs:19:16 + | +19 | pub fn __ink_message(&self, __ink_input: __ink_Type) -> __ink_Type { + | ^^^^^^^^^^^^^ + +error: encountered invalid identifier starting with __ink_ + --> tests/ui/contract/fail/module-use-forbidden-idents.rs:19:37 + | +19 | pub fn __ink_message(&self, __ink_input: __ink_Type) -> __ink_Type { + | ^^^^^^^^^^^ + +error: encountered invalid identifier starting with __ink_ + --> tests/ui/contract/fail/module-use-forbidden-idents.rs:19:50 + | +19 | pub fn __ink_message(&self, __ink_input: __ink_Type) -> __ink_Type { + | ^^^^^^^^^^ + +error: encountered invalid identifier starting with __ink_ + --> tests/ui/contract/fail/module-use-forbidden-idents.rs:19:65 + | +19 | pub fn __ink_message(&self, __ink_input: __ink_Type) -> __ink_Type { + | ^^^^^^^^^^ + +error: encountered invalid identifier starting with __ink_ + --> tests/ui/contract/fail/module-use-forbidden-idents.rs:20:17 + | +20 | let __ink_first = (); + | ^^^^^^^^^^^ diff --git a/crates/lang/tests/ui/contract/fail/storage-conflicting-event.rs b/crates/lang/tests/ui/contract/fail/storage-conflicting-event.rs new file mode 100644 index 00000000000..af8635e42c2 --- /dev/null +++ b/crates/lang/tests/ui/contract/fail/storage-conflicting-event.rs @@ -0,0 +1,20 @@ +use ink_lang as ink; + +#[ink::contract] +mod contract { + #[ink(storage)] + #[ink(event)] + pub struct Contract {} + + impl Contract { + #[ink(constructor)] + pub fn constructor() -> Self { + Self {} + } + + #[ink(message)] + pub fn message(&self) {} + } +} + +fn main() {} diff --git a/crates/lang/tests/ui/contract/fail/storage-conflicting-event.stderr b/crates/lang/tests/ui/contract/fail/storage-conflicting-event.stderr new file mode 100644 index 00000000000..84ef72bc72b --- /dev/null +++ b/crates/lang/tests/ui/contract/fail/storage-conflicting-event.stderr @@ -0,0 +1,5 @@ +error: encountered conflicting ink! attribute argument + --> tests/ui/contract/fail/storage-conflicting-event.rs:6:11 + | +6 | #[ink(event)] + | ^^^^^ diff --git a/crates/lang/macro/tests/ui/contract/fail/S-03-struct-unknown-ink-marker.rs b/crates/lang/tests/ui/contract/fail/storage-unknown-marker.rs similarity index 64% rename from crates/lang/macro/tests/ui/contract/fail/S-03-struct-unknown-ink-marker.rs rename to crates/lang/tests/ui/contract/fail/storage-unknown-marker.rs index 8816f81729d..748544af783 100644 --- a/crates/lang/macro/tests/ui/contract/fail/S-03-struct-unknown-ink-marker.rs +++ b/crates/lang/tests/ui/contract/fail/storage-unknown-marker.rs @@ -1,14 +1,12 @@ use ink_lang as ink; #[ink::contract] -mod unknown_ink_marker_on_struct { +mod contract { #[ink(storage)] - pub struct UnknownInkMarkerOnStruct {} - #[ink(unknown_or_unsupported)] - pub struct HasUnknownMarker {} + pub struct Contract {} - impl UnknownInkMarkerOnStruct { + impl Contract { #[ink(constructor)] pub fn constructor() -> Self { Self {} diff --git a/crates/lang/tests/ui/contract/fail/storage-unknown-marker.stderr b/crates/lang/tests/ui/contract/fail/storage-unknown-marker.stderr new file mode 100644 index 00000000000..b24ba558ba2 --- /dev/null +++ b/crates/lang/tests/ui/contract/fail/storage-unknown-marker.stderr @@ -0,0 +1,5 @@ +error: unknown ink! attribute (path) + --> tests/ui/contract/fail/storage-unknown-marker.rs:6:11 + | +6 | #[ink(unknown_or_unsupported)] + | ^^^^^^^^^^^^^^^^^^^^^^ diff --git a/crates/lang/tests/ui/contract/fail/trait-impl-namespace-invalid.rs b/crates/lang/tests/ui/contract/fail/trait-impl-namespace-invalid.rs new file mode 100644 index 00000000000..b344b011ee8 --- /dev/null +++ b/crates/lang/tests/ui/contract/fail/trait-impl-namespace-invalid.rs @@ -0,0 +1,30 @@ +use ink_lang as ink; + +#[ink::trait_definition] +pub trait TraitDefinition { + #[ink(message)] + fn message(&self); +} + +#[ink::contract] +mod contract { + use super::TraitDefinition; + + #[ink(storage)] + pub struct Contract {} + + impl Contract { + #[ink(constructor)] + pub fn constructor() -> Self { + Self {} + } + } + + #[ink(namespace = "namespace")] + impl TraitDefinition for Contract { + #[ink(message)] + fn message(&self) {} + } +} + +fn main() {} diff --git a/crates/lang/tests/ui/contract/fail/trait-impl-namespace-invalid.stderr b/crates/lang/tests/ui/contract/fail/trait-impl-namespace-invalid.stderr new file mode 100644 index 00000000000..bf03fd4337f --- /dev/null +++ b/crates/lang/tests/ui/contract/fail/trait-impl-namespace-invalid.stderr @@ -0,0 +1,9 @@ +error: namespace ink! property is not allowed on ink! trait implementation blocks + --> tests/ui/contract/fail/trait-impl-namespace-invalid.rs:23:5 + | +23 | / #[ink(namespace = "namespace")] +24 | | impl TraitDefinition for Contract { +25 | | #[ink(message)] +26 | | fn message(&self) {} +27 | | } + | |_____^ diff --git a/crates/lang/tests/ui/contract/fail/trait-message-payable-mismatch.rs b/crates/lang/tests/ui/contract/fail/trait-message-payable-mismatch.rs new file mode 100644 index 00000000000..163d3c194bb --- /dev/null +++ b/crates/lang/tests/ui/contract/fail/trait-message-payable-mismatch.rs @@ -0,0 +1,29 @@ +use ink_lang as ink; + +#[ink::trait_definition] +pub trait TraitDefinition { + #[ink(message)] + fn message(&self); +} + +#[ink::contract] +mod contract { + use super::TraitDefinition; + + #[ink(storage)] + pub struct Contract {} + + impl Contract { + #[ink(constructor)] + pub fn constructor() -> Self { + Self {} + } + } + + impl TraitDefinition for Contract { + #[ink(message, payable)] + fn message(&self) {} + } +} + +fn main() {} diff --git a/crates/lang/tests/ui/contract/fail/trait-message-payable-mismatch.stderr b/crates/lang/tests/ui/contract/fail/trait-message-payable-mismatch.stderr new file mode 100644 index 00000000000..4c6a717217f --- /dev/null +++ b/crates/lang/tests/ui/contract/fail/trait-message-payable-mismatch.stderr @@ -0,0 +1,8 @@ +error[E0308]: mismatched types + --> tests/ui/contract/fail/trait-message-payable-mismatch.rs:25:9 + | +25 | fn message(&self) {} + | ^^^^^^^^^^^^^^^^^^^^ expected `false`, found `true` + | + = note: expected struct `TraitMessagePayable` + found struct `TraitMessagePayable` diff --git a/crates/lang/tests/ui/contract/fail/trait-message-selector-mismatch.rs b/crates/lang/tests/ui/contract/fail/trait-message-selector-mismatch.rs new file mode 100644 index 00000000000..c342ec457b8 --- /dev/null +++ b/crates/lang/tests/ui/contract/fail/trait-message-selector-mismatch.rs @@ -0,0 +1,29 @@ +use ink_lang as ink; + +#[ink::trait_definition] +pub trait TraitDefinition { + #[ink(message, selector = 1)] + fn message(&self); +} + +#[ink::contract] +mod contract { + use super::TraitDefinition; + + #[ink(storage)] + pub struct Contract {} + + impl Contract { + #[ink(constructor)] + pub fn constructor() -> Self { + Self {} + } + } + + impl TraitDefinition for Contract { + #[ink(message, selector = 2)] + fn message(&self) {} + } +} + +fn main() {} diff --git a/crates/lang/tests/ui/contract/fail/trait-message-selector-mismatch.stderr b/crates/lang/tests/ui/contract/fail/trait-message-selector-mismatch.stderr new file mode 100644 index 00000000000..5e93d3f7594 --- /dev/null +++ b/crates/lang/tests/ui/contract/fail/trait-message-selector-mismatch.stderr @@ -0,0 +1,8 @@ +error[E0308]: mismatched types + --> tests/ui/contract/fail/trait-message-selector-mismatch.rs:25:9 + | +25 | fn message(&self) {} + | ^^^^^^^^^^^^^^^^^^^^ expected `1_u32`, found `2_u32` + | + = note: expected struct `TraitMessageSelector<1_u32>` + found struct `TraitMessageSelector<2_u32>` diff --git a/crates/lang/tests/ui/contract/fail/trait-message-selector-overlap-1.rs b/crates/lang/tests/ui/contract/fail/trait-message-selector-overlap-1.rs new file mode 100644 index 00000000000..b23b5973252 --- /dev/null +++ b/crates/lang/tests/ui/contract/fail/trait-message-selector-overlap-1.rs @@ -0,0 +1,51 @@ +mod foo1 { + use ink_lang as ink; + + #[ink::trait_definition] + pub trait TraitDefinition { + #[ink(message)] + fn message(&self); + } +} + +mod foo2 { + use ink_lang as ink; + + #[ink::trait_definition] + pub trait TraitDefinition { + #[ink(message)] + fn message(&self); + } +} + +use ink_lang as ink; + +#[ink::contract] +pub mod contract { + use super::{ + foo1::TraitDefinition as TraitDefinition1, + foo2::TraitDefinition as TraitDefinition2, + }; + + #[ink(storage)] + pub struct Contract {} + + impl Contract { + #[ink(constructor)] + pub fn constructor() -> Self { + Self {} + } + } + + impl TraitDefinition1 for Contract { + #[ink(message)] + fn message(&self) {} + } + + impl TraitDefinition2 for Contract { + #[ink(message)] + fn message(&self) {} + } +} + +fn main() {} diff --git a/crates/lang/tests/ui/contract/fail/trait-message-selector-overlap-1.stderr b/crates/lang/tests/ui/contract/fail/trait-message-selector-overlap-1.stderr new file mode 100644 index 00000000000..8c550382817 --- /dev/null +++ b/crates/lang/tests/ui/contract/fail/trait-message-selector-overlap-1.stderr @@ -0,0 +1,8 @@ +error[E0119]: conflicting implementations of trait `ink_lang::reflect::DispatchableMessageInfo<1083895717_u32>` for type `contract::Contract` + --> tests/ui/contract/fail/trait-message-selector-overlap-1.rs:47:9 + | +42 | fn message(&self) {} + | ----------------- first implementation here +... +47 | fn message(&self) {} + | ^^^^^^^^^^^^^^^^^ conflicting implementation for `contract::Contract` diff --git a/crates/lang/tests/ui/contract/fail/trait-message-selector-overlap-2.rs b/crates/lang/tests/ui/contract/fail/trait-message-selector-overlap-2.rs new file mode 100644 index 00000000000..4c4f5bc1013 --- /dev/null +++ b/crates/lang/tests/ui/contract/fail/trait-message-selector-overlap-2.rs @@ -0,0 +1,51 @@ +mod foo1 { + use ink_lang as ink; + + #[ink::trait_definition(namespace = "same")] + pub trait TraitDefinition { + #[ink(message)] + fn message(&self); + } +} + +mod foo2 { + use ink_lang as ink; + + #[ink::trait_definition(namespace = "same")] + pub trait TraitDefinition { + #[ink(message)] + fn message(&self); + } +} + +use ink_lang as ink; + +#[ink::contract] +pub mod contract { + use super::{ + foo1::TraitDefinition as TraitDefinition1, + foo2::TraitDefinition as TraitDefinition2, + }; + + #[ink(storage)] + pub struct Contract {} + + impl Contract { + #[ink(constructor)] + pub fn constructor() -> Self { + Self {} + } + } + + impl TraitDefinition1 for Contract { + #[ink(message)] + fn message(&self) {} + } + + impl TraitDefinition2 for Contract { + #[ink(message)] + fn message(&self) {} + } +} + +fn main() {} diff --git a/crates/lang/tests/ui/contract/fail/trait-message-selector-overlap-2.stderr b/crates/lang/tests/ui/contract/fail/trait-message-selector-overlap-2.stderr new file mode 100644 index 00000000000..ab83edd8e3d --- /dev/null +++ b/crates/lang/tests/ui/contract/fail/trait-message-selector-overlap-2.stderr @@ -0,0 +1,8 @@ +error[E0119]: conflicting implementations of trait `ink_lang::reflect::DispatchableMessageInfo<1518209067_u32>` for type `contract::Contract` + --> tests/ui/contract/fail/trait-message-selector-overlap-2.rs:47:9 + | +42 | fn message(&self) {} + | ----------------- first implementation here +... +47 | fn message(&self) {} + | ^^^^^^^^^^^^^^^^^ conflicting implementation for `contract::Contract` diff --git a/crates/lang/tests/ui/contract/fail/trait-message-selector-overlap-3.rs b/crates/lang/tests/ui/contract/fail/trait-message-selector-overlap-3.rs new file mode 100644 index 00000000000..0594163daf6 --- /dev/null +++ b/crates/lang/tests/ui/contract/fail/trait-message-selector-overlap-3.rs @@ -0,0 +1,51 @@ +mod foo1 { + use ink_lang as ink; + + #[ink::trait_definition] + pub trait TraitDefinition1 { + #[ink(message, selector = 42)] + fn message1(&self); + } +} + +mod foo2 { + use ink_lang as ink; + + #[ink::trait_definition] + pub trait TraitDefinition2 { + #[ink(message, selector = 42)] + fn message2(&self); + } +} + +use ink_lang as ink; + +#[ink::contract] +pub mod contract { + use super::{ + foo1::TraitDefinition1, + foo2::TraitDefinition2, + }; + + #[ink(storage)] + pub struct Contract {} + + impl Contract { + #[ink(constructor)] + pub fn constructor() -> Self { + Self {} + } + } + + impl TraitDefinition1 for Contract { + #[ink(message)] + fn message1(&self) {} + } + + impl TraitDefinition2 for Contract { + #[ink(message)] + fn message2(&self) {} + } +} + +fn main() {} diff --git a/crates/lang/tests/ui/contract/fail/trait-message-selector-overlap-3.stderr b/crates/lang/tests/ui/contract/fail/trait-message-selector-overlap-3.stderr new file mode 100644 index 00000000000..3e99b03a02b --- /dev/null +++ b/crates/lang/tests/ui/contract/fail/trait-message-selector-overlap-3.stderr @@ -0,0 +1,8 @@ +error[E0119]: conflicting implementations of trait `ink_lang::reflect::DispatchableMessageInfo<42_u32>` for type `contract::Contract` + --> tests/ui/contract/fail/trait-message-selector-overlap-3.rs:47:9 + | +42 | fn message1(&self) {} + | ------------------ first implementation here +... +47 | fn message2(&self) {} + | ^^^^^^^^^^^^^^^^^^ conflicting implementation for `contract::Contract` diff --git a/crates/lang/tests/ui/contract/pass/config-compile-as-dependency-false.rs b/crates/lang/tests/ui/contract/pass/config-compile-as-dependency-false.rs new file mode 100644 index 00000000000..3f6997962ce --- /dev/null +++ b/crates/lang/tests/ui/contract/pass/config-compile-as-dependency-false.rs @@ -0,0 +1,19 @@ +use ink_lang as ink; + +#[ink::contract(compile_as_dependency = false)] +mod contract { + #[ink(storage)] + pub struct Contract {} + + impl Contract { + #[ink(constructor)] + pub fn constructor() -> Self { + Self {} + } + + #[ink(message)] + pub fn message(&self) {} + } +} + +fn main() {} diff --git a/crates/lang/tests/ui/contract/pass/config-compile-as-dependency-true.rs b/crates/lang/tests/ui/contract/pass/config-compile-as-dependency-true.rs new file mode 100644 index 00000000000..003163166a0 --- /dev/null +++ b/crates/lang/tests/ui/contract/pass/config-compile-as-dependency-true.rs @@ -0,0 +1,19 @@ +use ink_lang as ink; + +#[ink::contract(compile_as_dependency = true)] +mod contract { + #[ink(storage)] + pub struct Contract {} + + impl Contract { + #[ink(constructor)] + pub fn constructor() -> Self { + Self {} + } + + #[ink(message)] + pub fn message(&self) {} + } +} + +fn main() {} diff --git a/crates/lang/tests/ui/contract/pass/config-custom-env.rs b/crates/lang/tests/ui/contract/pass/config-custom-env.rs new file mode 100644 index 00000000000..613205f0075 --- /dev/null +++ b/crates/lang/tests/ui/contract/pass/config-custom-env.rs @@ -0,0 +1,34 @@ +use ink_lang as ink; + +pub struct CustomEnv; + +impl ink_env::Environment for CustomEnv { + const MAX_EVENT_TOPICS: usize = 3; + type AccountId = [u8; 32]; + type Balance = u64; + type Hash = [u8; 32]; + type Timestamp = u64; + type BlockNumber = u64; + type ChainExtension = (); + // Too lazy to define a test type that fits the required trait bounds. + type RentFraction = + ::RentFraction; +} + +#[ink::contract(env = super::CustomEnv)] +mod contract { + #[ink(storage)] + pub struct Contract {} + + impl Contract { + #[ink(constructor)] + pub fn constructor() -> Self { + Self {} + } + + #[ink(message)] + pub fn message(&self) {} + } +} + +fn main() {} diff --git a/crates/lang/tests/ui/contract/pass/config-dynamic-storage-allocator-false.rs b/crates/lang/tests/ui/contract/pass/config-dynamic-storage-allocator-false.rs new file mode 100644 index 00000000000..953d0afb4fe --- /dev/null +++ b/crates/lang/tests/ui/contract/pass/config-dynamic-storage-allocator-false.rs @@ -0,0 +1,19 @@ +use ink_lang as ink; + +#[ink::contract(dynamic_storage_allocator = true)] +mod contract { + #[ink(storage)] + pub struct Contract {} + + impl Contract { + #[ink(constructor)] + pub fn constructor() -> Self { + Self {} + } + + #[ink(message)] + pub fn message(&self) {} + } +} + +fn main() {} diff --git a/crates/lang/tests/ui/contract/pass/config-dynamic-storage-allocator-true.rs b/crates/lang/tests/ui/contract/pass/config-dynamic-storage-allocator-true.rs new file mode 100644 index 00000000000..953d0afb4fe --- /dev/null +++ b/crates/lang/tests/ui/contract/pass/config-dynamic-storage-allocator-true.rs @@ -0,0 +1,19 @@ +use ink_lang as ink; + +#[ink::contract(dynamic_storage_allocator = true)] +mod contract { + #[ink(storage)] + pub struct Contract {} + + impl Contract { + #[ink(constructor)] + pub fn constructor() -> Self { + Self {} + } + + #[ink(message)] + pub fn message(&self) {} + } +} + +fn main() {} diff --git a/crates/lang/tests/ui/contract/pass/constructor-many-inputs.rs b/crates/lang/tests/ui/contract/pass/constructor-many-inputs.rs new file mode 100644 index 00000000000..e1cc2c7af96 --- /dev/null +++ b/crates/lang/tests/ui/contract/pass/constructor-many-inputs.rs @@ -0,0 +1,125 @@ +use ink_lang as ink; + +#[ink::contract] +mod contract { + #[ink(storage)] + pub struct Contract {} + + impl Contract { + #[ink(constructor)] + pub fn constructor_0() -> Self { + Self {} + } + + #[ink(constructor)] + pub fn constructor_1(_input_1: i8) -> Self { + Self {} + } + + #[ink(constructor)] + pub fn constructor_2(_input_1: i8, _input_2: i16) -> Self { + Self {} + } + + #[ink(constructor)] + pub fn constructor_3(_input_1: i8, _input_2: i16, _input_3: i32) -> Self { + Self {} + } + + #[ink(constructor)] + pub fn constructor_4( + _input_1: i8, + _input_2: i16, + _input_3: i32, + _input_4: i64, + ) -> Self { + Self {} + } + + #[ink(constructor)] + pub fn constructor_5( + _input_1: i8, + _input_2: i16, + _input_3: i32, + _input_4: i64, + _input_5: i128, + ) -> Self { + Self {} + } + + #[ink(constructor)] + pub fn constructor_6( + _input_1: i8, + _input_2: i16, + _input_3: i32, + _input_4: i64, + _input_5: i128, + _input_6: u8, + ) -> Self { + Self {} + } + + #[ink(constructor)] + pub fn constructor_7( + _input_1: i8, + _input_2: i16, + _input_3: i32, + _input_4: i64, + _input_5: i128, + _input_6: u8, + _input_7: u16, + ) -> Self { + Self {} + } + + #[ink(constructor)] + pub fn constructor_8( + _input_1: i8, + _input_2: i16, + _input_3: i32, + _input_4: i64, + _input_5: i128, + _input_6: u8, + _input_7: u16, + _input_8: u32, + ) -> Self { + Self {} + } + + #[ink(constructor)] + pub fn constructor_9( + _input_1: i8, + _input_2: i16, + _input_3: i32, + _input_4: i64, + _input_5: i128, + _input_6: u8, + _input_7: u16, + _input_8: u32, + _input_9: u64, + ) -> Self { + Self {} + } + + #[ink(constructor)] + pub fn constructor_10( + _input_1: i8, + _input_2: i16, + _input_3: i32, + _input_4: i64, + _input_5: i128, + _input_6: u8, + _input_7: u16, + _input_8: u32, + _input_9: u64, + _input_10: u128, + ) -> Self { + Self {} + } + + #[ink(message)] + pub fn message(&self) {} + } +} + +fn main() {} diff --git a/crates/lang/tests/ui/contract/pass/constructor-selector.rs b/crates/lang/tests/ui/contract/pass/constructor-selector.rs new file mode 100644 index 00000000000..2fdadebaadc --- /dev/null +++ b/crates/lang/tests/ui/contract/pass/constructor-selector.rs @@ -0,0 +1,68 @@ +use contract::Contract; +use ink_lang as ink; +use ink_lang::selector_bytes; + +#[ink::contract] +mod contract { + #[ink(storage)] + pub struct Contract {} + + impl Contract { + #[ink(constructor)] + pub fn constructor_0() -> Self { + Self {} + } + + #[ink(constructor, selector = 1)] + pub fn constructor_1() -> Self { + Self {} + } + + #[ink(constructor, selector = 0xC0DE_CAFE)] + pub fn constructor_2() -> Self { + Self {} + } + + #[ink(message)] + pub fn message(&self) {} + } +} + +fn main() { + assert_eq!( + ::CONSTRUCTORS + }, + >>::IDS[0] + }, + >>::SELECTOR, + selector_bytes!("constructor_0") + ); + assert_eq!( + ::CONSTRUCTORS + }, + >>::IDS[1] + }, + >>::SELECTOR, + 1_u32.to_be_bytes(), + ); + assert_eq!( + ::CONSTRUCTORS + }, + >>::IDS[2] + }, + >>::SELECTOR, + 0xC0DE_CAFE_u32.to_be_bytes(), + ); +} diff --git a/crates/lang/tests/ui/contract/pass/dispatch-decoder-works.rs b/crates/lang/tests/ui/contract/pass/dispatch-decoder-works.rs new file mode 100644 index 00000000000..db374f94c16 --- /dev/null +++ b/crates/lang/tests/ui/contract/pass/dispatch-decoder-works.rs @@ -0,0 +1,132 @@ +use ink_lang as ink; +use ink_lang::{ + reflect::{ + ContractConstructorDecoder, + ContractMessageDecoder, + DecodeDispatch, + DispatchError, + }, + selector_bytes, +}; +use scale::Encode; + +#[ink::contract] +pub mod contract { + #[ink(storage)] + pub struct Contract {} + + impl Contract { + #[ink(constructor)] + pub fn constructor(_input_1: bool, _input_2: i32) -> Self { + Self {} + } + + #[ink(message)] + pub fn message(&self, _input_1: bool, _input_2: i32) {} + } +} + +use contract::Contract; + +fn main() { + constructor_decoder_works(); + message_decoder_works(); +} + +fn constructor_decoder_works() { + // Valid call to `constructor`: + { + let mut input_bytes = Vec::new(); + input_bytes.extend(selector_bytes!("constructor")); + input_bytes.extend(true.encode()); + input_bytes.extend(42i32.encode()); + assert!( + <::Type as DecodeDispatch>::decode_dispatch( + &mut &input_bytes[..]).is_ok() + ); + } + // Invalid call with invalid selector (or empty input). + { + let input_bytes = Vec::new(); + assert_eq!( + <::Type + as DecodeDispatch>::decode_dispatch(&mut &input_bytes[..]) + .map(|_| ()) + .unwrap_err(), + DispatchError::InvalidSelector, + ); + } + // Invalid call to `message` with unknown selector. + { + let mut input_bytes = Vec::new(); + input_bytes.extend(selector_bytes!("unknown_selector")); + assert_eq!( + <::Type + as DecodeDispatch>::decode_dispatch(&mut &input_bytes[..]) + .map(|_| ()) + .unwrap_err(), + DispatchError::UnknownSelector, + ); + } + // Invalid call to `message` with invalid (or missing) parameters. + { + let mut input_bytes = Vec::new(); + input_bytes.extend(selector_bytes!("constructor")); + assert_eq!( + <::Type + as DecodeDispatch>::decode_dispatch(&mut &input_bytes[..]) + .map(|_| ()) + .unwrap_err(), + DispatchError::InvalidParameters, + ); + } +} + +fn message_decoder_works() { + // Valid call to `message`: + { + let mut input_bytes = Vec::new(); + input_bytes.extend(selector_bytes!("message")); + input_bytes.extend(true.encode()); + input_bytes.extend(42i32.encode()); + assert!( + <::Type as DecodeDispatch>::decode_dispatch( + &mut &input_bytes[..]).is_ok() + ); + } + // Invalid call with invalid selector (or empty input). + { + let input_bytes = Vec::new(); + assert_eq!( + <::Type + as DecodeDispatch>::decode_dispatch(&mut &input_bytes[..]) + .map(|_| ()) + .unwrap_err(), + DispatchError::InvalidSelector, + ); + } + // Invalid call to `message` with unknown selector. + { + let mut input_bytes = Vec::new(); + input_bytes.extend(selector_bytes!("unknown_selector")); + assert_eq!( + <::Type + as DecodeDispatch>::decode_dispatch(&mut &input_bytes[..]) + .map(|_| ()) + .unwrap_err(), + DispatchError::UnknownSelector, + ); + } + // Invalid call to `message` with invalid (or missing) parameters. + { + let mut input_bytes = Vec::new(); + input_bytes.extend(selector_bytes!("message")); + assert_eq!( + <::Type + as DecodeDispatch>::decode_dispatch(&mut &input_bytes[..]) + .map(|_| ()) + .unwrap_err(), + DispatchError::InvalidParameters, + ); + } +} diff --git a/crates/lang/tests/ui/contract/pass/env-access.rs b/crates/lang/tests/ui/contract/pass/env-access.rs new file mode 100644 index 00000000000..46437085c51 --- /dev/null +++ b/crates/lang/tests/ui/contract/pass/env-access.rs @@ -0,0 +1,40 @@ +use ink_lang as ink; + +#[ink::contract] +mod contract { + #[ink(storage)] + pub struct Contract {} + + impl Contract { + #[ink(constructor)] + pub fn new() -> Self { + let _ = Self::env().account_id(); + let _ = Self::env().balance(); + let _ = Self::env().block_timestamp(); + let _ = Self::env().block_number(); + let _ = Self::env().caller(); + let _ = Self::env().gas_left(); + let _ = Self::env().minimum_balance(); + let _ = Self::env().random(&[]); + let _ = Self::env().transferred_balance(); + let _ = Self::env().weight_to_fee(0); + Self {} + } + + #[ink(message)] + pub fn message(&self) { + let _ = self.env().account_id(); + let _ = self.env().balance(); + let _ = self.env().block_timestamp(); + let _ = self.env().block_number(); + let _ = self.env().caller(); + let _ = self.env().gas_left(); + let _ = self.env().minimum_balance(); + let _ = self.env().random(&[]); + let _ = self.env().transferred_balance(); + let _ = self.env().weight_to_fee(0); + } + } +} + +fn main() {} diff --git a/crates/lang/tests/ui/contract/pass/event-anonymous.rs b/crates/lang/tests/ui/contract/pass/event-anonymous.rs new file mode 100644 index 00000000000..7489ee198b0 --- /dev/null +++ b/crates/lang/tests/ui/contract/pass/event-anonymous.rs @@ -0,0 +1,115 @@ +use ink_lang as ink; + +#[ink::contract] +mod contract { + #[ink(storage)] + pub struct Contract {} + + #[ink(event, anonymous)] + pub struct Event0 {} + + #[ink(event, anonymous)] + pub struct Event1 { + #[ink(topic)] + arg_1: i8, + } + + #[ink(event, anonymous)] + pub struct Event2 { + #[ink(topic)] + arg_1: i8, + #[ink(topic)] + arg_2: i16, + } + + #[ink(event, anonymous)] + pub struct Event3 { + #[ink(topic)] + arg_1: i8, + #[ink(topic)] + arg_2: i16, + #[ink(topic)] + arg_3: i32, + } + + #[ink(event, anonymous)] + pub struct Event4 { + #[ink(topic)] + arg_1: i8, + #[ink(topic)] + arg_2: i16, + #[ink(topic)] + arg_3: i32, + #[ink(topic)] + arg_4: i64, + } + + #[ink(event, anonymous)] + pub struct Event5 { + #[ink(topic)] + arg_1: i8, + #[ink(topic)] + arg_2: i16, + #[ink(topic)] + arg_3: i32, + #[ink(topic)] + arg_4: i64, + // #[ink(topic)] <- Cannot have more than 4 topics by default. + arg_5: i128, + } + + impl Contract { + #[ink(constructor)] + pub fn constructor() -> Self { + Self::env().emit_event(Event0 {}); + Self::env().emit_event(Event1 { arg_1: 1 }); + Self::env().emit_event(Event2 { arg_1: 1, arg_2: 2 }); + Self::env().emit_event(Event3 { + arg_1: 1, + arg_2: 2, + arg_3: 3, + }); + Self::env().emit_event(Event4 { + arg_1: 1, + arg_2: 2, + arg_3: 3, + arg_4: 4, + }); + Self::env().emit_event(Event5 { + arg_1: 1, + arg_2: 2, + arg_3: 3, + arg_4: 4, + arg_5: 5, + }); + Self {} + } + + #[ink(message)] + pub fn message(&self) { + self.env().emit_event(Event0 {}); + self.env().emit_event(Event1 { arg_1: 1 }); + self.env().emit_event(Event2 { arg_1: 1, arg_2: 2 }); + self.env().emit_event(Event3 { + arg_1: 1, + arg_2: 2, + arg_3: 3, + }); + self.env().emit_event(Event4 { + arg_1: 1, + arg_2: 2, + arg_3: 3, + arg_4: 4, + }); + self.env().emit_event(Event5 { + arg_1: 1, + arg_2: 2, + arg_3: 3, + arg_4: 4, + arg_5: 5, + }); + } + } +} + +fn main() {} diff --git a/crates/lang/tests/ui/contract/pass/event-config-more-topics.rs b/crates/lang/tests/ui/contract/pass/event-config-more-topics.rs new file mode 100644 index 00000000000..e7b73525a89 --- /dev/null +++ b/crates/lang/tests/ui/contract/pass/event-config-more-topics.rs @@ -0,0 +1,86 @@ +use ink_env::{ + DefaultEnvironment, + Environment, +}; +use ink_lang as ink; + +pub struct EnvironmentMoreTopics; + +impl ink_env::Environment for EnvironmentMoreTopics { + const MAX_EVENT_TOPICS: usize = 10; // Default is 4. + + type AccountId = ::AccountId; + type Balance = ::Balance; + type Hash = ::Hash; + type Timestamp = ::Timestamp; + type BlockNumber = ::BlockNumber; + type ChainExtension = (); + type RentFraction = ::RentFraction; +} + +#[ink::contract(env = super::EnvironmentMoreTopics)] +mod contract { + #[ink(storage)] + pub struct Contract {} + + #[ink(event, anonymous)] + pub struct EventWithManyTopics { + #[ink(topic)] + arg_1: i8, + #[ink(topic)] + arg_2: i16, + #[ink(topic)] + arg_3: i32, + #[ink(topic)] + arg_4: i64, + #[ink(topic)] + arg_5: i128, + #[ink(topic)] + arg_6: u8, + #[ink(topic)] + arg_7: u16, + #[ink(topic)] + arg_8: u32, + #[ink(topic)] + arg_9: u64, + #[ink(topic)] + arg_10: u128, + } + + impl Contract { + #[ink(constructor)] + pub fn constructor() -> Self { + Self::env().emit_event(EventWithManyTopics { + arg_1: 1, + arg_2: 2, + arg_3: 3, + arg_4: 4, + arg_5: 5, + arg_6: 6, + arg_7: 7, + arg_8: 8, + arg_9: 9, + arg_10: 10, + }); + Self {} + } + + #[ink(message)] + pub fn message(&self) { + self.env().emit_event(EventWithManyTopics { + arg_1: 1, + arg_2: 2, + arg_3: 3, + arg_4: 4, + arg_5: 5, + arg_6: 6, + arg_7: 7, + arg_8: 8, + arg_9: 9, + arg_10: 10, + }); + } + } +} + +fn main() {} diff --git a/crates/lang/tests/ui/contract/pass/event-many-definitions.rs b/crates/lang/tests/ui/contract/pass/event-many-definitions.rs new file mode 100644 index 00000000000..06e0600cf29 --- /dev/null +++ b/crates/lang/tests/ui/contract/pass/event-many-definitions.rs @@ -0,0 +1,100 @@ +use ink_lang as ink; + +#[ink::contract] +mod contract { + #[ink(storage)] + pub struct Contract {} + + #[ink(event)] + pub struct Event0 {} + + #[ink(event)] + pub struct Event1 { + arg_1: i8, + } + + #[ink(event)] + pub struct Event2 { + arg_1: i8, + arg_2: i16, + } + + #[ink(event)] + pub struct Event3 { + arg_1: i8, + arg_2: i16, + arg_3: i32, + } + + #[ink(event)] + pub struct Event4 { + arg_1: i8, + arg_2: i16, + arg_3: i32, + arg_4: i64, + } + + #[ink(event)] + pub struct Event5 { + arg_1: i8, + arg_2: i16, + arg_3: i32, + arg_4: i64, + arg_5: i128, + } + + impl Contract { + #[ink(constructor)] + pub fn constructor() -> Self { + Self::env().emit_event(Event0 {}); + Self::env().emit_event(Event1 { arg_1: 1 }); + Self::env().emit_event(Event2 { arg_1: 1, arg_2: 2 }); + Self::env().emit_event(Event3 { + arg_1: 1, + arg_2: 2, + arg_3: 3, + }); + Self::env().emit_event(Event4 { + arg_1: 1, + arg_2: 2, + arg_3: 3, + arg_4: 4, + }); + Self::env().emit_event(Event5 { + arg_1: 1, + arg_2: 2, + arg_3: 3, + arg_4: 4, + arg_5: 5, + }); + Self {} + } + + #[ink(message)] + pub fn message(&self) { + self.env().emit_event(Event0 {}); + self.env().emit_event(Event1 { arg_1: 1 }); + self.env().emit_event(Event2 { arg_1: 1, arg_2: 2 }); + self.env().emit_event(Event3 { + arg_1: 1, + arg_2: 2, + arg_3: 3, + }); + self.env().emit_event(Event4 { + arg_1: 1, + arg_2: 2, + arg_3: 3, + arg_4: 4, + }); + self.env().emit_event(Event5 { + arg_1: 1, + arg_2: 2, + arg_3: 3, + arg_4: 4, + arg_5: 5, + }); + } + } +} + +fn main() {} diff --git a/crates/lang/tests/ui/contract/pass/event-single-definition.rs b/crates/lang/tests/ui/contract/pass/event-single-definition.rs new file mode 100644 index 00000000000..ae2896bb0d2 --- /dev/null +++ b/crates/lang/tests/ui/contract/pass/event-single-definition.rs @@ -0,0 +1,22 @@ +use ink_lang as ink; + +#[ink::contract] +mod contract { + #[ink(storage)] + pub struct Contract {} + + #[ink(event)] + pub struct Event0 {} + + impl Contract { + #[ink(constructor)] + pub fn constructor() -> Self { + Self {} + } + + #[ink(message)] + pub fn message(&self) {} + } +} + +fn main() {} diff --git a/crates/lang/tests/ui/contract/pass/event-topics.rs b/crates/lang/tests/ui/contract/pass/event-topics.rs new file mode 100644 index 00000000000..500a8323174 --- /dev/null +++ b/crates/lang/tests/ui/contract/pass/event-topics.rs @@ -0,0 +1,115 @@ +use ink_lang as ink; + +#[ink::contract] +mod contract { + #[ink(storage)] + pub struct Contract {} + + #[ink(event)] + pub struct Event0 {} + + #[ink(event)] + pub struct Event1 { + #[ink(topic)] + arg_1: i8, + } + + #[ink(event)] + pub struct Event2 { + #[ink(topic)] + arg_1: i8, + #[ink(topic)] + arg_2: i16, + } + + #[ink(event)] + pub struct Event3 { + #[ink(topic)] + arg_1: i8, + #[ink(topic)] + arg_2: i16, + #[ink(topic)] + arg_3: i32, + } + + #[ink(event)] + pub struct Event4 { + #[ink(topic)] + arg_1: i8, + #[ink(topic)] + arg_2: i16, + #[ink(topic)] + arg_3: i32, + #[ink(topic)] + arg_4: i64, + } + + #[ink(event)] + pub struct Event5 { + #[ink(topic)] + arg_1: i8, + #[ink(topic)] + arg_2: i16, + #[ink(topic)] + arg_3: i32, + #[ink(topic)] + arg_4: i64, + // #[ink(topic)] <- Cannot have more than 4 topics by default. + arg_5: i128, + } + + impl Contract { + #[ink(constructor)] + pub fn constructor() -> Self { + Self::env().emit_event(Event0 {}); + Self::env().emit_event(Event1 { arg_1: 1 }); + Self::env().emit_event(Event2 { arg_1: 1, arg_2: 2 }); + Self::env().emit_event(Event3 { + arg_1: 1, + arg_2: 2, + arg_3: 3, + }); + Self::env().emit_event(Event4 { + arg_1: 1, + arg_2: 2, + arg_3: 3, + arg_4: 4, + }); + Self::env().emit_event(Event5 { + arg_1: 1, + arg_2: 2, + arg_3: 3, + arg_4: 4, + arg_5: 5, + }); + Self {} + } + + #[ink(message)] + pub fn message(&self) { + self.env().emit_event(Event0 {}); + self.env().emit_event(Event1 { arg_1: 1 }); + self.env().emit_event(Event2 { arg_1: 1, arg_2: 2 }); + self.env().emit_event(Event3 { + arg_1: 1, + arg_2: 2, + arg_3: 3, + }); + self.env().emit_event(Event4 { + arg_1: 1, + arg_2: 2, + arg_3: 3, + arg_4: 4, + }); + self.env().emit_event(Event5 { + arg_1: 1, + arg_2: 2, + arg_3: 3, + arg_4: 4, + arg_5: 5, + }); + } + } +} + +fn main() {} diff --git a/crates/lang/macro/tests/ui/contract/pass/04-erc20-contract.rs b/crates/lang/tests/ui/contract/pass/example-erc20-works.rs similarity index 94% rename from crates/lang/macro/tests/ui/contract/pass/04-erc20-contract.rs rename to crates/lang/tests/ui/contract/pass/example-erc20-works.rs index 153bb79af07..4ae6274d649 100644 --- a/crates/lang/macro/tests/ui/contract/pass/04-erc20-contract.rs +++ b/crates/lang/tests/ui/contract/pass/example-erc20-works.rs @@ -119,7 +119,7 @@ mod erc20 { } fn balance_of_or_zero(&self, owner: &AccountId) -> Balance { - *self.balances.get(owner).unwrap_or(&0) + self.balances.get(owner).copied().unwrap_or_default() } fn allowance_of_or_zero( @@ -127,7 +127,10 @@ mod erc20 { owner: &AccountId, spender: &AccountId, ) -> Balance { - *self.allowances.get(&(*owner, *spender)).unwrap_or(&0) + self.allowances + .get(&(*owner, *spender)) + .copied() + .unwrap_or_default() } } } diff --git a/crates/lang/macro/tests/ui/contract/pass/05-erc721-contract.rs b/crates/lang/tests/ui/contract/pass/example-erc721-works.rs similarity index 100% rename from crates/lang/macro/tests/ui/contract/pass/05-erc721-contract.rs rename to crates/lang/tests/ui/contract/pass/example-erc721-works.rs diff --git a/crates/lang/macro/tests/ui/contract/pass/07-flipper-as-dependency.rs b/crates/lang/tests/ui/contract/pass/example-flipper-works.rs similarity index 72% rename from crates/lang/macro/tests/ui/contract/pass/07-flipper-as-dependency.rs rename to crates/lang/tests/ui/contract/pass/example-flipper-works.rs index ace96903dfe..0aef590fb60 100644 --- a/crates/lang/macro/tests/ui/contract/pass/07-flipper-as-dependency.rs +++ b/crates/lang/tests/ui/contract/pass/example-flipper-works.rs @@ -1,6 +1,7 @@ +use flipper::Flipper; use ink_lang as ink; -#[ink::contract(compile_as_dependency = true)] +#[ink::contract] mod flipper { #[ink(storage)] pub struct Flipper { @@ -13,11 +14,6 @@ mod flipper { Self { value: init_value } } - #[ink(constructor)] - pub fn default() -> Self { - Self::new(false) - } - #[ink(message)] pub fn flip(&mut self) { self.value = !self.value; @@ -30,4 +26,9 @@ mod flipper { } } -fn main() {} +fn main() { + let mut flipper = Flipper::new(false); + assert!(!flipper.get()); + flipper.flip(); + assert!(flipper.get()); +} diff --git a/crates/lang/tests/ui/contract/pass/example-incrementer-works.rs b/crates/lang/tests/ui/contract/pass/example-incrementer-works.rs new file mode 100644 index 00000000000..959f0dfc02a --- /dev/null +++ b/crates/lang/tests/ui/contract/pass/example-incrementer-works.rs @@ -0,0 +1,36 @@ +use incrementer::Incrementer; +use ink_lang as ink; + +#[ink::contract] +mod incrementer { + #[ink(storage)] + pub struct Incrementer { + value: i64, + } + + impl Incrementer { + #[ink(constructor)] + pub fn new(init_value: i64) -> Self { + Self { value: init_value } + } + + #[ink(message)] + pub fn inc_by(&mut self, delta: i64) { + self.value += delta; + } + + #[ink(message)] + pub fn get(&self) -> i64 { + self.value + } + } +} + +fn main() { + let mut incrementer = Incrementer::new(0); + assert_eq!(incrementer.get(), 0); + incrementer.inc_by(1); + assert_eq!(incrementer.get(), 1); + incrementer.inc_by(-1); + assert_eq!(incrementer.get(), 0); +} diff --git a/crates/lang/tests/ui/contract/pass/example-trait-flipper-works.rs b/crates/lang/tests/ui/contract/pass/example-trait-flipper-works.rs new file mode 100644 index 00000000000..8a8f920ca1e --- /dev/null +++ b/crates/lang/tests/ui/contract/pass/example-trait-flipper-works.rs @@ -0,0 +1,50 @@ +use flipper::Flipper; +use ink_lang as ink; + +#[ink::trait_definition] +pub trait Flip { + #[ink(message)] + fn flip(&mut self); + + #[ink(message)] + fn get(&self) -> bool; +} + +#[ink::contract] +mod flipper { + use super::Flip; + + #[ink(storage)] + pub struct Flipper { + value: bool, + } + + impl Flipper { + #[ink(constructor)] + pub fn new(init_value: bool) -> Self { + Self { value: init_value } + } + } + + impl Flip for Flipper { + #[ink(message)] + fn flip(&mut self) { + self.value = !self.value; + } + + #[ink(message)] + fn get(&self) -> bool { + self.value + } + } +} + +fn main() { + // We use the verbose universal call syntax for trait methods + // in order to make sure that the trait flipper example actually + // implements its messages as Rust traits. + let mut flipper = Flipper::new(false); + assert!(!::get(&flipper)); + ::flip(&mut flipper); + assert!(::get(&flipper)); +} diff --git a/crates/lang/tests/ui/contract/pass/example-trait-incrementer-works.rs b/crates/lang/tests/ui/contract/pass/example-trait-incrementer-works.rs new file mode 100644 index 00000000000..493021ddfe9 --- /dev/null +++ b/crates/lang/tests/ui/contract/pass/example-trait-incrementer-works.rs @@ -0,0 +1,75 @@ +use incrementer::Incrementer; +use ink_lang as ink; + +#[ink::trait_definition] +pub trait Increment { + #[ink(message)] + fn inc(&mut self); + + #[ink(message)] + fn get(&self) -> i64; +} + +#[ink::trait_definition] +pub trait Reset { + #[ink(message)] + fn reset(&mut self); +} + +#[ink::contract] +mod incrementer { + use super::{ + Increment, + Reset, + }; + + #[ink(storage)] + pub struct Incrementer { + value: i64, + } + + impl Incrementer { + #[ink(constructor)] + pub fn new(init_value: i64) -> Self { + Self { value: init_value } + } + + #[ink(message)] + pub fn inc_by(&mut self, delta: i64) { + self.value += delta; + } + } + + impl Increment for Incrementer { + #[ink(message)] + fn inc(&mut self) { + self.inc_by(1) + } + + #[ink(message)] + fn get(&self) -> i64 { + self.value + } + } + + impl Reset for Incrementer { + #[ink(message)] + fn reset(&mut self) { + self.value = 0; + } + } +} + +fn main() { + let mut incrementer = Incrementer::new(0); + assert_eq!(::get(&incrementer), 0); + incrementer.inc_by(1); + assert_eq!(::get(&incrementer), 1); + incrementer.inc_by(-1); + assert_eq!(::get(&incrementer), 0); + + ::inc(&mut incrementer); + assert_eq!(::get(&incrementer), 1); + ::inc(&mut incrementer); + assert_eq!(::get(&incrementer), 2); +} diff --git a/crates/lang/tests/ui/contract/pass/impl-alias-storage.rs b/crates/lang/tests/ui/contract/pass/impl-alias-storage.rs new file mode 100644 index 00000000000..fbbebe55ad1 --- /dev/null +++ b/crates/lang/tests/ui/contract/pass/impl-alias-storage.rs @@ -0,0 +1,21 @@ +use ink_lang as ink; + +#[ink::contract] +mod contract { + #[ink(storage)] + pub struct Contract {} + + type ContractAlias = Contract; + + impl ContractAlias { + #[ink(constructor)] + pub fn constructor() -> Self { + Self {} + } + + #[ink(message)] + pub fn message(&self) {} + } +} + +fn main() {} diff --git a/crates/lang/tests/ui/contract/pass/impl-block-namespace.rs b/crates/lang/tests/ui/contract/pass/impl-block-namespace.rs new file mode 100644 index 00000000000..a8f055a9465 --- /dev/null +++ b/crates/lang/tests/ui/contract/pass/impl-block-namespace.rs @@ -0,0 +1,22 @@ +use ink_lang as ink; + +#[ink::contract] +mod contract { + #[ink(storage)] + pub struct Contract {} + + impl Contract { + #[ink(constructor)] + pub fn constructor() -> Self { + Self {} + } + } + + #[ink(namespace = "namespace")] + impl Contract { + #[ink(message)] + pub fn message(&self) {} + } +} + +fn main() {} diff --git a/crates/lang/tests/ui/contract/pass/impl-block-using-env.rs b/crates/lang/tests/ui/contract/pass/impl-block-using-env.rs new file mode 100644 index 00000000000..6c58107d946 --- /dev/null +++ b/crates/lang/tests/ui/contract/pass/impl-block-using-env.rs @@ -0,0 +1,35 @@ +use ink_lang as ink; + +#[ink::contract] +mod contract { + #[ink(storage)] + pub struct Contract {} + + impl Contract { + #[ink(constructor)] + pub fn constructor() -> Self { + Self::constructor_impl() + } + } + + impl Contract { + #[ink(message)] + pub fn message(&self) { + self.message_impl(); + } + } + + #[ink(impl)] + impl Contract { + fn constructor_impl() -> Self { + let _ = Self::env().caller(); + Self {} + } + + fn message_impl(&self) { + let _ = self.env().caller(); + } + } +} + +fn main() {} diff --git a/crates/lang/tests/ui/contract/pass/impl-with-property.rs b/crates/lang/tests/ui/contract/pass/impl-with-property.rs new file mode 100644 index 00000000000..8f58c32e656 --- /dev/null +++ b/crates/lang/tests/ui/contract/pass/impl-with-property.rs @@ -0,0 +1,23 @@ +use ink_lang as ink; + +#[ink::contract] +mod contract { + #[ink(storage)] + pub struct Contract {} + + #[ink(impl)] + impl Contract {} + + #[ink(impl)] + impl Contract { + #[ink(constructor)] + pub fn constructor() -> Self { + Self {} + } + + #[ink(message)] + pub fn message(&self) {} + } +} + +fn main() {} diff --git a/crates/lang/tests/ui/contract/pass/message-many-inputs.rs b/crates/lang/tests/ui/contract/pass/message-many-inputs.rs new file mode 100644 index 00000000000..556ab08e072 --- /dev/null +++ b/crates/lang/tests/ui/contract/pass/message-many-inputs.rs @@ -0,0 +1,222 @@ +use ink_lang as ink; + +#[ink::contract] +mod contract { + #[ink(storage)] + pub struct Contract {} + + impl Contract { + #[ink(constructor)] + pub fn constructor() -> Self { + Self {} + } + + #[ink(message)] + pub fn message_0_ref(&self) {} + + #[ink(message)] + pub fn message_0_mut(&mut self) {} + + #[ink(message)] + pub fn message_1_ref(&self, _input_1: i8) {} + + #[ink(message)] + pub fn message_1_mut(&mut self, _input_1: i8) {} + + #[ink(message)] + pub fn message_2_ref(&self, _input_1: i8, _input_2: i16) {} + + #[ink(message)] + pub fn message_2_mut(&mut self, _input_1: i8, _input_2: i16) {} + + #[ink(message)] + pub fn message_3_ref(&self, _input_1: i8, _input_2: i16, _input_3: i32) {} + + #[ink(message)] + pub fn message_3_mut(&mut self, _input_1: i8, _input_2: i16, _input_3: i32) {} + + #[ink(message)] + pub fn message_4_ref( + &self, + _input_1: i8, + _input_2: i16, + _input_3: i32, + _input_4: i64, + ) { + } + + #[ink(message)] + pub fn message_4_mut( + &mut self, + _input_1: i8, + _input_2: i16, + _input_3: i32, + _input_4: i64, + ) { + } + + #[ink(message)] + pub fn message_5_ref( + &self, + _input_1: i8, + _input_2: i16, + _input_3: i32, + _input_4: i64, + _input_5: i128, + ) { + } + + #[ink(message)] + pub fn message_5_mut( + &mut self, + _input_1: i8, + _input_2: i16, + _input_3: i32, + _input_4: i64, + _input_5: i128, + ) { + } + + #[ink(message)] + pub fn message_6_ref( + &self, + _input_1: i8, + _input_2: i16, + _input_3: i32, + _input_4: i64, + _input_5: i128, + _input_6: u8, + ) { + } + + #[ink(message)] + pub fn message_6_mut( + &mut self, + _input_1: i8, + _input_2: i16, + _input_3: i32, + _input_4: i64, + _input_5: i128, + _input_6: u8, + ) { + } + + #[ink(message)] + pub fn message_7_ref( + &self, + _input_1: i8, + _input_2: i16, + _input_3: i32, + _input_4: i64, + _input_5: i128, + _input_6: u8, + _input_7: u16, + ) { + } + + #[ink(message)] + pub fn message_7_mut( + &mut self, + _input_1: i8, + _input_2: i16, + _input_3: i32, + _input_4: i64, + _input_5: i128, + _input_6: u8, + _input_7: u16, + ) { + } + + #[ink(message)] + pub fn message_8_ref( + &self, + _input_1: i8, + _input_2: i16, + _input_3: i32, + _input_4: i64, + _input_5: i128, + _input_6: u8, + _input_7: u16, + _input_8: u32, + ) { + } + + #[ink(message)] + pub fn message_8_mut( + &mut self, + _input_1: i8, + _input_2: i16, + _input_3: i32, + _input_4: i64, + _input_5: i128, + _input_6: u8, + _input_7: u16, + _input_8: u32, + ) { + } + + #[ink(message)] + pub fn message_9_ref( + &self, + _input_1: i8, + _input_2: i16, + _input_3: i32, + _input_4: i64, + _input_5: i128, + _input_6: u8, + _input_7: u16, + _input_8: u32, + _input_9: u64, + ) { + } + + #[ink(message)] + pub fn message_9_mut( + &mut self, + _input_1: i8, + _input_2: i16, + _input_3: i32, + _input_4: i64, + _input_5: i128, + _input_6: u8, + _input_7: u16, + _input_8: u32, + _input_9: u64, + ) { + } + + #[ink(message)] + pub fn message_10_ref( + &self, + _input_1: i8, + _input_2: i16, + _input_3: i32, + _input_4: i64, + _input_5: i128, + _input_6: u8, + _input_7: u16, + _input_8: u32, + _input_9: u64, + _input_10: u128, + ) { + } + + #[ink(message)] + pub fn message_10_mut( + &mut self, + _input_1: i8, + _input_2: i16, + _input_3: i32, + _input_4: i64, + _input_5: i128, + _input_6: u8, + _input_7: u16, + _input_8: u32, + _input_9: u64, + _input_10: u128, + ) { + } + } +} + +fn main() {} diff --git a/crates/lang/tests/ui/contract/pass/message-many-outputs.rs b/crates/lang/tests/ui/contract/pass/message-many-outputs.rs new file mode 100644 index 00000000000..5ff863ed93c --- /dev/null +++ b/crates/lang/tests/ui/contract/pass/message-many-outputs.rs @@ -0,0 +1,72 @@ +use ink_lang as ink; + +#[ink::contract] +mod contract { + #[ink(storage)] + pub struct Contract {} + + impl Contract { + #[ink(constructor)] + pub fn constructor() -> Self { + Self {} + } + + #[ink(message)] + pub fn message_0_ref(&self) {} + + #[ink(message)] + pub fn message_0_mut(&mut self) {} + + #[ink(message)] + pub fn message_1_ref(&self) -> i8 { + unimplemented!() + } + + #[ink(message)] + pub fn message_1_mut(&mut self) -> i8 { + unimplemented!() + } + + #[ink(message)] + pub fn message_2_ref(&self) -> (i8, i16) { + unimplemented!() + } + + #[ink(message)] + pub fn message_2_mut(&mut self) -> (i8, i16) { + unimplemented!() + } + + #[ink(message)] + pub fn message_3_ref(&self) -> (i8, i16, i32) { + unimplemented!() + } + + #[ink(message)] + pub fn message_3_mut(&mut self) -> (i8, i16, i32) { + unimplemented!() + } + + #[ink(message)] + pub fn message_4_ref(&self) -> (i8, i16, i32, i64) { + unimplemented!() + } + + #[ink(message)] + pub fn message_4_mut(&mut self) -> (i8, i16, i32, i64) { + unimplemented!() + } + + #[ink(message)] + pub fn message_5_ref(&self) -> (i8, i16, i32, i64, i128) { + unimplemented!() + } + + #[ink(message)] + pub fn message_5_mut(&mut self) -> (i8, i16, i32, i64, i128) { + unimplemented!() + } + } +} + +fn main() {} diff --git a/crates/lang/tests/ui/contract/pass/message-payable.rs b/crates/lang/tests/ui/contract/pass/message-payable.rs new file mode 100644 index 00000000000..74f5035c7a4 --- /dev/null +++ b/crates/lang/tests/ui/contract/pass/message-payable.rs @@ -0,0 +1,27 @@ +use ink_lang as ink; + +#[ink::contract] +mod contract { + #[ink(storage)] + pub struct Contract {} + + impl Contract { + #[ink(constructor)] + pub fn constructor() -> Self { + Self {} + } + + #[ink(message, selector = 1, payable)] + pub fn message_1(&self) {} + + #[ink(message, selector = 2)] + pub fn message_2(&self) {} + } +} + +use contract::Contract; + +fn main() { + assert!(>::PAYABLE); + assert!(!>::PAYABLE); +} diff --git a/crates/lang/tests/ui/contract/pass/message-selector.rs b/crates/lang/tests/ui/contract/pass/message-selector.rs new file mode 100644 index 00000000000..303c6990769 --- /dev/null +++ b/crates/lang/tests/ui/contract/pass/message-selector.rs @@ -0,0 +1,63 @@ +use contract::Contract; +use ink_lang as ink; + +#[ink::contract] +mod contract { + #[ink(storage)] + pub struct Contract {} + + impl Contract { + #[ink(constructor)] + pub fn constructor() -> Self { + Self {} + } + + #[ink(message)] + pub fn message_0(&self) {} + + #[ink(message, selector = 1)] + pub fn message_1(&self) {} + + #[ink(message, selector = 0xC0DE_CAFE)] + pub fn message_2(&self) {} + } +} + +fn main() { + assert_eq!( + ::MESSAGES + }, + >>::IDS[0] + }, + >>::SELECTOR, + [0x5A, 0x6A, 0xC1, 0x5D], + ); + assert_eq!( + ::MESSAGES + }, + >>::IDS[1] + }, + >>::SELECTOR, + 1_u32.to_be_bytes(), + ); + assert_eq!( + ::MESSAGES + }, + >>::IDS[2] + }, + >>::SELECTOR, + 0xC0DE_CAFE_u32.to_be_bytes(), + ); +} diff --git a/crates/lang/tests/ui/contract/pass/minimal-contract.rs b/crates/lang/tests/ui/contract/pass/minimal-contract.rs new file mode 100644 index 00000000000..f20a5b77ffa --- /dev/null +++ b/crates/lang/tests/ui/contract/pass/minimal-contract.rs @@ -0,0 +1,19 @@ +use ink_lang as ink; + +#[ink::contract] +mod contract { + #[ink(storage)] + pub struct Contract {} + + impl Contract { + #[ink(constructor)] + pub fn constructor() -> Self { + Self {} + } + + #[ink(message)] + pub fn message(&self) {} + } +} + +fn main() {} diff --git a/crates/lang/tests/ui/contract/pass/module-env-types.rs b/crates/lang/tests/ui/contract/pass/module-env-types.rs new file mode 100644 index 00000000000..149bdd244f3 --- /dev/null +++ b/crates/lang/tests/ui/contract/pass/module-env-types.rs @@ -0,0 +1,25 @@ +use ink_lang as ink; + +#[ink::contract] +mod contract { + #[ink(storage)] + pub struct Contract { + account_id: AccountId, + balance: Balance, + hash: Hash, + timestamp: Timestamp, + block_number: BlockNumber, + } + + impl Contract { + #[ink(constructor)] + pub fn constructor() -> Self { + unimplemented!() + } + + #[ink(message)] + pub fn message(&self) {} + } +} + +fn main() {} diff --git a/crates/lang/tests/ui/contract/pass/module-non-ink-items.rs b/crates/lang/tests/ui/contract/pass/module-non-ink-items.rs new file mode 100644 index 00000000000..3cb84013f8a --- /dev/null +++ b/crates/lang/tests/ui/contract/pass/module-non-ink-items.rs @@ -0,0 +1,48 @@ +use ink_lang as ink; + +#[ink::contract] +mod contract { + #[ink(storage)] + pub struct Contract {} + + impl Contract { + #[ink(constructor)] + pub fn constructor() -> Self { + Self {} + } + + #[ink(message)] + pub fn message(&self) {} + } + + pub enum RustEnum { + A, + B, + } + pub union RustUnion { + pub field_a: i32, + pub field_b: [u8; 4], + } + pub struct RustStruct { + pub a: i32, + pub b: i32, + } + + impl RustStruct { + pub fn rust_method() {} + } + + pub const RUST_CONST: bool = true; + pub type RustAlias = RustStruct; +} + +fn main() { + // Use the Rust types defined in the ink! smart contract module + // in order to assert that they are properly re-generated by ink!. + let _ = contract::RustEnum::A; + let _ = contract::RustUnion { field_a: 42 }; + let _ = contract::RustStruct { a: 0, b: 1 }; + let _ = contract::RustStruct::rust_method(); + let _ = contract::RUST_CONST; + let _: contract::RustAlias = contract::RustStruct { a: 0, b: 1 }; +} diff --git a/crates/lang/tests/ui/contract/pass/no-implicit-prelude.rs b/crates/lang/tests/ui/contract/pass/no-implicit-prelude.rs new file mode 100644 index 00000000000..e288ee4a578 --- /dev/null +++ b/crates/lang/tests/ui/contract/pass/no-implicit-prelude.rs @@ -0,0 +1,19 @@ +#![no_implicit_prelude] + +#[::ink_lang::contract] +mod contract { + #[ink(storage)] + pub struct Contract {} + + impl Contract { + #[ink(constructor)] + pub fn constructor() -> Self { + Self {} + } + + #[ink(message)] + pub fn message(&self) {} + } +} + +fn main() {} diff --git a/crates/lang/tests/ui/contract/pass/storage-many-fields.rs b/crates/lang/tests/ui/contract/pass/storage-many-fields.rs new file mode 100644 index 00000000000..f713eb19261 --- /dev/null +++ b/crates/lang/tests/ui/contract/pass/storage-many-fields.rs @@ -0,0 +1,30 @@ +use ink_lang as ink; + +#[ink::contract] +mod contract { + #[ink(storage)] + pub struct Contract { + field_1: i8, + field_2: i16, + field_3: i32, + field_4: i64, + field_5: i128, + field_6: u8, + field_7: u16, + field_8: u32, + field_9: u64, + field_10: u128, + } + + impl Contract { + #[ink(constructor)] + pub fn constructor() -> Self { + unimplemented!() + } + + #[ink(message)] + pub fn message(&self) {} + } +} + +fn main() {} diff --git a/crates/lang/tests/ui/contract/pass/storage-packed-fields.rs b/crates/lang/tests/ui/contract/pass/storage-packed-fields.rs new file mode 100644 index 00000000000..dc86aab8a7a --- /dev/null +++ b/crates/lang/tests/ui/contract/pass/storage-packed-fields.rs @@ -0,0 +1,51 @@ +use ink_lang as ink; + +#[ink::contract] +mod contract { + use ink_storage::traits::{ + PackedLayout, + SpreadLayout, + StorageLayout, + }; + + #[ink(storage)] + pub struct Contract { + packed: PackedFields, + } + + #[derive( + Debug, + Default, + SpreadLayout, + PackedLayout, + StorageLayout, + scale::Encode, + scale::Decode, + )] + pub struct PackedFields { + field_1: i8, + field_2: i16, + field_3: i32, + field_4: i64, + field_5: i128, + field_6: u8, + field_7: u16, + field_8: u32, + field_9: u64, + field_10: u128, + } + + impl Contract { + #[ink(constructor)] + pub fn constructor() -> Self { + Self { + packed: Default::default(), + } + } + + #[ink(message)] + pub fn message(&self) {} + } +} + +fn main() {} diff --git a/crates/lang/macro/tests/ui/contract/pass/10-derive-for-storage.rs b/crates/lang/tests/ui/contract/pass/storage-single-field.rs similarity index 60% rename from crates/lang/macro/tests/ui/contract/pass/10-derive-for-storage.rs rename to crates/lang/tests/ui/contract/pass/storage-single-field.rs index 9f823ee4989..acda95e192c 100644 --- a/crates/lang/macro/tests/ui/contract/pass/10-derive-for-storage.rs +++ b/crates/lang/tests/ui/contract/pass/storage-single-field.rs @@ -1,15 +1,16 @@ use ink_lang as ink; #[ink::contract] -mod derive_for_storage { +mod contract { #[ink(storage)] - #[derive(Default)] - pub struct DeriveForStorage {} + pub struct Contract { + field: i8, + } - impl DeriveForStorage { + impl Contract { #[ink(constructor)] pub fn constructor() -> Self { - Default::default() + Self { field: 0 } } #[ink(message)] diff --git a/crates/lang/tests/ui/contract/pass/storage-with-derives.rs b/crates/lang/tests/ui/contract/pass/storage-with-derives.rs new file mode 100644 index 00000000000..20e709298ec --- /dev/null +++ b/crates/lang/tests/ui/contract/pass/storage-with-derives.rs @@ -0,0 +1,23 @@ +use contract::Contract; +use ink_lang as ink; + +#[ink::contract] +mod contract { + #[ink(storage)] + #[derive(Debug, Default, PartialEq, Eq)] + pub struct Contract {} + + impl Contract { + #[ink(constructor)] + pub fn constructor() -> Self { + Self::default() + } + + #[ink(message)] + pub fn message(&self) {} + } +} + +fn main() { + assert_eq!(Contract::constructor(), Contract::default()); +} diff --git a/crates/lang/tests/ui/contract/pass/trait-message-payable-guard.rs b/crates/lang/tests/ui/contract/pass/trait-message-payable-guard.rs new file mode 100644 index 00000000000..888c2735bd0 --- /dev/null +++ b/crates/lang/tests/ui/contract/pass/trait-message-payable-guard.rs @@ -0,0 +1,29 @@ +use ink_lang as ink; + +#[ink::trait_definition] +pub trait TraitDefinition { + #[ink(message, payable)] + fn message(&self); +} + +#[ink::contract] +mod contract { + use super::TraitDefinition; + + #[ink(storage)] + pub struct Contract {} + + impl Contract { + #[ink(constructor)] + pub fn constructor() -> Self { + Self {} + } + } + + impl TraitDefinition for Contract { + #[ink(message, payable)] + fn message(&self) {} + } +} + +fn main() {} diff --git a/crates/lang/tests/ui/contract/pass/trait-message-selector-guard.rs b/crates/lang/tests/ui/contract/pass/trait-message-selector-guard.rs new file mode 100644 index 00000000000..632c9fd4393 --- /dev/null +++ b/crates/lang/tests/ui/contract/pass/trait-message-selector-guard.rs @@ -0,0 +1,29 @@ +use ink_lang as ink; + +#[ink::trait_definition] +pub trait TraitDefinition { + #[ink(message, selector = 1)] + fn message(&self); +} + +#[ink::contract] +mod contract { + use super::TraitDefinition; + + #[ink(storage)] + pub struct Contract {} + + impl Contract { + #[ink(constructor)] + pub fn constructor() -> Self { + Self {} + } + } + + impl TraitDefinition for Contract { + #[ink(message, selector = 1)] + fn message(&self) {} + } +} + +fn main() {} diff --git a/crates/lang/macro/tests/ui/selector_bytes/fail/invalid_parameter_type_01.rs b/crates/lang/tests/ui/selector_bytes/fail/invalid_parameter_type_01.rs similarity index 100% rename from crates/lang/macro/tests/ui/selector_bytes/fail/invalid_parameter_type_01.rs rename to crates/lang/tests/ui/selector_bytes/fail/invalid_parameter_type_01.rs diff --git a/crates/lang/macro/tests/ui/selector_bytes/fail/invalid_parameter_type_01.stderr b/crates/lang/tests/ui/selector_bytes/fail/invalid_parameter_type_01.stderr similarity index 73% rename from crates/lang/macro/tests/ui/selector_bytes/fail/invalid_parameter_type_01.stderr rename to crates/lang/tests/ui/selector_bytes/fail/invalid_parameter_type_01.stderr index 784d320b7b1..1324671f7ad 100644 --- a/crates/lang/macro/tests/ui/selector_bytes/fail/invalid_parameter_type_01.stderr +++ b/crates/lang/tests/ui/selector_bytes/fail/invalid_parameter_type_01.stderr @@ -1,5 +1,5 @@ error: expected string or byte string literal as input. found Bool(LitBool { value: true }) - --> $DIR/invalid_parameter_type_01.rs:3:37 + --> tests/ui/selector_bytes/fail/invalid_parameter_type_01.rs:3:37 | 3 | const _: u32 = ink::selector_bytes!(true); | ^^^^ diff --git a/crates/lang/macro/tests/ui/selector_bytes/fail/invalid_parameter_type_02.rs b/crates/lang/tests/ui/selector_bytes/fail/invalid_parameter_type_02.rs similarity index 100% rename from crates/lang/macro/tests/ui/selector_bytes/fail/invalid_parameter_type_02.rs rename to crates/lang/tests/ui/selector_bytes/fail/invalid_parameter_type_02.rs diff --git a/crates/lang/macro/tests/ui/selector_bytes/fail/invalid_parameter_type_02.stderr b/crates/lang/tests/ui/selector_bytes/fail/invalid_parameter_type_02.stderr similarity index 72% rename from crates/lang/macro/tests/ui/selector_bytes/fail/invalid_parameter_type_02.stderr rename to crates/lang/tests/ui/selector_bytes/fail/invalid_parameter_type_02.stderr index 770056f4635..14976c8846e 100644 --- a/crates/lang/macro/tests/ui/selector_bytes/fail/invalid_parameter_type_02.stderr +++ b/crates/lang/tests/ui/selector_bytes/fail/invalid_parameter_type_02.stderr @@ -1,5 +1,5 @@ error: expected string or byte string literal as input. found Int(LitInt { token: 42 }) - --> $DIR/invalid_parameter_type_02.rs:3:37 + --> tests/ui/selector_bytes/fail/invalid_parameter_type_02.rs:3:37 | 3 | const _: u32 = ink::selector_bytes!(42); | ^^ diff --git a/crates/lang/macro/tests/ui/selector_bytes/fail/missing_parameter.rs b/crates/lang/tests/ui/selector_bytes/fail/missing_parameter.rs similarity index 100% rename from crates/lang/macro/tests/ui/selector_bytes/fail/missing_parameter.rs rename to crates/lang/tests/ui/selector_bytes/fail/missing_parameter.rs diff --git a/crates/lang/macro/tests/ui/selector_bytes/fail/missing_parameter.stderr b/crates/lang/tests/ui/selector_bytes/fail/missing_parameter.stderr similarity index 84% rename from crates/lang/macro/tests/ui/selector_bytes/fail/missing_parameter.stderr rename to crates/lang/tests/ui/selector_bytes/fail/missing_parameter.stderr index 9ef98ced0f4..b9c47fed494 100644 --- a/crates/lang/macro/tests/ui/selector_bytes/fail/missing_parameter.stderr +++ b/crates/lang/tests/ui/selector_bytes/fail/missing_parameter.stderr @@ -1,5 +1,5 @@ error: expected string or byte string literal as input: unexpected end of input, expected literal - --> $DIR/missing_parameter.rs:3:16 + --> tests/ui/selector_bytes/fail/missing_parameter.rs:3:16 | 3 | const _: u32 = ink::selector_bytes!(); | ^^^^^^^^^^^^^^^^^^^^^^ diff --git a/crates/lang/macro/tests/ui/selector_bytes/fail/non_literal_parameter.rs b/crates/lang/tests/ui/selector_bytes/fail/non_literal_parameter.rs similarity index 100% rename from crates/lang/macro/tests/ui/selector_bytes/fail/non_literal_parameter.rs rename to crates/lang/tests/ui/selector_bytes/fail/non_literal_parameter.rs diff --git a/crates/lang/macro/tests/ui/selector_bytes/fail/non_literal_parameter.stderr b/crates/lang/tests/ui/selector_bytes/fail/non_literal_parameter.stderr similarity index 72% rename from crates/lang/macro/tests/ui/selector_bytes/fail/non_literal_parameter.stderr rename to crates/lang/tests/ui/selector_bytes/fail/non_literal_parameter.stderr index 481785cd06a..8e70cd06ac5 100644 --- a/crates/lang/macro/tests/ui/selector_bytes/fail/non_literal_parameter.stderr +++ b/crates/lang/tests/ui/selector_bytes/fail/non_literal_parameter.stderr @@ -1,5 +1,5 @@ error: expected string or byte string literal as input: expected literal - --> $DIR/non_literal_parameter.rs:4:37 + --> tests/ui/selector_bytes/fail/non_literal_parameter.rs:4:37 | 4 | const _: u32 = ink::selector_bytes!(INPUT); | ^^^^^ diff --git a/crates/lang/macro/tests/ui/selector_bytes/pass/bytestring_input.rs b/crates/lang/tests/ui/selector_bytes/pass/bytestring_input.rs similarity index 89% rename from crates/lang/macro/tests/ui/selector_bytes/pass/bytestring_input.rs rename to crates/lang/tests/ui/selector_bytes/pass/bytestring_input.rs index 03e7731d982..19d956bece1 100644 --- a/crates/lang/macro/tests/ui/selector_bytes/pass/bytestring_input.rs +++ b/crates/lang/tests/ui/selector_bytes/pass/bytestring_input.rs @@ -7,7 +7,7 @@ macro_rules! assert_macro_eq { const HASH: [u8; 4] = ink::selector_bytes!($input); assert_eq!( HASH, - *ir::Selector::new($input).as_bytes(), + ir::Selector::compute($input).to_bytes(), ); }}; } diff --git a/crates/lang/macro/tests/ui/selector_bytes/pass/no_implicit_prelude.rs b/crates/lang/tests/ui/selector_bytes/pass/no_implicit_prelude.rs similarity index 100% rename from crates/lang/macro/tests/ui/selector_bytes/pass/no_implicit_prelude.rs rename to crates/lang/tests/ui/selector_bytes/pass/no_implicit_prelude.rs diff --git a/crates/lang/macro/tests/ui/selector_bytes/pass/string_input.rs b/crates/lang/tests/ui/selector_bytes/pass/string_input.rs similarity index 87% rename from crates/lang/macro/tests/ui/selector_bytes/pass/string_input.rs rename to crates/lang/tests/ui/selector_bytes/pass/string_input.rs index 0291b2d4b1a..fb9aa255072 100644 --- a/crates/lang/macro/tests/ui/selector_bytes/pass/string_input.rs +++ b/crates/lang/tests/ui/selector_bytes/pass/string_input.rs @@ -7,7 +7,7 @@ macro_rules! assert_macro_eq { const HASH: [u8; 4] = ink::selector_bytes!($input); assert_eq!( HASH, - *ir::Selector::new($input.as_bytes()).as_bytes(), + ir::Selector::compute($input.as_bytes()).to_bytes(), ); }}; } diff --git a/crates/lang/macro/tests/ui/selector_id/fail/invalid_parameter_type_01.rs b/crates/lang/tests/ui/selector_id/fail/invalid_parameter_type_01.rs similarity index 100% rename from crates/lang/macro/tests/ui/selector_id/fail/invalid_parameter_type_01.rs rename to crates/lang/tests/ui/selector_id/fail/invalid_parameter_type_01.rs diff --git a/crates/lang/macro/tests/ui/selector_id/fail/invalid_parameter_type_01.stderr b/crates/lang/tests/ui/selector_id/fail/invalid_parameter_type_01.stderr similarity index 73% rename from crates/lang/macro/tests/ui/selector_id/fail/invalid_parameter_type_01.stderr rename to crates/lang/tests/ui/selector_id/fail/invalid_parameter_type_01.stderr index 01bb41caa05..6ff0319a589 100644 --- a/crates/lang/macro/tests/ui/selector_id/fail/invalid_parameter_type_01.stderr +++ b/crates/lang/tests/ui/selector_id/fail/invalid_parameter_type_01.stderr @@ -1,5 +1,5 @@ error: expected string or byte string literal as input. found Bool(LitBool { value: true }) - --> $DIR/invalid_parameter_type_01.rs:3:34 + --> tests/ui/selector_id/fail/invalid_parameter_type_01.rs:3:34 | 3 | const _: u32 = ink::selector_id!(true); | ^^^^ diff --git a/crates/lang/macro/tests/ui/selector_id/fail/invalid_parameter_type_02.rs b/crates/lang/tests/ui/selector_id/fail/invalid_parameter_type_02.rs similarity index 100% rename from crates/lang/macro/tests/ui/selector_id/fail/invalid_parameter_type_02.rs rename to crates/lang/tests/ui/selector_id/fail/invalid_parameter_type_02.rs diff --git a/crates/lang/macro/tests/ui/selector_id/fail/invalid_parameter_type_02.stderr b/crates/lang/tests/ui/selector_id/fail/invalid_parameter_type_02.stderr similarity index 72% rename from crates/lang/macro/tests/ui/selector_id/fail/invalid_parameter_type_02.stderr rename to crates/lang/tests/ui/selector_id/fail/invalid_parameter_type_02.stderr index b559036512a..37f957377f6 100644 --- a/crates/lang/macro/tests/ui/selector_id/fail/invalid_parameter_type_02.stderr +++ b/crates/lang/tests/ui/selector_id/fail/invalid_parameter_type_02.stderr @@ -1,5 +1,5 @@ error: expected string or byte string literal as input. found Int(LitInt { token: 42 }) - --> $DIR/invalid_parameter_type_02.rs:3:34 + --> tests/ui/selector_id/fail/invalid_parameter_type_02.rs:3:34 | 3 | const _: u32 = ink::selector_id!(42); | ^^ diff --git a/crates/lang/macro/tests/ui/selector_id/fail/missing_parameter.rs b/crates/lang/tests/ui/selector_id/fail/missing_parameter.rs similarity index 100% rename from crates/lang/macro/tests/ui/selector_id/fail/missing_parameter.rs rename to crates/lang/tests/ui/selector_id/fail/missing_parameter.rs diff --git a/crates/lang/macro/tests/ui/selector_id/fail/missing_parameter.stderr b/crates/lang/tests/ui/selector_id/fail/missing_parameter.stderr similarity index 84% rename from crates/lang/macro/tests/ui/selector_id/fail/missing_parameter.stderr rename to crates/lang/tests/ui/selector_id/fail/missing_parameter.stderr index f26d1abbfa4..27fc30eb563 100644 --- a/crates/lang/macro/tests/ui/selector_id/fail/missing_parameter.stderr +++ b/crates/lang/tests/ui/selector_id/fail/missing_parameter.stderr @@ -1,5 +1,5 @@ error: expected string or byte string literal as input: unexpected end of input, expected literal - --> $DIR/missing_parameter.rs:3:16 + --> tests/ui/selector_id/fail/missing_parameter.rs:3:16 | 3 | const _: u32 = ink::selector_id!(); | ^^^^^^^^^^^^^^^^^^^ diff --git a/crates/lang/macro/tests/ui/selector_id/fail/non_literal_parameter.rs b/crates/lang/tests/ui/selector_id/fail/non_literal_parameter.rs similarity index 100% rename from crates/lang/macro/tests/ui/selector_id/fail/non_literal_parameter.rs rename to crates/lang/tests/ui/selector_id/fail/non_literal_parameter.rs diff --git a/crates/lang/macro/tests/ui/selector_id/fail/non_literal_parameter.stderr b/crates/lang/tests/ui/selector_id/fail/non_literal_parameter.stderr similarity index 73% rename from crates/lang/macro/tests/ui/selector_id/fail/non_literal_parameter.stderr rename to crates/lang/tests/ui/selector_id/fail/non_literal_parameter.stderr index 226fc0471ea..ea9ade30629 100644 --- a/crates/lang/macro/tests/ui/selector_id/fail/non_literal_parameter.stderr +++ b/crates/lang/tests/ui/selector_id/fail/non_literal_parameter.stderr @@ -1,5 +1,5 @@ error: expected string or byte string literal as input: expected literal - --> $DIR/non_literal_parameter.rs:4:34 + --> tests/ui/selector_id/fail/non_literal_parameter.rs:4:34 | 4 | const _: u32 = ink::selector_id!(INPUT); | ^^^^^ diff --git a/crates/lang/macro/tests/ui/selector_id/pass/bytestring_input.rs b/crates/lang/tests/ui/selector_id/pass/bytestring_input.rs similarity index 88% rename from crates/lang/macro/tests/ui/selector_id/pass/bytestring_input.rs rename to crates/lang/tests/ui/selector_id/pass/bytestring_input.rs index 9e837be2e02..8136c259ac1 100644 --- a/crates/lang/macro/tests/ui/selector_id/pass/bytestring_input.rs +++ b/crates/lang/tests/ui/selector_id/pass/bytestring_input.rs @@ -7,7 +7,7 @@ macro_rules! assert_macro_eq { const HASH: u32 = ink::selector_id!($input); assert_eq!( HASH, - ir::Selector::new($input).into_be_u32(), + ir::Selector::compute($input).into_be_u32(), ); }}; } diff --git a/crates/lang/macro/tests/ui/selector_id/pass/no_implicit_prelude.rs b/crates/lang/tests/ui/selector_id/pass/no_implicit_prelude.rs similarity index 100% rename from crates/lang/macro/tests/ui/selector_id/pass/no_implicit_prelude.rs rename to crates/lang/tests/ui/selector_id/pass/no_implicit_prelude.rs diff --git a/crates/lang/macro/tests/ui/selector_id/pass/string_input.rs b/crates/lang/tests/ui/selector_id/pass/string_input.rs similarity index 86% rename from crates/lang/macro/tests/ui/selector_id/pass/string_input.rs rename to crates/lang/tests/ui/selector_id/pass/string_input.rs index 21d98a37a1d..ca7eb30e251 100644 --- a/crates/lang/macro/tests/ui/selector_id/pass/string_input.rs +++ b/crates/lang/tests/ui/selector_id/pass/string_input.rs @@ -7,7 +7,7 @@ macro_rules! assert_macro_eq { const HASH: u32 = ink::selector_id!($input); assert_eq!( HASH, - ir::Selector::new($input.as_bytes()).into_be_u32(), + ir::Selector::compute($input.as_bytes()).into_be_u32(), ); }}; } diff --git a/crates/lang/tests/ui/trait_def/fail/config_namespace_invalid_1.rs b/crates/lang/tests/ui/trait_def/fail/config_namespace_invalid_1.rs new file mode 100644 index 00000000000..4a1469bf840 --- /dev/null +++ b/crates/lang/tests/ui/trait_def/fail/config_namespace_invalid_1.rs @@ -0,0 +1,9 @@ +use ink_lang as ink; + +#[ink::trait_definition(namespace = "::invalid::rust::identifier")] +pub trait TraitDefinition { + #[ink(message)] + fn message(&self); +} + +fn main() {} diff --git a/crates/lang/tests/ui/trait_def/fail/config_namespace_invalid_1.stderr b/crates/lang/tests/ui/trait_def/fail/config_namespace_invalid_1.stderr new file mode 100644 index 00000000000..408b47b84c3 --- /dev/null +++ b/crates/lang/tests/ui/trait_def/fail/config_namespace_invalid_1.stderr @@ -0,0 +1,5 @@ +error: encountered invalid Rust identifier for the ink! namespace configuration parameter + --> tests/ui/trait_def/fail/config_namespace_invalid_1.rs:3:37 + | +3 | #[ink::trait_definition(namespace = "::invalid::rust::identifier")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/crates/lang/tests/ui/trait_def/fail/config_namespace_invalid_2.rs b/crates/lang/tests/ui/trait_def/fail/config_namespace_invalid_2.rs new file mode 100644 index 00000000000..902c33b8193 --- /dev/null +++ b/crates/lang/tests/ui/trait_def/fail/config_namespace_invalid_2.rs @@ -0,0 +1,9 @@ +use ink_lang as ink; + +#[ink::trait_definition(namespace)] +pub trait TraitDefinition { + #[ink(message)] + fn message(&self); +} + +fn main() {} diff --git a/crates/lang/tests/ui/trait_def/fail/config_namespace_invalid_2.stderr b/crates/lang/tests/ui/trait_def/fail/config_namespace_invalid_2.stderr new file mode 100644 index 00000000000..704e3fb61b7 --- /dev/null +++ b/crates/lang/tests/ui/trait_def/fail/config_namespace_invalid_2.stderr @@ -0,0 +1,5 @@ +error: ink! config options require an argument separated by '=' + --> tests/ui/trait_def/fail/config_namespace_invalid_2.rs:3:25 + | +3 | #[ink::trait_definition(namespace)] + | ^^^^^^^^^ diff --git a/crates/lang/tests/ui/trait_def/fail/config_namespace_invalid_3.rs b/crates/lang/tests/ui/trait_def/fail/config_namespace_invalid_3.rs new file mode 100644 index 00000000000..a958d19b9ab --- /dev/null +++ b/crates/lang/tests/ui/trait_def/fail/config_namespace_invalid_3.rs @@ -0,0 +1,9 @@ +use ink_lang as ink; + +#[ink::trait_definition(namespace = "")] +pub trait TraitDefinition { + #[ink(message)] + fn message(&self); +} + +fn main() {} diff --git a/crates/lang/tests/ui/trait_def/fail/config_namespace_invalid_3.stderr b/crates/lang/tests/ui/trait_def/fail/config_namespace_invalid_3.stderr new file mode 100644 index 00000000000..38ae37022b8 --- /dev/null +++ b/crates/lang/tests/ui/trait_def/fail/config_namespace_invalid_3.stderr @@ -0,0 +1,5 @@ +error: encountered invalid Rust identifier for the ink! namespace configuration parameter + --> tests/ui/trait_def/fail/config_namespace_invalid_3.rs:3:37 + | +3 | #[ink::trait_definition(namespace = "")] + | ^^ diff --git a/crates/lang/tests/ui/trait_def/fail/config_namespace_invalid_4.rs b/crates/lang/tests/ui/trait_def/fail/config_namespace_invalid_4.rs new file mode 100644 index 00000000000..8a3bf563569 --- /dev/null +++ b/crates/lang/tests/ui/trait_def/fail/config_namespace_invalid_4.rs @@ -0,0 +1,9 @@ +use ink_lang as ink; + +#[ink::trait_definition(namespace = true)] +pub trait TraitDefinition { + #[ink(message)] + fn message(&self); +} + +fn main() {} diff --git a/crates/lang/tests/ui/trait_def/fail/config_namespace_invalid_4.stderr b/crates/lang/tests/ui/trait_def/fail/config_namespace_invalid_4.stderr new file mode 100644 index 00000000000..ff715414d97 --- /dev/null +++ b/crates/lang/tests/ui/trait_def/fail/config_namespace_invalid_4.stderr @@ -0,0 +1,5 @@ +error: expected a string literal for `namespace` ink! trait definition configuration argument + --> tests/ui/trait_def/fail/config_namespace_invalid_4.rs:3:25 + | +3 | #[ink::trait_definition(namespace = true)] + | ^^^^^^^^^^^^^^^^ diff --git a/crates/lang/tests/ui/trait_def/fail/definition_assoc_const.rs b/crates/lang/tests/ui/trait_def/fail/definition_assoc_const.rs new file mode 100644 index 00000000000..ffcefe15f5e --- /dev/null +++ b/crates/lang/tests/ui/trait_def/fail/definition_assoc_const.rs @@ -0,0 +1,11 @@ +use ink_lang as ink; + +#[ink::trait_definition] +pub trait TraitDefinition { + const CONST: bool; + + #[ink(message)] + fn message(&self); +} + +fn main() {} diff --git a/crates/lang/tests/ui/trait_def/fail/definition_assoc_const.stderr b/crates/lang/tests/ui/trait_def/fail/definition_assoc_const.stderr new file mode 100644 index 00000000000..7ab42234547 --- /dev/null +++ b/crates/lang/tests/ui/trait_def/fail/definition_assoc_const.stderr @@ -0,0 +1,5 @@ +error: associated constants in ink! trait definitions are not supported, yet + --> tests/ui/trait_def/fail/definition_assoc_const.rs:5:5 + | +5 | const CONST: bool; + | ^^^^^^^^^^^^^^^^^^ diff --git a/crates/lang/tests/ui/trait_def/fail/definition_assoc_type.rs b/crates/lang/tests/ui/trait_def/fail/definition_assoc_type.rs new file mode 100644 index 00000000000..ec92bf262fc --- /dev/null +++ b/crates/lang/tests/ui/trait_def/fail/definition_assoc_type.rs @@ -0,0 +1,11 @@ +use ink_lang as ink; + +#[ink::trait_definition] +pub trait TraitDefinition { + type Type; + + #[ink(message)] + fn message(&self); +} + +fn main() {} diff --git a/crates/lang/tests/ui/trait_def/fail/definition_assoc_type.stderr b/crates/lang/tests/ui/trait_def/fail/definition_assoc_type.stderr new file mode 100644 index 00000000000..14bdfbd199b --- /dev/null +++ b/crates/lang/tests/ui/trait_def/fail/definition_assoc_type.stderr @@ -0,0 +1,5 @@ +error: associated types in ink! trait definitions are not supported, yet + --> tests/ui/trait_def/fail/definition_assoc_type.rs:5:5 + | +5 | type Type; + | ^^^^^^^^^^ diff --git a/crates/lang/tests/ui/trait_def/fail/definition_constructor.rs b/crates/lang/tests/ui/trait_def/fail/definition_constructor.rs new file mode 100644 index 00000000000..d33b5b2ac90 --- /dev/null +++ b/crates/lang/tests/ui/trait_def/fail/definition_constructor.rs @@ -0,0 +1,9 @@ +use ink_lang as ink; + +#[ink::trait_definition] +pub trait TraitDefinition { + #[ink(constructor)] + fn constructor() -> Self; +} + +fn main() {} diff --git a/crates/lang/tests/ui/trait_def/fail/definition_constructor.stderr b/crates/lang/tests/ui/trait_def/fail/definition_constructor.stderr new file mode 100644 index 00000000000..3460253bf3b --- /dev/null +++ b/crates/lang/tests/ui/trait_def/fail/definition_constructor.stderr @@ -0,0 +1,6 @@ +error: ink! trait definitions must not have constructors + --> tests/ui/trait_def/fail/definition_constructor.rs:5:5 + | +5 | / #[ink(constructor)] +6 | | fn constructor() -> Self; + | |_____________________________^ diff --git a/crates/lang/tests/ui/trait_def/fail/definition_empty.rs b/crates/lang/tests/ui/trait_def/fail/definition_empty.rs new file mode 100644 index 00000000000..16017df57ce --- /dev/null +++ b/crates/lang/tests/ui/trait_def/fail/definition_empty.rs @@ -0,0 +1,6 @@ +use ink_lang as ink; + +#[ink::trait_definition] +pub trait TraitDefinition {} + +fn main() {} diff --git a/crates/lang/tests/ui/trait_def/fail/definition_empty.stderr b/crates/lang/tests/ui/trait_def/fail/definition_empty.stderr new file mode 100644 index 00000000000..4b9db3aacf7 --- /dev/null +++ b/crates/lang/tests/ui/trait_def/fail/definition_empty.stderr @@ -0,0 +1,5 @@ +error: encountered invalid empty ink! trait definition + --> tests/ui/trait_def/fail/definition_empty.rs:4:1 + | +4 | pub trait TraitDefinition {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/crates/lang/tests/ui/trait_def/fail/definition_generic.rs b/crates/lang/tests/ui/trait_def/fail/definition_generic.rs new file mode 100644 index 00000000000..7d1d4dd1e2e --- /dev/null +++ b/crates/lang/tests/ui/trait_def/fail/definition_generic.rs @@ -0,0 +1,9 @@ +use ink_lang as ink; + +#[ink::trait_definition] +pub trait TraitDefinition { + #[ink(message)] + fn message(&self); +} + +fn main() {} diff --git a/crates/lang/tests/ui/trait_def/fail/definition_generic.stderr b/crates/lang/tests/ui/trait_def/fail/definition_generic.stderr new file mode 100644 index 00000000000..a0381027e1a --- /dev/null +++ b/crates/lang/tests/ui/trait_def/fail/definition_generic.stderr @@ -0,0 +1,5 @@ +error: ink! trait definitions must not be generic + --> tests/ui/trait_def/fail/definition_generic.rs:4:27 + | +4 | pub trait TraitDefinition { + | ^ diff --git a/crates/lang/tests/ui/trait_def/fail/definition_non_pub.rs b/crates/lang/tests/ui/trait_def/fail/definition_non_pub.rs new file mode 100644 index 00000000000..01feb85065b --- /dev/null +++ b/crates/lang/tests/ui/trait_def/fail/definition_non_pub.rs @@ -0,0 +1,9 @@ +use ink_lang as ink; + +#[ink::trait_definition] +trait TraitDefinition { + #[ink(message)] + fn message(&self); +} + +fn main() {} diff --git a/crates/lang/tests/ui/trait_def/fail/definition_non_pub.stderr b/crates/lang/tests/ui/trait_def/fail/definition_non_pub.stderr new file mode 100644 index 00000000000..b5599832038 --- /dev/null +++ b/crates/lang/tests/ui/trait_def/fail/definition_non_pub.stderr @@ -0,0 +1,7 @@ +error: ink! trait definitions must have public visibility + --> tests/ui/trait_def/fail/definition_non_pub.rs:3:1 + | +3 | #[ink::trait_definition] + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this error originates in the attribute macro `ink::trait_definition` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/crates/lang/tests/ui/trait_def/fail/definition_rust_method.rs b/crates/lang/tests/ui/trait_def/fail/definition_rust_method.rs new file mode 100644 index 00000000000..61618c1d58f --- /dev/null +++ b/crates/lang/tests/ui/trait_def/fail/definition_rust_method.rs @@ -0,0 +1,8 @@ +use ink_lang as ink; + +#[ink::trait_definition] +pub trait TraitDefinition { + fn rust_method(&self); +} + +fn main() {} diff --git a/crates/lang/tests/ui/trait_def/fail/definition_rust_method.stderr b/crates/lang/tests/ui/trait_def/fail/definition_rust_method.stderr new file mode 100644 index 00000000000..d47c5818d41 --- /dev/null +++ b/crates/lang/tests/ui/trait_def/fail/definition_rust_method.stderr @@ -0,0 +1,5 @@ +error: missing #[ink(message)] or #[ink(constructor)] flags on ink! trait method + --> tests/ui/trait_def/fail/definition_rust_method.rs:5:5 + | +5 | fn rust_method(&self); + | ^^^^^^^^^^^^^^^^^^^^^^ diff --git a/crates/lang/tests/ui/trait_def/fail/definition_super_trait_invalid_1.rs b/crates/lang/tests/ui/trait_def/fail/definition_super_trait_invalid_1.rs new file mode 100644 index 00000000000..7ac4c1bd7eb --- /dev/null +++ b/crates/lang/tests/ui/trait_def/fail/definition_super_trait_invalid_1.rs @@ -0,0 +1,11 @@ +use ink_lang as ink; + +pub trait SuperTraitDefinition {} + +#[ink::trait_definition] +pub trait TraitDefinition: SuperTraitDefinition { + #[ink(message)] + fn message(&self); +} + +fn main() {} diff --git a/crates/lang/tests/ui/trait_def/fail/definition_super_trait_invalid_1.stderr b/crates/lang/tests/ui/trait_def/fail/definition_super_trait_invalid_1.stderr new file mode 100644 index 00000000000..f66a8d803d8 --- /dev/null +++ b/crates/lang/tests/ui/trait_def/fail/definition_super_trait_invalid_1.stderr @@ -0,0 +1,5 @@ +error: ink! trait definitions with supertraits are not supported, yet + --> tests/ui/trait_def/fail/definition_super_trait_invalid_1.rs:6:28 + | +6 | pub trait TraitDefinition: SuperTraitDefinition { + | ^^^^^^^^^^^^^^^^^^^^ diff --git a/crates/lang/tests/ui/trait_def/fail/definition_super_trait_invalid_2.rs b/crates/lang/tests/ui/trait_def/fail/definition_super_trait_invalid_2.rs new file mode 100644 index 00000000000..7cd35ebd119 --- /dev/null +++ b/crates/lang/tests/ui/trait_def/fail/definition_super_trait_invalid_2.rs @@ -0,0 +1,15 @@ +use ink_lang as ink; + +#[ink::trait_definition] +pub trait SuperTraitDefinition { + #[ink(message)] + fn super_message(&self); +} + +#[ink::trait_definition] +pub trait TraitDefinition: SuperTraitDefinition { + #[ink(message)] + fn message(&self); +} + +fn main() {} diff --git a/crates/lang/tests/ui/trait_def/fail/definition_super_trait_invalid_2.stderr b/crates/lang/tests/ui/trait_def/fail/definition_super_trait_invalid_2.stderr new file mode 100644 index 00000000000..3350a2b89c2 --- /dev/null +++ b/crates/lang/tests/ui/trait_def/fail/definition_super_trait_invalid_2.stderr @@ -0,0 +1,5 @@ +error: ink! trait definitions with supertraits are not supported, yet + --> tests/ui/trait_def/fail/definition_super_trait_invalid_2.rs:10:28 + | +10 | pub trait TraitDefinition: SuperTraitDefinition { + | ^^^^^^^^^^^^^^^^^^^^ diff --git a/crates/lang/tests/ui/trait_def/fail/definition_unsafe.rs b/crates/lang/tests/ui/trait_def/fail/definition_unsafe.rs new file mode 100644 index 00000000000..cd4b6a23d99 --- /dev/null +++ b/crates/lang/tests/ui/trait_def/fail/definition_unsafe.rs @@ -0,0 +1,9 @@ +use ink_lang as ink; + +#[ink::trait_definition] +pub unsafe trait TraitDefinition { + #[ink(message)] + fn message(&self); +} + +fn main() {} diff --git a/crates/lang/tests/ui/trait_def/fail/definition_unsafe.stderr b/crates/lang/tests/ui/trait_def/fail/definition_unsafe.stderr new file mode 100644 index 00000000000..063a7c2d239 --- /dev/null +++ b/crates/lang/tests/ui/trait_def/fail/definition_unsafe.stderr @@ -0,0 +1,5 @@ +error: ink! trait definitions cannot be unsafe + --> tests/ui/trait_def/fail/definition_unsafe.rs:4:5 + | +4 | pub unsafe trait TraitDefinition { + | ^^^^^^ diff --git a/crates/lang/tests/ui/trait_def/fail/message_abi_invalid.rs b/crates/lang/tests/ui/trait_def/fail/message_abi_invalid.rs new file mode 100644 index 00000000000..187c6fd5655 --- /dev/null +++ b/crates/lang/tests/ui/trait_def/fail/message_abi_invalid.rs @@ -0,0 +1,12 @@ +use ink_lang as ink; + +#[ink::trait_definition] +pub trait TraitDefinition { + #[ink(message)] + extern "C" fn message_ref(&self); + + #[ink(message)] + extern "C" fn message_mut(&mut self); +} + +fn main() {} diff --git a/crates/lang/tests/ui/trait_def/fail/message_abi_invalid.stderr b/crates/lang/tests/ui/trait_def/fail/message_abi_invalid.stderr new file mode 100644 index 00000000000..4a3c6d02a5a --- /dev/null +++ b/crates/lang/tests/ui/trait_def/fail/message_abi_invalid.stderr @@ -0,0 +1,5 @@ +error: ink! trait methods with non default ABI are not supported + --> tests/ui/trait_def/fail/message_abi_invalid.rs:6:5 + | +6 | extern "C" fn message_ref(&self); + | ^^^^^^^^^^ diff --git a/crates/lang/tests/ui/trait_def/fail/message_async_invalid.rs b/crates/lang/tests/ui/trait_def/fail/message_async_invalid.rs new file mode 100644 index 00000000000..0f28b00f519 --- /dev/null +++ b/crates/lang/tests/ui/trait_def/fail/message_async_invalid.rs @@ -0,0 +1,12 @@ +use ink_lang as ink; + +#[ink::trait_definition] +pub trait TraitDefinition { + #[ink(message)] + async fn message_ref(&self); + + #[ink(message)] + async fn message_mut(&mut self); +} + +fn main() {} diff --git a/crates/lang/tests/ui/trait_def/fail/message_async_invalid.stderr b/crates/lang/tests/ui/trait_def/fail/message_async_invalid.stderr new file mode 100644 index 00000000000..b1e6354058d --- /dev/null +++ b/crates/lang/tests/ui/trait_def/fail/message_async_invalid.stderr @@ -0,0 +1,5 @@ +error: async ink! trait methods are not supported + --> tests/ui/trait_def/fail/message_async_invalid.rs:6:5 + | +6 | async fn message_ref(&self); + | ^^^^^ diff --git a/crates/lang/tests/ui/trait_def/fail/message_const_invalid.rs b/crates/lang/tests/ui/trait_def/fail/message_const_invalid.rs new file mode 100644 index 00000000000..e5fec9a3a0f --- /dev/null +++ b/crates/lang/tests/ui/trait_def/fail/message_const_invalid.rs @@ -0,0 +1,12 @@ +use ink_lang as ink; + +#[ink::trait_definition] +pub trait TraitDefinition { + #[ink(message)] + const fn message_ref(&self); + + #[ink(message)] + const fn message_mut(&mut self); +} + +fn main() {} diff --git a/crates/lang/tests/ui/trait_def/fail/message_const_invalid.stderr b/crates/lang/tests/ui/trait_def/fail/message_const_invalid.stderr new file mode 100644 index 00000000000..4cf9f2fa071 --- /dev/null +++ b/crates/lang/tests/ui/trait_def/fail/message_const_invalid.stderr @@ -0,0 +1,5 @@ +error: const ink! trait methods are not supported + --> tests/ui/trait_def/fail/message_const_invalid.rs:6:5 + | +6 | const fn message_ref(&self); + | ^^^^^ diff --git a/crates/lang/tests/ui/trait_def/fail/message_constructor_conflict.rs b/crates/lang/tests/ui/trait_def/fail/message_constructor_conflict.rs new file mode 100644 index 00000000000..304b077bef1 --- /dev/null +++ b/crates/lang/tests/ui/trait_def/fail/message_constructor_conflict.rs @@ -0,0 +1,9 @@ +use ink_lang as ink; + +#[ink::trait_definition] +pub trait TraitDefinition { + #[ink(message, constructor)] + fn message(&self); +} + +fn main() {} diff --git a/crates/lang/tests/ui/trait_def/fail/message_constructor_conflict.stderr b/crates/lang/tests/ui/trait_def/fail/message_constructor_conflict.stderr new file mode 100644 index 00000000000..53b3112851c --- /dev/null +++ b/crates/lang/tests/ui/trait_def/fail/message_constructor_conflict.stderr @@ -0,0 +1,5 @@ +error: encountered conflicting ink! attribute argument + --> tests/ui/trait_def/fail/message_constructor_conflict.rs:5:20 + | +5 | #[ink(message, constructor)] + | ^^^^^^^^^^^ diff --git a/crates/lang/tests/ui/trait_def/fail/message_default_impl.rs b/crates/lang/tests/ui/trait_def/fail/message_default_impl.rs new file mode 100644 index 00000000000..9345cd8d27f --- /dev/null +++ b/crates/lang/tests/ui/trait_def/fail/message_default_impl.rs @@ -0,0 +1,9 @@ +use ink_lang as ink; + +#[ink::trait_definition] +pub trait TraitDefinition { + #[ink(message)] + fn message(&self) {} +} + +fn main() {} diff --git a/crates/lang/tests/ui/trait_def/fail/message_default_impl.stderr b/crates/lang/tests/ui/trait_def/fail/message_default_impl.stderr new file mode 100644 index 00000000000..aba46f5059d --- /dev/null +++ b/crates/lang/tests/ui/trait_def/fail/message_default_impl.stderr @@ -0,0 +1,5 @@ +error: ink! trait methods with default implementations are not supported + --> tests/ui/trait_def/fail/message_default_impl.rs:6:23 + | +6 | fn message(&self) {} + | ^^ diff --git a/crates/lang/tests/ui/trait_def/fail/message_generic_invalid.rs b/crates/lang/tests/ui/trait_def/fail/message_generic_invalid.rs new file mode 100644 index 00000000000..9c618b3dcfd --- /dev/null +++ b/crates/lang/tests/ui/trait_def/fail/message_generic_invalid.rs @@ -0,0 +1,12 @@ +use ink_lang as ink; + +#[ink::trait_definition] +pub trait TraitDefinition { + #[ink(message)] + fn message_ref(&self); + + #[ink(message)] + fn message_mut(&mut self); +} + +fn main() {} diff --git a/crates/lang/tests/ui/trait_def/fail/message_generic_invalid.stderr b/crates/lang/tests/ui/trait_def/fail/message_generic_invalid.stderr new file mode 100644 index 00000000000..c2df8eda524 --- /dev/null +++ b/crates/lang/tests/ui/trait_def/fail/message_generic_invalid.stderr @@ -0,0 +1,5 @@ +error: generic ink! trait methods are not supported + --> tests/ui/trait_def/fail/message_generic_invalid.rs:6:20 + | +6 | fn message_ref(&self); + | ^ diff --git a/crates/lang/tests/ui/trait_def/fail/message_input_non_codec.rs b/crates/lang/tests/ui/trait_def/fail/message_input_non_codec.rs new file mode 100644 index 00000000000..597145606f3 --- /dev/null +++ b/crates/lang/tests/ui/trait_def/fail/message_input_non_codec.rs @@ -0,0 +1,11 @@ +use ink_lang as ink; + +pub struct NonCodec; + +#[ink::trait_definition] +pub trait TraitDefinition { + #[ink(message)] + fn message(&self, input: NonCodec); +} + +fn main() {} diff --git a/crates/lang/tests/ui/trait_def/fail/message_input_non_codec.stderr b/crates/lang/tests/ui/trait_def/fail/message_input_non_codec.stderr new file mode 100644 index 00000000000..9cf33c6922b --- /dev/null +++ b/crates/lang/tests/ui/trait_def/fail/message_input_non_codec.stderr @@ -0,0 +1,40 @@ +error[E0277]: the trait bound `NonCodec: WrapperTypeDecode` is not satisfied + --> tests/ui/trait_def/fail/message_input_non_codec.rs:8:23 + | +8 | fn message(&self, input: NonCodec); + | ^^^^^^^^^^^^^^^ the trait `WrapperTypeDecode` is not implemented for `NonCodec` + | + = note: required because of the requirements on the impl of `parity_scale_codec::Decode` for `NonCodec` +note: required by a bound in `DispatchInput` + --> src/codegen/dispatch/type_check.rs:41:8 + | +41 | T: scale::Decode + 'static; + | ^^^^^^^^^^^^^ required by this bound in `DispatchInput` + +error[E0277]: the trait bound `NonCodec: WrapperTypeEncode` is not satisfied + --> tests/ui/trait_def/fail/message_input_non_codec.rs:5:1 + | +5 | #[ink::trait_definition] + | ^^^^^^^^^^^^^^^^^^^^^^^^ the trait `WrapperTypeEncode` is not implemented for `NonCodec` +6 | pub trait TraitDefinition { +7 | / #[ink(message)] +8 | | fn message(&self, input: NonCodec); + | |_______________________________________- required by a bound introduced by this call + | + = note: required because of the requirements on the impl of `Encode` for `NonCodec` + = note: this error originates in the attribute macro `ink::trait_definition` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0599]: the method `fire` exists for struct `CallBuilder::AccountId>, Unset, Unset<::Balance>, Set, ArgumentList>>>, Set<()>>`, but its trait bounds were not satisfied + --> tests/ui/trait_def/fail/message_input_non_codec.rs:7:5 + | +7 | / #[ink(message)] +8 | | fn message(&self, input: NonCodec); + | |_______________________________________^ method cannot be called on `CallBuilder::AccountId>, Unset, Unset<::Balance>, Set, ArgumentList>>>, Set<()>>` due to unsatisfied trait bounds + | + ::: $WORKSPACE/crates/env/src/call/execution_input.rs + | + | pub struct ArgumentList { + | ----------------------------------- doesn't satisfy `_: Encode` + | + = note: the following trait bounds were not satisfied: + `ArgumentList, ArgumentList>: Encode` diff --git a/crates/lang/tests/ui/trait_def/fail/message_input_pattern_invalid.rs b/crates/lang/tests/ui/trait_def/fail/message_input_pattern_invalid.rs new file mode 100644 index 00000000000..a38362c3d9e --- /dev/null +++ b/crates/lang/tests/ui/trait_def/fail/message_input_pattern_invalid.rs @@ -0,0 +1,9 @@ +use ink_lang as ink; + +#[ink::trait_definition] +pub trait TraitDefinition { + #[ink(message)] + fn message(&self, (a, b): (i32, i32)); +} + +fn main() {} diff --git a/crates/lang/tests/ui/trait_def/fail/message_input_pattern_invalid.stderr b/crates/lang/tests/ui/trait_def/fail/message_input_pattern_invalid.stderr new file mode 100644 index 00000000000..dedc7539320 --- /dev/null +++ b/crates/lang/tests/ui/trait_def/fail/message_input_pattern_invalid.stderr @@ -0,0 +1,5 @@ +error[E0642]: patterns aren't allowed in functions without bodies + --> tests/ui/trait_def/fail/message_input_pattern_invalid.rs:6:23 + | +6 | fn message(&self, (a, b): (i32, i32)); + | ^^^^^^ pattern not allowed in function without body diff --git a/crates/lang/tests/ui/trait_def/fail/message_output_non_codec.rs b/crates/lang/tests/ui/trait_def/fail/message_output_non_codec.rs new file mode 100644 index 00000000000..32555e9d6d2 --- /dev/null +++ b/crates/lang/tests/ui/trait_def/fail/message_output_non_codec.rs @@ -0,0 +1,11 @@ +use ink_lang as ink; + +pub struct NonCodec; + +#[ink::trait_definition] +pub trait TraitDefinition { + #[ink(message)] + fn message(&self) -> NonCodec; +} + +fn main() {} diff --git a/crates/lang/tests/ui/trait_def/fail/message_output_non_codec.stderr b/crates/lang/tests/ui/trait_def/fail/message_output_non_codec.stderr new file mode 100644 index 00000000000..ffdb0343f25 --- /dev/null +++ b/crates/lang/tests/ui/trait_def/fail/message_output_non_codec.stderr @@ -0,0 +1,36 @@ +error[E0277]: the trait bound `NonCodec: WrapperTypeEncode` is not satisfied + --> tests/ui/trait_def/fail/message_output_non_codec.rs:8:26 + | +8 | fn message(&self) -> NonCodec; + | ^^^^^^^^ the trait `WrapperTypeEncode` is not implemented for `NonCodec` + | + = note: required because of the requirements on the impl of `Encode` for `NonCodec` +note: required by a bound in `DispatchOutput` + --> src/codegen/dispatch/type_check.rs:69:8 + | +69 | T: scale::Encode + 'static; + | ^^^^^^^^^^^^^ required by this bound in `DispatchOutput` + +error[E0599]: the method `fire` exists for struct `CallBuilder::AccountId>, Unset, Unset<::Balance>, Set>>, Set>>`, but its trait bounds were not satisfied + --> tests/ui/trait_def/fail/message_output_non_codec.rs:7:5 + | +3 | pub struct NonCodec; + | -------------------- doesn't satisfy `NonCodec: parity_scale_codec::Decode` +... +7 | / #[ink(message)] +8 | | fn message(&self) -> NonCodec; + | |__________________________________^ method cannot be called on `CallBuilder::AccountId>, Unset, Unset<::Balance>, Set>>, Set>>` due to unsatisfied trait bounds + | + = note: the following trait bounds were not satisfied: + `NonCodec: parity_scale_codec::Decode` +note: the following trait must be implemented + --> $CARGO/parity-scale-codec-2.3.1/src/codec.rs + | + | / pub trait Decode: Sized { + | | // !INTERNAL USE ONLY! + | | // This const helps SCALE to optimize the encoding/decoding by doing fake specialization. + | | #[doc(hidden)] +... | + | | } + | | } + | |_^ diff --git a/crates/lang/tests/ui/trait_def/fail/message_payable_invalid_1.rs b/crates/lang/tests/ui/trait_def/fail/message_payable_invalid_1.rs new file mode 100644 index 00000000000..9810fc483f8 --- /dev/null +++ b/crates/lang/tests/ui/trait_def/fail/message_payable_invalid_1.rs @@ -0,0 +1,9 @@ +use ink_lang as ink; + +#[ink::trait_definition] +pub trait TraitDefinition { + #[ink(message, payable = false)] + fn message(&self); +} + +fn main() {} diff --git a/crates/lang/tests/ui/trait_def/fail/message_payable_invalid_1.stderr b/crates/lang/tests/ui/trait_def/fail/message_payable_invalid_1.stderr new file mode 100644 index 00000000000..5b341d5a4e8 --- /dev/null +++ b/crates/lang/tests/ui/trait_def/fail/message_payable_invalid_1.stderr @@ -0,0 +1,5 @@ +error: unknown ink! attribute argument (name = value) + --> tests/ui/trait_def/fail/message_payable_invalid_1.rs:5:20 + | +5 | #[ink(message, payable = false)] + | ^^^^^^^^^^^^^^^ diff --git a/crates/lang/tests/ui/trait_def/fail/message_payable_invalid_2.rs b/crates/lang/tests/ui/trait_def/fail/message_payable_invalid_2.rs new file mode 100644 index 00000000000..efef5826302 --- /dev/null +++ b/crates/lang/tests/ui/trait_def/fail/message_payable_invalid_2.rs @@ -0,0 +1,9 @@ +use ink_lang as ink; + +#[ink::trait_definition] +pub trait TraitDefinition { + #[ink(message, payable = true)] + fn message(&self); +} + +fn main() {} diff --git a/crates/lang/tests/ui/trait_def/fail/message_payable_invalid_2.stderr b/crates/lang/tests/ui/trait_def/fail/message_payable_invalid_2.stderr new file mode 100644 index 00000000000..f794664ce1f --- /dev/null +++ b/crates/lang/tests/ui/trait_def/fail/message_payable_invalid_2.stderr @@ -0,0 +1,5 @@ +error: unknown ink! attribute argument (name = value) + --> tests/ui/trait_def/fail/message_payable_invalid_2.rs:5:20 + | +5 | #[ink(message, payable = true)] + | ^^^^^^^^^^^^^^ diff --git a/crates/lang/tests/ui/trait_def/fail/message_receiver_invalid_1.rs b/crates/lang/tests/ui/trait_def/fail/message_receiver_invalid_1.rs new file mode 100644 index 00000000000..bd3551d85cf --- /dev/null +++ b/crates/lang/tests/ui/trait_def/fail/message_receiver_invalid_1.rs @@ -0,0 +1,9 @@ +use ink_lang as ink; + +#[ink::trait_definition] +pub trait TraitDefinition { + #[ink(message)] + fn message(); +} + +fn main() {} diff --git a/crates/lang/tests/ui/trait_def/fail/message_receiver_invalid_1.stderr b/crates/lang/tests/ui/trait_def/fail/message_receiver_invalid_1.stderr new file mode 100644 index 00000000000..c81f1bd2d4f --- /dev/null +++ b/crates/lang/tests/ui/trait_def/fail/message_receiver_invalid_1.stderr @@ -0,0 +1,5 @@ +error: missing or malformed `&self` or `&mut self` receiver for ink! message + --> tests/ui/trait_def/fail/message_receiver_invalid_1.rs:6:5 + | +6 | fn message(); + | ^^^^^^^^^^^^ diff --git a/crates/lang/tests/ui/trait_def/fail/message_receiver_invalid_2.rs b/crates/lang/tests/ui/trait_def/fail/message_receiver_invalid_2.rs new file mode 100644 index 00000000000..1569878e5a2 --- /dev/null +++ b/crates/lang/tests/ui/trait_def/fail/message_receiver_invalid_2.rs @@ -0,0 +1,9 @@ +use ink_lang as ink; + +#[ink::trait_definition] +pub trait TraitDefinition { + #[ink(message)] + fn message(this: Self); +} + +fn main() {} diff --git a/crates/lang/tests/ui/trait_def/fail/message_receiver_invalid_2.stderr b/crates/lang/tests/ui/trait_def/fail/message_receiver_invalid_2.stderr new file mode 100644 index 00000000000..bbe8fef44a8 --- /dev/null +++ b/crates/lang/tests/ui/trait_def/fail/message_receiver_invalid_2.stderr @@ -0,0 +1,5 @@ +error: missing or malformed `&self` or `&mut self` receiver for ink! message + --> tests/ui/trait_def/fail/message_receiver_invalid_2.rs:6:5 + | +6 | fn message(this: Self); + | ^^^^^^^^^^^^^^^^^^^^^^ diff --git a/crates/lang/tests/ui/trait_def/fail/message_receiver_invalid_3.rs b/crates/lang/tests/ui/trait_def/fail/message_receiver_invalid_3.rs new file mode 100644 index 00000000000..91a28bf11a5 --- /dev/null +++ b/crates/lang/tests/ui/trait_def/fail/message_receiver_invalid_3.rs @@ -0,0 +1,9 @@ +use ink_lang as ink; + +#[ink::trait_definition] +pub trait TraitDefinition { + #[ink(message)] + fn message(this: &Self); +} + +fn main() {} diff --git a/crates/lang/tests/ui/trait_def/fail/message_receiver_invalid_3.stderr b/crates/lang/tests/ui/trait_def/fail/message_receiver_invalid_3.stderr new file mode 100644 index 00000000000..59e41cd4c4a --- /dev/null +++ b/crates/lang/tests/ui/trait_def/fail/message_receiver_invalid_3.stderr @@ -0,0 +1,5 @@ +error: missing or malformed `&self` or `&mut self` receiver for ink! message + --> tests/ui/trait_def/fail/message_receiver_invalid_3.rs:6:5 + | +6 | fn message(this: &Self); + | ^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/crates/lang/tests/ui/trait_def/fail/message_receiver_missing.rs b/crates/lang/tests/ui/trait_def/fail/message_receiver_missing.rs new file mode 100644 index 00000000000..bd3551d85cf --- /dev/null +++ b/crates/lang/tests/ui/trait_def/fail/message_receiver_missing.rs @@ -0,0 +1,9 @@ +use ink_lang as ink; + +#[ink::trait_definition] +pub trait TraitDefinition { + #[ink(message)] + fn message(); +} + +fn main() {} diff --git a/crates/lang/tests/ui/trait_def/fail/message_receiver_missing.stderr b/crates/lang/tests/ui/trait_def/fail/message_receiver_missing.stderr new file mode 100644 index 00000000000..3f89dbd8c7a --- /dev/null +++ b/crates/lang/tests/ui/trait_def/fail/message_receiver_missing.stderr @@ -0,0 +1,5 @@ +error: missing or malformed `&self` or `&mut self` receiver for ink! message + --> tests/ui/trait_def/fail/message_receiver_missing.rs:6:5 + | +6 | fn message(); + | ^^^^^^^^^^^^ diff --git a/crates/lang/tests/ui/trait_def/fail/message_selector_invalid_1.rs b/crates/lang/tests/ui/trait_def/fail/message_selector_invalid_1.rs new file mode 100644 index 00000000000..e90847277f2 --- /dev/null +++ b/crates/lang/tests/ui/trait_def/fail/message_selector_invalid_1.rs @@ -0,0 +1,9 @@ +use ink_lang as ink; + +#[ink::trait_definition] +pub trait TraitDefinition { + #[ink(message, selector = true)] + fn message(&self); +} + +fn main() {} diff --git a/crates/lang/tests/ui/trait_def/fail/message_selector_invalid_1.stderr b/crates/lang/tests/ui/trait_def/fail/message_selector_invalid_1.stderr new file mode 100644 index 00000000000..33da5adc7d0 --- /dev/null +++ b/crates/lang/tests/ui/trait_def/fail/message_selector_invalid_1.stderr @@ -0,0 +1,5 @@ +error: expecteded 4-digit hexcode for `selector` argument, e.g. #[ink(selector = 0xC0FEBABE] + --> tests/ui/trait_def/fail/message_selector_invalid_1.rs:5:20 + | +5 | #[ink(message, selector = true)] + | ^^^^^^^^^^^^^^^ diff --git a/crates/lang/tests/ui/trait_def/fail/message_selector_invalid_2.rs b/crates/lang/tests/ui/trait_def/fail/message_selector_invalid_2.rs new file mode 100644 index 00000000000..d76a5e1ca84 --- /dev/null +++ b/crates/lang/tests/ui/trait_def/fail/message_selector_invalid_2.rs @@ -0,0 +1,9 @@ +use ink_lang as ink; + +#[ink::trait_definition] +pub trait TraitDefinition { + #[ink(message, selector)] + fn message(&self); +} + +fn main() {} diff --git a/crates/lang/tests/ui/trait_def/fail/message_selector_invalid_2.stderr b/crates/lang/tests/ui/trait_def/fail/message_selector_invalid_2.stderr new file mode 100644 index 00000000000..1b47d602f98 --- /dev/null +++ b/crates/lang/tests/ui/trait_def/fail/message_selector_invalid_2.stderr @@ -0,0 +1,5 @@ +error: encountered #[ink(selector)] that is missing its u32 parameter. Did you mean #[ink(selector = value: u32)] ? + --> tests/ui/trait_def/fail/message_selector_invalid_2.rs:5:20 + | +5 | #[ink(message, selector)] + | ^^^^^^^^ diff --git a/crates/lang/tests/ui/trait_def/fail/message_selector_overlap.rs b/crates/lang/tests/ui/trait_def/fail/message_selector_overlap.rs new file mode 100644 index 00000000000..0201eb88029 --- /dev/null +++ b/crates/lang/tests/ui/trait_def/fail/message_selector_overlap.rs @@ -0,0 +1,12 @@ +use ink_lang as ink; + +#[ink::trait_definition] +pub trait TraitDefinition { + #[ink(message, selector = 1)] + fn message_1(&self); + + #[ink(message, selector = 1)] + fn message_2(&self); +} + +fn main() {} diff --git a/crates/lang/tests/ui/trait_def/fail/message_selector_overlap.stderr b/crates/lang/tests/ui/trait_def/fail/message_selector_overlap.stderr new file mode 100644 index 00000000000..26530c48584 --- /dev/null +++ b/crates/lang/tests/ui/trait_def/fail/message_selector_overlap.stderr @@ -0,0 +1,11 @@ +error: encountered duplicate selector ([0, 0, 0, 1]) in the same ink! trait definition + --> tests/ui/trait_def/fail/message_selector_overlap.rs:9:8 + | +9 | fn message_2(&self); + | ^^^^^^^^^ + +error: first ink! trait constructor or message with same selector found here + --> tests/ui/trait_def/fail/message_selector_overlap.rs:6:8 + | +6 | fn message_1(&self); + | ^^^^^^^^^ diff --git a/crates/lang/tests/ui/trait_def/fail/message_unsafe_invalid.rs b/crates/lang/tests/ui/trait_def/fail/message_unsafe_invalid.rs new file mode 100644 index 00000000000..b208bc3869c --- /dev/null +++ b/crates/lang/tests/ui/trait_def/fail/message_unsafe_invalid.rs @@ -0,0 +1,12 @@ +use ink_lang as ink; + +#[ink::trait_definition] +pub trait TraitDefinition { + #[ink(message)] + unsafe fn message_ref(&self); + + #[ink(message)] + unsafe fn message_mut(&mut self); +} + +fn main() {} diff --git a/crates/lang/tests/ui/trait_def/fail/message_unsafe_invalid.stderr b/crates/lang/tests/ui/trait_def/fail/message_unsafe_invalid.stderr new file mode 100644 index 00000000000..4d7d1fe35a6 --- /dev/null +++ b/crates/lang/tests/ui/trait_def/fail/message_unsafe_invalid.stderr @@ -0,0 +1,5 @@ +error: unsafe ink! trait methods are not supported + --> tests/ui/trait_def/fail/message_unsafe_invalid.rs:6:5 + | +6 | unsafe fn message_ref(&self); + | ^^^^^^ diff --git a/crates/lang/tests/ui/trait_def/pass/avoid_overlap_with_namespace.rs b/crates/lang/tests/ui/trait_def/pass/avoid_overlap_with_namespace.rs new file mode 100644 index 00000000000..8c92867e21e --- /dev/null +++ b/crates/lang/tests/ui/trait_def/pass/avoid_overlap_with_namespace.rs @@ -0,0 +1,54 @@ +mod foo { + use ink_lang as ink; + + #[ink::trait_definition(namespace = "foo")] + pub trait TraitDefinition { + #[ink(message)] + fn message(&self); + } +} + +mod bar { + use ink_lang as ink; + + #[ink::trait_definition(namespace = "bar")] + pub trait TraitDefinition { + #[ink(message)] + fn message(&self); + } +} + +use bar::TraitDefinition as TraitDefinition2; +use foo::TraitDefinition as TraitDefinition1; +use ink_env::DefaultEnvironment; +use ink_lang::{ + reflect::{ + TraitDefinitionRegistry, + TraitMessageInfo, + }, + selector_bytes, + selector_id, +}; + +fn main() { + macro_rules! assert_selector_eq { + ( $trait_ident:path, $message_id:literal, $expected_selector:expr $(,)? ) => { + assert_eq!( + < as $trait_ident>::__ink_TraitInfo + as TraitMessageInfo<{selector_id!($message_id)}>>::SELECTOR, + $expected_selector + ); + } + } + + assert_selector_eq!( + TraitDefinition1, + "message", + selector_bytes!("foo::TraitDefinition::message"), + ); + assert_selector_eq!( + TraitDefinition2, + "message", + selector_bytes!("bar::TraitDefinition::message"), + ); +} diff --git a/crates/lang/tests/ui/trait_def/pass/many_inputs.rs b/crates/lang/tests/ui/trait_def/pass/many_inputs.rs new file mode 100644 index 00000000000..da988352ab2 --- /dev/null +++ b/crates/lang/tests/ui/trait_def/pass/many_inputs.rs @@ -0,0 +1,31 @@ +use ink_lang as ink; + +#[ink::trait_definition] +pub trait ManyInputs { + #[ink(message)] + fn input0(&self); + #[ink(message)] + fn input0_mut(&mut self); + + #[ink(message)] + fn input1(&self, a: i8); + #[ink(message)] + fn input1_mut(&mut self, a: i8); + + #[ink(message)] + fn input2(&self, a: i8, b: i16); + #[ink(message)] + fn input2_mut(&mut self, a: i8, b: i16); + + #[ink(message)] + fn input3(&self, a: i8, b: i16, c: i32); + #[ink(message)] + fn input3_mut(&mut self, a: i8, b: i16, c: i32); + + #[ink(message)] + fn input4(&self, a: i8, b: i16, c: i32, d: i64); + #[ink(message)] + fn input4_mut(&mut self, a: i8, b: i16, c: i32, d: i64); +} + +fn main() {} diff --git a/crates/lang/tests/ui/trait_def/pass/many_outputs.rs b/crates/lang/tests/ui/trait_def/pass/many_outputs.rs new file mode 100644 index 00000000000..4f3cafb15d8 --- /dev/null +++ b/crates/lang/tests/ui/trait_def/pass/many_outputs.rs @@ -0,0 +1,31 @@ +use ink_lang as ink; + +#[ink::trait_definition] +pub trait ManyOutputs { + #[ink(message)] + fn output0(&self); + #[ink(message)] + fn output0_mut(&mut self); + + #[ink(message)] + fn output1(&self) -> i8; + #[ink(message)] + fn output1_mut(&mut self) -> i8; + + #[ink(message)] + fn output2(&self) -> (i8, i16); + #[ink(message)] + fn output2_mut(&mut self) -> (i8, i16); + + #[ink(message)] + fn output3(&self) -> (i8, i16, i32); + #[ink(message)] + fn output3_mut(&mut self) -> (i8, i16, i32); + + #[ink(message)] + fn output4(&self) -> (i8, i16, i32, i64); + #[ink(message)] + fn output4_mut(&mut self) -> (i8, i16, i32, i64); +} + +fn main() {} diff --git a/crates/lang/tests/ui/trait_def/pass/no-implicit-prelude.rs b/crates/lang/tests/ui/trait_def/pass/no-implicit-prelude.rs new file mode 100644 index 00000000000..29180717b1b --- /dev/null +++ b/crates/lang/tests/ui/trait_def/pass/no-implicit-prelude.rs @@ -0,0 +1,11 @@ +#![no_implicit_prelude] + +#[::ink_lang::trait_definition] +pub trait TraitDefinition { + #[ink(message)] + fn message(&self); + #[ink(message)] + fn message_mut(&mut self); +} + +fn main() {} diff --git a/crates/lang/tests/ui/trait_def/pass/payable_message.rs b/crates/lang/tests/ui/trait_def/pass/payable_message.rs new file mode 100644 index 00000000000..6f05e8574bf --- /dev/null +++ b/crates/lang/tests/ui/trait_def/pass/payable_message.rs @@ -0,0 +1,53 @@ +use ink_lang as ink; + +#[ink::trait_definition] +pub trait PayableDefinition { + #[ink(message, payable)] + fn payable(&self); + + #[ink(message, payable)] + fn payable_mut(&mut self); + + #[ink(message)] + fn unpayable(&self); + + #[ink(message)] + fn unpayable_mut(&mut self); +} + +use ink_lang::selector_id; + +const PAYABLE_ID: u32 = selector_id!("payable"); +const PAYABLE_MUT_ID: u32 = selector_id!("payable_mut"); +const UNPAYABLE_ID: u32 = selector_id!("unpayable"); +const UNPAYABLE_MUT_ID: u32 = selector_id!("unpayable_mut"); + +fn main() { + use ink_env::DefaultEnvironment; + use ink_lang::reflect::{ + TraitDefinitionRegistry, + TraitMessageInfo, + }; + assert!( + < + as PayableDefinition>::__ink_TraitInfo + as TraitMessageInfo>::PAYABLE, + ); + assert!( + < + as PayableDefinition>::__ink_TraitInfo + as TraitMessageInfo>::PAYABLE, + ); + assert_eq!( + < + as PayableDefinition>::__ink_TraitInfo + as TraitMessageInfo>::PAYABLE, + false + ); + assert_eq!( + < + as PayableDefinition>::__ink_TraitInfo + as TraitMessageInfo>::PAYABLE, + false + ); +} diff --git a/crates/lang/tests/ui/trait_def/pass/simple_definition.rs b/crates/lang/tests/ui/trait_def/pass/simple_definition.rs new file mode 100644 index 00000000000..3bf7133d5d5 --- /dev/null +++ b/crates/lang/tests/ui/trait_def/pass/simple_definition.rs @@ -0,0 +1,11 @@ +use ink_lang as ink; + +#[ink::trait_definition] +pub trait TraitDefinition { + #[ink(message)] + fn message(&self); + #[ink(message)] + fn message_mut(&mut self); +} + +fn main() {} diff --git a/crates/lang/tests/ui/trait_def/pass/using-env-types.rs b/crates/lang/tests/ui/trait_def/pass/using-env-types.rs new file mode 100644 index 00000000000..21500739bf5 --- /dev/null +++ b/crates/lang/tests/ui/trait_def/pass/using-env-types.rs @@ -0,0 +1,14 @@ +use ink_env::Environment; +use ink_lang::reflect::ContractEnv; + +use ink_lang as ink; + +#[ink::trait_definition] +pub trait TraitDefinition { + #[ink(message)] + fn message_1(&self) -> <::Env as Environment>::AccountId; + #[ink(message)] + fn message_2(&self, input: <::Env as Environment>::Balance); +} + +fn main() {} diff --git a/crates/lang/tests/ui/trait_def/pass/valid_selectors.rs b/crates/lang/tests/ui/trait_def/pass/valid_selectors.rs new file mode 100644 index 00000000000..fc00d76e05c --- /dev/null +++ b/crates/lang/tests/ui/trait_def/pass/valid_selectors.rs @@ -0,0 +1,39 @@ +use ink_lang as ink; + +#[ink::trait_definition] +pub trait TraitDefinition { + #[ink(message)] + fn message1(&self); + + #[ink(message, selector = 42)] + fn message2(&self); + + #[ink(message, selector = 0xC0DECAFE)] + fn message3(&mut self); +} + +use ink_env::DefaultEnvironment; +use ink_lang::{ + reflect::{ + TraitDefinitionRegistry, + TraitMessageInfo, + }, + selector_bytes, + selector_id, +}; + +fn main() { + macro_rules! assert_selector_eq { + ( $message_id:literal, $expected_selector:expr $(,)? ) => { + assert_eq!( + < as TraitDefinition>::__ink_TraitInfo + as TraitMessageInfo<{selector_id!($message_id)}>>::SELECTOR, + $expected_selector + ); + } + } + + assert_selector_eq!("message1", selector_bytes!("TraitDefinition::message1"),); + assert_selector_eq!("message2", [0, 0, 0, 42],); + assert_selector_eq!("message3", [0xC0, 0xDE, 0xCA, 0xFE],); +} diff --git a/crates/lang/tests/ui/trait_def/pass/valid_selectors_namespace.rs b/crates/lang/tests/ui/trait_def/pass/valid_selectors_namespace.rs new file mode 100644 index 00000000000..402ee896824 --- /dev/null +++ b/crates/lang/tests/ui/trait_def/pass/valid_selectors_namespace.rs @@ -0,0 +1,42 @@ +use ink_lang as ink; + +#[ink::trait_definition(namespace = "foo")] +pub trait TraitDefinition { + #[ink(message)] + fn message1(&self); + + #[ink(message, selector = 42)] + fn message2(&self); + + #[ink(message, selector = 0xC0DECAFE)] + fn message3(&mut self); +} + +use ink_env::DefaultEnvironment; +use ink_lang::{ + reflect::{ + TraitDefinitionRegistry, + TraitMessageInfo, + }, + selector_bytes, + selector_id, +}; + +fn main() { + macro_rules! assert_selector_eq { + ( $message_id:literal, $expected_selector:expr $(,)? ) => { + assert_eq!( + < as TraitDefinition>::__ink_TraitInfo + as TraitMessageInfo<{selector_id!($message_id)}>>::SELECTOR, + $expected_selector + ); + } + } + + assert_selector_eq!( + "message1", + selector_bytes!("foo::TraitDefinition::message1"), + ); + assert_selector_eq!("message2", [0, 0, 0, 42],); + assert_selector_eq!("message3", [0xC0, 0xDE, 0xCA, 0xFE],); +} diff --git a/crates/lang/tests/ui/trait_def/pass/with_namespace.rs b/crates/lang/tests/ui/trait_def/pass/with_namespace.rs new file mode 100644 index 00000000000..3664134030f --- /dev/null +++ b/crates/lang/tests/ui/trait_def/pass/with_namespace.rs @@ -0,0 +1,9 @@ +use ink_lang as ink; + +#[ink::trait_definition(namespace = "my_trait_namespace")] +pub trait WithNamespace { + #[ink(message)] + fn message(&self); +} + +fn main() {} diff --git a/crates/lang/macro/tests/unique_topics.rs b/crates/lang/tests/unique_topics.rs similarity index 100% rename from crates/lang/macro/tests/unique_topics.rs rename to crates/lang/tests/unique_topics.rs diff --git a/crates/metadata/Cargo.toml b/crates/metadata/Cargo.toml index 94fa5453789..3fda3597d7e 100644 --- a/crates/metadata/Cargo.toml +++ b/crates/metadata/Cargo.toml @@ -24,8 +24,8 @@ derive_more = { version = "0.99", default-features = false, features = ["from"] scale-info = { version = "1.0", default-features = false, features = ["derive", "serde", "decode"] } [dev-dependencies] -pretty_assertions = "1.0.0" -serde_json = "1.0" +pretty_assertions = "1" +serde_json = "1" [features] default = [ diff --git a/crates/metadata/src/layout/tests.rs b/crates/metadata/src/layout/tests.rs index d49166e1a00..453dcde3683 100644 --- a/crates/metadata/src/layout/tests.rs +++ b/crates/metadata/src/layout/tests.rs @@ -14,7 +14,6 @@ use super::*; use ink_primitives::KeyPtr; -use pretty_assertions::assert_eq; #[test] fn layout_key_works() { diff --git a/crates/metadata/src/specs.rs b/crates/metadata/src/specs.rs index f166a115aab..30383131f10 100644 --- a/crates/metadata/src/specs.rs +++ b/crates/metadata/src/specs.rs @@ -290,12 +290,15 @@ pub struct ConstructorSpecBuilder { impl ConstructorSpec { /// Creates a new constructor spec builder. - fn from_name_segments( - segments: Vec<&'static str>, - ) -> ConstructorSpecBuilder> { + fn from_name_segments( + segments: T, + ) -> ConstructorSpecBuilder> + where + T: IntoIterator, + { ConstructorSpecBuilder { spec: Self { - name: segments, + name: segments.into_iter().collect(), selector: Selector::default(), args: Vec::new(), docs: Vec::new(), @@ -308,7 +311,7 @@ impl ConstructorSpec { pub fn from_name( name: &'static str, ) -> ConstructorSpecBuilder> { - Self::from_name_segments(vec![name]) + Self::from_name_segments([name]) } /// Creates a new constructor spec builder for a trait provided constructor. @@ -316,7 +319,7 @@ impl ConstructorSpec { trait_name: &'static str, constructor_name: &'static str, ) -> ConstructorSpecBuilder> { - Self::from_name_segments(vec![trait_name, constructor_name]) + Self::from_name_segments([trait_name, constructor_name]) } } diff --git a/crates/storage/derive/Cargo.toml b/crates/storage/derive/Cargo.toml index cdbb5ef5174..439ca3c1fff 100644 --- a/crates/storage/derive/Cargo.toml +++ b/crates/storage/derive/Cargo.toml @@ -18,9 +18,9 @@ include = ["Cargo.toml", "src/**/*.rs", "README.md", "LICENSE"] proc-macro = true [dependencies] -quote = "1.0" -syn = { version = "1.0", features = ["full"] } -proc-macro2 = "1.0" +quote = "1" +syn = { version = "1", features = ["full"] } +proc-macro2 = "1" synstructure = "0.12.4" [dev-dependencies] @@ -28,4 +28,5 @@ scale = { package = "parity-scale-codec", version = "2", default-features = fals ink_env = { version = "3.0.0-rc6", path = "../../env" } ink_primitives = { version = "3.0.0-rc6", path = "../../primitives" } ink_metadata = { version = "3.0.0-rc6", path = "../../metadata" } +ink_prelude = { version = "3.0.0-rc6", path = "../../prelude/" } ink_storage = { version = "3.0.0-rc6", path = ".." } diff --git a/crates/storage/derive/src/storage_layout.rs b/crates/storage/derive/src/storage_layout.rs index c215121855c..f4949f061bc 100644 --- a/crates/storage/derive/src/storage_layout.rs +++ b/crates/storage/derive/src/storage_layout.rs @@ -51,7 +51,7 @@ fn storage_layout_struct(s: &synstructure::Structure) -> TokenStream2 { gen impl ::ink_storage::traits::StorageLayout for @Self { fn layout(__key_ptr: &mut ::ink_storage::traits::KeyPtr) -> ::ink_metadata::layout::Layout { ::ink_metadata::layout::Layout::Struct( - ::ink_metadata::layout::StructLayout::new(vec![ + ::ink_metadata::layout::StructLayout::new([ #(#field_layouts ,)* ]) ) @@ -79,7 +79,7 @@ fn storage_layout_enum(s: &synstructure::Structure) -> TokenStream2 { let mut __key_ptr = &mut __variant_key_ptr; ( ::ink_metadata::layout::Discriminant::from(#discriminant), - ::ink_metadata::layout::StructLayout::new(vec![ + ::ink_metadata::layout::StructLayout::new([ #(#field_layouts ,)* ]), ) @@ -93,7 +93,7 @@ fn storage_layout_enum(s: &synstructure::Structure) -> TokenStream2 { ::ink_metadata::layout::Layout::Enum( ::ink_metadata::layout::EnumLayout::new( ::ink_metadata::layout::LayoutKey::from(dispatch_key), - vec![ + [ #(#variant_layouts ,)* ] ) diff --git a/crates/storage/derive/src/tests/storage_layout.rs b/crates/storage/derive/src/tests/storage_layout.rs index cee27839ac1..3ebb49f5784 100644 --- a/crates/storage/derive/src/tests/storage_layout.rs +++ b/crates/storage/derive/src/tests/storage_layout.rs @@ -31,7 +31,7 @@ fn unit_struct_works() { impl ::ink_storage::traits::StorageLayout for UnitStruct { fn layout(__key_ptr: &mut ::ink_storage::traits::KeyPtr) -> ::ink_metadata::layout::Layout { ::ink_metadata::layout::Layout::Struct( - ::ink_metadata::layout::StructLayout::new(vec![]) + ::ink_metadata::layout::StructLayout::new([]) ) } } @@ -51,7 +51,7 @@ fn tuple_struct_works() { impl ::ink_storage::traits::StorageLayout for TupleStruct { fn layout(__key_ptr: &mut ::ink_storage::traits::KeyPtr) -> ::ink_metadata::layout::Layout { ::ink_metadata::layout::Layout::Struct( - ::ink_metadata::layout::StructLayout::new(vec![ + ::ink_metadata::layout::StructLayout::new([ ::ink_metadata::layout::FieldLayout::new( ::core::option::Option::None, ::layout(__key_ptr), @@ -88,7 +88,7 @@ fn named_fields_struct_works() { impl ::ink_storage::traits::StorageLayout for NamedFieldsStruct { fn layout(__key_ptr: &mut ::ink_storage::traits::KeyPtr) -> ::ink_metadata::layout::Layout { ::ink_metadata::layout::Layout::Struct( - ::ink_metadata::layout::StructLayout::new(vec![ + ::ink_metadata::layout::StructLayout::new([ ::ink_metadata::layout::FieldLayout::new( ::core::option::Option::Some("a"), ::layout(__key_ptr), @@ -124,13 +124,13 @@ fn clike_enum_works() { ::ink_metadata::layout::Layout::Enum( ::ink_metadata::layout::EnumLayout::new( ::ink_metadata::layout::LayoutKey::from(dispatch_key), - vec![ + [ { let mut __variant_key_ptr = *__key_ptr; let mut __key_ptr = &mut __variant_key_ptr; ( ::ink_metadata::layout::Discriminant::from(0usize), - ::ink_metadata::layout::StructLayout::new(vec![]), + ::ink_metadata::layout::StructLayout::new([]), ) }, { @@ -138,7 +138,7 @@ fn clike_enum_works() { let mut __key_ptr = &mut __variant_key_ptr; ( ::ink_metadata::layout::Discriminant::from(1usize), - ::ink_metadata::layout::StructLayout::new(vec![]), + ::ink_metadata::layout::StructLayout::new([]), ) }, { @@ -146,7 +146,7 @@ fn clike_enum_works() { let mut __key_ptr = &mut __variant_key_ptr; ( ::ink_metadata::layout::Discriminant::from(2usize), - ::ink_metadata::layout::StructLayout::new(vec![]), + ::ink_metadata::layout::StructLayout::new([]), ) }, ] @@ -181,13 +181,13 @@ fn mixed_enum_works() { ::ink_metadata::layout::Layout::Enum( ::ink_metadata::layout::EnumLayout::new( ::ink_metadata::layout::LayoutKey::from(dispatch_key), - vec![ + [ { let mut __variant_key_ptr = *__key_ptr; let mut __key_ptr = &mut __variant_key_ptr; ( ::ink_metadata::layout::Discriminant::from(0usize), - ::ink_metadata::layout::StructLayout::new(vec![]), + ::ink_metadata::layout::StructLayout::new([]), ) }, { @@ -195,7 +195,7 @@ fn mixed_enum_works() { let mut __key_ptr = &mut __variant_key_ptr; ( ::ink_metadata::layout::Discriminant::from(1usize), - ::ink_metadata::layout::StructLayout::new(vec![ + ::ink_metadata::layout::StructLayout::new([ ::ink_metadata::layout::FieldLayout::new( ::core::option::Option::None, ::layout(__key_ptr), @@ -216,7 +216,7 @@ fn mixed_enum_works() { let mut __key_ptr = &mut __variant_key_ptr; ( ::ink_metadata::layout::Discriminant::from(2usize), - ::ink_metadata::layout::StructLayout::new(vec![ + ::ink_metadata::layout::StructLayout::new([ ::ink_metadata::layout::FieldLayout::new( ::core::option::Option::Some("a"), ::layout(__key_ptr), diff --git a/crates/storage/src/alloc/allocator.rs b/crates/storage/src/alloc/allocator.rs index 26c8dea52c4..8a551300890 100644 --- a/crates/storage/src/alloc/allocator.rs +++ b/crates/storage/src/alloc/allocator.rs @@ -40,7 +40,7 @@ const _: () = { impl StorageLayout for DynamicAllocator { fn layout(key_ptr: &mut KeyPtr) -> Layout { - Layout::Struct(StructLayout::new(vec![FieldLayout::new( + Layout::Struct(StructLayout::new([FieldLayout::new( "allocations", ::layout(key_ptr), )])) diff --git a/crates/storage/src/alloc/boxed/storage.rs b/crates/storage/src/alloc/boxed/storage.rs index c48f616e5e7..122f92e149a 100644 --- a/crates/storage/src/alloc/boxed/storage.rs +++ b/crates/storage/src/alloc/boxed/storage.rs @@ -56,7 +56,7 @@ const _: () = { fn type_info() -> scale_info::Type { scale_info::Type::builder() .path( - scale_info::Path::from_segments(vec!["ink_storage", "alloc", "Box"]) + scale_info::Path::from_segments(["ink_storage", "alloc", "Box"]) .expect("encountered invalid Rust path"), ) // Unfortunately we cannot encode the type parameters of the box since they diff --git a/crates/storage/src/collections/binary_heap/storage.rs b/crates/storage/src/collections/binary_heap/storage.rs index 88e42cb7a34..e6dc3320022 100644 --- a/crates/storage/src/collections/binary_heap/storage.rs +++ b/crates/storage/src/collections/binary_heap/storage.rs @@ -39,7 +39,7 @@ const _: () = { T: PackedLayout + Ord + TypeInfo + 'static, { fn layout(key_ptr: &mut KeyPtr) -> Layout { - Layout::Struct(StructLayout::new(vec![FieldLayout::new( + Layout::Struct(StructLayout::new([FieldLayout::new( "elements", as StorageLayout>::layout(key_ptr), )])) diff --git a/crates/storage/src/collections/bitstash/storage.rs b/crates/storage/src/collections/bitstash/storage.rs index 6a72a86ee7b..40e67977f61 100644 --- a/crates/storage/src/collections/bitstash/storage.rs +++ b/crates/storage/src/collections/bitstash/storage.rs @@ -43,7 +43,7 @@ const _: () = { impl StorageLayout for BitStash { fn layout(key_ptr: &mut KeyPtr) -> Layout { - Layout::Struct(StructLayout::new(vec![ + Layout::Struct(StructLayout::new([ FieldLayout::new( "counts", as StorageLayout>::layout(key_ptr), diff --git a/crates/storage/src/collections/bitvec/storage.rs b/crates/storage/src/collections/bitvec/storage.rs index 29c24657b3e..a7f291cdc87 100644 --- a/crates/storage/src/collections/bitvec/storage.rs +++ b/crates/storage/src/collections/bitvec/storage.rs @@ -44,7 +44,7 @@ const _: () = { impl StorageLayout for StorageBitvec { fn layout(key_ptr: &mut KeyPtr) -> Layout { - Layout::Struct(StructLayout::new(vec![ + Layout::Struct(StructLayout::new([ FieldLayout::new("len", as StorageLayout>::layout(key_ptr)), FieldLayout::new( "elems", diff --git a/crates/storage/src/collections/hashmap/storage.rs b/crates/storage/src/collections/hashmap/storage.rs index 917ece1c77a..920d90b8f6e 100644 --- a/crates/storage/src/collections/hashmap/storage.rs +++ b/crates/storage/src/collections/hashmap/storage.rs @@ -59,7 +59,7 @@ const _: () = { Key: From<::Type>, { fn layout(key_ptr: &mut KeyPtr) -> Layout { - Layout::Struct(StructLayout::new(vec![ + Layout::Struct(StructLayout::new([ FieldLayout::new( "keys", as StorageLayout>::layout(key_ptr), diff --git a/crates/storage/src/collections/smallvec/storage.rs b/crates/storage/src/collections/smallvec/storage.rs index cabbe2d1d87..85578130353 100644 --- a/crates/storage/src/collections/smallvec/storage.rs +++ b/crates/storage/src/collections/smallvec/storage.rs @@ -37,7 +37,7 @@ const _: () = { T: PackedLayout + TypeInfo + 'static, { fn layout(key_ptr: &mut KeyPtr) -> Layout { - Layout::Struct(StructLayout::new(vec![ + Layout::Struct(StructLayout::new([ FieldLayout::new("len", ::layout(key_ptr)), FieldLayout::new( "elems", diff --git a/crates/storage/src/collections/stash/storage.rs b/crates/storage/src/collections/stash/storage.rs index 6ef6ec704ce..fcab4595f1c 100644 --- a/crates/storage/src/collections/stash/storage.rs +++ b/crates/storage/src/collections/stash/storage.rs @@ -60,7 +60,7 @@ const _: () = { T: PackedLayout + TypeInfo + 'static, { fn layout(key_ptr: &mut KeyPtr) -> Layout { - Layout::Struct(StructLayout::new(vec![ + Layout::Struct(StructLayout::new([ FieldLayout::new("header",

::layout(key_ptr)), FieldLayout::new( "entries", diff --git a/crates/storage/src/collections/vec/storage.rs b/crates/storage/src/collections/vec/storage.rs index 7fb5c5e19ef..8d290fe9b9e 100644 --- a/crates/storage/src/collections/vec/storage.rs +++ b/crates/storage/src/collections/vec/storage.rs @@ -42,7 +42,7 @@ const _: () = { T: PackedLayout + TypeInfo + 'static, { fn layout(key_ptr: &mut KeyPtr) -> Layout { - Layout::Struct(StructLayout::new(vec![ + Layout::Struct(StructLayout::new([ FieldLayout::new("len", as StorageLayout>::layout(key_ptr)), FieldLayout::new( "elems", diff --git a/crates/storage/src/traits/layout/impls.rs b/crates/storage/src/traits/layout/impls.rs index d82bd2da2d5..04a37f38b8e 100644 --- a/crates/storage/src/traits/layout/impls.rs +++ b/crates/storage/src/traits/layout/impls.rs @@ -99,7 +99,7 @@ macro_rules! impl_layout_for_tuple { { fn layout(key_ptr: &mut KeyPtr) -> Layout { Layout::Struct( - StructLayout::new(vec![ + StructLayout::new([ $( FieldLayout::new(None, <$frag as StorageLayout>::layout(key_ptr)), )* @@ -137,10 +137,10 @@ where let dispatch_key = key_ptr.advance_by(1); Layout::Enum(EnumLayout::new( *dispatch_key, - vec![ + [ ( Discriminant::from(0), - StructLayout::new(vec![FieldLayout::new( + StructLayout::new([FieldLayout::new( None, ::layout(&mut key_ptr.clone()), )]), @@ -160,17 +160,17 @@ where let dispatch_key = key_ptr.advance_by(1); Layout::Enum(EnumLayout::new( *dispatch_key, - vec![ + [ ( Discriminant::from(0), - StructLayout::new(vec![FieldLayout::new( + StructLayout::new([FieldLayout::new( None, ::layout(&mut key_ptr.clone()), )]), ), ( Discriminant::from(1), - StructLayout::new(vec![FieldLayout::new( + StructLayout::new([FieldLayout::new( None, ::layout(&mut key_ptr.clone()), )]), diff --git a/examples/delegator/accumulator/lib.rs b/examples/delegator/accumulator/lib.rs index 8fd207c4a8d..fae1482a9a6 100644 --- a/examples/delegator/accumulator/lib.rs +++ b/examples/delegator/accumulator/lib.rs @@ -1,6 +1,10 @@ #![cfg_attr(not(feature = "std"), no_std)] -pub use self::accumulator::Accumulator; +pub use self::accumulator::{ + Accumulator, + AccumulatorRef, +}; + use ink_lang as ink; #[ink::contract] diff --git a/examples/delegator/adder/lib.rs b/examples/delegator/adder/lib.rs index 91af1fd12f7..5996ed1a787 100644 --- a/examples/delegator/adder/lib.rs +++ b/examples/delegator/adder/lib.rs @@ -1,23 +1,27 @@ #![cfg_attr(not(feature = "std"), no_std)] -pub use self::adder::Adder; +pub use self::adder::{ + Adder, + AdderRef, +}; + use ink_lang as ink; #[ink::contract] mod adder { - use accumulator::Accumulator; + use accumulator::AccumulatorRef; /// Increments the underlying `accumulator` value. #[ink(storage)] pub struct Adder { /// The `accumulator` to store the value. - accumulator: accumulator::Accumulator, + accumulator: AccumulatorRef, } impl Adder { /// Creates a new `adder` from the given `accumulator`. #[ink(constructor)] - pub fn new(accumulator: Accumulator) -> Self { + pub fn new(accumulator: AccumulatorRef) -> Self { Self { accumulator } } diff --git a/examples/delegator/lib.rs b/examples/delegator/lib.rs index 5fa7e0fbd8b..52fd773be75 100644 --- a/examples/delegator/lib.rs +++ b/examples/delegator/lib.rs @@ -4,8 +4,8 @@ use ink_lang as ink; #[ink::contract] mod delegator { - use accumulator::Accumulator; - use adder::Adder; + use accumulator::AccumulatorRef; + use adder::AdderRef; use ink_storage::{ traits::{ PackedLayout, @@ -13,7 +13,7 @@ mod delegator { }, Lazy, }; - use subber::Subber; + use subber::SubberRef; /// Specifies the state of the `delegator` contract. /// @@ -44,21 +44,26 @@ mod delegator { /// Delegates calls to an `adder` or `subber` contract to mutate /// a value in an `accumulator` contract. /// + /// # Note + /// /// In order to deploy the `delegator` smart contract we first /// have to manually put the code of the `accumulator`, `adder` /// and `subber` smart contracts, receive their code hashes from /// the signalled events and put their code hash into our /// `delegator` smart contract. + /// + /// The `AccumulatorRef`, `AdderRef` and `SubberRef` are smart contract + /// reference types that have been automatically generated by ink!. #[ink(storage)] pub struct Delegator { /// Says which of `adder` or `subber` is currently in use. which: Which, /// The `accumulator` smart contract. - accumulator: Lazy, + accumulator: Lazy, /// The `adder` smart contract. - adder: Lazy, + adder: Lazy, /// The `subber` smart contract. - subber: Lazy, + subber: Lazy, } impl Delegator { @@ -73,24 +78,33 @@ mod delegator { ) -> Self { let total_balance = Self::env().balance(); let salt = version.to_le_bytes(); - let accumulator = Accumulator::new(init_value) + let accumulator = AccumulatorRef::new(init_value) .endowment(total_balance / 4) .code_hash(accumulator_code_hash) .salt_bytes(salt) .instantiate() - .expect("failed at instantiating the `Accumulator` contract"); - let adder = Adder::new(accumulator.clone()) + .unwrap_or_else(|error| { + panic!( + "failed at instantiating the Accumulator contract: {:?}", + error + ) + }); + let adder = AdderRef::new(accumulator.clone()) .endowment(total_balance / 4) .code_hash(adder_code_hash) .salt_bytes(salt) .instantiate() - .expect("failed at instantiating the `Adder` contract"); - let subber = Subber::new(accumulator.clone()) + .unwrap_or_else(|error| { + panic!("failed at instantiating the Adder contract: {:?}", error) + }); + let subber = SubberRef::new(accumulator.clone()) .endowment(total_balance / 4) .code_hash(subber_code_hash) .salt_bytes(salt) .instantiate() - .expect("failed at instantiating the `Subber` contract"); + .unwrap_or_else(|error| { + panic!("failed at instantiating the Subber contract: {:?}", error) + }); Self { which: Which::Adder, accumulator: Lazy::new(accumulator), diff --git a/examples/delegator/subber/lib.rs b/examples/delegator/subber/lib.rs index 30d5424c776..35180f9bbf2 100644 --- a/examples/delegator/subber/lib.rs +++ b/examples/delegator/subber/lib.rs @@ -1,23 +1,27 @@ #![cfg_attr(not(feature = "std"), no_std)] -pub use self::subber::Subber; +pub use self::subber::{ + Subber, + SubberRef, +}; + use ink_lang as ink; #[ink::contract] mod subber { - use accumulator::Accumulator; + use accumulator::AccumulatorRef; /// Decreases the underlying `accumulator` value. #[ink(storage)] pub struct Subber { /// The `accumulator` to store the value. - accumulator: accumulator::Accumulator, + accumulator: AccumulatorRef, } impl Subber { /// Creates a new `subber` from the given `accumulator`. #[ink(constructor)] - pub fn new(accumulator: Accumulator) -> Self { + pub fn new(accumulator: AccumulatorRef) -> Self { Self { accumulator } } diff --git a/examples/erc1155/lib.rs b/examples/erc1155/lib.rs index d7303462421..d91d5af194d 100644 --- a/examples/erc1155/lib.rs +++ b/examples/erc1155/lib.rs @@ -152,7 +152,7 @@ pub trait Erc1155TokenReceiver { /// /// Any callers must revert if they receive anything other than `ON_ERC_1155_RECEIVED_SELECTOR` as a return /// value. - #[ink(message)] + #[ink(message, selector = 0xF23A6E61)] fn on_received( &mut self, operator: AccountId, @@ -172,7 +172,7 @@ pub trait Erc1155TokenReceiver { /// /// Any callers must revert if they receive anything other than `BATCH_ON_ERC_1155_RECEIVED_SELECTOR` as a return /// value. - #[ink(message)] + #[ink(message, selector = 0xBC197C81)] fn on_batch_received( &mut self, operator: AccountId, diff --git a/examples/erc20/lib.rs b/examples/erc20/lib.rs index 755be1d6bbf..ad05e33d598 100644 --- a/examples/erc20/lib.rs +++ b/examples/erc20/lib.rs @@ -198,7 +198,7 @@ mod erc20 { /// Imports all the definitions from the outer scope so we can use them here. use super::*; - type Event = ::Type; + type Event = ::Type; use ink_lang as ink; @@ -518,7 +518,7 @@ mod erc20 { use ink_env::Clear; use ink_lang as ink; - type Event = ::Type; + type Event = ::Type; fn assert_transfer_event( event: &ink_env::test::EmittedEvent, diff --git a/examples/multisig/lib.rs b/examples/multisig/lib.rs index 9ada7e43c29..658fc335f5a 100755 --- a/examples/multisig/lib.rs +++ b/examples/multisig/lib.rs @@ -484,7 +484,7 @@ mod multisig { self.ensure_confirmed(trans_id); let t = self.take_transaction(trans_id).expect(WRONG_TRANSACTION_ID); assert!(self.env().transferred_balance() == t.transferred_value); - let result = build_call::<::Env>() + let result = build_call::<::Env>() .callee(t.callee) .gas_limit(t.gas_limit) .transferred_value(t.transferred_value) @@ -513,7 +513,7 @@ mod multisig { ) -> Result, Error> { self.ensure_confirmed(trans_id); let t = self.take_transaction(trans_id).expect(WRONG_TRANSACTION_ID); - let result = build_call::<::Env>() + let result = build_call::<::Env>() .callee(t.callee) .gas_limit(t.gas_limit) .transferred_value(t.transferred_value) diff --git a/examples/trait-erc20/lib.rs b/examples/trait-erc20/lib.rs index a7a737dc436..383eab63af0 100644 --- a/examples/trait-erc20/lib.rs +++ b/examples/trait-erc20/lib.rs @@ -4,16 +4,7 @@ use ink_lang as ink; #[ink::contract] mod erc20 { - #[cfg(not(feature = "ink-as-dependency"))] use ink_lang as ink; - - #[cfg(not(feature = "ink-as-dependency"))] - use ink_lang::{ - EmitEvent, - Env, - }; - - #[cfg(not(feature = "ink-as-dependency"))] use ink_storage::{ collections::HashMap as StorageHashMap, lazy::Lazy, @@ -35,10 +26,6 @@ mod erc20 { /// Trait implemented by all ERC-20 respecting smart contracts. #[ink::trait_definition] pub trait BaseErc20 { - /// Creates a new ERC-20 contract with the specified initial supply. - #[ink(constructor)] - fn new(initial_supply: Balance) -> Self; - /// Returns the total token supply. #[ink(message)] fn total_supply(&self) -> Balance; @@ -105,10 +92,10 @@ mod erc20 { value: Balance, } - impl BaseErc20 for Erc20 { + impl Erc20 { /// Creates a new ERC-20 contract with the specified initial supply. #[ink(constructor)] - fn new(initial_supply: Balance) -> Self { + pub fn new(initial_supply: Balance) -> Self { let caller = Self::env().caller(); let mut balances = StorageHashMap::new(); balances.insert(caller, initial_supply); @@ -124,7 +111,9 @@ mod erc20 { }); instance } + } + impl BaseErc20 for Erc20 { /// Returns the total token supply. #[ink(message)] fn total_supply(&self) -> Balance { @@ -211,6 +200,7 @@ mod erc20 { } } + #[ink(impl)] impl Erc20 { /// Transfers `value` amount of tokens from the caller's account to account `to`. /// @@ -257,7 +247,7 @@ mod erc20 { }; use ink_lang as ink; - type Event = ::Type; + type Event = ::Type; fn assert_transfer_event( event: &ink_env::test::EmittedEvent, @@ -293,7 +283,7 @@ mod erc20 { result.as_mut()[0..copy_len].copy_from_slice(&hash_output[0..copy_len]); result } - let expected_topics = vec![ + let expected_topics = [ encoded_into_hash(&PrefixedValue { prefix: b"", value: b"Erc20::Transfer", diff --git a/examples/trait-flipper/lib.rs b/examples/trait-flipper/lib.rs index 8bc0347b30d..46a7c01148e 100644 --- a/examples/trait-flipper/lib.rs +++ b/examples/trait-flipper/lib.rs @@ -1,13 +1,10 @@ #![cfg_attr(not(feature = "std"), no_std)] +#![allow(clippy::new_without_default)] use ink_lang as ink; #[ink::trait_definition] pub trait Flip { - /// Creates a new flipper smart contract initialized with the given value. - #[ink(constructor)] - fn new(init_value: bool) -> Self; - /// Flips the current value of the Flipper's boolean. #[ink(message)] fn flip(&mut self); @@ -29,17 +26,14 @@ pub mod flipper { impl Flipper { /// Creates a new flipper smart contract initialized to `false`. #[ink(constructor)] - pub fn default() -> Self { - Self::new(Default::default()) + pub fn new() -> Self { + Self { + value: Default::default(), + } } } impl Flip for Flipper { - #[ink(constructor)] - fn new(init_value: bool) -> Self { - Self { value: init_value } - } - #[ink(message)] fn flip(&mut self) { self.value = !self.value; @@ -58,13 +52,13 @@ pub mod flipper { #[ink::test] fn default_works() { - let flipper = Flipper::default(); + let flipper = Flipper::new(); assert!(!flipper.get()); } #[ink::test] fn it_works() { - let mut flipper = Flipper::new(false); + let mut flipper = Flipper::new(); // Can call using universal call syntax using the trait. assert!(!::get(&flipper)); ::flip(&mut flipper); diff --git a/examples/trait-incrementer/lib.rs b/examples/trait-incrementer/lib.rs index da1ec86357a..09d701f8f53 100644 --- a/examples/trait-incrementer/lib.rs +++ b/examples/trait-incrementer/lib.rs @@ -53,10 +53,8 @@ pub mod incrementer { impl Incrementer { /// Creates a new incrementer smart contract initialized with zero. #[ink(constructor)] - pub fn new() -> Self { - Self { - value: Default::default(), - } + pub fn new(init_value: u64) -> Self { + Self { value: init_value } } /// Increases the value of the incrementer by an amount. @@ -91,13 +89,13 @@ pub mod incrementer { #[test] fn default_works() { - let incrementer = Incrementer::new(); + let incrementer = Incrementer::new(0); assert_eq!(incrementer.get(), 0); } #[test] fn it_works() { - let mut incrementer = Incrementer::new(); + let mut incrementer = Incrementer::new(0); // Can call using universal call syntax using the trait. assert_eq!(::get(&incrementer), 0); ::inc(&mut incrementer);