Skip to content

Commit

Permalink
Unique trait id as a parameter for CallBuilder trait impls (#1141)
Browse files Browse the repository at this point in the history
* Copy multiple foreign traits example from other PR

* Rename to TraitInfo and add ID const

* Use TraitInfo::ID to resolve CallBuilder impl

* clean up todos and docs

* clippy

* Fix message selector overlap UI tests

* Update examples/trait-incrementer/Cargo.toml

Co-authored-by: Hernando Castano <[email protected]>

* Update examples/trait-incrementer/Cargo.toml

Co-authored-by: Hernando Castano <[email protected]>

* Update examples/trait-incrementer/traits/lib.rs

Co-authored-by: Hernando Castano <[email protected]>

* Update examples/trait-incrementer/traits/Cargo.toml

Co-authored-by: Hernando Castano <[email protected]>

* Update examples/trait-incrementer/traits/Cargo.toml

Co-authored-by: Hernando Castano <[email protected]>

Co-authored-by: Hernando Castano <[email protected]>
  • Loading branch information
ascjones and HCastano authored Feb 21, 2022
1 parent 9fd0317 commit 2f86746
Show file tree
Hide file tree
Showing 16 changed files with 153 additions and 41 deletions.
4 changes: 2 additions & 2 deletions crates/lang/codegen/src/generator/arg_list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ pub fn generate_reference_to_trait_info(
trait_path: &syn::Path,
) -> TokenStream2 {
quote_spanned!(span=>
<::ink_lang::reflect::TraitDefinitionRegistry<Environment>
as #trait_path>::__ink_TraitInfo
<<::ink_lang::reflect::TraitDefinitionRegistry<Environment>
as #trait_path>::__ink_TraitInfo as ::ink_lang::reflect::TraitInfo>::ID
)
}
14 changes: 7 additions & 7 deletions crates/lang/codegen/src/generator/as_dependency/call_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 = <<Self as #trait_path>::__ink_TraitInfo as ::ink_lang::codegen::TraitCallForwarder>::Forwarder;

#[inline]
Expand Down Expand Up @@ -217,7 +217,7 @@ impl CallBuilder<'_> {
#[inline]
fn build(&self) -> &<Self::Forwarder as ::ink_lang::codegen::TraitCallBuilder>::Builder {
<_ as ::ink_lang::codegen::TraitCallBuilder>::call(
<Self as ::ink_lang::codegen::TraitCallForwarderFor<#trait_info>>::forward(self)
<Self as ::ink_lang::codegen::TraitCallForwarderFor<{#trait_info_id}>>::forward(self)
)
}

Expand All @@ -226,7 +226,7 @@ impl CallBuilder<'_> {
-> &mut <Self::Forwarder as ::ink_lang::codegen::TraitCallBuilder>::Builder
{
<_ as ::ink_lang::codegen::TraitCallBuilder>::call_mut(
<Self as ::ink_lang::codegen::TraitCallForwarderFor<#trait_info>>::forward_mut(self)
<Self as ::ink_lang::codegen::TraitCallForwarderFor<{#trait_info_id}>>::forward_mut(self)
)
}
}
Expand Down Expand Up @@ -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()
Expand All @@ -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;

Expand All @@ -294,7 +294,7 @@ impl CallBuilder<'_> {
#( , #input_bindings: #input_types )*
) -> Self::#output_ident {
<_ as #trait_path>::#message_ident(
<Self as ::ink_lang::codegen::TraitCallForwarderFor<#trait_info>>::#build_cmd(self)
<Self as ::ink_lang::codegen::TraitCallForwarderFor<{#trait_info_id}>>::#build_cmd(self)
#( , #input_bindings )*
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand All @@ -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(
<Self as ::ink_lang::codegen::TraitCallBuilder>::#call_operator(self),
)
#( , #input_bindings )*
Expand Down
24 changes: 23 additions & 1 deletion crates/lang/codegen/src/generator/trait_def/trait_registry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -256,10 +257,12 @@ impl TraitRegistry<'_> {

#trait_message_info

impl<E> ::ink_lang::reflect::TraitModulePath for #trait_info_ident<E>
impl<E> ::ink_lang::reflect::TraitInfo for #trait_info_ident<E>
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);
Expand All @@ -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 {
Expand Down
2 changes: 1 addition & 1 deletion crates/lang/src/codegen/trait_def/call_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<TraitInfo> {
pub trait TraitCallForwarderFor<const TRAIT_ID: u32> {
type Forwarder: TraitCallBuilder;

/// Forwards the `&self` call.
Expand Down
2 changes: 1 addition & 1 deletion crates/lang/src/reflect/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ pub use self::{
event::ContractEventBase,
trait_def::{
TraitDefinitionRegistry,
TraitInfo,
TraitMessageInfo,
TraitModulePath,
},
};
10 changes: 5 additions & 5 deletions crates/lang/src/reflect/trait_def/info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -136,11 +136,11 @@ pub trait TraitMessageInfo<const TRAIT_LOCAL_MESSAGE_ID: u32> {
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
Expand Down
2 changes: 1 addition & 1 deletion crates/lang/src/reflect/trait_def/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ mod registry;

pub use self::{
info::{
TraitInfo,
TraitMessageInfo,
TraitModulePath,
},
registry::TraitDefinitionRegistry,
};
Original file line number Diff line number Diff line change
Expand Up @@ -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`
Original file line number Diff line number Diff line change
Expand Up @@ -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`
Original file line number Diff line number Diff line change
Expand Up @@ -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`
2 changes: 2 additions & 0 deletions examples/trait-incrementer/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -29,5 +30,6 @@ std = [
"ink_lang/std",
"scale/std",
"scale-info/std",
"traits/std",
]
ink-as-dependency = []
22 changes: 1 addition & 21 deletions examples/trait-incrementer/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};
Expand Down
9 changes: 9 additions & 0 deletions examples/trait-incrementer/traits/.gitignore
Original file line number Diff line number Diff line change
@@ -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
33 changes: 33 additions & 0 deletions examples/trait-incrementer/traits/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
[package]
name = "traits"
version = "3.0.0-rc8"
authors = ["Parity Technologies <[email protected]>"]
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 = []
39 changes: 39 additions & 0 deletions examples/trait-incrementer/traits/lib.rs
Original file line number Diff line number Diff line change
@@ -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);
}

0 comments on commit 2f86746

Please sign in to comment.