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

refactor: Move T:Config into where clause in #[benchmarks] macro if needed #7418

Open
wants to merge 5 commits into
base: master
Choose a base branch
from

Conversation

tsenovilla
Copy link
Contributor

Description

Currently, the #[benchmarks] macro always add <T:Config> to the expanded code even if a where clause is used. Using a where clause which also includes a trait bound for the generic T is triggering this clippy warning from Rust 1.78 onwards. We've found that here in LAOS, as we need to include T: pallet_vesting::Config in the where clause, here's the outcome:

error: bound is defined in more than one place
   --> pallets/precompiles-benchmark/src/precompiles/vesting/benchmarking.rs:130:1
    |
130 | / #[benchmarks(
131 | |     where
132 | |         T: Config + pallet_vesting::Config,
    | |         ^
133 | |         T::AccountIdToH160: ConvertBack<T::AccountId, H160>,
134 | |         BalanceOf<T>: Into<U256>,
135 | |         BlockNumberFor<T>: Into<U256>
136 | | )]
    | |__^
    |
    = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#multiple_bound_locations
    = note: `-D clippy::multiple-bound-locations` implied by `-D warnings`
    = help: to override `-D warnings` add `#[allow(clippy::multiple_bound_locations)]`
    = note: this error originates in the attribute macro `benchmarks` (in Nightly builds, run with -Z macro-backtrace for more info)

While this is a harmless warning, only thrown due to a trait bound for T is being defined twice in an expanded code that nobody will see, and while annotating the benchmarks module with #[allow(clippy::multiple_bound_locations)] is enough to get rid of it, it might cause unnecessary concerns.

Hence, I think it's worth slightly modifying the macro to avoid this.

Review Notes

What I propose is to include <T: Config> (or its instance version) in the expanded code only if no where clause was specified, and include that trait bound in the where clause if one is present.

I considered always creating a where clause which includes <T: Config> even if the macro doesn't specify a where clause and totally getting rid of <T: Config>, but discarded the idea for simplicity.

I also considered checking if T:Config is present in the provided where clause (as it's the case in the LAOS example I provided) before adding it, but discarded the idea as well due to it implies a bit more computation and having T:Config defined twice in the where clause is harmless: the compiler will ignore the second occurrence and nobody will see it's there.

If you think this change is worth it and one of the discarded ideas would be a better approach, I'm happy to push that code instead.

@tsenovilla tsenovilla requested a review from a team as a code owner February 1, 2025 11:12
@tsenovilla tsenovilla changed the title refactor: Move T:Config into where clause in #[benchmarks] macro if needed refactor: Move T:Config into where clause in #[benchmarks] macro if needed Feb 1, 2025
Copy link
Contributor

@gui1117 gui1117 left a comment

Choose a reason for hiding this comment

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

Maybe we can always use the where clause to bound the types then? doesn't it look a bit better?

@@ -482,7 +482,19 @@ pub fn benchmarks(
let mod_span = module.span();
let where_clause = match syn::parse::<Nothing>(attrs.clone()) {
Ok(_) => quote!(),
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
Ok(_) => quote!(),
Ok(_) => match instance {
false => quote!(T: Config),
true => quote!(T: Config<I>, I: 'static),
},

Comment on lines 583 to 590
let type_impl_generics = if where_clause.is_empty() {
match instance {
false => quote!(T: Config),
true => quote!(T: Config<I>, I: 'static),
}
} else {
type_use_generics.clone()
};
Copy link
Contributor

Choose a reason for hiding this comment

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

replace the type_impl_generics with type_use_generics in the expansion.

Suggested change
let type_impl_generics = if where_clause.is_empty() {
match instance {
false => quote!(T: Config),
true => quote!(T: Config<I>, I: 'static),
}
} else {
type_use_generics.clone()
};

Comment on lines 949 to 956
let type_impl_generics = if where_clause.is_empty() {
match is_instance {
false => quote!(T: Config),
true => quote!(T: Config<I>, I: 'static),
}
} else {
type_use_generics.clone()
};
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
let type_impl_generics = if where_clause.is_empty() {
match is_instance {
false => quote!(T: Config),
true => quote!(T: Config<I>, I: 'static),
}
} else {
type_use_generics.clone()
};

@paritytech-review-bot paritytech-review-bot bot requested a review from a team February 1, 2025 16:04
@tsenovilla
Copy link
Contributor Author

tsenovilla commented Feb 1, 2025

Maybe we can always use the where clause to bound the types then? doesn't it look a bit better?

Yep I agree it looks nicer. I applied the changes + removed T: frame_system::Config from where clauses as T: Config would be in always in the where clause and all pallets are tightly coupled to frame_system

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants