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

Automatic pallet parts in construct_runtime #9681

Merged
merged 53 commits into from
Oct 31, 2021
Merged
Show file tree
Hide file tree
Changes from 29 commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
5c8e299
implement automatic parts
gui1117 Jul 15, 2021
62afa97
ui tests
gui1117 Sep 2, 2021
2dc4412
rename
gui1117 Sep 2, 2021
a790517
Merge remote-tracking branch 'origin/master' into gui-construct_runti…
gui1117 Sep 2, 2021
5bec9dd
remove unnecessary exclude
gui1117 Sep 2, 2021
8c9c19b
better doc
gui1117 Sep 6, 2021
4b405a3
better doc
gui1117 Sep 6, 2021
afde346
fix genesis config
gui1117 Sep 6, 2021
eebea7b
fix UI tests
gui1117 Sep 6, 2021
a910351
fix UI test
gui1117 Sep 6, 2021
ea68d3a
Merge remote-tracking branch 'origin/master' into gui-construct_runti…
gui1117 Sep 6, 2021
6352dfd
Revert "fix UI test"
gui1117 Sep 6, 2021
480af69
Merge remote-tracking branch 'origin/master' into gui-construct_runti…
gui1117 Sep 13, 2021
4127c1e
implemented used_parts
gui1117 Sep 13, 2021
aaac25a
Update frame/support/procedural/src/construct_runtime/mod.rs
gui1117 Sep 15, 2021
4fb77ab
doc + fmt
gui1117 Sep 15, 2021
a2442a4
Update frame/support/procedural/src/construct_runtime/parse.rs
gui1117 Sep 15, 2021
534f293
add doc in the macro
gui1117 Sep 15, 2021
0c72e96
Merge remote-tracking branch 'origin/master' into gui-construct_runti…
gui1117 Sep 15, 2021
6871c71
remove yet some more parts
gui1117 Sep 15, 2021
3d463f5
fix ui test
gui1117 Sep 15, 2021
62a34d5
more determnistic error message + fix ui tests
gui1117 Sep 16, 2021
32ab5a4
fix ui test
gui1117 Sep 16, 2021
318c26c
Apply suggestions from code review
gui1117 Sep 20, 2021
4b1b178
do refactor + fix ui tests
gui1117 Sep 20, 2021
134b8df
Merge remote-tracking branch 'origin/master' into gui-construct_runti…
gui1117 Sep 20, 2021
e571e9e
fmt
gui1117 Sep 20, 2021
ebb14fb
fix test
gui1117 Sep 20, 2021
5ea0535
fix test
gui1117 Sep 20, 2021
5787ad6
fix ui test
gui1117 Sep 20, 2021
03cb587
Apply suggestions from code review
gui1117 Sep 28, 2021
89cf841
refactor
gui1117 Sep 29, 2021
871b8bf
Merge remote-tracking branch 'origin/master' into gui-construct_runti…
gui1117 Sep 30, 2021
25811d7
remove even more part in node-runtime
gui1117 Sep 30, 2021
cdb7151
fix test
gui1117 Oct 7, 2021
8b39dc0
Add flow chart for the construct_runtime! execution flow
KiChjang Oct 8, 2021
41baa40
Fix typo
KiChjang Oct 8, 2021
b9ade81
Ignore snippets that don't contain code
KiChjang Oct 9, 2021
e5e6adc
Merge remote-tracking branch 'origin/master' into gui-construct_runti…
KiChjang Oct 9, 2021
3c34573
Refactor some code in expand_after
KiChjang Oct 9, 2021
a22cf5b
Rename expand_after to match_and_insert
KiChjang Oct 9, 2021
b381c21
cargo fmt
KiChjang Oct 9, 2021
8dc3dab
Fix rename
KiChjang Oct 9, 2021
ffc053f
Remove frame_support argument to construct_runtime_parts
KiChjang Oct 10, 2021
d7e14b4
Make use of tt-call to simplify intermediate expansions
KiChjang Oct 10, 2021
eda408b
cargo fmt
KiChjang Oct 10, 2021
67d9cfa
Update match_and_insert documentation
KiChjang Oct 10, 2021
2584262
Reset cursor to 0 when no matching patterns are found
KiChjang Oct 12, 2021
c10400f
Reorder struct fields on MatchAndInsertDef
KiChjang Oct 12, 2021
cfe4d54
Add test for dependency renames and fix frame-support import
KiChjang Oct 13, 2021
8964094
Add more doc comments
KiChjang Oct 14, 2021
9681c77
Update frame/support/test/compile_pass/src/lib.rs
KiChjang Oct 18, 2021
b032085
Merge branch 'master' into gui-construct_runtime-auto
shawntabrizi Oct 31, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions bin/node/cli/src/chain_spec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -363,6 +363,8 @@ pub fn testnet_genesis(
vesting: Default::default(),
gilt: Default::default(),
transaction_storage: Default::default(),
scheduler: Default::default(),
transaction_payment: Default::default(),
}
}

