-
Notifications
You must be signed in to change notification settings - Fork 798
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
base: master
Are you sure you want to change the base?
Changes from 3 commits
9e75ca9
214fdce
668adef
12d85f0
5344a60
d71f878
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
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 |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -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. | ||||||
#[pallet::no_default] | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
type RuntimeTask: Task; | ||||||
|
@@ -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>; | ||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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>>> { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. IMO it would be much "cooler" 👀 if the There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 For frame-system pallet, I guess we need something so that the macro can know that the pallet is There was a problem hiding this comment. Choose a reason for hiding this commentThe 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
For this, I simply opted for checking if the pallet config contains a system supertrait, i.e |
||
/// Type representing the weight of this pallet | ||
type WeightInfo: WeightInfo; | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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. | ||
|
@@ -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>>> { | ||
/// ``` | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As a note personally I would always bound the supertrait: 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, | ||
|
@@ -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() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should do what @gui1117 proposed and introduce a |
||
.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![]; | ||
|
@@ -589,6 +633,7 @@ impl ConfigDef { | |
consts_metadata, | ||
associated_types_metadata, | ||
has_event_type, | ||
has_event_bound, | ||
where_clause, | ||
default_sub_trait, | ||
}) | ||
|
@@ -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)); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.