From f4480cd5486019874bdfdddb3ebddb06a37cdb1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Mon, 4 Dec 2023 02:10:11 +0100 Subject: [PATCH] Introduce proc_macro::DeriveExpansionOptions --- compiler/rustc_expand/src/proc_macro.rs | 11 +-- .../rustc_expand/src/proc_macro_server.rs | 13 +++- compiler/rustc_passes/src/check_attr.rs | 68 +++++++++++++++---- compiler/rustc_span/src/symbol.rs | 1 + library/proc_macro/src/bridge/client.rs | 33 ++++++++- library/proc_macro/src/bridge/mod.rs | 1 + library/proc_macro/src/lib.rs | 28 ++++++++ .../crates/proc-macro-srv/src/server.rs | 5 ++ .../auxiliary/is-derive-const.rs | 13 ++++ .../const_derives/derive-const-custom.rs | 20 ++++++ .../signature-proc-macro-derive.rs | 19 ++++++ .../signature-proc-macro-derive.stderr | 16 +++++ 12 files changed, 205 insertions(+), 23 deletions(-) create mode 100644 tests/ui/rfcs/rfc-2632-const-trait-impl/const_derives/auxiliary/is-derive-const.rs create mode 100644 tests/ui/rfcs/rfc-2632-const-trait-impl/const_derives/derive-const-custom.rs create mode 100644 tests/ui/rfcs/rfc-2632-const-trait-impl/const_derives/signature-proc-macro-derive.rs create mode 100644 tests/ui/rfcs/rfc-2632-const-trait-impl/const_derives/signature-proc-macro-derive.stderr diff --git a/compiler/rustc_expand/src/proc_macro.rs b/compiler/rustc_expand/src/proc_macro.rs index 39a16259fa6e5..20aa036036235 100644 --- a/compiler/rustc_expand/src/proc_macro.rs +++ b/compiler/rustc_expand/src/proc_macro.rs @@ -59,7 +59,7 @@ impl base::BangProcMacro for BangProcMacro { let proc_macro_backtrace = ecx.ecfg.proc_macro_backtrace; let strategy = exec_strategy(ecx); - let server = proc_macro_server::Rustc::new(ecx); + let server = proc_macro_server::Rustc::new(ecx, Default::default()); self.client.run(&strategy, server, input, proc_macro_backtrace).map_err(|e| { ecx.sess.emit_err(errors::ProcMacroPanicked { span, @@ -90,7 +90,7 @@ impl base::AttrProcMacro for AttrProcMacro { let proc_macro_backtrace = ecx.ecfg.proc_macro_backtrace; let strategy = exec_strategy(ecx); - let server = proc_macro_server::Rustc::new(ecx); + let server = proc_macro_server::Rustc::new(ecx, Default::default()); self.client.run(&strategy, server, annotation, annotated, proc_macro_backtrace).map_err( |e| { let mut err = ecx.struct_span_err(span, "custom attribute panicked"); @@ -114,7 +114,7 @@ impl MultiItemModifier for DeriveProcMacro { span: Span, _meta_item: &ast::MetaItem, item: Annotatable, - _is_derive_const: bool, + is_derive_const: bool, ) -> ExpandResult, Annotatable> { // We need special handling for statement items // (e.g. `fn foo() { #[derive(Debug)] struct Bar; }`) @@ -142,7 +142,10 @@ impl MultiItemModifier for DeriveProcMacro { }); let proc_macro_backtrace = ecx.ecfg.proc_macro_backtrace; let strategy = exec_strategy(ecx); - let server = proc_macro_server::Rustc::new(ecx); + let server = proc_macro_server::Rustc::new( + ecx, + proc_macro_server::ExpansionOptions { is_derive_const }, + ); match self.client.run(&strategy, server, input, proc_macro_backtrace) { Ok(stream) => stream, Err(e) => { diff --git a/compiler/rustc_expand/src/proc_macro_server.rs b/compiler/rustc_expand/src/proc_macro_server.rs index b057a645f8195..f78c461290488 100644 --- a/compiler/rustc_expand/src/proc_macro_server.rs +++ b/compiler/rustc_expand/src/proc_macro_server.rs @@ -370,10 +370,11 @@ pub(crate) struct Rustc<'a, 'b> { mixed_site: Span, krate: CrateNum, rebased_spans: FxHashMap, + options: ExpansionOptions, } impl<'a, 'b> Rustc<'a, 'b> { - pub fn new(ecx: &'a mut ExtCtxt<'b>) -> Self { + pub fn new(ecx: &'a mut ExtCtxt<'b>, options: ExpansionOptions) -> Self { let expn_data = ecx.current_expansion.id.expn_data(); Rustc { def_site: ecx.with_def_site_ctxt(expn_data.def_site), @@ -381,6 +382,7 @@ impl<'a, 'b> Rustc<'a, 'b> { mixed_site: ecx.with_mixed_site_ctxt(expn_data.call_site), krate: expn_data.macro_def_id.unwrap().krate, rebased_spans: FxHashMap::default(), + options, ecx, } } @@ -390,6 +392,11 @@ impl<'a, 'b> Rustc<'a, 'b> { } } +#[derive(Default)] +pub(crate) struct ExpansionOptions { + pub(crate) is_derive_const: bool, +} + impl server::Types for Rustc<'_, '_> { type FreeFunctions = FreeFunctions; type TokenStream = TokenStream; @@ -477,6 +484,10 @@ impl server::FreeFunctions for Rustc<'_, '_> { } self.sess().span_diagnostic.emit_diagnostic(&mut diag); } + + fn is_derive_const(&mut self) -> bool { + self.options.is_derive_const + } } impl server::TokenStream for Rustc<'_, '_> { diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 4910d63010c6e..13deff2f503d8 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -2285,10 +2285,17 @@ impl CheckAttrVisitor<'_> { } let tcx = self.tcx; - let Some(token_stream_def_id) = tcx.get_diagnostic_item(sym::TokenStream) else { + + let Some(token_stream) = tcx + .get_diagnostic_item(sym::TokenStream) + .and_then(|did| tcx.type_of(did).no_bound_vars()) + else { return; }; - let Some(token_stream) = tcx.type_of(token_stream_def_id).no_bound_vars() else { + let Some(derive_expansion_options) = tcx + .get_diagnostic_item(sym::DeriveExpansionOptions) + .and_then(|did| tcx.type_of(did).no_bound_vars()) + else { return; }; @@ -2324,8 +2331,24 @@ impl CheckAttrVisitor<'_> { Unsafety::Normal, Abi::Rust, ); + let expected_options_sig = tcx.mk_fn_sig( + [token_stream, derive_expansion_options], + token_stream, + false, + Unsafety::Normal, + Abi::Rust, + ); - if let Err(terr) = ocx.eq(&cause, param_env, expected_sig, sig) { + let mut result = infcx.probe(|_| ocx.eq(&cause, param_env, expected_sig, sig)); + if result.is_err() + && let ProcMacroKind::Derive = kind + { + if infcx.probe(|_| ocx.eq(&cause, param_env, expected_options_sig, sig)).is_ok() { + result = Ok(()); + } + } + + if let Err(terr) = result { let mut diag = tcx.sess.create_err(errors::ProcMacroBadSig { span, kind }); let hir_sig = tcx.hir().fn_sig_by_hir_id(hir_id); @@ -2360,18 +2383,33 @@ impl CheckAttrVisitor<'_> { } } - infcx.err_ctxt().note_type_err( - &mut diag, - &cause, - None, - Some(ValuePairs::PolySigs(ExpectedFound { - expected: ty::Binder::dummy(expected_sig), - found: ty::Binder::dummy(sig), - })), - terr, - false, - false, - ); + let mut note_expected_found = |expected_sig| { + infcx.err_ctxt().note_type_err( + &mut diag, + &cause, + None, + Some(ValuePairs::PolySigs(ExpectedFound { + expected: ty::Binder::dummy(expected_sig), + found: ty::Binder::dummy(sig), + })), + terr, + false, + false, + ) + }; + + note_expected_found(expected_sig); + + if let ProcMacroKind::Derive = kind + && tcx + .features() + .declared_lib_features + .iter() + .any(|&(feature, _)| feature == sym::derive_const) + { + note_expected_found(expected_options_sig); + } + diag.emit(); self.abort.set(true); } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 40b0387424221..1ee633bd526e3 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -183,6 +183,7 @@ symbols! { DecorateLint, Default, Deref, + DeriveExpansionOptions, DiagnosticMessage, DirBuilder, Display, diff --git a/library/proc_macro/src/bridge/client.rs b/library/proc_macro/src/bridge/client.rs index 52a08cad9110f..e1d0de159db1a 100644 --- a/library/proc_macro/src/bridge/client.rs +++ b/library/proc_macro/src/bridge/client.rs @@ -484,12 +484,16 @@ impl ProcMacro { } } - pub const fn custom_derive( + pub const fn custom_derive( trait_name: &'static str, attributes: &'static [&'static str], - expand: impl Fn(crate::TokenStream) -> crate::TokenStream + Copy, + expand: impl ~const ExpandCustomDerive, ) -> Self { - ProcMacro::CustomDerive { trait_name, attributes, client: Client::expand1(expand) } + ProcMacro::CustomDerive { + trait_name, + attributes, + client: Client::expand1(expand.into_fn()), + } } pub const fn attr( @@ -506,3 +510,26 @@ impl ProcMacro { ProcMacro::Bang { name, client: Client::expand1(expand) } } } + +#[const_trait] +pub trait ExpandCustomDerive { + fn into_fn(self) -> impl Fn(crate::TokenStream) -> crate::TokenStream + Copy; +} + +impl const ExpandCustomDerive<()> for F +where + F: Fn(crate::TokenStream) -> crate::TokenStream + Copy, +{ + fn into_fn(self) -> impl Fn(crate::TokenStream) -> crate::TokenStream + Copy { + self + } +} + +impl const ExpandCustomDerive for F +where + 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()) + } +} diff --git a/library/proc_macro/src/bridge/mod.rs b/library/proc_macro/src/bridge/mod.rs index 86ce5d9c6d5fe..b3e73d877f22b 100644 --- a/library/proc_macro/src/bridge/mod.rs +++ b/library/proc_macro/src/bridge/mod.rs @@ -59,6 +59,7 @@ macro_rules! with_api { fn track_path(path: &str); fn literal_from_str(s: &str) -> Result, ()>; fn emit_diagnostic(diagnostic: Diagnostic<$S::Span>); + fn is_derive_const() -> bool; }, TokenStream { fn drop($self: $S::TokenStream); diff --git a/library/proc_macro/src/lib.rs b/library/proc_macro/src/lib.rs index d3f1fba9369ca..d08534b76162d 100644 --- a/library/proc_macro/src/lib.rs +++ b/library/proc_macro/src/lib.rs @@ -25,7 +25,9 @@ #![feature(rustc_allow_const_fn_unstable)] #![feature(staged_api)] #![feature(allow_internal_unstable)] +#![feature(const_trait_impl)] #![feature(decl_macro)] +#![feature(effects)] #![feature(maybe_uninit_write_slice)] #![feature(negative_impls)] #![feature(new_uninit)] @@ -85,6 +87,32 @@ impl !Send for TokenStream {} #[stable(feature = "proc_macro_lib", since = "1.15.0")] impl !Sync for TokenStream {} +/// Derive expansion options. +#[rustc_diagnostic_item = "DeriveExpansionOptions"] +#[unstable(feature = "derive_const", issue = "none")] +#[derive(Default, Clone)] +#[non_exhaustive] +pub struct DeriveExpansionOptions; + +impl DeriveExpansionOptions { + /// Returns the default options. + #[unstable(feature = "derive_const", issue = "none")] + pub fn new() -> Self { + Self::default() + } + + /// Whether this is a `#[derive_const]` or a `#[derive]`. + #[unstable(feature = "derive_const", issue = "none")] + pub fn is_const(&self) -> bool { + bridge::client::FreeFunctions::is_derive_const() + } +} + +#[unstable(feature = "derive_const", issue = "none")] +impl !Send for DeriveExpansionOptions {} +#[unstable(feature = "derive_const", issue = "none")] +impl !Sync for DeriveExpansionOptions {} + /// Error returned from `TokenStream::from_str`. #[stable(feature = "proc_macro_lib", since = "1.15.0")] #[non_exhaustive] diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server.rs index fe18451d38482..ec9004e778458 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server.rs @@ -76,6 +76,11 @@ impl server::FreeFunctions for RustAnalyzer { fn emit_diagnostic(&mut self, _: bridge::Diagnostic) { // FIXME handle diagnostic } + + fn is_derive_const(&mut self) -> bool { + // FIXME: pass the correct information + false + } } impl server::TokenStream for RustAnalyzer { diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/const_derives/auxiliary/is-derive-const.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/const_derives/auxiliary/is-derive-const.rs new file mode 100644 index 0000000000000..edd5cf0b13b36 --- /dev/null +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/const_derives/auxiliary/is-derive-const.rs @@ -0,0 +1,13 @@ +// force-host +// no-prefer-dynamic +#![crate_type = "proc-macro"] +#![feature(derive_const)] + +extern crate proc_macro; + +use proc_macro::{TokenStream, DeriveExpansionOptions}; + +#[proc_macro_derive(IsDeriveConst)] +pub fn is_derive_const(_: TokenStream, options: DeriveExpansionOptions) -> TokenStream { + format!("const IS_DERIVE_CONST: bool = {};", options.is_const()).parse().unwrap() +} diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/const_derives/derive-const-custom.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/const_derives/derive-const-custom.rs new file mode 100644 index 0000000000000..8c755b73aa134 --- /dev/null +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/const_derives/derive-const-custom.rs @@ -0,0 +1,20 @@ +// check-pass +// edition: 2021 +// aux-crate:is_derive_const=is-derive-const.rs +#![feature(derive_const)] + +const _: () = { + #[derive(is_derive_const::IsDeriveConst)] + struct _Type; + + assert!(!IS_DERIVE_CONST); +}; + +const _: () = { + #[derive_const(is_derive_const::IsDeriveConst)] + struct _Type; + + assert!(IS_DERIVE_CONST); +}; + +fn main() {} diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/const_derives/signature-proc-macro-derive.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/const_derives/signature-proc-macro-derive.rs new file mode 100644 index 0000000000000..e34a42e75b640 --- /dev/null +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/const_derives/signature-proc-macro-derive.rs @@ -0,0 +1,19 @@ +// Check that we suggest *both* possible signatures of derive proc macros, namely +// fn(TokenStream) -> TokenStream +// and +// fn(TokenStream, DeriveExpansionOptions) -> TokenStream +// provided libs feature `derive_const` is enabled. + +// force-host +// no-prefer-dynamic + +#![crate_type = "proc-macro"] +#![feature(derive_const)] + +extern crate proc_macro; + +#[proc_macro_derive(Blah)] +pub fn bad_input() -> proc_macro::TokenStream { + //~^ ERROR derive proc macro has incorrect signature + Default::default() +} diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/const_derives/signature-proc-macro-derive.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/const_derives/signature-proc-macro-derive.stderr new file mode 100644 index 0000000000000..a3dc943d9ebf0 --- /dev/null +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/const_derives/signature-proc-macro-derive.stderr @@ -0,0 +1,16 @@ +error: derive proc macro has incorrect signature + --> $DIR/signature-proc-macro-derive.rs:16:1 + | +LL | pub fn bad_input() -> proc_macro::TokenStream { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | incorrect number of function parameters + | incorrect number of function parameters + | + = note: expected signature `fn(proc_macro::TokenStream) -> proc_macro::TokenStream` + found signature `fn() -> proc_macro::TokenStream` + = note: expected signature `fn(proc_macro::TokenStream, DeriveExpansionOptions) -> proc_macro::TokenStream` + found signature `fn() -> proc_macro::TokenStream` + +error: aborting due to 1 previous error +