Expand Down
74 changes: 37 additions & 37 deletions bin/node/runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1237,47 +1237,47 @@ construct_runtime!(
NodeBlock = node_primitives::Block,
UncheckedExtrinsic = UncheckedExtrinsic
{
System: frame_system::{Pallet, Call, Config, Storage, Event<T>},
Utility: pallet_utility::{Pallet, Call, Event},
Babe: pallet_babe::{Pallet, Call, Storage, Config, ValidateUnsigned},
Timestamp: pallet_timestamp::{Pallet, Call, Storage, Inherent},
Authorship: pallet_authorship::{Pallet, Call, Storage, Inherent},
Indices: pallet_indices::{Pallet, Call, Storage, Config<T>, Event<T>},
Balances: pallet_balances::{Pallet, Call, Storage, Config<T>, Event<T>},
TransactionPayment: pallet_transaction_payment::{Pallet, Storage},
ElectionProviderMultiPhase: pallet_election_provider_multi_phase::{Pallet, Call, Storage, Event<T>, ValidateUnsigned},
Staking: pallet_staking::{Pallet, Call, Config<T>, Storage, Event<T>},
System: frame_system,
Utility: pallet_utility,
Babe: pallet_babe,
Timestamp: pallet_timestamp,
Authorship: pallet_authorship,
Indices: pallet_indices,
Balances: pallet_balances,
TransactionPayment: pallet_transaction_payment,
ElectionProviderMultiPhase: pallet_election_provider_multi_phase,
Staking: pallet_staking,
Session: pallet_session::{Pallet, Call, Storage, Event, Config<T>},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The <T> generic here still makes me a bit sad, but that's really what #8743 aims to solve.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes but I expect nobody to use this syntax anymore once all pallet use pallet macro.
maybe we can explicit whitelist syntax like use_parts as well.

Democracy: pallet_democracy::{Pallet, Call, Storage, Config<T>, Event<T>},
Council: pallet_collective::<Instance1>::{Pallet, Call, Storage, Origin<T>, Event<T>, Config<T>},
TechnicalCommittee: pallet_collective::<Instance2>::{Pallet, Call, Storage, Origin<T>, Event<T>, Config<T>},
Elections: pallet_elections_phragmen::{Pallet, Call, Storage, Event<T>, Config<T>},
TechnicalMembership: pallet_membership::<Instance1>::{Pallet, Call, Storage, Event<T>, Config<T>},
Grandpa: pallet_grandpa::{Pallet, Call, Storage, Config, Event, ValidateUnsigned},
Treasury: pallet_treasury::{Pallet, Call, Storage, Config, Event<T>},
Contracts: pallet_contracts::{Pallet, Call, Storage, Event<T>},
Sudo: pallet_sudo::{Pallet, Call, Config<T>, Storage, Event<T>},
ImOnline: pallet_im_online::{Pallet, Call, Storage, Event<T>, ValidateUnsigned, Config<T>},
AuthorityDiscovery: pallet_authority_discovery::{Pallet, Config},
Offences: pallet_offences::{Pallet, Storage, Event},
Democracy: pallet_democracy,
Council: pallet_collective::<Instance1>,
TechnicalCommittee: pallet_collective::<Instance2>,
Elections: pallet_elections_phragmen,
TechnicalMembership: pallet_membership::<Instance1>,
Grandpa: pallet_grandpa,
Treasury: pallet_treasury,
Contracts: pallet_contracts,
Sudo: pallet_sudo,
ImOnline: pallet_im_online,
AuthorityDiscovery: pallet_authority_discovery,
Offences: pallet_offences,
Historical: pallet_session_historical::{Pallet},
RandomnessCollectiveFlip: pallet_randomness_collective_flip::{Pallet, Storage},
Identity: pallet_identity::{Pallet, Call, Storage, Event<T>},
RandomnessCollectiveFlip: pallet_randomness_collective_flip,
Identity: pallet_identity,
Society: pallet_society::{Pallet, Call, Storage, Event<T>, Config<T>},
Recovery: pallet_recovery::{Pallet, Call, Storage, Event<T>},
Vesting: pallet_vesting::{Pallet, Call, Storage, Event<T>, Config<T>},
Scheduler: pallet_scheduler::{Pallet, Call, Storage, Event<T>},
Proxy: pallet_proxy::{Pallet, Call, Storage, Event<T>},
Multisig: pallet_multisig::{Pallet, Call, Storage, Event<T>},
Recovery: pallet_recovery,
Vesting: pallet_vesting,
Scheduler: pallet_scheduler,
Proxy: pallet_proxy,
Multisig: pallet_multisig,
Bounties: pallet_bounties::{Pallet, Call, Storage, Event<T>},
Tips: pallet_tips::{Pallet, Call, Storage, Event<T>},
Assets: pallet_assets::{Pallet, Call, Storage, Event<T>},
Mmr: pallet_mmr::{Pallet, Storage},
Lottery: pallet_lottery::{Pallet, Call, Storage, Event<T>},
Gilt: pallet_gilt::{Pallet, Call, Storage, Event<T>, Config},
Uniques: pallet_uniques::{Pallet, Call, Storage, Event<T>},
TransactionStorage: pallet_transaction_storage::{Pallet, Call, Storage, Inherent, Config<T>, Event<T>},
BagsList: pallet_bags_list::{Pallet, Call, Storage, Event<T>},
Tips: pallet_tips,
Assets: pallet_assets,
Mmr: pallet_mmr,
Lottery: pallet_lottery,
Gilt: pallet_gilt,
Uniques: pallet_uniques,
TransactionStorage: pallet_transaction_storage,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

❤️ This looks a lot cleaner.

BagsList: pallet_bags_list,
}
);

