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

Adds ability to provide defaults for types provided by construct_runtime #14682

Merged
merged 46 commits into from
Aug 25, 2023
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
bfc3ff7
Adds ability to use defaults for verbatim types
gupnik Jul 30, 2023
18dc0ac
Adds RuntimeOrigin and PalletInfo in DefaultConfig
gupnik Jul 30, 2023
7fb9749
Adds RuntimeEvent in DefaultConfig
gupnik Jul 30, 2023
43fff80
Adds RuntimeEvent in DefaultConfig
gupnik Jul 30, 2023
8e45209
Minor fix
gupnik Jul 30, 2023
9af59d6
Minor fix
gupnik Jul 30, 2023
44f2845
Everything in frame_system can now have a default
gupnik Jul 30, 2023
eed9dc8
Adds docs
gupnik Aug 4, 2023
d2a3546
Adds UI Test for no_bounds
gupnik Aug 4, 2023
821424a
Updates docs
gupnik Aug 4, 2023
5e54173
Adds UI tests for verbatim
gupnik Aug 4, 2023
cd3e530
Merge branch 'master' of github.com:paritytech/substrate into gupnik/…
gupnik Aug 4, 2023
641cc9b
Minor update
gupnik Aug 4, 2023
90af58e
Minor updates
gupnik Aug 4, 2023
c3f5134
Minor updates
gupnik Aug 4, 2023
7f80e65
Addresses review comments
gupnik Aug 9, 2023
38c0601
Merge branch 'master' of github.com:paritytech/substrate into gupnik/…
gupnik Aug 9, 2023
846f296
Fixes test
gupnik Aug 9, 2023
6dfb929
Update frame/support/procedural/src/derive_impl.rs
gupnik Aug 13, 2023
0ddf452
Minor fix
gupnik Aug 13, 2023
3e90665
Merge branch 'master' of github.com:paritytech/substrate into gupnik/…
gupnik Aug 14, 2023
de6e5b6
Minor
gupnik Aug 14, 2023
ce1ad29
Optionally keep verbatim to be replaced later
gupnik Aug 14, 2023
821758b
Fixes build
gupnik Aug 14, 2023
d33f51f
Uses runtime_type
gupnik Aug 15, 2023
96fc62c
Merge branch 'master' of github.com:paritytech/substrate into gupnik/…
gupnik Aug 17, 2023
e051985
Fixes comment
gupnik Aug 17, 2023
16028ff
Fixes comment
gupnik Aug 17, 2023
34f66f6
Fixes test
gupnik Aug 17, 2023
97e470c
Merge branch 'master' of github.com:paritytech/substrate into gupnik/…
gupnik Aug 17, 2023
92ddd1e
Merge branch 'master' of github.com:paritytech/substrate into gupnik/…
gupnik Aug 17, 2023
3524c14
Uses no_aggregated_types as an option in derive_impl
gupnik Aug 21, 2023
1218faf
Uses specific imports
gupnik Aug 21, 2023
01071a9
Fmt
gupnik Aug 21, 2023
a3db9f4
Updates doc
gupnik Aug 21, 2023
28f7cec
Merge branch 'master' of github.com:paritytech/substrate into gupnik/…
gupnik Aug 22, 2023
9d31723
Update frame/support/procedural/src/derive_impl.rs
gupnik Aug 23, 2023
3f1fcc6
Update frame/support/procedural/src/derive_impl.rs
gupnik Aug 23, 2023
58762cf
Addresses review comment
gupnik Aug 23, 2023
cb2a8e0
Addresses review comment
gupnik Aug 23, 2023
32f33ae
fmt
gupnik Aug 23, 2023
88f6253
Renames test files
gupnik Aug 23, 2023
bb30704
Adds docs using docify
gupnik Aug 24, 2023
8f3191f
Fixes test
gupnik Aug 24, 2023
718c1ce
Fixes UI tests
gupnik Aug 24, 2023
969bf0e
Merge branch 'master' of github.com:paritytech/substrate into gupnik/…
gupnik Aug 25, 2023
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
25 changes: 12 additions & 13 deletions frame/examples/default-config/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,6 @@ pub mod pallet {
pub mod tests {
use super::*;
use frame_support::derive_impl;
use sp_runtime::traits::ConstU64;

use super::pallet as pallet_default_config_example;

Expand All @@ -123,18 +122,6 @@ pub mod tests {

#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)]
impl frame_system::Config for Test {
// these items are defined by frame-system as `no_default`, so we must specify them here.
// Note that these are types that actually rely on the outer runtime, and can't sensibly
// have an _independent_ default.
type Block = Block;
type BlockHashCount = ConstU64<10>;
type BaseCallFilter = frame_support::traits::Everything;
type RuntimeOrigin = RuntimeOrigin;
type RuntimeCall = RuntimeCall;
type RuntimeEvent = RuntimeEvent;
type PalletInfo = PalletInfo;
type OnSetCode = ();

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

// type Nonce = u32;
Expand All @@ -157,6 +144,18 @@ pub mod tests {
// type BlockWeights = ();
// type BlockLength = ();
// type DbWeight = ();
// type BaseCallFilter = frame_support::traits::Everything;
// type BlockHashCount = ConstU64<10>;
// type OnSetCode = ();

// These are marked as `#[verbatim]`. Hence, they are being injected as
// defaults with same names.

// type Block = Block;
// type RuntimeOrigin = RuntimeOrigin;
// type RuntimeCall = RuntimeCall;
// type RuntimeEvent = RuntimeEvent;
// type PalletInfo = PalletInfo;

// you could still overwrite any of them if desired.
type SS58Prefix = frame_support::traits::ConstU16<456>;
Expand Down
49 changes: 47 additions & 2 deletions frame/support/procedural/src/derive_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,40 @@ use macro_magic::mm_core::ForeignPath;
use proc_macro2::TokenStream as TokenStream2;
use quote::{quote, ToTokens};
use std::collections::HashSet;
use syn::{parse2, parse_quote, spanned::Spanned, Ident, ImplItem, ItemImpl, Path, Result, Token};
use syn::{
parse2, parse_quote, spanned::Spanned, token, Ident, ImplItem, ItemImpl, Path, Result, Token,
};

mod keyword {
syn::custom_keyword!(verbatim);
}

#[derive(derive_syn_parse::Parse, PartialEq, Eq)]
pub enum PalletAttrType {
#[peek(keyword::verbatim, name = "verbatim")]
Verbatim(keyword::verbatim),
Copy link
Contributor

Choose a reason for hiding this comment

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

nice making this extendible 💯

}

#[derive(derive_syn_parse::Parse)]
pub struct PalletAttr {
_pound: Token![#],
#[bracket]
_bracket: token::Bracket,
#[inside(_bracket)]
typ: PalletAttrType,
}

fn take_first_item_pallet_attr<Attr>(item: &mut syn::ImplItemType) -> syn::Result<Option<Attr>>
gupnik marked this conversation as resolved.
Show resolved Hide resolved
where
Attr: syn::parse::Parse,
{
let attrs = &mut item.attrs;
if let Some(pallet_attr) = attrs.get(0) {
Ok(Some(syn::parse2(pallet_attr.into_token_stream())?))
} else {
Ok(None)
}
gupnik marked this conversation as resolved.
Show resolved Hide resolved
}

#[derive(Parse)]
pub struct DeriveImplAttrArgs {
Expand Down Expand Up @@ -96,7 +129,19 @@ fn combine_impls(
// do not copy colliding items that have an ident
return None
}
if matches!(item, ImplItem::Type(_)) {
if let ImplItem::Type(item) = item.clone() {
let mut item = item.clone();
if let Ok(Some(pallet_attr)) = take_first_item_pallet_attr::<PalletAttr>(&mut item)
{
match pallet_attr.typ {
PalletAttrType::Verbatim(_) => {
let modified_item: ImplItem = parse_quote! {
type #ident = #ident;
};
return Some(modified_item)
},
}
}
gupnik marked this conversation as resolved.
Show resolved Hide resolved
// modify and insert uncolliding type items
let modified_item: ImplItem = parse_quote! {
type #ident = <#default_impl_path as #disambiguation_path>::#ident;
Expand Down
26 changes: 26 additions & 0 deletions frame/support/procedural/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -774,6 +774,19 @@ pub fn no_default(_: TokenStream, _: TokenStream) -> TokenStream {
pallet_macro_stub()
}

/// The optional attribute `#[pallet::no_bounds]` can be attached to trait items within a
/// `Config` trait impl that has [`#[pallet::config(with_default)]`](`macro@config`) attached.
///
/// Attaching this attribute to a trait item ensures that the generated trait `DefaultConfig`
/// will not have any bounds for this trait item.
///
/// As an example, if you have a trait item `type AccountId: SomeTrait;` in your `Config` trait,
/// the generated `DefaultConfig` will only have `type AccountId;` with no trait bound.
#[proc_macro_attribute]
pub fn no_bounds(_: TokenStream, _: TokenStream) -> TokenStream {
gupnik marked this conversation as resolved.
Show resolved Hide resolved
pallet_macro_stub()
}

/// Attach this attribute to an impl statement that you want to use with
/// [`#[derive_impl(..)]`](`macro@derive_impl`).
///
Expand Down Expand Up @@ -843,6 +856,19 @@ pub fn register_default_impl(attrs: TokenStream, tokens: TokenStream) -> TokenSt
}
}

/// The optional attribute `#[verbatim]` can be attached to any item in an impl
/// statement that has `#[register_default_impl]` attached.
///
/// Attaching this attribute to an item ensures that the combined impl generated via
/// [`#[derive_impl(..)]`](`macro@derive_impl`) will use the same name for the default.
///
/// As an example, if you have an impl item `#[verbatim] type RuntimeEvent = ();` in
/// your impl statement, the combined impl will have `type RuntimeEvent = RuntimeEvent;` instead.
#[proc_macro_attribute]
pub fn verbatim(_: TokenStream, tokens: TokenStream) -> TokenStream {
tokens
}

/// Used internally to decorate pallet attribute macro stubs when they are erroneously used
/// outside of a pallet module
fn pallet_macro_stub() -> TokenStream {
Expand Down
18 changes: 17 additions & 1 deletion frame/support/procedural/src/pallet/expand/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,23 @@ Consequently, a runtime that wants to include this pallet must implement this tr
// we only emit `DefaultConfig` if there are trait items, so an empty `DefaultConfig` is
// impossible consequently
if config.default_sub_trait.len() > 0 {
let trait_items = &config.default_sub_trait;
let trait_items = &config
.default_sub_trait
.iter()
.map(|item| {
if item.1 {
if let syn::TraitItem::Type(item) = item.0.clone() {
let mut item = item.clone();
item.bounds.clear();
syn::TraitItem::Type(item)
} else {
item.0.clone()
}
} else {
item.0.clone()
}
})
.collect::<Vec<_>>();
quote!(
/// Based on [`Config`]. Auto-generated by
/// [`#[pallet::config(with_default)]`](`frame_support::pallet_macros::config`).
Expand Down
30 changes: 27 additions & 3 deletions frame/support/procedural/src/pallet/parse/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ mod keyword {
syn::custom_keyword!(frame_system);
syn::custom_keyword!(disable_frame_system_supertrait_check);
syn::custom_keyword!(no_default);
syn::custom_keyword!(no_bounds);
syn::custom_keyword!(constant);
}

Expand All @@ -59,7 +60,11 @@ pub struct ConfigDef {
/// Contains default sub-trait items (instantiated by `#[pallet::config(with_default)]`).
/// Vec will be empty if `#[pallet::config(with_default)]` is not specified or if there are
/// no trait items
pub default_sub_trait: Vec<syn::TraitItem>,
///
/// A bool for each sub-trait item indicates whether the item has `#[pallet::no_bounds]`
/// attached to it. If true, the item will not have any bounds in the generated default
/// sub-trait.
pub default_sub_trait: Vec<(syn::TraitItem, bool)>,
}

/// Input definition for a constant in pallet config.
Expand Down Expand Up @@ -136,6 +141,8 @@ impl syn::parse::Parse for DisableFrameSystemSupertraitCheck {
pub enum PalletAttrType {
#[peek(keyword::no_default, name = "no_default")]
NoDefault(keyword::no_default),
#[peek(keyword::no_bounds, name = "no_bounds")]
NoBounds(keyword::no_bounds),
#[peek(keyword::constant, name = "constant")]
Constant(keyword::constant),
}
Expand Down Expand Up @@ -348,6 +355,7 @@ impl ConfigDef {

let mut already_no_default = false;
let mut already_constant = false;
let mut already_no_bounds = false;

while let Ok(Some(pallet_attr)) =
helper::take_first_item_pallet_attr::<PalletAttr>(trait_item)
Expand Down Expand Up @@ -384,11 +392,27 @@ impl ConfigDef {
}
already_no_default = true;
},
(PalletAttrType::NoBounds(_), _) => {
if !enable_default {
return Err(syn::Error::new(
pallet_attr._bracket.span.join(),
"`#[pallet:no_bounds]` can only be used if `#[pallet::config(with_default)]` \
has been specified"
))
}
if already_no_bounds {
return Err(syn::Error::new(
pallet_attr._bracket.span.join(),
"Duplicate #[pallet::no_bounds] attribute not allowed.",
))
}
already_no_bounds = true;
},
}
}

if !already_no_default && !is_event && enable_default {
default_sub_trait.push(trait_item.clone());
if !already_no_default && enable_default {
default_sub_trait.push((trait_item.clone(), already_no_bounds));
}
}

Expand Down
4 changes: 2 additions & 2 deletions frame/support/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2905,8 +2905,8 @@ pub mod pallet_macros {
call_index, compact, composite_enum, config, constant,
disable_frame_system_supertrait_check, error, event, extra_constants, generate_deposit,
generate_store, genesis_build, genesis_config, getter, hooks, import_section, inherent,
no_default, origin, pallet_section, storage, storage_prefix, storage_version, type_value,
unbounded, validate_unsigned, weight, whitelist_storage,
no_bounds, no_default, origin, pallet_section, storage, storage_prefix, storage_version,
type_value, unbounded, validate_unsigned, verbatim, weight, whitelist_storage,
};
}

Expand Down
25 changes: 25 additions & 0 deletions frame/support/test/tests/derive_impl_ui/pass/verbatim_working.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
use frame_support::{*, pallet_macros::verbatim};
use static_assertions::assert_type_eq_all;

pub trait Shape {
type Area;
}

type Area = f32;

struct DefaultShape;

#[register_default_impl(DefaultShape)]
impl Shape for DefaultShape {
#[verbatim]
type Area = ();
gupnik marked this conversation as resolved.
Show resolved Hide resolved
}

struct Circle;

#[derive_impl(DefaultShape)] // Injects type Area = Area;
impl Shape for Circle {}

assert_type_eq_all!(<Circle as Shape>::Area, Area);

fn main() {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
use frame_support::{*, pallet_macros::verbatim};
use static_assertions::assert_type_eq_all;

pub trait Shape {
type Area;
}

struct DefaultShape;

#[register_default_impl(DefaultShape)]
impl Shape for DefaultShape {
#[verbatim]
type Area = ();
}

struct Circle;

#[derive_impl(DefaultShape)] // Injects type Area = Area;
impl Shape for Circle {}

assert_type_eq_all!(<Circle as Shape>::Area, Area);

fn main() {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
error[E0412]: cannot find type `Area` in this scope
--> tests/derive_impl_ui/verbatim_fails_when_type_not_in_scope.rs:13:10
|
13 | type Area = ();
| ^^^^ help: you might have meant to use the associated type: `Self::Area`
...
18 | #[derive_impl(DefaultShape)] // Injects type Area = Area;
| ---------------------------- in this macro invocation
|
= note: this error originates in the macro `__export_tokens_tt_default_shape` which comes from the expansion of the macro `frame_support::macro_magic::forward_tokens` (in Nightly builds, run with -Z macro-backtrace for more info)

error[E0412]: cannot find type `Area` in this scope
--> tests/derive_impl_ui/verbatim_fails_when_type_not_in_scope.rs:21:46
|
21 | assert_type_eq_all!(<Circle as Shape>::Area, Area);
| ^^^^ not found in this scope
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#[frame_support::pallet]
mod pallet {
use frame_support::pallet_prelude::*;
use frame_system::pallet_prelude::*;

#[pallet::config]
pub trait Config: frame_system::Config {
#[pallet::constant]
#[pallet::no_bounds]
Copy link
Member

Choose a reason for hiding this comment

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

Eventually we could think about grouping those.

type MyGetParam2: Get<u32>;
}

#[pallet::pallet]
pub struct Pallet<T>(core::marker::PhantomData<T>);

#[pallet::hooks]
impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {}

#[pallet::call]
impl<T: Config> Pallet<T> {}
}

fn main() {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
error: `#[pallet:no_bounds]` can only be used if `#[pallet::config(with_default)]` has been specified
--> tests/pallet_ui/no_bounds_but_missing_with_default.rs:9:4
|
9 | #[pallet::no_bounds]
| ^^^^^^^^^^^^^^^^^^^
Loading