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

Provide a way for custom derives to know if they were invoked via #[derive_const] #118580

Closed

Conversation

fmease
Copy link
Member

@fmease fmease commented Dec 4, 2023

Fixes #118304.

cc @rust-lang/wg-macros
r? compiler and libs-api

@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Dec 4, 2023
@fmease fmease added the T-libs-api Relevant to the library API team, which will review and decide on the PR/issue. label Dec 4, 2023
#[unstable(feature = "derive_const", issue = "none")]
#[derive(Default, Clone)]
#[non_exhaustive]
pub struct DeriveExpansionOptions;
Copy link
Member Author

Choose a reason for hiding this comment

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

bikeshed name

Comment on lines +110 to +114
#[unstable(feature = "derive_const", issue = "none")]
impl !Send for DeriveExpansionOptions {}
#[unstable(feature = "derive_const", issue = "none")]
impl !Sync for DeriveExpansionOptions {}
Copy link
Member Author

Choose a reason for hiding this comment

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

for forward compatibility

@rust-log-analyzer

This comment has been minimized.

@fmease fmease force-pushed the libproc_macro-is_derive_const branch from 87da6b8 to 1e4af5e Compare December 4, 2023 01:32
@rust-log-analyzer

This comment has been minimized.

@fmease fmease force-pushed the libproc_macro-is_derive_const branch from 1e4af5e to 25c6726 Compare December 4, 2023 11:26
@fmease fmease marked this pull request as ready for review December 4, 2023 11:27
Comment on lines +7 to +8
| incorrect number of function parameters
| incorrect number of function parameters
Copy link
Member Author

@fmease fmease Dec 4, 2023

Choose a reason for hiding this comment

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

This duplication makes me sad but it's annoying to fix. I'd need to modify note_type_err just for this.

@rust-log-analyzer

This comment has been minimized.

@fmease fmease force-pushed the libproc_macro-is_derive_const branch from 25c6726 to 541dd8c Compare December 4, 2023 15:17
@rust-log-analyzer

This comment has been minimized.

@fmease
Copy link
Member Author

fmease commented Dec 4, 2023

Ah, I need to update r-a, too (proc-macro-srv/src/server.rs).

@fmease fmease force-pushed the libproc_macro-is_derive_const branch from 541dd8c to f4480cd Compare December 4, 2023 18:05
@rustbot
Copy link
Collaborator

rustbot commented Dec 4, 2023

rust-analyzer is developed in its own repository. If possible, consider making this change to rust-lang/rust-analyzer instead.

cc @rust-lang/rust-analyzer

@fmease
Copy link
Member Author

fmease commented Dec 4, 2023

rust-analyzer is developed in its own repository. If possible, consider making this change to rust-lang/rust-analyzer instead.

Pretty sure that this in-tree change is unavoidable.

@fmease
Copy link
Member Author

fmease commented Dec 14, 2023

@rust-lang/libs-api, would you like to see an ACP for this first?

TL;DR: Allow derive-proc-macros to have the signature fn(TokenStream, DeriveExpansionOptions) -> TokenStream where DeriveExpansionOptions is an abstract type with the method fn is_const(&self) -> bool which returns whether the macro was invoked via #[derive_const] or #[derive]. Afaik, #[derive_const] isn't backed by an RFC just like the rest of const_trait_impl/effects. Just happy to receive feedback from you if you're roughly okay with the shape of this unstable API or not :)

@petrochenkov
Copy link
Contributor

(I should get to this on new year holidays.)

@bors
Copy link
Contributor

bors commented Dec 26, 2023

