Skip to content
This repository has been archived by the owner on Nov 15, 2023. It is now read-only.

make types within generate_solution_type macro explicit #8447

Merged
7 commits merged into from
Mar 28, 2021
Merged
2 changes: 1 addition & 1 deletion frame/election-provider-multi-phase/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ pub(crate) type TargetIndex = u16;

sp_npos_elections::generate_solution_type!(
#[compact]
pub struct TestCompact::<VoterIndex, TargetIndex, PerU16>(16)
pub struct TestCompact::<VoterIndex = VoterIndex, CandidateIndex = TargetIndex, Accuracy = PerU16>(16)
);

/// All events of this pallet.
Expand Down
6 changes: 5 additions & 1 deletion frame/staking/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -364,7 +364,11 @@ pub type RewardPoint = u32;
// Note: Maximum nomination limit is set here -- 16.
sp_npos_elections::generate_solution_type!(
#[compact]
pub struct CompactAssignments::<NominatorIndex, ValidatorIndex, OffchainAccuracy>(16)
pub struct CompactAssignments::<
VoterIndex = NominatorIndex,
CandidateIndex = ValidatorIndex,
Accuracy = OffchainAccuracy,
>(16)
);

/// Accuracy used for off-chain election. This better be small.
Expand Down
31 changes: 25 additions & 6 deletions primitives/npos-elections/compact/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,12 @@ pub(crate) fn syn_err(message: &'static str) -> syn::Error {
/// type, `u8` target type and `Perbill` accuracy with maximum of 8 edges per voter.
///
/// ```ignore
/// generate_solution_type!(pub struct TestSolution<u16, u8, Perbill>::(8))
/// # use sp_npos_elections_compact::generate_solution_type;
/// generate_solution_type!(pub struct TestSolution::<
/// VoterIndex = u16,
/// CandidateIndex = u8,
/// Accuracy = Perbill,
/// >(8));
/// ```
///
/// The given struct provides function to convert from/to Assignment:
Expand All @@ -66,10 +71,11 @@ pub(crate) fn syn_err(message: &'static str) -> syn::Error {
/// for numbers will be used, similar to how `parity-scale-codec`'s `Compact` works.
///
/// ```ignore
coriolinus marked this conversation as resolved.
Show resolved Hide resolved
/// # use sp_npos_elections_compact::generate_solution_type;
/// generate_solution_type!(
/// #[compact]
/// pub struct TestSolutionCompact<u16, u8, Perbill>::(8)
/// )
/// pub struct TestSolutionCompact::<VoterIndex = u16, CandidateIndex = u8, Accuracy = Perbill>(8)
/// );
/// ```
#[proc_macro]
pub fn generate_solution_type(item: TokenStream) -> TokenStream {
Expand Down Expand Up @@ -386,7 +392,7 @@ fn check_compact_attr(input: ParseStream) -> Result<bool> {
}
}

/// #[compact] pub struct CompactName::<u32, u32, u32>()
/// #[compact] pub struct CompactName::<VoterIndex = u32, CandidateIndex = u32, Accuracy = u32>()
impl Parse for SolutionDef {
fn parse(input: ParseStream) -> syn::Result<Self> {
// optional #[compact]
Expand All @@ -405,9 +411,22 @@ impl Parse for SolutionDef {
return Err(syn_err("Must provide 3 generic args."))
}

let mut types: Vec<syn::Type> = generics.args.iter().map(|t|
let expected_types = ["VoterIndex", "CandidateIndex", "Accuracy"];

let mut types: Vec<syn::Type> = generics.args.iter().zip(expected_types.iter()).map(|(t, expected)|
match t {
syn::GenericArgument::Type(ty) => Ok(ty.clone()),
syn::GenericArgument::Type(ty) => {
// this is now an error
Err(syn::Error::new_spanned(ty, format!("Expected binding: `{} = ...`", expected)))
},
syn::GenericArgument::Binding(syn::Binding{ident, ty, ..}) => {
// check that we have the right keyword for this position in the argument list
if ident == expected {
Ok(ty.clone())
} else {
Err(syn::Error::new_spanned(ident, format!("Expected `{}`", expected)))
}
}
_ => Err(syn_err("Wrong type of generic provided. Must be a `type`.")),
}
).collect::<Result<_>>()?;
Expand Down
6 changes: 5 additions & 1 deletion primitives/npos-elections/fuzzer/src/compact.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@ use sp_npos_elections::sp_arithmetic::Percent;
use sp_runtime::codec::{Encode, Error};

fn main() {
generate_solution_type!(#[compact] pub struct InnerTestSolutionCompact::<u32, u32, Percent>(16));
generate_solution_type!(#[compact] pub struct InnerTestSolutionCompact::<
VoterIndex = u32,
CandidateIndex = u32,
Accuracy = Percent,
>(16));
loop {
fuzz!(|fuzzer_data: &[u8]| {
let result_decoded: Result<InnerTestSolutionCompact, Error> =
Expand Down
20 changes: 16 additions & 4 deletions primitives/npos-elections/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1148,7 +1148,11 @@ mod solution_type {

type TestAccuracy = Percent;

generate_solution_type!(pub struct TestSolutionCompact::<u32, u8, TestAccuracy>(16));
generate_solution_type!(pub struct TestSolutionCompact::<
VoterIndex = u32,
CandidateIndex = u8,
Accuracy = TestAccuracy,
>(16));

#[allow(dead_code)]
mod __private {
Expand All @@ -1158,15 +1162,19 @@ mod solution_type {
use sp_arithmetic::Percent;
generate_solution_type!(
#[compact]
struct InnerTestSolutionCompact::<u32, u8, Percent>(12)
struct InnerTestSolutionCompact::<VoterIndex = u32, CandidateIndex = u8, Accuracy = Percent>(12)
);
}

#[test]
fn solution_struct_works_with_and_without_compact() {
// we use u32 size to make sure compact is smaller.
let without_compact = {
generate_solution_type!(pub struct InnerTestSolution::<u32, u32, Percent>(16));
generate_solution_type!(pub struct InnerTestSolution::<
VoterIndex = u32,
CandidateIndex = u32,
Accuracy = Percent,
>(16));
let compact = InnerTestSolution {
votes1: vec![(2, 20), (4, 40)],
votes2: vec![
Expand All @@ -1180,7 +1188,11 @@ mod solution_type {
};

let with_compact = {
generate_solution_type!(#[compact] pub struct InnerTestSolutionCompact::<u32, u32, Percent>(16));
generate_solution_type!(#[compact] pub struct InnerTestSolutionCompact::<
VoterIndex = u32,
CandidateIndex = u32,
Accuracy = Percent,
>(16));
let compact = InnerTestSolutionCompact {
votes1: vec![(2, 20), (4, 40)],
votes2: vec![
Expand Down