From 838dbcbe91695980a6a2defbf59346d1d9764d65 Mon Sep 17 00:00:00 2001
From: Gavin Wood <gavin@parity.io>
Date: Wed, 1 Jun 2022 17:27:47 +0100
Subject: [PATCH] Helper macro for `Morph` impls (#11570)

* Helper macro for Morph impls

* No need to deprecate for now

* Improved macro

* Doc tests

* Grumbles
---
 .../frame/ranked-collective/src/tests.rs      |   4 +-
 substrate/frame/support/src/traits.rs         |   2 +-
 .../frame/support/src/traits/dispatch.rs      |  24 --
 substrate/frame/support/src/traits/misc.rs    |  72 +---
 substrate/primitives/runtime/src/traits.rs    | 369 +++++++++++++++++-
 5 files changed, 376 insertions(+), 95 deletions(-)

diff --git a/substrate/frame/ranked-collective/src/tests.rs b/substrate/frame/ranked-collective/src/tests.rs
index 1426012b63c..4344a1be730 100644
--- a/substrate/frame/ranked-collective/src/tests.rs
+++ b/substrate/frame/ranked-collective/src/tests.rs
@@ -23,12 +23,12 @@ use frame_support::{
 	assert_noop, assert_ok,
 	error::BadOrigin,
 	parameter_types,
-	traits::{ConstU16, ConstU32, ConstU64, EitherOf, Everything, MapSuccess, Polling, ReduceBy},
+	traits::{ConstU16, ConstU32, ConstU64, EitherOf, Everything, MapSuccess, Polling},
 };
 use sp_core::H256;
 use sp_runtime::{
 	testing::Header,
-	traits::{BlakeTwo256, Identity, IdentityLookup},
+	traits::{BlakeTwo256, Identity, IdentityLookup, ReduceBy},
 };
 
 use super::*;
diff --git a/substrate/frame/support/src/traits.rs b/substrate/frame/support/src/traits.rs
index fe983f5c292..53bdd219aa3 100644
--- a/substrate/frame/support/src/traits.rs
+++ b/substrate/frame/support/src/traits.rs
@@ -97,7 +97,7 @@ mod dispatch;
 pub use dispatch::EnsureOneOf;
 pub use dispatch::{
 	AsEnsureOriginWithArg, DispatchableWithStorageLayer, EitherOf, EitherOfDiverse, EnsureOrigin,
-	EnsureOriginWithArg, MapSuccess, NeverEnsureOrigin, OriginTrait, ReduceBy, TryMapSuccess,
+	EnsureOriginWithArg, MapSuccess, NeverEnsureOrigin, OriginTrait, TryMapSuccess,
 	UnfilteredDispatchable,
 };
 
diff --git a/substrate/frame/support/src/traits/dispatch.rs b/substrate/frame/support/src/traits/dispatch.rs
index 720baf6c8a4..9bfb0145009 100644
--- a/substrate/frame/support/src/traits/dispatch.rs
+++ b/substrate/frame/support/src/traits/dispatch.rs
@@ -18,15 +18,12 @@
 //! Traits for dealing with dispatching calls and the origin from which they are dispatched.
 
 use crate::dispatch::{DispatchResultWithPostInfo, Parameter, RawOrigin};
-use sp_arithmetic::traits::{CheckedSub, Zero};
 use sp_runtime::{
 	traits::{BadOrigin, Member, Morph, TryMorph},
 	Either,
 };
 use sp_std::marker::PhantomData;
 
-use super::TypedGet;
-
 /// Some sort of check on the origin is performed by this object.
 pub trait EnsureOrigin<OuterOrigin> {
 	/// A return type.
@@ -226,27 +223,6 @@ impl<
 	}
 }
 