☔ The latest upstream changes (presumably #119146) made this pull request unmergeable. Please resolve the merge conflicts.

@fmease fmease force-pushed the libproc_macro-is_derive_const branch from f4480cd to 882ab9b Compare January 3, 2024 09:30
@bors
Copy link
Contributor

bors commented Jan 4, 2024

☔ The latest upstream changes (presumably #119542) made this pull request unmergeable. Please resolve the merge conflicts.

@fmease fmease force-pushed the libproc_macro-is_derive_const branch from 882ab9b to 3768c8f Compare January 5, 2024 14:03
Comment on lines +2412 to +2416
&& tcx
.features()
.declared_lib_features
.iter()
.any(|&(feature, _)| feature == sym::derive_const)
Copy link
Member Author

Choose a reason for hiding this comment

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

I think I should actually be able to do:

Suggested change
&& tcx
.features()
.declared_lib_features
.iter()
.any(|&(feature, _)| feature == sym::derive_const)
&& tcx.features().declared(sym::derive_const)

or is it

Suggested change
&& tcx
.features()
.declared_lib_features
.iter()
.any(|&(feature, _)| feature == sym::derive_const)
&& tcx.features().active(sym::derive_const)

trait_name: &'static str,
attributes: &'static [&'static str],
expand: impl Fn(crate::TokenStream) -> crate::TokenStream + Copy,
expand: impl ~const ExpandCustomDerive<Discriminant>,
Copy link
Member Author

@fmease fmease Jan 6, 2024

Choose a reason for hiding this comment

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

It's unfortunate that I had to enable const_trait_impl and effects to make this work. If I hadn't done it, I wouldn't have been able to call into_fn in this const fn.

@@ -506,3 +510,26 @@ impl ProcMacro {
ProcMacro::Bang { name, client: Client::expand1(expand) }
}
}

#[const_trait]
pub trait ExpandCustomDerive<Discriminant> {
Copy link
Member Author

@fmease fmease Jan 6, 2024

Choose a reason for hiding this comment

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

I know that adding a “discriminant” to this trait is slightly ugly but it's necessary to make the two Fn impls non-overlapping. Sadly, Fn(T) and Fn(T, U) are considered to be overlapping which obviously makes sense looking at their desugared form Fn<(T,)> and Fn<(T, U)>. Theoretically a single type can implement both of those trait refs (under #![feature(unboxed_closures)]).

@@ -484,12 +484,16 @@ impl ProcMacro {
}
}

