diff --git a/crates/lang/codegen/src/generator/arg_list.rs b/crates/lang/codegen/src/generator/arg_list.rs index bcc771a46cb..913fbc64dc0 100644 --- a/crates/lang/codegen/src/generator/arg_list.rs +++ b/crates/lang/codegen/src/generator/arg_list.rs @@ -98,7 +98,7 @@ pub fn generate_reference_to_trait_info( trait_path: &syn::Path, ) -> TokenStream2 { quote_spanned!(span=> - <::ink_lang::reflect::TraitDefinitionRegistry - as #trait_path>::__ink_TraitInfo + <<::ink_lang::reflect::TraitDefinitionRegistry + as #trait_path>::__ink_TraitInfo as ::ink_lang::reflect::TraitInfo>::ID ) } diff --git a/crates/lang/codegen/src/generator/as_dependency/call_builder.rs b/crates/lang/codegen/src/generator/as_dependency/call_builder.rs index 17735a0ae05..bdd5a2fc1f3 100644 --- a/crates/lang/codegen/src/generator/as_dependency/call_builder.rs +++ b/crates/lang/codegen/src/generator/as_dependency/call_builder.rs @@ -182,10 +182,10 @@ impl CallBuilder<'_> { ) -> 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); + let trait_info_id = generator::generate_reference_to_trait_info(span, trait_path); quote_spanned!(span=> #[doc(hidden)] - impl ::ink_lang::codegen::TraitCallForwarderFor<#trait_info> for #cb_ident { + impl ::ink_lang::codegen::TraitCallForwarderFor<{#trait_info_id}> for #cb_ident { type Forwarder = <::__ink_TraitInfo as ::ink_lang::codegen::TraitCallForwarder>::Forwarder; #[inline] @@ -217,7 +217,7 @@ impl CallBuilder<'_> { #[inline] fn build(&self) -> &::Builder { <_ as ::ink_lang::codegen::TraitCallBuilder>::call( - >::forward(self) + >::forward(self) ) } @@ -226,7 +226,7 @@ impl CallBuilder<'_> { -> &mut ::Builder { <_ as ::ink_lang::codegen::TraitCallBuilder>::call_mut( - >::forward_mut(self) + >::forward_mut(self) ) } } @@ -265,7 +265,7 @@ impl CallBuilder<'_> { 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 trait_info_id = generator::generate_reference_to_trait_info(span, trait_path); let (input_bindings, input_types): (Vec<_>, Vec<_>) = message .callable() .inputs() @@ -283,7 +283,7 @@ impl CallBuilder<'_> { quote_spanned!(span=> type #output_ident = <<< Self - as ::ink_lang::codegen::TraitCallForwarderFor<#trait_info>>::Forwarder + as ::ink_lang::codegen::TraitCallForwarderFor<{#trait_info_id}>>::Forwarder as ::ink_lang::codegen::TraitCallBuilder>::Builder as #trait_path>::#output_ident; @@ -294,7 +294,7 @@ impl CallBuilder<'_> { #( , #input_bindings: #input_types )* ) -> Self::#output_ident { <_ as #trait_path>::#message_ident( - >::#build_cmd(self) + >::#build_cmd(self) #( , #input_bindings )* ) } diff --git a/crates/lang/codegen/src/generator/as_dependency/contract_ref.rs b/crates/lang/codegen/src/generator/as_dependency/contract_ref.rs index 292ed7a009a..d1c4a8921d3 100644 --- a/crates/lang/codegen/src/generator/as_dependency/contract_ref.rs +++ b/crates/lang/codegen/src/generator/as_dependency/contract_ref.rs @@ -242,7 +242,7 @@ impl ContractRef<'_> { ) -> TokenStream2 { use ir::Callable as _; let span = message.span(); - let trait_info = generator::generate_reference_to_trait_info(span, trait_path); + let trait_info_id = 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() { @@ -266,7 +266,7 @@ impl ContractRef<'_> { #( , #input_bindings : #input_types )* ) -> Self::#output_ident { <_ as #trait_path>::#message_ident( - <_ as ::ink_lang::codegen::TraitCallForwarderFor<#trait_info>>::#forward_operator( + <_ as ::ink_lang::codegen::TraitCallForwarderFor<{#trait_info_id}>>::#forward_operator( ::#call_operator(self), ) #( , #input_bindings )* diff --git a/crates/lang/codegen/src/generator/trait_def/trait_registry.rs b/crates/lang/codegen/src/generator/trait_def/trait_registry.rs index a7b822e94c2..8b536d6708b 100644 --- a/crates/lang/codegen/src/generator/trait_def/trait_registry.rs +++ b/crates/lang/codegen/src/generator/trait_def/trait_registry.rs @@ -243,6 +243,7 @@ impl TraitRegistry<'_> { /// 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_id = self.generate_trait_id(); 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(); @@ -256,10 +257,12 @@ impl TraitRegistry<'_> { #trait_message_info - impl ::ink_lang::reflect::TraitModulePath for #trait_info_ident + impl ::ink_lang::reflect::TraitInfo for #trait_info_ident where E: ::ink_env::Environment, { + const ID: u32 = #trait_id; + const PATH: &'static ::core::primitive::str = ::core::module_path!(); const NAME: &'static ::core::primitive::str = ::core::stringify!(#trait_ident); @@ -274,6 +277,25 @@ impl TraitRegistry<'_> { ) } + /// Generates a unique id for the trait, as an XOR of the set of selectors. + fn generate_trait_id(&self) -> syn::LitInt { + let span = self.span(); + let mut id = 0u32; + debug_assert!( + self.trait_def + .trait_def + .item() + .iter_items() + .next() + .is_some(), + "invalid empty ink! trait definition" + ); + for (_, selector) in self.trait_def.trait_def.item().iter_items() { + id ^= selector.into_be_u32() + } + syn::LitInt::new(&format!("{}", id), span) + } + /// 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 { diff --git a/crates/lang/src/codegen/trait_def/call_builder.rs b/crates/lang/src/codegen/trait_def/call_builder.rs index c1622b3c200..74ec7cd5876 100644 --- a/crates/lang/src/codegen/trait_def/call_builder.rs +++ b/crates/lang/src/codegen/trait_def/call_builder.rs @@ -48,7 +48,7 @@ pub trait TraitCallForwarder { /// 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 { +pub trait TraitCallForwarderFor { type Forwarder: TraitCallBuilder; /// Forwards the `&self` call. diff --git a/crates/lang/src/reflect/mod.rs b/crates/lang/src/reflect/mod.rs index bca4f0aca01..3f59becff34 100644 --- a/crates/lang/src/reflect/mod.rs +++ b/crates/lang/src/reflect/mod.rs @@ -49,7 +49,7 @@ pub use self::{ event::ContractEventBase, trait_def::{ TraitDefinitionRegistry, + TraitInfo, TraitMessageInfo, - TraitModulePath, }, }; diff --git a/crates/lang/src/reflect/trait_def/info.rs b/crates/lang/src/reflect/trait_def/info.rs index 3b2734db150..5c561b09306 100644 --- a/crates/lang/src/reflect/trait_def/info.rs +++ b/crates/lang/src/reflect/trait_def/info.rs @@ -136,11 +136,11 @@ pub trait TraitMessageInfo { 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 { +/// Captures info about an ink! trait definition. +pub trait TraitInfo { + /// The unique id of the ink! trait definition. + const ID: u32; + /// The module path of the ink! trait definition. /// /// This is equivalent to Rust's builtin `module_path!` macro diff --git a/crates/lang/src/reflect/trait_def/mod.rs b/crates/lang/src/reflect/trait_def/mod.rs index 54884f41026..9f9fdb64064 100644 --- a/crates/lang/src/reflect/trait_def/mod.rs +++ b/crates/lang/src/reflect/trait_def/mod.rs @@ -17,8 +17,8 @@ mod registry; pub use self::{ info::{ + TraitInfo, TraitMessageInfo, - TraitModulePath, }, registry::TraitDefinitionRegistry, }; 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 index 8c550382817..6ea91ecc4eb 100644 --- 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 @@ -6,3 +6,12 @@ error[E0119]: conflicting implementations of trait `ink_lang::reflect::Dispatcha ... 47 | fn message(&self) {} | ^^^^^^^^^^^^^^^^^ conflicting implementation for `contract::Contract` + +error[E0119]: conflicting implementations of trait `ink_lang::codegen::TraitCallForwarderFor<1083895717_u32>` for type `contract::_::CallBuilder` + --> tests/ui/contract/fail/trait-message-selector-overlap-1.rs:45:5 + | +40 | impl TraitDefinition1 for Contract { + | ---------------------------------- first implementation here +... +45 | impl TraitDefinition2 for Contract { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `contract::_::CallBuilder` 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 index ab83edd8e3d..9fd4346af04 100644 --- 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 @@ -6,3 +6,12 @@ error[E0119]: conflicting implementations of trait `ink_lang::reflect::Dispatcha ... 47 | fn message(&self) {} | ^^^^^^^^^^^^^^^^^ conflicting implementation for `contract::Contract` + +error[E0119]: conflicting implementations of trait `ink_lang::codegen::TraitCallForwarderFor<1518209067_u32>` for type `contract::_::CallBuilder` + --> tests/ui/contract/fail/trait-message-selector-overlap-2.rs:45:5 + | +40 | impl TraitDefinition1 for Contract { + | ---------------------------------- first implementation here +... +45 | impl TraitDefinition2 for Contract { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `contract::_::CallBuilder` 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 index 3e99b03a02b..d8b948ebb4e 100644 --- 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 @@ -6,3 +6,12 @@ error[E0119]: conflicting implementations of trait `ink_lang::reflect::Dispatcha ... 47 | fn message2(&self) {} | ^^^^^^^^^^^^^^^^^^ conflicting implementation for `contract::Contract` + +error[E0119]: conflicting implementations of trait `ink_lang::codegen::TraitCallForwarderFor<42_u32>` for type `contract::_::CallBuilder` + --> tests/ui/contract/fail/trait-message-selector-overlap-3.rs:45:5 + | +40 | impl TraitDefinition1 for Contract { + | ---------------------------------- first implementation here +... +45 | impl TraitDefinition2 for Contract { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `contract::_::CallBuilder` diff --git a/examples/trait-incrementer/Cargo.toml b/examples/trait-incrementer/Cargo.toml index 6ef71a3987b..3741c05c65c 100644 --- a/examples/trait-incrementer/Cargo.toml +++ b/examples/trait-incrementer/Cargo.toml @@ -13,6 +13,7 @@ ink_lang = { version = "3.0.0-rc8", path = "../../crates/lang", default-features scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] } scale-info = { version = "2", default-features = false, features = ["derive"], optional = true } +traits = { path = "./traits", default-features = false } [lib] name = "trait_incrementer" @@ -29,5 +30,6 @@ std = [ "ink_lang/std", "scale/std", "scale-info/std", + "traits/std", ] ink-as-dependency = [] diff --git a/examples/trait-incrementer/lib.rs b/examples/trait-incrementer/lib.rs index 8f3374d9141..af21de87f83 100644 --- a/examples/trait-incrementer/lib.rs +++ b/examples/trait-incrementer/lib.rs @@ -3,29 +3,9 @@ use ink_lang as ink; -/// Allows to increment and get the current value. -#[ink::trait_definition] -pub trait Increment { - /// Increments the current value of the implementer by one (1). - #[ink(message)] - fn inc(&mut self); - - /// Returns the current value of the implementer. - #[ink(message)] - fn get(&self) -> u64; -} - -/// Allows to reset the current value. -#[ink::trait_definition] -pub trait Reset { - /// Resets the current value to zero. - #[ink(message)] - fn reset(&mut self); -} - #[ink::contract] pub mod incrementer { - use super::{ + use traits::{ Increment, Reset, }; diff --git a/examples/trait-incrementer/traits/.gitignore b/examples/trait-incrementer/traits/.gitignore new file mode 100644 index 00000000000..bf910de10af --- /dev/null +++ b/examples/trait-incrementer/traits/.gitignore @@ -0,0 +1,9 @@ +# Ignore build artifacts from the local tests sub-crate. +/target/ + +# Ignore backup files creates by cargo fmt. +**/*.rs.bk + +# Remove Cargo.lock when creating an executable, leave it for libraries +# More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock +Cargo.lock \ No newline at end of file diff --git a/examples/trait-incrementer/traits/Cargo.toml b/examples/trait-incrementer/traits/Cargo.toml new file mode 100644 index 00000000000..69a32d9f964 --- /dev/null +++ b/examples/trait-incrementer/traits/Cargo.toml @@ -0,0 +1,33 @@ +[package] +name = "traits" +version = "3.0.0-rc8" +authors = ["Parity Technologies "] +edition = "2021" + +[dependencies] +ink_primitives = { version = "3.0.0-rc8", path = "../../../crates/primitives", default-features = false } +ink_metadata = { version = "3.0.0-rc8", path = "../../../crates/metadata", default-features = false, features = ["derive"], optional = true } +ink_env = { version = "3.0.0-rc8", path = "../../../crates/env", default-features = false } +ink_storage = { version = "3.0.0-rc8", path = "../../../crates/storage", default-features = false } +ink_lang = { version = "3.0.0-rc8", path = "../../../crates/lang", default-features = false } + +scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] } +scale-info = { version = "2", default-features = false, features = ["derive"], optional = true } + +[lib] +name = "traits" +path = "lib.rs" +crate-type = ["rlib"] + +[features] +default = ["std"] +std = [ + "ink_primitives/std", + "ink_metadata/std", + "ink_env/std", + "ink_storage/std", + "ink_lang/std", + "scale/std", + "scale-info/std", +] +ink-as-dependency = [] diff --git a/examples/trait-incrementer/traits/lib.rs b/examples/trait-incrementer/traits/lib.rs new file mode 100644 index 00000000000..ef7d844d4b7 --- /dev/null +++ b/examples/trait-incrementer/traits/lib.rs @@ -0,0 +1,39 @@ +// Copyright 2018-2022 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. +#![cfg_attr(not(feature = "std"), no_std)] + +//! Traits are extracted into a separate crate to show how the user can import +//! several foreign traits and implement those for the contract. + +use ink_lang as ink; + +/// Allows to increment and get the current value. +#[ink::trait_definition] +pub trait Increment { + /// Increments the current value of the implementer by one (1). + #[ink(message)] + fn inc(&mut self); + + /// Returns the current value of the implementer. + #[ink(message)] + fn get(&self) -> u64; +} + +/// Allows to reset the current value. +#[ink::trait_definition] +pub trait Reset { + /// Resets the current value to zero. + #[ink(message)] + fn reset(&mut self); +}