Expand Down
2 changes: 2 additions & 0 deletions bin/node/testing/src/genesis.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,5 +100,7 @@ pub fn config_endowed(
vesting: Default::default(),
gilt: Default::default(),
transaction_storage: Default::default(),
scheduler: Default::default(),
transaction_payment: Default::default(),
}
}
215 changes: 129 additions & 86 deletions frame/support/procedural/src/construct_runtime/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,115 +15,158 @@
// See the License for the specific language governing permissions and
// limitations under the License.

//! Implementation of `construct_runtime`.
//!
//! `construct_runtime` implementation is recursive and can generate code which will call itself in
//! order to get all the pallet parts for each pallet.
//!
//! Pallets define their parts (`Call`, `Storage`, ..) either explicitly with the syntax
//! `::{Call, ...}` or implicitly.
//!
//! In case a pallet define their part implicitly, then the pallet must provide the macro
//! `construct_runtime_parts`. `construct_rutime` will generate some code which calls the macro
//! `construct_runtime_parts` of the pallet. The `construct_runtime_parts` will generate some code
//! which calls `construct_runtime` again with this time all explicit definition of parts.
//!
//! E.g.
//! ```ignore
//! construct_runtime!(
//! ...
//! {
//! System: frame_system = 0, // Implicit definition of parts
//! Balances: pallet_balances = 1, // Implicit definition of parts
//! }
//! );
//! ```
//! This call has some implicit pallet parts, thus it will expand to:
//! ```ignore
//! pallet_balances::construct_runtime_parts!(
//! // First *argument* is the path to frame_support crate.
//! { frame_support }
//! // Second *argument* is the token identifying the pallet on which `construct_runtime_parts`
//! // must add the parts.
//! { Balances: pallet_balances }
//! // The other tokens are the tokens in which the parts must be added and once the parts are
//! // added it is the tokens the expand into.
//! frame_system::construct_runtime_parts!(
//! { frame_support }
//! { System: frame_system }
//! construct_runtime!(
//! ...
//! {
//! System: frame_system = 0, // Implicit definition of parts
//! Balances: pallet_balances = 1, // Implicit definition of parts
//! }
//! );
//! );
//! );
//! ```
//! `construct_runtime_parts` must be define add the pallet parts inside some tokens and then
//! expand to those tokens with the parts added.
//! Thus `pallet_balances::construct_runtime_parts` must expand to:
//! ```ignore
//! frame_system::construct_runtime_parts!(
//! { frame_support }
//! { System: frame_system }
//! construct_runtime!(
//! ...
//! {
//! System: frame_system = 0, // Implicit definition of parts
//! Balances: pallet_balances::{Pallet, Call} = 1, // Explicit definition of parts
//! }
//! );
//! );
//! ```
//! Then `frame_system::construct_runtime_parts` must expand to:
//! ```ignore
//! construct_runtime!(
//! ...
//! {
//! System: frame_system::{Pallet, Call} = 0, // Explicit definition of parts
//! Balances: pallet_balances::{Pallet, Call} = 1, // Explicit definition of parts
//! }
//! );
//! ```
//! This call has no implicit pallet parts, thus it will expand to the runtime construction:
//! ```ignore
//! pub struct Runtime { ... }
//! pub struct Call { ... }
//! impl Call ...
//! pub enum Origin { ... }
//! ...
//! ```