pub const fn custom_derive(
pub const fn custom_derive<Discriminant>(
Copy link
Member Author

@fmease fmease Jan 6, 2024

Choose a reason for hiding this comment

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

I really hope that this type parameter doesn't “leak” in the sense that Discriminant doesn't lead to inference failures. See my other comment for why Discriminant is necessary.

By the way, I can't replace this type parameter with impl-Trait since nested impl-Traits aren't supported at the moment (here: impl ~const ExpandCustomDerive<impl Discriminant>). It would at least prevent users from being able to explicitly specify it. Of course, this function is not considered part of the public API since this entire module is #[doc(hidden)] if I remember correctly (hence no stability attributes anywhere in this file).

F: Fn(crate::TokenStream, crate::DeriveExpansionOptions) -> crate::TokenStream + Copy,
{
fn into_fn(self) -> impl Fn(crate::TokenStream) -> crate::TokenStream + Copy {
move |input| self(input, Default::default())
Copy link
Member Author

@fmease fmease Jan 6, 2024

Choose a reason for hiding this comment

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

This feels a bit weird and it demonstrates that DeriveExpansionOptions is truly a “sham”. I fear that the way it's wired right now, this impl isn't extensible. Consider the hypothetical in which we do want to store something meaningful in DeriveExpansionOptions, we wouldn't be able to actually retrieve it, only receive it with an RPC, e.g. via FreeFunctions. Maybe this is a non-issue, I'm just rambling at this point ^^'

@fmease
Copy link
Member Author

fmease commented Jan 15, 2024

Nominating for t-libs-api

TL;DR: Allow derive-proc-macros to have the signature fn(TokenStream, DeriveExpansionOptions) -> TokenStream where DeriveExpansionOptions is an abstract type with the method fn is_const(&self) -> bool which returns whether the macro was invoked via #[derive_const] or #[derive]. Afaik, #[derive_const] isn't backed by an RFC just like the rest of const_trait_impl/effects. Just happy to receive feedback from you if you're roughly okay with the shape of this unstable API or not :)

@fmease fmease added the I-libs-api-nominated Nominated for discussion during a libs-api team meeting. label Jan 15, 2024
@m-ou-se
Copy link
Member

m-ou-se commented Jan 16, 2024

We briefly discussed this in the libs-api meeting. As an unstable feature in proc_macro, this is fine. Feel free to go ahead, as long as there is a tracking issue attached to these unstable APIs and there will be an libs-api FCP before stabilization.

@m-ou-se m-ou-se removed the I-libs-api-nominated Nominated for discussion during a libs-api team meeting. label Jan 16, 2024
@petrochenkov
Copy link
Contributor

Both #[derive_const] itself, and this, are maximally ad hoc tools for addressing the issue.

How macros currently take their input - by a token stream, or two token streams if the macro has two inputs like #[attr(input1)] INPUT2.

This PR adds a third backdoor input tailored for a very specific use case.
The #[derive_const] added a whole new macro just to add one configuration bit to already existing #[derive] macro.

I would suggest keeping the current model and using already existing token-based methods for configuring macro behaviors.

  • Either helper attributes, like many derive macros, including derive_more, already do
  • Or giving derive macros the second input token stream - #[derive(Clone(input1))] INPUT2, that would also improve consistency with attributes.

Then the macros themselves can process the inputs as they want.
E.g. built-in macros could conventionally take a const token to produce a const impl - #[derive(Clone(const))] struct S;, others may accept more configuration.

If #[derive] needs some sugar to pass const to multiple derive macros that it applies at once, then it can grow such sugar itself, instead of delegating to a whole new special-purpose macro.
E.g. #[derive(const: Clone, PartialEq, PartialOrd)] struct S; or anything else, tokens inside the parentheses are an arbitrary token stream, I remind you, so #[derive] can create its own language for its input, like any other attribute macro.

@petrochenkov petrochenkov added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Jan 19, 2024
@petrochenkov
Copy link
Contributor

On a second thought, there's a third way to pass inputs - the "global data" in struct Rustc.

Proc macro methods without arguments like Span::call_site() take their return values from that global data.
I also planned to add some more data there to be available to proc macros, like the kind of brackets used by the macro.

If "derive expansion options" are placed into struct Rustc, then there's no need to add a second parameter to #[proc_macro_derive] functions for passing them.

@petrochenkov
Copy link
Contributor

What other similar configuration values we may need in the future?

Will we need to pass them to derive macros only, or for attribute possibly for attribute or fn-like macros too?
With derive macros we have an intermediate layer, the #[derive] macro which has access to compiler magic to set such configuration values, but for attribute and fn-like macros we do not.

@bors
Copy link
Contributor

bors commented Feb 16, 2024

☔ The latest upstream changes (presumably #120486) made this pull request unmergeable. Please resolve the merge conflicts.

@Dylan-DPC
Copy link
Member

@fmease any updates on this? thanks

@fmease
Copy link
Member Author

fmease commented Feb 18, 2024

No updates yet, my plan is to discuss petrochenkov's very good feedback with the rest of project-const-traits as well as with wg-macros.
@rustbot blocked

@rustbot rustbot added S-blocked Status: Marked as blocked ❌ on something else such as an RFC or other implementation work. and removed S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. labels Feb 18, 2024
@fmease
Copy link
Member Author

fmease commented Jul 1, 2024

I've updated #118304 to mention petrochenkov's valid criticism and I will close this PR to preserve history instead of keeping it open until we work out a redesign of this feature and implementing it here.

@fmease fmease closed this Jul 1, 2024
@fmease fmease deleted the libproc_macro-is_derive_const branch July 1, 2024 22:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
S-blocked Status: Marked as blocked ❌ on something else such as an RFC or other implementation work. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. T-libs-api Relevant to the library API team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Provide a way for derives to know if they were invoked with #[derive_const]
7 participants