-/// Mutator which reduces a scalar by a particular amount.
-pub struct ReduceBy<N>(PhantomData<N>);
-impl<N: TypedGet> TryMorph<N::Type> for ReduceBy<N>
-where
-	N::Type: CheckedSub,
-{
-	type Outcome = N::Type;
-	fn try_morph(r: N::Type) -> Result<N::Type, ()> {
-		r.checked_sub(&N::get()).ok_or(())
-	}
-}
-impl<N: TypedGet> Morph<N::Type> for ReduceBy<N>
-where
-	N::Type: CheckedSub + Zero,
-{
-	type Outcome = N::Type;
-	fn morph(r: N::Type) -> N::Type {
-		r.checked_sub(&N::get()).unwrap_or(Zero::zero())
-	}
-}
-
 /// Type that can be dispatched with an origin but without checking the origin filter.
 ///
 /// Implemented for pallet dispatchable type by `decl_module` and for runtime dispatchable by
diff --git a/substrate/frame/support/src/traits/misc.rs b/substrate/frame/support/src/traits/misc.rs
index bea4e2a3944..ced6df8f971 100644
--- a/substrate/frame/support/src/traits/misc.rs
+++ b/substrate/frame/support/src/traits/misc.rs
@@ -21,6 +21,11 @@ use crate::dispatch::Parameter;
 use codec::{CompactLen, Decode, DecodeAll, Encode, EncodeLike, Input, MaxEncodedLen};
 use scale_info::{build::Fields, meta_type, Path, Type, TypeInfo, TypeParameter};
 use sp_arithmetic::traits::{CheckedAdd, CheckedMul, CheckedSub, Saturating};
+#[doc(hidden)]
+pub use sp_runtime::traits::{
+	ConstBool, ConstI128, ConstI16, ConstI32, ConstI64, ConstI8, ConstU128, ConstU16, ConstU32,
+	ConstU64, ConstU8, Get, GetDefault, TypedGet,
+};
 use sp_runtime::{traits::Block as BlockT, DispatchError};
 use sp_std::{cmp::Ordering, prelude::*};
 
@@ -387,73 +392,6 @@ where
 	}
 }
 
-/// A trait for querying a single value from a type defined in the trait.
-///
-/// It is not required that the value is constant.
-pub trait TypedGet {
-	/// The type which is returned.
-	type Type;
-	/// Return the current value.
-	fn get() -> Self::Type;
-}
-
-/// A trait for querying a single value from a type.
-///
-/// It is not required that the value is constant.
-pub trait Get<T> {
-	/// Return the current value.
-	fn get() -> T;
-}
-
-impl<T: Default> Get<T> for () {
-	fn get() -> T {
-		T::default()
-	}
-}
-
-/// Implement Get by returning Default for any type that implements Default.
-pub struct GetDefault;
-impl<T: Default> Get<T> for GetDefault {
-	fn get() -> T {
-		T::default()
-	}
-}
-
-macro_rules! impl_const_get {
-	($name:ident, $t:ty) => {
-		#[derive($crate::RuntimeDebug)]
-		pub struct $name<const T: $t>;
-		impl<const T: $t> Get<$t> for $name<T> {
-			fn get() -> $t {
-				T
-			}
-		}
-		impl<const T: $t> Get<Option<$t>> for $name<T> {
-			fn get() -> Option<$t> {
-				Some(T)
-			}
-		}
-		impl<const T: $t> TypedGet for $name<T> {
-			type Type = $t;
-			fn get() -> $t {
-				T
-			}
-		}
-	};
-}
-
-impl_const_get!(ConstBool, bool);
-impl_const_get!(ConstU8, u8);
-impl_const_get!(ConstU16, u16);
-impl_const_get!(ConstU32, u32);
-impl_const_get!(ConstU64, u64);
-impl_const_get!(ConstU128, u128);
-impl_const_get!(ConstI8, i8);
-impl_const_get!(ConstI16, i16);
-impl_const_get!(ConstI32, i32);
-impl_const_get!(ConstI64, i64);
-impl_const_get!(ConstI128, i128);
-
 /// A type for which some values make sense to be able to drop without further consideration.
 pub trait TryDrop: Sized {
 	/// Drop an instance cleanly. Only works if its value represents "no-operation".
diff --git a/substrate/primitives/runtime/src/traits.rs b/substrate/primitives/runtime/src/traits.rs
index ee6a526e95e..aa5908526b8 100644
--- a/substrate/primitives/runtime/src/traits.rs
+++ b/substrate/primitives/runtime/src/traits.rs
@@ -37,7 +37,9 @@ pub use sp_arithmetic::traits::{
 	UniqueSaturatedFrom, UniqueSaturatedInto, Zero,
 };
 use sp_core::{self, storage::StateVersion, Hasher, RuntimeDebug, TypeId};
-use sp_std::{self, fmt::Debug, marker::PhantomData, prelude::*};
+#[doc(hidden)]
+pub use sp_std::marker::PhantomData;
+use sp_std::{self, fmt::Debug, prelude::*};
 #[cfg(feature = "std")]
 use std::fmt::Display;
 #[cfg(feature = "std")]
@@ -274,6 +276,192 @@ where
 	}
 }
 
