Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FRAME] Simplify pallet config definition: remove RuntimeEvent associated type #7229

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
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
32 changes: 32 additions & 0 deletions prdoc/pr_7229.prdoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0
# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json

title: "FRAME: `RuntimeEvent` as associated type bound`"
doc:
- audience: Runtime Dev
description: |
Using stabilized feature of associated type bounds, we can reduce redundant types defined in the pallet's `Config` traits. This PR adds
support for `RuntimeEvent` as an associated type bound.

With this change, we can do this:

```rs
#[pallet::config]
pub trait Config: frame_system::Config<RuntimeEvent: From<Event<Self>>> {
}
```
instead of this:

```rs
#[pallet::config]
pub trait Config: frame_system::Config {
/// Overarching event type.
type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
}
```

crates:
- name: frame-support
bump: minor
- name: pallet-examples
bump: minor
7 changes: 3 additions & 4 deletions substrate/frame/examples/basic/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -153,14 +153,13 @@ pub mod pallet {
///
/// `frame_system::Config` should always be included.
#[pallet::config]
pub trait Config: pallet_balances::Config + frame_system::Config {
pub trait Config:
pallet_balances::Config + frame_system::Config<RuntimeEvent: From<Event<Self>>>
{
// Setting a constant config parameter from the runtime
#[pallet::constant]
type MagicNumber: Get<Self::Balance>;

/// The overarching event type.
type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;

/// Type representing the weight of this pallet
type WeightInfo: WeightInfo;
}
Expand Down
1 change: 0 additions & 1 deletion substrate/frame/examples/basic/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,6 @@ impl pallet_balances::Config for Test {

impl Config for Test {
type MagicNumber = ConstU64<1_000_000_000>;
type RuntimeEvent = RuntimeEvent;
type WeightInfo = ();
}

Expand Down
10 changes: 2 additions & 8 deletions substrate/frame/examples/default-config/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,7 @@ pub mod pallet {
///
/// It will be an identical, but won't have anything that is `#[pallet::no_default]`.
#[pallet::config(with_default)]
pub trait Config: frame_system::Config {
/// The overarching event type. This is coming from the runtime, and cannot have a default.
/// In general, `Runtime*`-oriented types cannot have a sensible default.
#[pallet::no_default] // optional. `RuntimeEvent` is automatically excluded as well.
type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;

pub trait Config: frame_system::Config<RuntimeEvent: From<Event<Self>>> {
/// The overarching task type.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
/// The overarching task type.
/// The overarching task type. This is coming from the runtime, and cannot have a default.
/// In general, `Runtime*`-oriented types cannot have a sensible default.

#[pallet::no_default]
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
#[pallet::no_default]
#[pallet::no_default] // optional. `RuntimeEvent` is automatically excluded as well.

type RuntimeTask: Task;
Expand Down Expand Up @@ -196,8 +191,7 @@ pub mod tests {

#[derive_impl(TestDefaultConfig as pallet::DefaultConfig)]
impl pallet_default_config_example::Config for Runtime {
// These two both cannot have defaults.
type RuntimeEvent = RuntimeEvent;
// This cannot have default.
type RuntimeTask = RuntimeTask;

type HasNoDefault = frame_support::traits::ConstU32<1>;
Expand Down
6 changes: 3 additions & 3 deletions substrate/frame/examples/dev-mode/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,9 @@ pub mod pallet {
use frame_system::pallet_prelude::*;

#[pallet::config]
pub trait Config: pallet_balances::Config + frame_system::Config {
/// The overarching event type.
type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
pub trait Config:
pallet_balances::Config + frame_system::Config<RuntimeEvent: From<Event<Self>>>
{
}

// Simple declaration of the `Pallet` type. It is placeholder we use to implement traits and
Expand Down
4 changes: 1 addition & 3 deletions substrate/frame/examples/dev-mode/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,7 @@ impl pallet_balances::Config for Test {
type AccountStore = System;
}

impl Config for Test {
type RuntimeEvent = RuntimeEvent;
}
impl Config for Test {}

// This function basically just builds a genesis storage key/value store according to
// our desired mockup.
Expand Down
8 changes: 2 additions & 6 deletions substrate/frame/examples/frame-crate/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,7 @@ pub mod pallet {
use super::*;

#[pallet::config]
pub trait Config: frame_system::Config {
type RuntimeEvent: IsType<<Self as frame_system::Config>::RuntimeEvent> + From<Event<Self>>;
}
pub trait Config: frame_system::Config<RuntimeEvent: From<Event<Self>>> {}

#[pallet::pallet]
pub struct Pallet<T>(_);
Expand Down Expand Up @@ -60,7 +58,5 @@ mod tests {
type Block = MockBlock<Self>;
}

impl my_pallet::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
}
impl my_pallet::Config for Runtime {}
}
5 changes: 1 addition & 4 deletions substrate/frame/examples/kitchensink/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,7 @@ pub mod pallet {
/// * `#[pallet::disable_frame_system_supertrait_check]` would remove the need for
/// `frame_system::Config` to exist, which you should almost never need.
#[pallet::config]
pub trait Config: frame_system::Config {
/// The overarching runtime event type.
type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;

pub trait Config: frame_system::Config<RuntimeEvent: From<Event<Self>>> {
/// Type representing the weight of this pallet
type WeightInfo: WeightInfo;

Expand Down
1 change: 0 additions & 1 deletion substrate/frame/examples/kitchensink/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,6 @@ parameter_types! {
}

impl Config for Test {
type RuntimeEvent = RuntimeEvent;
type WeightInfo = ();

type Currency = Balances;
Expand Down
7 changes: 3 additions & 4 deletions substrate/frame/examples/offchain-worker/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -125,14 +125,13 @@ pub mod pallet {
/// This pallet's configuration trait
#[pallet::config]
pub trait Config:
CreateSignedTransaction<Call<Self>> + CreateInherent<Call<Self>> + frame_system::Config
CreateSignedTransaction<Call<Self>>
+ CreateInherent<Call<Self>>
+ frame_system::Config<RuntimeEvent: From<Event<Self>>>
{
/// The identifier type for an offchain worker.
type AuthorityId: AppCrypto<Self::Public, Self::Signature>;

/// The overarching event type.
type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;

// Configuration parameters

/// A grace period after we send transaction.
Expand Down
1 change: 0 additions & 1 deletion substrate/frame/examples/offchain-worker/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,6 @@ parameter_types! {
}

impl Config for Test {
type RuntimeEvent = RuntimeEvent;
type AuthorityId = crypto::TestAuthId;
type GracePeriod = ConstU64<5>;
type UnsignedInterval = ConstU64<128>;
Expand Down
4 changes: 1 addition & 3 deletions substrate/frame/examples/split/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,7 @@ pub mod pallet {

/// Configure the pallet by specifying the parameters and types on which it depends.
#[pallet::config]
pub trait Config: frame_system::Config {
/// Because this pallet emits events, it depends on the runtime's definition of an event.
type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
pub trait Config: frame_system::Config<RuntimeEvent: From<Event<Self>>> {
Copy link
Member

Choose a reason for hiding this comment

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

IMO it would be much "cooler" 👀 if the pallet::config macro would auto generate all of these. And then we could put a deprecation warning over RuntimeEvent, RuntimeCall etc inclusion in the Config trait later.

Copy link
Contributor

Choose a reason for hiding this comment

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

I agree, for the implementation all pallets except frame-system can bound this From<Event> automatically.

For frame-system pallet, I guess we need something so that the macro can know that the pallet is frame-system, I suggest a new attribute somewhere (maybe on pallet::config) like #[pallet::is_frame_system].
But if you find a better way, please do.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ok, I have made the changes to automatically append the type bound to system supertrait. I did this on the macro expansion level instead of parsing, since we need to know if the pallet Event struct is actually defined.

For frame-system pallet, I guess we need something so that the macro can know that the pallet is frame-system, I suggest a new attribute somewhere (maybe on pallet::config) like #[pallet::is_frame_system].
But if you find a better way, please do.

For this, I simply opted for checking if the pallet config contains a system supertrait, i.e frame_system::Config. If it doesn't, it's a system pallet.

/// Type representing the weight of this pallet
type WeightInfo: WeightInfo;
}
Expand Down
1 change: 0 additions & 1 deletion substrate/frame/examples/split/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@ impl frame_system::Config for Test {
}

impl pallet_template::Config for Test {
type RuntimeEvent = RuntimeEvent;
type WeightInfo = ();
}

Expand Down
2 changes: 0 additions & 2 deletions substrate/frame/parameters/src/tests/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -140,8 +140,6 @@ impl Config for Runtime {
impl pallet_example_basic::Config for Runtime {
// Use the dynamic key in the pallet config:
type MagicNumber = dynamic_params::pallet1::Key1;

type RuntimeEvent = RuntimeEvent;
type WeightInfo = ();
}

Expand Down
2 changes: 0 additions & 2 deletions substrate/frame/parameters/src/tests/test_renamed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,6 @@ impl Config for Runtime {
impl pallet_example_basic::Config for Runtime {
// Use the dynamic key in the pallet config:
type MagicNumber = dynamic_params::pallet1::Key1;

type RuntimeEvent = RuntimeEvent;
type WeightInfo = ();
}

Expand Down
14 changes: 11 additions & 3 deletions substrate/frame/support/procedural/src/pallet/expand/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -143,17 +143,25 @@ pub fn expand_event(def: &mut Def) -> proc_macro2::TokenStream {

let PalletEventDepositAttr { fn_vis, fn_span, .. } = deposit_event;

// `RuntimeEvent` can be defined either as associated type in `Config` or as a type bound in
// system supertrait.
let runtime_event_path = if def.config.has_event_bound {
quote::quote! { <T as #frame_system::Config>::RuntimeEvent }
} else {
quote::quote! { <T as Config #trait_use_gen>::RuntimeEvent }
};

quote::quote_spanned!(*fn_span =>
impl<#type_impl_gen> #pallet_ident<#type_use_gen> #completed_where_clause {
#fn_vis fn deposit_event(event: Event<#event_use_gen>) {
let event = <
<T as Config #trait_use_gen>::RuntimeEvent as
#runtime_event_path as
From<Event<#event_use_gen>>
>::from(event);

let event = <
<T as Config #trait_use_gen>::RuntimeEvent as
Into<<T as #frame_system::Config>::RuntimeEvent>
#runtime_event_path as
Into<#runtime_event_path>
>::into(event);

<#frame_system::Pallet<T>>::deposit_event(event)
Expand Down
105 changes: 101 additions & 4 deletions substrate/frame/support/procedural/src/pallet/parse/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ pub struct ConfigDef {
/// * `IsType<Self as frame_system::Config>::RuntimeEvent`
/// * `From<Event>` or `From<Event<T>>` or `From<Event<T, I>>`
pub has_event_type: bool,
/// Whether the supertrait `frame_system::Config` defines associated type `RuntimeEvent`.
pub has_event_bound: bool,
/// The where clause on trait definition but modified so `Self` is `T`.
pub where_clause: Option<syn::WhereClause>,
/// Whether a default sub-trait should be generated.
Expand Down Expand Up @@ -366,6 +368,38 @@ fn contains_type_info_bound(ty: &TraitItemType) -> bool {
})
}

/// Check that supertrait `Config` contains `RuntimeEvent` associated type bound with
/// `From<Event<Self>>`.
///
/// NOTE: Does not check if the supertrait path is valid system config path.
///
/// ```rs
/// pub trait Config: frame_system::Config<RuntimeEvent: From<Event<Self>>> {
/// ```
Copy link
Contributor

Choose a reason for hiding this comment

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

As a note personally I would always bound the supertrait: frame_system::Config<RuntimeEvent: From<Event>>, always, except if there is the flag is_frame_system.
If user wants to write it, he can do, then it will still compile fine.
https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=7cfad8342293e593a0c8041cd4b5a958

But this is also ok.

fn contains_runtime_event_associated_type_bound(supertrait: &syn::Path) -> bool {
if let Some(args) = supertrait.segments.iter().find(|s| s.ident == "Config") {
if let syn::PathArguments::AngleBracketed(args) = &args.arguments {
for arg in &args.args {
if let syn::GenericArgument::Constraint(c) = arg {
if c.ident != "RuntimeEvent" {
continue;
}

// Check `From<Event<Self>>` bound
let from_event_bound = c
.bounds
.iter()
.find_map(|s| syn::parse2::<FromEventParse>(s.to_token_stream()).ok());

return from_event_bound.is_some();
}
}
}
}

false
}

impl ConfigDef {
pub fn try_from(
frame_system: &syn::Path,
Expand Down Expand Up @@ -406,10 +440,20 @@ impl ConfigDef {
false
};

let has_frame_system_supertrait = item.supertraits.iter().any(|s| {
syn::parse2::<syn::Path>(s.to_token_stream())
.map_or(false, |b| has_expected_system_config(b, frame_system))
});
let (has_frame_system_supertrait, has_event_bound) = item
.supertraits
.iter()
Copy link
Member

Choose a reason for hiding this comment

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

We should do what @gui1117 proposed and introduce a frame_system_pallet attribute macro that we will use in frame_system. Every other pallet will then be assumed to not be frame-system.

.filter_map(|supertrait| syn::parse2::<syn::Path>(supertrait.to_token_stream()).ok())
.fold((false, false), |(mut has_system, mut has_event_bound), supertrait| {
if has_expected_system_config(supertrait.clone(), frame_system) {
has_system = true;
// only check for `RuntimeEvent` bound if `frame_system::Config` is found.
has_event_bound = has_event_bound ||
contains_runtime_event_associated_type_bound(&supertrait);
}

(has_system, has_event_bound)
});

let mut has_event_type = false;
let mut consts_metadata = vec![];
Expand Down Expand Up @@ -589,6 +633,7 @@ impl ConfigDef {
consts_metadata,
associated_types_metadata,
has_event_type,
has_event_bound,
where_clause,
default_sub_trait,
})
Expand Down Expand Up @@ -712,4 +757,56 @@ mod tests {
let path = syn::parse2::<syn::Path>(quote::quote!(something::Config)).unwrap();
assert!(!has_expected_system_config(path, &frame_system));
}

#[test]
fn contains_runtime_event_associated_type_bound_no_bound() {
let supertrait = syn::parse2::<syn::Path>(quote::quote!(frame_system::Config)).unwrap();
assert!(!contains_runtime_event_associated_type_bound(&supertrait));
}

#[test]
fn contains_runtime_event_associated_type_bound_works() {
let supertrait = syn::parse2::<syn::Path>(quote::quote!(
Config<RuntimeEvent: From<Event<Self>>>
));
assert!(contains_runtime_event_associated_type_bound(&supertrait.unwrap()));
}
#[test]
fn contains_runtime_event_associated_type_bound_works_interface() {
let supertrait = syn::parse2::<syn::Path>(quote::quote!(
Config<RuntimeEvent: From<Event<Self, I>>>
));
assert!(contains_runtime_event_associated_type_bound(&supertrait.unwrap()));
}

#[test]
fn contains_runtime_event_associated_type_bound_works_full_path() {
let supertrait = syn::parse2::<syn::Path>(quote::quote!(
frame_system::Config<RuntimeEvent: From<Event<Self>>>
));
assert!(contains_runtime_event_associated_type_bound(&supertrait.unwrap()));
}

#[test]
fn contains_runtime_event_associated_type_bound_invalid_supertrait() {
let supertrait =
syn::parse2::<syn::Path>(quote::quote!(SystemConfig<RuntimeEvent: From<Event<Self>>>))
.unwrap();
assert!(!contains_runtime_event_associated_type_bound(&supertrait));
}

#[test]
fn contains_runtime_event_associated_type_bound_invalid_assoc_type_name() {
let supertrait =
syn::parse2::<syn::Path>(quote::quote!(Config<NonRuntimeEvent: From<Event<Self>>>))
.unwrap();
assert!(!contains_runtime_event_associated_type_bound(&supertrait));
}
#[test]
fn contains_runtime_event_associated_type_bound_invalid_trait_bound() {
let supertrait =
syn::parse2::<syn::Path>(quote::quote!(Config<RuntimeEvent: TryFrom<Event<Self>>>))
.unwrap();
assert!(!contains_runtime_event_associated_type_bound(&supertrait));
}
}
Loading
Loading