Skip to content

Commit 83ae018

Browse files
gupnikKiChjangbkchr
authored
Adds ability to provide defaults for types provided by construct_runtime (#14682)
* Adds ability to use defaults for verbatim types * Adds RuntimeOrigin and PalletInfo in DefaultConfig * Adds RuntimeEvent in DefaultConfig * Adds RuntimeEvent in DefaultConfig * Minor fix * Minor fix * Everything in frame_system can now have a default * Adds docs * Adds UI Test for no_bounds * Updates docs * Adds UI tests for verbatim * Minor update * Minor updates * Minor updates * Addresses review comments * Fixes test * Update frame/support/procedural/src/derive_impl.rs Co-authored-by: Keith Yeung <[email protected]> * Minor fix * Minor * Fixes build * Uses runtime_type * Fixes comment * Fixes comment * Fixes test * Uses no_aggregated_types as an option in derive_impl * Uses specific imports * Fmt * Updates doc * Update frame/support/procedural/src/derive_impl.rs Co-authored-by: Bastian Köcher <[email protected]> * Update frame/support/procedural/src/derive_impl.rs Co-authored-by: Bastian Köcher <[email protected]> * Addresses review comment * Addresses review comment * fmt * Renames test files * Adds docs using docify * Fixes test * Fixes UI tests --------- Co-authored-by: Keith Yeung <[email protected]> Co-authored-by: Bastian Köcher <[email protected]>
1 parent 32541bd commit 83ae018

21 files changed

+409
-41
lines changed

substrate/Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

substrate/frame/balances/src/lib.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -220,11 +220,14 @@ pub mod pallet {
220220

221221
pub struct TestDefaultConfig;
222222

223-
#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)]
223+
#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig, no_aggregated_types)]
224224
impl frame_system::DefaultConfig for TestDefaultConfig {}
225225

226226
#[frame_support::register_default_impl(TestDefaultConfig)]
227227
impl DefaultConfig for TestDefaultConfig {
228+
#[inject_runtime_type]
229+
type RuntimeEvent = ();
230+
228231
type Balance = u64;
229232

230233
type ReserveIdentifier = ();
@@ -242,6 +245,7 @@ pub mod pallet {
242245
#[pallet::config(with_default)]
243246
pub trait Config<I: 'static = ()>: frame_system::Config {
244247
/// The overarching event type.
248+
#[pallet::no_default_bounds]
245249
type RuntimeEvent: From<Event<Self, I>>
246250
+ IsType<<Self as frame_system::Config>::RuntimeEvent>;
247251

substrate/frame/examples/default-config/src/lib.rs

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ pub mod pallet {
8787
/// A type providing default configurations for this pallet in testing environment.
8888
pub struct TestDefaultConfig;
8989

90-
#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)]
90+
#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig, no_aggregated_types)]
9191
impl frame_system::DefaultConfig for TestDefaultConfig {}
9292

9393
#[frame_support::register_default_impl(TestDefaultConfig)]
@@ -109,7 +109,7 @@ pub mod pallet {
109109
/// example, we simple derive `frame_system::config_preludes::TestDefaultConfig` again.
110110
pub struct OtherDefaultConfig;
111111

112-
#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)]
112+
#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig, no_aggregated_types)]
113113
impl frame_system::DefaultConfig for OtherDefaultConfig {}
114114

115115
#[frame_support::register_default_impl(OtherDefaultConfig)]
@@ -147,16 +147,7 @@ pub mod tests {
147147
#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)]
148148
impl frame_system::Config for Runtime {
149149
// these items are defined by frame-system as `no_default`, so we must specify them here.
150-
// Note that these are types that actually rely on the outer runtime, and can't sensibly
151-
// have an _independent_ default.
152150
type Block = Block;
153-
type BlockHashCount = frame_support::traits::ConstU64<10>;
154-
type BaseCallFilter = frame_support::traits::Everything;
155-
type RuntimeOrigin = RuntimeOrigin;
156-
type RuntimeCall = RuntimeCall;
157-
type RuntimeEvent = RuntimeEvent;
158-
type PalletInfo = PalletInfo;
159-
type OnSetCode = ();
160151

161152
// all of this is coming from `frame_system::config_preludes::TestDefaultConfig`.
162153

@@ -177,6 +168,17 @@ pub mod tests {
177168
// type BlockWeights = ();
178169
// type BlockLength = ();
179170
// type DbWeight = ();
171+
// type BaseCallFilter = frame_support::traits::Everything;
172+
// type BlockHashCount = frame_support::traits::ConstU64<10>;
173+
// type OnSetCode = ();
174+
175+
// These are marked as `#[inject_runtime_type]`. Hence, they are being injected as
176+
// types generated by `construct_runtime`.
177+
178+
// type RuntimeOrigin = RuntimeOrigin;
179+
// type RuntimeCall = RuntimeCall;
180+
// type RuntimeEvent = RuntimeEvent;
181+
// type PalletInfo = PalletInfo;
180182

181183
// you could still overwrite any of them if desired.
182184
type SS58Prefix = frame_support::traits::ConstU16<456>;

substrate/frame/support/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ environmental = { version = "1.1.4", default-features = false }
4444
sp-genesis-builder = { version = "0.1.0", default-features=false, path = "../../primitives/genesis-builder" }
4545
serde_json = { version = "1.0.85", default-features = false, features = ["alloc"] }
4646
docify = "0.2.1"
47+
static_assertions = "1.1.0"
4748

4849
aquamarine = { version = "0.3.2" }
4950

substrate/frame/support/procedural/src/derive_impl.rs

Lines changed: 59 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,46 @@ use macro_magic::mm_core::ForeignPath;
2222
use proc_macro2::TokenStream as TokenStream2;
2323
use quote::{quote, ToTokens};
2424
use std::collections::HashSet;
25-
use syn::{parse2, parse_quote, spanned::Spanned, Ident, ImplItem, ItemImpl, Path, Result, Token};
25+
use syn::{
26+
parse2, parse_quote, spanned::Spanned, token, Ident, ImplItem, ItemImpl, Path, Result, Token,
27+
};
2628

27-
#[derive(Parse)]
29+
mod keyword {
30+
syn::custom_keyword!(inject_runtime_type);
31+
syn::custom_keyword!(no_aggregated_types);
32+
}
33+
34+
#[derive(derive_syn_parse::Parse, PartialEq, Eq)]
35+
pub enum PalletAttrType {
36+
#[peek(keyword::inject_runtime_type, name = "inject_runtime_type")]
37+
RuntimeType(keyword::inject_runtime_type),
38+
}
39+
40+
#[derive(derive_syn_parse::Parse)]
41+
pub struct PalletAttr {
42+
_pound: Token![#],
43+
#[bracket]
44+
_bracket: token::Bracket,
45+
#[inside(_bracket)]
46+
typ: PalletAttrType,
47+
}
48+
49+
fn get_first_item_pallet_attr<Attr>(item: &syn::ImplItemType) -> syn::Result<Option<Attr>>
50+
where
51+
Attr: syn::parse::Parse,
52+
{
53+
item.attrs.get(0).map(|a| syn::parse2(a.into_token_stream())).transpose()
54+
}
55+
56+
#[derive(Parse, Debug)]
2857
pub struct DeriveImplAttrArgs {
2958
pub default_impl_path: Path,
3059
_as: Option<Token![as]>,
3160
#[parse_if(_as.is_some())]
3261
pub disambiguation_path: Option<Path>,
62+
_comma: Option<Token![,]>,
63+
#[parse_if(_comma.is_some())]
64+
pub no_aggregated_types: Option<keyword::no_aggregated_types>,
3365
}
3466

3567
impl ForeignPath for DeriveImplAttrArgs {
@@ -43,6 +75,8 @@ impl ToTokens for DeriveImplAttrArgs {
4375
tokens.extend(self.default_impl_path.to_token_stream());
4476
tokens.extend(self._as.to_token_stream());
4577
tokens.extend(self.disambiguation_path.to_token_stream());
78+
tokens.extend(self._comma.to_token_stream());
79+
tokens.extend(self.no_aggregated_types.to_token_stream());
4680
}
4781
}
4882

@@ -78,6 +112,7 @@ fn combine_impls(
78112
foreign_impl: ItemImpl,
79113
default_impl_path: Path,
80114
disambiguation_path: Path,
115+
inject_runtime_types: bool,
81116
) -> ItemImpl {
82117
let (existing_local_keys, existing_unsupported_items): (HashSet<ImplItem>, HashSet<ImplItem>) =
83118
local_impl
@@ -96,7 +131,20 @@ fn combine_impls(
96131
// do not copy colliding items that have an ident
97132
return None
98133
}
99-
if matches!(item, ImplItem::Type(_)) {
134+
if let ImplItem::Type(typ) = item.clone() {
135+
let mut typ = typ.clone();
136+
if let Ok(Some(PalletAttr { typ: PalletAttrType::RuntimeType(_), .. })) =
137+
get_first_item_pallet_attr::<PalletAttr>(&mut typ)
138+
{
139+
let item: ImplItem = if inject_runtime_types {
140+
parse_quote! {
141+
type #ident = #ident;
142+
}
143+
} else {
144+
item
145+
};
146+
return Some(item)
147+
}
100148
// modify and insert uncolliding type items
101149
let modified_item: ImplItem = parse_quote! {
102150
type #ident = <#default_impl_path as #disambiguation_path>::#ident;
@@ -132,6 +180,7 @@ pub fn derive_impl(
132180
foreign_tokens: TokenStream2,
133181
local_tokens: TokenStream2,
134182
disambiguation_path: Option<Path>,
183+
no_aggregated_types: Option<keyword::no_aggregated_types>,
135184
) -> Result<TokenStream2> {
136185
let local_impl = parse2::<ItemImpl>(local_tokens)?;
137186
let foreign_impl = parse2::<ItemImpl>(foreign_tokens)?;
@@ -151,8 +200,13 @@ pub fn derive_impl(
151200
};
152201

153202
// generate the combined impl
154-
let combined_impl =
155-
combine_impls(local_impl, foreign_impl, default_impl_path, disambiguation_path);
203+
let combined_impl = combine_impls(
204+
local_impl,
205+
foreign_impl,
206+
default_impl_path,
207+
disambiguation_path,
208+
no_aggregated_types.is_none(),
209+
);
156210

157211
Ok(quote!(#combined_impl))
158212
}

substrate/frame/support/procedural/src/lib.rs

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ use macro_magic::import_tokens_attr;
3838
use proc_macro::TokenStream;
3939
use quote::{quote, ToTokens};
4040
use std::{cell::RefCell, str::FromStr};
41-
use syn::{parse_macro_input, Error, ItemImpl, ItemMod};
41+
use syn::{parse_macro_input, Error, ItemImpl, ItemMod, TraitItemType};
4242

4343
pub(crate) const INHERENT_INSTANCE_NAME: &str = "__InherentHiddenInstance";
4444

@@ -596,6 +596,19 @@ pub fn storage_alias(attributes: TokenStream, input: TokenStream) -> TokenStream
596596
///
597597
/// Conversely, the `default_impl_path` argument is required and cannot be omitted.
598598
///
599+
/// Optionally, `no_aggregated_types` can be specified as follows:
600+
///
601+
/// ```ignore
602+
/// #[derive_impl(default_impl_path as disambiguation_path, no_aggregated_types)]
603+
/// impl SomeTrait for SomeStruct {
604+
/// ...
605+
/// }
606+
/// ```
607+
///
608+
/// If specified, this indicates that the aggregated types (as denoted by impl items
609+
/// attached with [`#[inject_runtime_type]`]) should not be injected with the respective concrete
610+
/// types. By default, all such types are injected.
611+
///
599612
/// You can also make use of `#[pallet::no_default]` on specific items in your default impl that you
600613
/// want to ensure will not be copied over but that you nonetheless want to use locally in the
601614
/// context of the foreign impl and the pallet (or context) in which it is defined.
@@ -759,6 +772,7 @@ pub fn derive_impl(attrs: TokenStream, input: TokenStream) -> TokenStream {
759772
attrs.into(),
760773
input.into(),
761774
custom_attrs.disambiguation_path,
775+
custom_attrs.no_aggregated_types,
762776
)
763777
.unwrap_or_else(|r| r.into_compile_error())
764778
.into()
@@ -774,6 +788,19 @@ pub fn no_default(_: TokenStream, _: TokenStream) -> TokenStream {
774788
pallet_macro_stub()
775789
}
776790

791+
/// The optional attribute `#[pallet::no_default_bounds]` can be attached to trait items within a
792+
/// `Config` trait impl that has [`#[pallet::config(with_default)]`](`macro@config`) attached.
793+
///
794+
/// Attaching this attribute to a trait item ensures that the generated trait `DefaultConfig`
795+
/// will not have any bounds for this trait item.
796+
///
797+
/// As an example, if you have a trait item `type AccountId: SomeTrait;` in your `Config` trait,
798+
/// the generated `DefaultConfig` will only have `type AccountId;` with no trait bound.
799+
#[proc_macro_attribute]
800+
pub fn no_default_bounds(_: TokenStream, _: TokenStream) -> TokenStream {
801+
pallet_macro_stub()
802+
}
803+
777804
/// Attach this attribute to an impl statement that you want to use with
778805
/// [`#[derive_impl(..)]`](`macro@derive_impl`).
779806
///
@@ -843,6 +870,25 @@ pub fn register_default_impl(attrs: TokenStream, tokens: TokenStream) -> TokenSt
843870
}
844871
}
845872

873+
#[proc_macro_attribute]
874+
pub fn inject_runtime_type(_: TokenStream, tokens: TokenStream) -> TokenStream {
875+
let item = tokens.clone();
876+
let item = syn::parse_macro_input!(item as TraitItemType);
877+
if item.ident != "RuntimeCall" &&
878+
item.ident != "RuntimeEvent" &&
879+
item.ident != "RuntimeOrigin" &&
880+
item.ident != "PalletInfo"
881+
{
882+
return syn::Error::new_spanned(
883+
item,
884+
"`#[inject_runtime_type]` can only be attached to `RuntimeCall`, `RuntimeEvent`, `RuntimeOrigin` or `PalletInfo`",
885+
)
886+
.to_compile_error()
887+
.into();
888+
}
889+
tokens
890+
}
891+
846892
/// Used internally to decorate pallet attribute macro stubs when they are erroneously used
847893
/// outside of a pallet module
848894
fn pallet_macro_stub() -> TokenStream {

substrate/frame/support/procedural/src/pallet/expand/config.rs

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,23 @@ Consequently, a runtime that wants to include this pallet must implement this tr
5252
// impossible consequently.
5353
match &config.default_sub_trait {
5454
Some(default_sub_trait) if default_sub_trait.items.len() > 0 => {
55-
let trait_items = &default_sub_trait.items;
55+
let trait_items = &default_sub_trait
56+
.items
57+
.iter()
58+
.map(|item| {
59+
if item.1 {
60+
if let syn::TraitItem::Type(item) = item.0.clone() {
61+
let mut item = item.clone();
62+
item.bounds.clear();
63+
syn::TraitItem::Type(item)
64+
} else {
65+
item.0.clone()
66+
}
67+
} else {
68+
item.0.clone()
69+
}
70+
})
71+
.collect::<Vec<_>>();
5672

5773
let type_param_bounds = if default_sub_trait.has_system {
5874
let system = &def.frame_system;

substrate/frame/support/procedural/src/pallet/parse/config.rs

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,16 @@ mod keyword {
3434
syn::custom_keyword!(frame_system);
3535
syn::custom_keyword!(disable_frame_system_supertrait_check);
3636
syn::custom_keyword!(no_default);
37+
syn::custom_keyword!(no_default_bounds);
3738
syn::custom_keyword!(constant);
3839
}
3940

4041
#[derive(Default)]
4142
pub struct DefaultTrait {
42-
pub items: Vec<syn::TraitItem>,
43+
/// A bool for each sub-trait item indicates whether the item has
44+
/// `#[pallet::no_default_bounds]` attached to it. If true, the item will not have any bounds
45+
/// in the generated default sub-trait.
46+
pub items: Vec<(syn::TraitItem, bool)>,
4347
pub has_system: bool,
4448
}
4549

@@ -142,6 +146,8 @@ impl syn::parse::Parse for DisableFrameSystemSupertraitCheck {
142146
pub enum PalletAttrType {
143147
#[peek(keyword::no_default, name = "no_default")]
144148
NoDefault(keyword::no_default),
149+
#[peek(keyword::no_default_bounds, name = "no_default_bounds")]
150+
NoBounds(keyword::no_default_bounds),
145151
#[peek(keyword::constant, name = "constant")]
146152
Constant(keyword::constant),
147153
}
@@ -366,6 +372,7 @@ impl ConfigDef {
366372

367373
let mut already_no_default = false;
368374
let mut already_constant = false;
375+
let mut already_no_default_bounds = false;
369376

370377
while let Ok(Some(pallet_attr)) =
371378
helper::take_first_item_pallet_attr::<PalletAttr>(trait_item)
@@ -403,15 +410,31 @@ impl ConfigDef {
403410

404411
already_no_default = true;
405412
},
413+
(PalletAttrType::NoBounds(_), _) => {
414+
if !enable_default {
415+
return Err(syn::Error::new(
416+
pallet_attr._bracket.span.join(),
417+
"`#[pallet:no_default_bounds]` can only be used if `#[pallet::config(with_default)]` \
418+
has been specified"
419+
))
420+
}
421+
if already_no_default_bounds {
422+
return Err(syn::Error::new(
423+
pallet_attr._bracket.span.join(),
424+
"Duplicate #[pallet::no_default_bounds] attribute not allowed.",
425+
))
426+
}
427+
already_no_default_bounds = true;
428+
},
406429
}
407430
}
408431

409-
if !already_no_default && !is_event && enable_default {
432+
if !already_no_default && enable_default {
410433
default_sub_trait
411434
.as_mut()
412435
.expect("is 'Some(_)' if 'enable_default'; qed")
413436
.items
414-
.push(trait_item.clone());
437+
.push((trait_item.clone(), already_no_default_bounds));
415438
}
416439
}
417440

0 commit comments

Comments
 (0)