+/// A trait for querying a single value from a type defined in the trait.
+///
+/// It is not required that the value is constant.
+pub trait TypedGet {
+	/// The type which is returned.
+	type Type;
+	/// Return the current value.
+	fn get() -> Self::Type;
+}
+
+/// A trait for querying a single value from a type.
+///
+/// It is not required that the value is constant.
+pub trait Get<T> {
+	/// Return the current value.
+	fn get() -> T;
+}
+
+impl<T: Default> Get<T> for () {
+	fn get() -> T {
+		T::default()
+	}
+}
+
+/// Implement Get by returning Default for any type that implements Default.
+pub struct GetDefault;
+impl<T: Default> Get<T> for GetDefault {
+	fn get() -> T {
+		T::default()
+	}
+}
+
+macro_rules! impl_const_get {
+	($name:ident, $t:ty) => {
+		#[doc = "Const getter for a basic type."]
+		#[derive($crate::RuntimeDebug)]
+		pub struct $name<const T: $t>;
+		impl<const T: $t> Get<$t> for $name<T> {
+			fn get() -> $t {
+				T
+			}
+		}
+		impl<const T: $t> Get<Option<$t>> for $name<T> {
+			fn get() -> Option<$t> {
+				Some(T)
+			}
+		}
+		impl<const T: $t> TypedGet for $name<T> {
+			type Type = $t;
+			fn get() -> $t {
+				T
+			}
+		}
+	};
+}
+
+impl_const_get!(ConstBool, bool);
+impl_const_get!(ConstU8, u8);
+impl_const_get!(ConstU16, u16);
+impl_const_get!(ConstU32, u32);
+impl_const_get!(ConstU64, u64);
+impl_const_get!(ConstU128, u128);
+impl_const_get!(ConstI8, i8);
+impl_const_get!(ConstI16, i16);
+impl_const_get!(ConstI32, i32);
+impl_const_get!(ConstI64, i64);
+impl_const_get!(ConstI128, i128);
+
+/// Create new implementations of the [`Get`](crate::traits::Get) trait.
+///
+/// The so-called parameter type can be created in four different ways:
+///
+/// - Using `const` to create a parameter type that provides a `const` getter. It is required that
+///   the `value` is const.
+///
+/// - Declare the parameter type without `const` to have more freedom when creating the value.
+///
+/// NOTE: A more substantial version of this macro is available in `frame_support` crate which
+/// allows mutable and persistant variants.
+///
+/// # Examples
+///
+/// ```
+/// # use sp_runtime::traits::Get;
+/// # use sp_runtime::parameter_types;
+/// // This function cannot be used in a const context.
+/// fn non_const_expression() -> u64 { 99 }
+///
+/// const FIXED_VALUE: u64 = 10;
+/// parameter_types! {
+///    pub const Argument: u64 = 42 + FIXED_VALUE;
+///    /// Visibility of the type is optional
+///    OtherArgument: u64 = non_const_expression();
+/// }
+///
+/// trait Config {
+///    type Parameter: Get<u64>;
+///    type OtherParameter: Get<u64>;
+/// }
+///
+/// struct Runtime;
+/// impl Config for Runtime {
+///    type Parameter = Argument;
+///    type OtherParameter = OtherArgument;
+/// }
+/// ```
+///
+/// # Invalid example:
+///
+/// ```compile_fail
+/// # use sp_runtime::traits::Get;
+/// # use sp_runtime::parameter_types;
+/// // This function cannot be used in a const context.
+/// fn non_const_expression() -> u64 { 99 }
+///
+/// parameter_types! {
+///    pub const Argument: u64 = non_const_expression();
+/// }
+/// ```
+#[macro_export]
+macro_rules! parameter_types {
+	(
+		$( #[ $attr:meta ] )*
+		$vis:vis const $name:ident: $type:ty = $value:expr;
+		$( $rest:tt )*
+	) => (
+		$( #[ $attr ] )*
+		$vis struct $name;
+		$crate::parameter_types!(@IMPL_CONST $name , $type , $value);
+		$crate::parameter_types!( $( $rest )* );
+	);
+	(
+		$( #[ $attr:meta ] )*
+		$vis:vis $name:ident: $type:ty = $value:expr;
+		$( $rest:tt )*
+	) => (
+		$( #[ $attr ] )*
+		$vis struct $name;
+		$crate::parameter_types!(@IMPL $name, $type, $value);
+		$crate::parameter_types!( $( $rest )* );
+	);
+	() => ();
+	(@IMPL_CONST $name:ident, $type:ty, $value:expr) => {
+		impl $name {
+			/// Returns the value of this parameter type.
+			pub const fn get() -> $type {
+				$value
+			}
+		}
+
+		impl<I: From<$type>> $crate::traits::Get<I> for $name {
+			fn get() -> I {
+				I::from(Self::get())
+			}
+		}
+
+		impl $crate::traits::TypedGet for $name {
+			type Type = $type;
+			fn get() -> $type {
+				Self::get()
+			}
+		}
+	};
+	(@IMPL $name:ident, $type:ty, $value:expr) => {
+		impl $name {
+			/// Returns the value of this parameter type.
+			pub fn get() -> $type {
+				$value
+			}
+		}
+
+		impl<I: From<$type>> $crate::traits::Get<I> for $name {
+			fn get() -> I {
+				I::from(Self::get())
+			}
+		}
+
+		impl $crate::traits::TypedGet for $name {
+			type Type = $type;
+			fn get() -> $type {
+				Self::get()
+			}
+		}
+	};
+}
+
 /// Extensible conversion trait. Generic over only source type, with destination type being
 /// associated.
 pub trait Morph<A> {
@@ -310,6 +498,185 @@ impl<T> TryMorph<T> for Identity {
 	}
 }
 
+/// Create a `Morph` and/or `TryMorph` impls with a simple closure-like expression.
+///
+/// # Examples
+///
+/// ```
+/// # use sp_runtime::{morph_types, traits::{Morph, TryMorph, TypedGet, ConstU32}};
+/// # use sp_arithmetic::traits::CheckedSub;
+///
+/// morph_types! {
+///    /// Replace by some other value; produce both `Morph` and `TryMorph` implementations
+///    pub type Replace<V: TypedGet> = |_| -> V::Type { V::get() };
+///    /// A private `Morph` implementation to reduce a `u32` by 10.
+///    type ReduceU32ByTen: Morph = |r: u32| -> u32 { r - 10 };
+///    /// A `TryMorph` implementation to reduce a scalar by a particular amount, checking for
+///    /// underflow.
+///    pub type CheckedReduceBy<N: TypedGet>: TryMorph = |r: N::Type| -> Result<N::Type, ()> {
+///        r.checked_sub(&N::get()).ok_or(())
+///    } where N::Type: CheckedSub;
+/// }
+///
+/// trait Config {
+///    type TestMorph1: Morph<u32>;
+///    type TestTryMorph1: TryMorph<u32>;
+///    type TestMorph2: Morph<u32>;
+///    type TestTryMorph2: TryMorph<u32>;
+/// }
+///
+/// struct Runtime;
+/// impl Config for Runtime {
+///    type TestMorph1 = Replace<ConstU32<42>>;
+///    type TestTryMorph1 = Replace<ConstU32<42>>;
+///    type TestMorph2 = ReduceU32ByTen;
+///    type TestTryMorph2 = CheckedReduceBy<ConstU32<10>>;
+/// }
+/// ```
+#[macro_export]
+macro_rules! morph_types {
+	(
+		@DECL $( #[doc = $doc:expr] )* $vq:vis $name:ident ()
+	) => {
+		$( #[doc = $doc] )* $vq struct $name;
+	};
+	(
+		@DECL $( #[doc = $doc:expr] )* $vq:vis $name:ident ( $( $bound_id:ident ),+ )
+	) => {
+		$( #[doc = $doc] )*
+		$vq struct $name < $($bound_id,)* > ( $crate::traits::PhantomData< ( $($bound_id,)* ) > ) ;
+	};
+	(
+		@IMPL $name:ty : ( $( $bounds:tt )* ) ( $( $where:tt )* )
+		= |$var:ident: $var_type:ty| -> $outcome:ty { $( $ex:expr )* }
+	) => {
+		impl<$($bounds)*> $crate::traits::Morph<$var_type> for $name $( $where )? {
+			type Outcome = $outcome;
+			fn morph($var: $var_type) -> Self::Outcome { $( $ex )* }
+		}
+	};
+	(
+		@IMPL_TRY $name:ty : ( $( $bounds:tt )* ) ( $( $where:tt )* )
+		= |$var:ident: $var_type:ty| -> $outcome:ty { $( $ex:expr )* }
+	) => {
+		impl<$($bounds)*> $crate::traits::TryMorph<$var_type> for $name $( $where )? {
+			type Outcome = $outcome;
+			fn try_morph($var: $var_type) -> Result<Self::Outcome, ()> { $( $ex )* }
+		}
+	};
+	(
+		@IMPL $name:ty : () ( $( $where:tt )* )
+		= |$var:ident: $var_type:ty| -> $outcome:ty { $( $ex:expr )* }
+	) => {
+		impl $crate::traits::Morph<$var_type> for $name $( $where )? {
+			type Outcome = $outcome;
+			fn morph($var: $var_type) -> Self::Outcome { $( $ex )* }
+		}
+	};
+	(
+		@IMPL_TRY $name:ty : () ( $( $where:tt )* )
+		= |$var:ident: $var_type:ty| -> $outcome:ty { $( $ex:expr )* }
+	) => {
+		impl $crate::traits::TryMorph<$var_type> for $name $( $where )? {
+			type Outcome = $outcome;
+			fn try_morph($var: $var_type) -> Result<Self::Outcome, ()> { $( $ex )* }
+		}
+	};
+	(
+		@IMPL_BOTH $name:ty : ( $( $bounds:tt )* ) ( $( $where:tt )* )
+		= |$var:ident: $var_type:ty| -> $outcome:ty { $( $ex:expr )* }
+	) => {
+		morph_types! {
+			@IMPL $name : ($($bounds)*) ($($where)*)
+			= |$var: $var_type| -> $outcome { $( $ex )* }
+		}
+		morph_types! {
+			@IMPL_TRY $name : ($($bounds)*) ($($where)*)
+			= |$var: $var_type| -> $outcome { Ok({$( $ex )*}) }
+		}
+	};
+
+	(
+		$( #[doc = $doc:expr] )* $vq:vis type $name:ident
+		$( < $( $bound_id:ident $( : $bound_head:path $( | $bound_tail:path )* )? ),+ > )?
+		$(: $type:tt)?
+		= |_| -> $outcome:ty { $( $ex:expr )* };
+		$( $rest:tt )*
+	) => {
+		morph_types! {
+			$( #[doc = $doc] )* $vq type $name
+			$( < $( $bound_id $( : $bound_head $( | $bound_tail )* )? ),+ > )?
+			EXTRA_GENERIC(X)
+			$(: $type)?
+			= |_x: X| -> $outcome { $( $ex )* };
+			$( $rest )*
+		}
+	};
+	(
+		$( #[doc = $doc:expr] )* $vq:vis type $name:ident
+		$( < $( $bound_id:ident $( : $bound_head:path $( | $bound_tail:path )* )? ),+ > )?
+		$( EXTRA_GENERIC ($extra:ident) )?
+		= |$var:ident: $var_type:ty| -> $outcome:ty { $( $ex:expr )* }
+		$( where $( $where_path:ty : $where_bound_head:path $( | $where_bound_tail:path )* ),* )?;
+		$( $rest:tt )*
+	) => {
+		morph_types! { @DECL $( #[doc = $doc] )* $vq $name ( $( $( $bound_id ),+ )? ) }
+		morph_types! {
+			@IMPL_BOTH $name $( < $( $bound_id ),* > )? :
+			( $( $( $bound_id $( : $bound_head $( + $bound_tail )* )? , )+ )? $( $extra )? )
+			( $( where $( $where_path : $where_bound_head $( + $where_bound_tail )* ),* )? )
+			= |$var: $var_type| -> $outcome { $( $ex )* }
+		}
+		morph_types!{ $($rest)* }
+	};
+	(
+		$( #[doc = $doc:expr] )* $vq:vis type $name:ident
+		$( < $( $bound_id:ident $( : $bound_head:path $( | $bound_tail:path )* )? ),+ > )?
+		$( EXTRA_GENERIC ($extra:ident) )?
+		: Morph
+		= |$var:ident: $var_type:ty| -> $outcome:ty { $( $ex:expr )* }
+		$( where $( $where_path:ty : $where_bound_head:path $( | $where_bound_tail:path )* ),* )?;
+		$( $rest:tt )*
+	) => {
+		morph_types! { @DECL $( #[doc = $doc] )* $vq $name ( $( $( $bound_id ),+ )? ) }
+		morph_types! {
+			@IMPL $name $( < $( $bound_id ),* > )? :
+			( $( $( $bound_id $( : $bound_head $( + $bound_tail )* )? , )+ )? $( $extra )? )
+			( $( where $( $where_path : $where_bound_head $( + $where_bound_tail )* ),* )? )
+			= |$var: $var_type| -> $outcome { $( $ex )* }
+		}
+		morph_types!{ $($rest)* }
+	};
+	(
+		$( #[doc = $doc:expr] )* $vq:vis type $name:ident
+		$( < $( $bound_id:ident $( : $bound_head:path $( | $bound_tail:path )* )? ),+ > )?
+		$( EXTRA_GENERIC ($extra:ident) )?
+		: TryMorph
+		= |$var:ident: $var_type:ty| -> Result<$outcome:ty, ()> { $( $ex:expr )* }
+		$( where $( $where_path:ty : $where_bound_head:path $( | $where_bound_tail:path )* ),* )?;
+		$( $rest:tt )*
+	) => {
+		morph_types! { @DECL $( #[doc = $doc] )* $vq $name ( $( $( $bound_id ),+ )? ) }
+		morph_types! {
+			@IMPL_TRY $name $( < $( $bound_id ),* > )? :
+			( $( $( $bound_id $( : $bound_head $( + $bound_tail )* )? , )+ )? $( $extra )? )
+			( $( where $( $where_path : $where_bound_head $( + $where_bound_tail )* ),* )? )
+			= |$var: $var_type| -> $outcome { $( $ex )* }
+		}
+		morph_types!{ $($rest)* }
+	};
+	() => {}
+}
+
+morph_types! {
+	/// Morpher to disregard the source value and replace with another.
+	pub type Replace<V: TypedGet> = |_| -> V::Type { V::get() };
+	/// Mutator which reduces a scalar by a particular amount.
+	pub type ReduceBy<N: TypedGet> = |r: N::Type| -> N::Type {
+		r.checked_sub(&N::get()).unwrap_or(Zero::zero())
+	} where N::Type: CheckedSub | Zero;
+}
+
 /// Extensible conversion trait. Generic over both source and destination types.
 pub trait Convert<A, B> {
 	/// Make conversion.
-- 
GitLab