mod expand;
mod parse;

use frame_support_procedural_tools::{
generate_crate_access, generate_hidden_includes, syn_ext as ext,
generate_crate_access, generate_crate_access_2018, generate_hidden_includes,
};
use parse::{
ExplicitRuntimeDeclaration, ImplicitRuntimeDeclaration, Pallet, RuntimeDeclaration,
WhereSection,
};
use parse::{PalletDeclaration, PalletPart, PalletPath, RuntimeDefinition, WhereSection};
use proc_macro::TokenStream;
use proc_macro2::TokenStream as TokenStream2;
use quote::quote;
use std::collections::HashMap;
use syn::{Ident, Result};

/// The fixed name of the system pallet.
const SYSTEM_PALLET_NAME: &str = "System";

/// The complete definition of a pallet with the resulting fixed index.
#[derive(Debug, Clone)]
pub struct Pallet {
pub name: Ident,
pub index: u8,
pub path: PalletPath,
pub instance: Option<Ident>,
pub pallet_parts: Vec<PalletPart>,
}

impl Pallet {
/// Get resolved pallet parts
fn pallet_parts(&self) -> &[PalletPart] {
&self.pallet_parts
}
/// Implementation of `construct_runtime` macro. Either expand to some code which will call
/// `construct_runtime` again, or expand to the final runtime definition.
pub fn construct_runtime(input: TokenStream) -> TokenStream {
let input_copy = input.clone();
let definition = syn::parse_macro_input!(input as RuntimeDeclaration);

/// Find matching parts
fn find_part(&self, name: &str) -> Option<&PalletPart> {
self.pallet_parts.iter().find(|part| part.name() == name)
}
let res = match definition {
RuntimeDeclaration::Implicit(implicit_def) =>
construct_runtime_intermediary_expansion(input_copy.into(), implicit_def),
RuntimeDeclaration::Explicit(explicit_decl) =>
construct_runtime_final_expansion(explicit_decl),
};

/// Return whether pallet contains part
fn exists_part(&self, name: &str) -> bool {
self.find_part(name).is_some()
}
res.unwrap_or_else(|e| e.to_compile_error()).into()
}

/// Convert from the parsed pallet to their final information.
/// Assign index to each pallet using same rules as rust for fieldless enum.
/// I.e. implicit are assigned number incrementedly from last explicit or 0.
fn complete_pallets(decl: impl Iterator<Item = PalletDeclaration>) -> syn::Result<Vec<Pallet>> {
let mut indices = HashMap::new();
let mut last_index: Option<u8> = None;
let mut names = HashMap::new();

decl.map(|pallet| {
let final_index = match pallet.index {
Some(i) => i,
None => last_index.map_or(Some(0), |i| i.checked_add(1)).ok_or_else(|| {
let msg = "Pallet index doesn't fit into u8, index is 256";
syn::Error::new(pallet.name.span(), msg)
})?,
};

last_index = Some(final_index);

if let Some(used_pallet) = indices.insert(final_index, pallet.name.clone()) {
let msg = format!(
"Pallet indices are conflicting: Both pallets {} and {} are at index {}",
used_pallet, pallet.name, final_index,
);
let mut err = syn::Error::new(used_pallet.span(), &msg);
err.combine(syn::Error::new(pallet.name.span(), msg));
return Err(err)
}

if let Some(used_pallet) = names.insert(pallet.name.clone(), pallet.name.span()) {
let msg = "Two pallets with the same name!";

let mut err = syn::Error::new(used_pallet, &msg);
err.combine(syn::Error::new(pallet.name.span(), &msg));
return Err(err)
}

Ok(Pallet {
name: pallet.name,
index: final_index,
path: pallet.path,
instance: pallet.instance,
pallet_parts: pallet.pallet_parts,
})
})
.collect()
}
/// When some pallet have implicit parts definition then the macro will expand into a macro call to
/// `construct_runtime_args` of each pallets, see root documentation.
fn construct_runtime_intermediary_expansion(
input: TokenStream2,
definition: ImplicitRuntimeDeclaration,
) -> Result<TokenStream2> {
let frame_support = generate_crate_access_2018("frame-support")?;
let mut expansion = quote::quote!(
#frame_support::construct_runtime! { #input }
);
for pallet in definition.pallets.iter().filter(|pallet| pallet.pallet_parts.is_none()) {
let pallet_path = &pallet.path;
let pallet_name = &pallet.name;
let pallet_instance = pallet.instance.as_ref().map(|instance| quote::quote!(::<#instance>));
expansion = quote::quote!(
#pallet_path::construct_runtime_parts! {
{ #frame_support }
{ #pallet_name: #pallet_path #pallet_instance }
#expansion
}
);
}

pub fn construct_runtime(input: TokenStream) -> TokenStream {
let definition = syn::parse_macro_input!(input as RuntimeDefinition);
construct_runtime_parsed(definition)
.unwrap_or_else(|e| e.to_compile_error())
.into()
Ok(expansion.into())
}

fn construct_runtime_parsed(definition: RuntimeDefinition) -> Result<TokenStream2> {
let RuntimeDefinition {
/// All pallets have explicit definition of parts, this will expand to the runtime declaration.
fn construct_runtime_final_expansion(
definition: ExplicitRuntimeDeclaration,
) -> Result<TokenStream2> {
let ExplicitRuntimeDeclaration {
name,
where_section: WhereSection { block, node_block, unchecked_extrinsic, .. },
pallets:
ext::Braces { content: ext::Punctuated { inner: pallets, .. }, token: pallets_token },
..
where_section: WhereSection { block, node_block, unchecked_extrinsic },
pallets,
pallets_token,
} = definition;

let pallets = complete_pallets(pallets.into_iter())?;

let hidden_crate_name = "construct_runtime";
let scrate = generate_crate_access(&hidden_crate_name, "frame-support");
let scrate_decl = generate_hidden_includes(&hidden_crate_name, "frame-support");
Expand Down
Loading