Unverified Commit a2a4cba4 authored by Keith Yeung's avatar Keith Yeung Committed by GitHub
Browse files

Use proc macros to generate conversion functions for MultiLocation (#3635)

* Use proc macros to generate conversion functions for MultiLocation

* Add compile test and missing conversion cases

* Add common derives for Parent and Ancestor

* Generate conversion functions for MultiLocation v0 via proc macro

* Add type conversion test and fix a bug

* cargo fmt

* Do not hardcode 8 as the number of max parents

* Use map instead of for loops when generating code fragments

* Spelling

* cargo fmt

* More mapping, less for-looping
parent 616667f1
Pipeline #153395 canceled with stages
in 26 minutes and 30 seconds
......@@ -11775,6 +11775,7 @@ dependencies = [
"impl-trait-for-tuples",
"log",
"parity-scale-codec",
"xcm-procedural",
]
[[package]]
......@@ -11835,6 +11836,15 @@ dependencies = [
"xcm-executor",
]
[[package]]
name = "xcm-procedural"
version = "0.1.0"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "xcm-simulator"
version = "0.9.9"
......
......@@ -43,6 +43,7 @@ members = [
"xcm/xcm-simulator",
"xcm/xcm-simulator/example",
"xcm/pallet-xcm",
"xcm/procedural",
"node/client",
"node/collation-generation",
"node/core/approval-voting",
......
......@@ -10,6 +10,7 @@ impl-trait-for-tuples = "0.2.0"
parity-scale-codec = { version = "2.0.0", default-features = false, features = [ "derive" ] }
derivative = {version = "2.2.0", default-features = false, features = [ "use_core" ] }
log = { version = "0.4.14", default-features = false }
xcm-procedural = { path = "procedural" }
[features]
default = ["std"]
......
[package]
authors = ["Parity Technologies <admin@parity.io>"]
name = "xcm-procedural"
version = "0.1.0"
edition = "2018"
[lib]
proc-macro = true
[dependencies]
proc-macro2 = "1.0.28"
quote = "1.0.9"
syn = "1.0.74"
// Copyright 2020 Parity Technologies (UK) Ltd.
// This file is part of Polkadot.
// Polkadot is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Polkadot is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
//! Procedural macros used in XCM.
use proc_macro::TokenStream;
mod v0;
mod v1;
#[proc_macro]
pub fn impl_conversion_functions_for_multilocation_v0(input: TokenStream) -> TokenStream {
v0::multilocation::generate_conversion_functions(input)
.unwrap_or_else(syn::Error::into_compile_error)
.into()
}
#[proc_macro]
pub fn impl_conversion_functions_for_multilocation_v1(input: TokenStream) -> TokenStream {
v1::multilocation::generate_conversion_functions(input)
.unwrap_or_else(syn::Error::into_compile_error)
.into()
}
// Copyright 2021 Parity Technologies (UK) Ltd.
// This file is part of Polkadot.
// Polkadot is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Polkadot is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
pub mod multilocation;
// Copyright 2021 Parity Technologies (UK) Ltd.
// This file is part of Polkadot.
// Polkadot is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Polkadot is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
use proc_macro2::{Span, TokenStream};
use quote::{format_ident, quote};
pub fn generate_conversion_functions(input: proc_macro::TokenStream) -> syn::Result<TokenStream> {
if !input.is_empty() {
return Err(syn::Error::new(Span::call_site(), "No arguments expected"))
}
let from_tuples = generate_conversion_from_tuples();
let from_v1 = generate_conversion_from_v1();
Ok(quote! {
#from_tuples
#from_v1
})
}
fn generate_conversion_from_tuples() -> TokenStream {
let from_tuples = (0..8usize)
.map(|num_junctions| {
let junctions =
(0..=num_junctions).map(|_| format_ident!("Junction")).collect::<Vec<_>>();
let idents = (0..=num_junctions).map(|i| format_ident!("j{}", i)).collect::<Vec<_>>();
let variant = &format_ident!("X{}", num_junctions + 1);
let array_size = num_junctions + 1;
quote! {
impl From<( #(#junctions,)* )> for MultiLocation {
fn from( ( #(#idents,)* ): ( #(#junctions,)* ) ) -> Self {
MultiLocation::#variant( #(#idents),* )
}
}
impl From<[Junction; #array_size]> for MultiLocation {
fn from(j: [Junction; #array_size]) -> Self {
let [#(#idents),*] = j;
MultiLocation::#variant( #(#idents),* )
}
}
}
})
.collect::<TokenStream>();
quote! {
impl From<()> for MultiLocation {
fn from(_: ()) -> Self {
MultiLocation::Null
}
}
impl From<Junction> for MultiLocation {
fn from(x: Junction) -> Self {
MultiLocation::X1(x)
}
}
impl From<[Junction; 0]> for MultiLocation {
fn from(_: [Junction; 0]) -> Self {
MultiLocation::Null
}
}
#from_tuples
}
}
fn generate_conversion_from_v1() -> TokenStream {
let match_variants = (0..8u8)
.map(|cur_num| {
let variant = format_ident!("X{}", cur_num + 1);
let idents = (1..=cur_num).map(|i| format_ident!("j{}", i)).collect::<Vec<_>>();
quote! {
crate::v1::Junctions::#variant( j0 #(, #idents)* ) => res
.pushed_with(Junction::from(j0))
#( .and_then(|res| res.pushed_with(Junction::from(#idents))) )*
.map_err(|_| ()),
}
})
.collect::<TokenStream>();
quote! {
impl TryFrom<crate::v1::MultiLocation> for MultiLocation {
type Error = ();
fn try_from(v1: crate::v1::MultiLocation) -> core::result::Result<Self, ()> {
let mut res = MultiLocation::Null;
for _ in 0..v1.parents {
res.push(Junction::Parent)?;
}
match v1.interior {
crate::v1::Junctions::Here => Ok(res),
#match_variants
}
}
}
}
}
// Copyright 2021 Parity Technologies (UK) Ltd.
// This file is part of Polkadot.
// Polkadot is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Polkadot is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
pub mod multilocation;
// Copyright 2021 Parity Technologies (UK) Ltd.
// This file is part of Polkadot.
// Polkadot is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Polkadot is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
use proc_macro2::{Span, TokenStream};
use quote::{format_ident, quote};
use syn::{Result, Token};
pub fn generate_conversion_functions(input: proc_macro::TokenStream) -> Result<TokenStream> {
if !input.is_empty() {
return Err(syn::Error::new(Span::call_site(), "No arguments expected"))
}
// Support up to 8 Parents in a tuple, assuming that most use cases don't go past 8 parents.
let from_tuples = generate_conversion_from_tuples(8);
let from_v0 = generate_conversion_from_v0();
Ok(quote! {
#from_tuples
#from_v0
})
}
fn generate_conversion_from_tuples(max_parents: u8) -> TokenStream {
let mut from_tuples = (0..8usize)
.map(|num_junctions| {
let junctions =
(0..=num_junctions).map(|_| format_ident!("Junction")).collect::<Vec<_>>();
let idents = (0..=num_junctions).map(|i| format_ident!("j{}", i)).collect::<Vec<_>>();
let variant = &format_ident!("X{}", num_junctions + 1);
let array_size = num_junctions + 1;
let mut from_tuple = quote! {
impl From<( #(#junctions,)* )> for MultiLocation {
fn from( ( #(#idents,)* ): ( #(#junctions,)* ) ) -> Self {
MultiLocation { parents: 0, interior: Junctions::#variant( #(#idents),* ) }
}
}
impl From<(u8, #(#junctions),*)> for MultiLocation {
fn from( ( parents, #(#idents),* ): (u8, #(#junctions),* ) ) -> Self {
MultiLocation { parents, interior: Junctions::#variant( #(#idents),* ) }
}
}
impl From<(Ancestor, #(#junctions),*)> for MultiLocation {
fn from( ( Ancestor(parents), #(#idents),* ): (Ancestor, #(#junctions),* ) ) -> Self {
MultiLocation { parents, interior: Junctions::#variant( #(#idents),* ) }
}
}
impl From<[Junction; #array_size]> for MultiLocation {
fn from(j: [Junction; #array_size]) -> Self {
let [#(#idents),*] = j;
MultiLocation { parents: 0, interior: Junctions::#variant( #(#idents),* ) }
}
}
};
let from_parent_tuples = (1..=max_parents).map(|cur_parents| {
let parents = (0..cur_parents).map(|_| format_ident!("Parent")).collect::<Vec<_>>();
let underscores =
(0..cur_parents).map(|_| Token![_](Span::call_site())).collect::<Vec<_>>();
quote! {
impl From<( #(#parents,)* #(#junctions),* )> for MultiLocation {
fn from( (#(#underscores,)* #(#idents),*): ( #(#parents,)* #(#junctions),* ) ) -> Self {
MultiLocation { parents: #cur_parents, interior: Junctions::#variant( #(#idents),* ) }
}
}
}
});
from_tuple.extend(from_parent_tuples);
from_tuple
})
.collect::<TokenStream>();
let from_parent_junctions_tuples = (1..=max_parents).map(|cur_parents| {
let parents = (0..cur_parents).map(|_| format_ident!("Parent")).collect::<Vec<_>>();
let underscores =
(0..cur_parents).map(|_| Token![_](Span::call_site())).collect::<Vec<_>>();
quote! {
impl From<( #(#parents,)* Junctions )> for MultiLocation {
fn from( (#(#underscores,)* junctions): ( #(#parents,)* Junctions ) ) -> Self {
MultiLocation { parents: #cur_parents, interior: junctions }
}
}
}
});
from_tuples.extend(from_parent_junctions_tuples);
quote! {
impl From<Junctions> for MultiLocation {
fn from(junctions: Junctions) -> Self {
MultiLocation { parents: 0, interior: junctions }
}
}
impl From<(u8, Junctions)> for MultiLocation {
fn from((parents, interior): (u8, Junctions)) -> Self {
MultiLocation { parents, interior }
}
}
impl From<(Ancestor, Junctions)> for MultiLocation {
fn from((Ancestor(parents), interior): (Ancestor, Junctions)) -> Self {
MultiLocation { parents, interior }
}
}
impl From<()> for MultiLocation {
fn from(_: ()) -> Self {
MultiLocation { parents: 0, interior: Junctions::Here }
}
}
impl From<(u8,)> for MultiLocation {
fn from((parents,): (u8,)) -> Self {
MultiLocation { parents, interior: Junctions::Here }
}
}
impl From<Junction> for MultiLocation {
fn from(x: Junction) -> Self {
MultiLocation { parents: 0, interior: Junctions::X1(x) }
}
}
impl From<[Junction; 0]> for MultiLocation {
fn from(_: [Junction; 0]) -> Self {
MultiLocation { parents: 0, interior: Junctions::Here }
}
}
#from_tuples
}
}
fn generate_conversion_from_v0() -> TokenStream {
let match_variants = (0..8u8)
.map(|cur_num| {
let num_ancestors = cur_num + 1;
let variant = format_ident!("X{}", num_ancestors);
let idents = (0..=cur_num).map(|i| format_ident!("j{}", i)).collect::<Vec<_>>();
let intermediate_match_arms = (1..num_ancestors)
.rev()
.map(|parent_count| {
let parent_idents =
(0..parent_count).map(|j| format_ident!("j{}", j)).collect::<Vec<_>>();
let junction_idents = (parent_count..num_ancestors)
.map(|j| format_ident!("j{}", j))
.collect::<Vec<_>>();
let junction_variant = format_ident!("X{}", num_ancestors - parent_count);
quote! {
crate::v0::MultiLocation::#variant( #(#idents),* )
if #( #parent_idents.is_parent() )&&* =>
Ok(MultiLocation {
parents: #parent_count,
interior: #junction_variant( #( #junction_idents.try_into()? ),* ),
}),
}
})
.collect::<TokenStream>();
quote! {
crate::v0::MultiLocation::#variant( #(#idents),* )
if #( #idents.is_parent() )&&* =>
Ok(MultiLocation::ancestor(#num_ancestors)),
#intermediate_match_arms
crate::v0::MultiLocation::#variant( #(#idents),* ) =>
Ok( #variant( #( #idents.try_into()? ),* ).into() ),
}
})
.collect::<TokenStream>();
quote! {
impl TryFrom<crate::v0::MultiLocation> for MultiLocation {
type Error = ();
fn try_from(v0: crate::v0::MultiLocation) -> core::result::Result<Self, ()> {
use Junctions::*;
match v0 {
crate::v0::MultiLocation::Null => Ok(Here.into()),
#match_variants
}
}
}
}
}
......@@ -17,7 +17,6 @@
//! Cross-Consensus Message format data structures.
use super::Junction;
use crate::v1::MultiLocation as MultiLocation1;
use core::{convert::TryFrom, mem, result};
use parity_scale_codec::{self, Decode, Encode};
......@@ -68,117 +67,7 @@ pub enum MultiLocation {
/// Maximum number of junctions a `MultiLocation` can contain.
pub const MAX_MULTILOCATION_LENGTH: usize = 8;
impl From<Junction> for MultiLocation {
fn from(x: Junction) -> Self {
MultiLocation::X1(x)
}
}
impl From<()> for MultiLocation {
fn from(_: ()) -> Self {
MultiLocation::Null
}
}
impl From<(Junction,)> for MultiLocation {
fn from(x: (Junction,)) -> Self {
MultiLocation::X1(x.0)
}
}
impl From<(Junction, Junction)> for MultiLocation {
fn from(x: (Junction, Junction)) -> Self {
MultiLocation::X2(x.0, x.1)
}
}
impl From<(Junction, Junction, Junction)> for MultiLocation {
fn from(x: (Junction, Junction, Junction)) -> Self {
MultiLocation::X3(x.0, x.1, x.2)
}
}
impl From<(Junction, Junction, Junction, Junction)> for MultiLocation {
fn from(x: (Junction, Junction, Junction, Junction)) -> Self {
MultiLocation::X4(x.0, x.1, x.2, x.3)
}
}
impl From<(Junction, Junction, Junction, Junction, Junction)> for MultiLocation {
fn from(x: (Junction, Junction, Junction, Junction, Junction)) -> Self {
MultiLocation::X5(x.0, x.1, x.2, x.3, x.4)
}
}
impl From<(Junction, Junction, Junction, Junction, Junction, Junction)> for MultiLocation {
fn from(x: (Junction, Junction, Junction, Junction, Junction, Junction)) -> Self {
MultiLocation::X6(x.0, x.1, x.2, x.3, x.4, x.5)
}
}
impl From<(Junction, Junction, Junction, Junction, Junction, Junction, Junction)>
for MultiLocation
{
fn from(x: (Junction, Junction, Junction, Junction, Junction, Junction, Junction)) -> Self {
MultiLocation::X7(x.0, x.1, x.2, x.3, x.4, x.5, x.6)
}
}
impl From<(Junction, Junction, Junction, Junction, Junction, Junction, Junction, Junction)>
for MultiLocation
{
fn from(
x: (Junction, Junction, Junction, Junction, Junction, Junction, Junction, Junction),
) -> Self {
MultiLocation::X8(x.0, x.1, x.2, x.3, x.4, x.5, x.6, x.7)
}
}
impl From<[Junction; 0]> for MultiLocation {
fn from(_: [Junction; 0]) -> Self {
MultiLocation::Null
}
}
impl From<[Junction; 1]> for MultiLocation {
fn from(x: [Junction; 1]) -> Self {
let [x0] = x;
MultiLocation::X1(x0)
}
}
impl From<[Junction; 2]> for MultiLocation {
fn from(x: [Junction; 2]) -> Self {
let [x0, x1] = x;
MultiLocation::X2(x0, x1)
}
}
impl From<[Junction; 3]> for MultiLocation {
fn from(x: [Junction; 3]) -> Self {
let [x0, x1, x2] = x;
MultiLocation::X3(x0, x1, x2)
}
}
impl From<[Junction; 4]> for MultiLocation {
fn from(x: [Junction; 4]) -> Self {
let [x0, x1, x2, x3] = x;
MultiLocation::X4(x0, x1, x2, x3)
}
}
impl From<[Junction; 5]> for MultiLocation {
fn from(x: [Junction; 5]) -> Self {
let [x0, x1, x2, x3, x4] = x;
MultiLocation::X5(x0, x1, x2, x3, x4)
}
}
impl From<[Junction; 6]> for MultiLocation {
fn from(x: [Junction; 6]) -> Self {
let [x0, x1, x2, x3, x4, x5] = x;
MultiLocation::X6(x0, x1, x2, x3, x4, x5)
}
}
impl From<[Junction; 7]> for MultiLocation {
fn from(x: [Junction; 7]) -> Self {
let [x0, x1, x2, x3, x4, x5, x6] = x;
MultiLocation::X7(x0, x1, x2, x3, x4, x5, x6)
}
}
impl From<[Junction; 8]> for MultiLocation {
fn from(x: [Junction; 8]) -> Self {
let [x0, x1, x2, x3, x4, x5, x6, x7] = x;
MultiLocation::X8(x0, x1, x2, x3, x4, x5, x6, x7)
}
}
xcm_procedural::impl_conversion_functions_for_multilocation_v0!();
pub struct MultiLocationIterator(MultiLocation);
impl Iterator for MultiLocationIterator {
......@@ -696,75 +585,9 @@ impl MultiLocation {
}
}
impl TryFrom<MultiLocation1> for MultiLocation {
type Error = ();
fn try_from(v1: MultiLocation1) -> result::Result<Self, ()> {
use crate::v1::Junctions::*;
let mut res = Self::Null;
for _ in 0..v1.parents {
res.push(Junction::Parent)?;
}
match v1.interior {
Here => Ok(res),
X1(j0) => res.pushed_with(Junction::from(j0)).map_err(|_| ()),
X2(j0, j1) => res
.pushed_with(Junction::from(j0))