From a8537ab84bf7fd14b2445a058075dac764932e47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Mon, 1 Sep 2025 15:21:02 +0200 Subject: [PATCH 1/7] Remove special implementation of `PartialEq` for `InvisibleOrigin` outside macro matching --- compiler/rustc_ast/src/token.rs | 20 +++---------------- compiler/rustc_expand/src/mbe/macro_parser.rs | 20 +++++++++++++++++-- 2 files changed, 21 insertions(+), 19 deletions(-) diff --git a/compiler/rustc_ast/src/token.rs b/compiler/rustc_ast/src/token.rs index ea98bebd30555..6dc6d1026f621 100644 --- a/compiler/rustc_ast/src/token.rs +++ b/compiler/rustc_ast/src/token.rs @@ -22,8 +22,7 @@ pub enum CommentKind { Block, } -// This type must not implement `Hash` due to the unusual `PartialEq` impl below. -#[derive(Copy, Clone, Debug, Encodable, Decodable, HashStable_Generic)] +#[derive(Copy, Clone, PartialEq, Debug, Encodable, Decodable, HashStable_Generic)] pub enum InvisibleOrigin { // From the expansion of a metavariable in a declarative macro. MetaVar(MetaVarKind), @@ -45,20 +44,6 @@ impl InvisibleOrigin { } } -impl PartialEq for InvisibleOrigin { - #[inline] - fn eq(&self, _other: &InvisibleOrigin) -> bool { - // When we had AST-based nonterminals we couldn't compare them, and the - // old `Nonterminal` type had an `eq` that always returned false, - // resulting in this restriction: - // https://doc.rust-lang.org/nightly/reference/macros-by-example.html#forwarding-a-matched-fragment - // This `eq` emulates that behaviour. We could consider lifting this - // restriction now but there are still cases involving invisible - // delimiters that make it harder than it first appears. - false - } -} - /// Annoyingly similar to `NonterminalKind`, but the slight differences are important. #[derive(Debug, Copy, Clone, PartialEq, Eq, Encodable, Decodable, Hash, HashStable_Generic)] pub enum MetaVarKind { @@ -142,7 +127,8 @@ impl Delimiter { } } - // This exists because `InvisibleOrigin`s should be compared. It is only used for assertions. + // This exists because `InvisibleOrigin`s should not be compared. It is only used for + // assertions. pub fn eq_ignoring_invisible_origin(&self, other: &Delimiter) -> bool { match (self, other) { (Delimiter::Parenthesis, Delimiter::Parenthesis) => true, diff --git a/compiler/rustc_expand/src/mbe/macro_parser.rs b/compiler/rustc_expand/src/mbe/macro_parser.rs index 0324057e331a9..ab8e059b7b77f 100644 --- a/compiler/rustc_expand/src/mbe/macro_parser.rs +++ b/compiler/rustc_expand/src/mbe/macro_parser.rs @@ -77,7 +77,7 @@ use std::rc::Rc; pub(crate) use NamedMatch::*; pub(crate) use ParseResult::*; -use rustc_ast::token::{self, DocComment, NonterminalKind, Token}; +use rustc_ast::token::{self, DocComment, NonterminalKind, Token, TokenKind}; use rustc_data_structures::fx::FxHashMap; use rustc_errors::ErrorGuaranteed; use rustc_lint_defs::pluralize; @@ -397,7 +397,23 @@ fn token_name_eq(t1: &Token, t2: &Token) -> bool { { ident1.name == ident2.name && is_raw1 == is_raw2 } else { - t1.kind == t2.kind + // Note: we SHOULD NOT use `t1.kind == t2.kind` here, and we should instead compare the + // tokens using the special comparison logic below. + // It makes sure that variants containing `InvisibleOrigin` will + // never compare equal to one another. + // + // When we had AST-based nonterminals we couldn't compare them, and the + // old `Nonterminal` type had an `eq` that always returned false, + // resulting in this restriction: + // + // This comparison logic emulates that behaviour. We could consider lifting this + // restriction now but there are still cases involving invisible + // delimiters that make it harder than it first appears. + match (t1.kind, t2.kind) { + (TokenKind::OpenInvisible(_) | TokenKind::CloseInvisible(_), _) + | (_, TokenKind::OpenInvisible(_) | TokenKind::CloseInvisible(_)) => false, + (a, b) => a == b, + } } } From cc292c843688d644948a6c120dac67592f869c7c Mon Sep 17 00:00:00 2001 From: Felix Rath Date: Wed, 13 Aug 2025 13:05:07 +0200 Subject: [PATCH 2/7] Implement incremental caching for derive macro expansions --- Cargo.lock | 1 + compiler/rustc_ast/src/tokenstream.rs | 6 +- compiler/rustc_expand/Cargo.toml | 1 + compiler/rustc_expand/src/lib.rs | 4 + compiler/rustc_expand/src/proc_macro.rs | 177 +++++++++++++++--- compiler/rustc_interface/src/passes.rs | 1 + compiler/rustc_middle/src/arena.rs | 1 + compiler/rustc_middle/src/query/erase.rs | 5 + compiler/rustc_middle/src/query/keys.rs | 17 +- compiler/rustc_middle/src/query/mod.rs | 10 +- .../rustc_middle/src/query/on_disk_cache.rs | 7 + compiler/rustc_session/src/options.rs | 2 + compiler/rustc_span/src/hygiene.rs | 6 + .../auxiliary/derive_nothing.rs | 20 ++ .../proc_macro_unchanged.rs | 39 ++++ 15 files changed, 262 insertions(+), 35 deletions(-) create mode 100644 tests/incremental/derive_macro_expansion/auxiliary/derive_nothing.rs create mode 100644 tests/incremental/derive_macro_expansion/proc_macro_unchanged.rs diff --git a/Cargo.lock b/Cargo.lock index 8b9c243879caf..d24dd3238fd0a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3748,6 +3748,7 @@ dependencies = [ "rustc_lexer", "rustc_lint_defs", "rustc_macros", + "rustc_middle", "rustc_parse", "rustc_proc_macro", "rustc_serialize", diff --git a/compiler/rustc_ast/src/tokenstream.rs b/compiler/rustc_ast/src/tokenstream.rs index a5d8fbfac612a..53f5cb52263f4 100644 --- a/compiler/rustc_ast/src/tokenstream.rs +++ b/compiler/rustc_ast/src/tokenstream.rs @@ -5,6 +5,7 @@ //! which are themselves a single [`Token`] or a `Delimited` subsequence of tokens. use std::borrow::Cow; +use std::hash::Hash; use std::ops::Range; use std::sync::Arc; use std::{cmp, fmt, iter, mem}; @@ -12,8 +13,9 @@ use std::{cmp, fmt, iter, mem}; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_data_structures::sync; use rustc_macros::{Decodable, Encodable, HashStable_Generic, Walkable}; -use rustc_serialize::{Decodable, Encodable}; -use rustc_span::{DUMMY_SP, Span, SpanDecoder, SpanEncoder, Symbol, sym}; +use rustc_serialize::{Decodable, Encodable, Encoder}; +use rustc_span::def_id::{CrateNum, DefIndex}; +use rustc_span::{ByteSymbol, DUMMY_SP, Span, SpanDecoder, SpanEncoder, Symbol, sym}; use thin_vec::ThinVec; use crate::ast::AttrStyle; diff --git a/compiler/rustc_expand/Cargo.toml b/compiler/rustc_expand/Cargo.toml index 9bb7143af51ee..d4064d0b65a2b 100644 --- a/compiler/rustc_expand/Cargo.toml +++ b/compiler/rustc_expand/Cargo.toml @@ -21,6 +21,7 @@ rustc_hir = { path = "../rustc_hir" } rustc_lexer = { path = "../rustc_lexer" } rustc_lint_defs = { path = "../rustc_lint_defs" } rustc_macros = { path = "../rustc_macros" } +rustc_middle = { path = "../rustc_middle" } rustc_parse = { path = "../rustc_parse" } # We must use the proc_macro version that we will compile proc-macros against, # not the one from our own sysroot. diff --git a/compiler/rustc_expand/src/lib.rs b/compiler/rustc_expand/src/lib.rs index b54dabbb8e26f..d95ad7dec5a36 100644 --- a/compiler/rustc_expand/src/lib.rs +++ b/compiler/rustc_expand/src/lib.rs @@ -31,4 +31,8 @@ pub mod module; #[allow(rustc::untranslatable_diagnostic)] pub mod proc_macro; +pub fn provide(providers: &mut rustc_middle::util::Providers) { + providers.derive_macro_expansion = proc_macro::provide_derive_macro_expansion; +} + rustc_fluent_macro::fluent_messages! { "../messages.ftl" } diff --git a/compiler/rustc_expand/src/proc_macro.rs b/compiler/rustc_expand/src/proc_macro.rs index 9bfda8764f552..110e7ce1c46ce 100644 --- a/compiler/rustc_expand/src/proc_macro.rs +++ b/compiler/rustc_expand/src/proc_macro.rs @@ -1,9 +1,15 @@ +use std::cell::Cell; +use std::ptr::NonNull; + use rustc_ast::tokenstream::TokenStream; +use rustc_data_structures::svh::Svh; use rustc_errors::ErrorGuaranteed; +use rustc_middle::ty::{self, TyCtxt}; use rustc_parse::parser::{ForceCollect, Parser}; +use rustc_session::Session; use rustc_session::config::ProcMacroExecutionStrategy; -use rustc_span::Span; use rustc_span::profiling::SpannedEventArgRecorder; +use rustc_span::{LocalExpnId, Span}; use {rustc_ast as ast, rustc_proc_macro as pm}; use crate::base::{self, *}; @@ -30,9 +36,9 @@ impl pm::bridge::server::MessagePipe for MessagePipe { } } -fn exec_strategy(ecx: &ExtCtxt<'_>) -> impl pm::bridge::server::ExecutionStrategy + 'static { +pub fn exec_strategy(sess: &Session) -> impl pm::bridge::server::ExecutionStrategy + 'static { pm::bridge::server::MaybeCrossThread::>::new( - ecx.sess.opts.unstable_opts.proc_macro_execution_strategy + sess.opts.unstable_opts.proc_macro_execution_strategy == ProcMacroExecutionStrategy::CrossThread, ) } @@ -54,7 +60,7 @@ impl base::BangProcMacro for BangProcMacro { }); let proc_macro_backtrace = ecx.ecfg.proc_macro_backtrace; - let strategy = exec_strategy(ecx); + let strategy = exec_strategy(ecx.sess); let server = proc_macro_server::Rustc::new(ecx); self.client.run(&strategy, server, input, proc_macro_backtrace).map_err(|e| { ecx.dcx().emit_err(errors::ProcMacroPanicked { @@ -85,7 +91,7 @@ impl base::AttrProcMacro for AttrProcMacro { }); let proc_macro_backtrace = ecx.ecfg.proc_macro_backtrace; - let strategy = exec_strategy(ecx); + let strategy = exec_strategy(ecx.sess); let server = proc_macro_server::Rustc::new(ecx); self.client.run(&strategy, server, annotation, annotated, proc_macro_backtrace).map_err( |e| { @@ -113,6 +119,13 @@ impl MultiItemModifier for DeriveProcMacro { item: Annotatable, _is_derive_const: bool, ) -> ExpandResult, Annotatable> { + let _timer = ecx.sess.prof.generic_activity_with_arg_recorder( + "expand_derive_proc_macro_outer", + |recorder| { + recorder.record_arg_with_span(ecx.sess.source_map(), ecx.expansion_descr(), span); + }, + ); + // We need special handling for statement items // (e.g. `fn foo() { #[derive(Debug)] struct Bar; }`) let is_stmt = matches!(item, Annotatable::Stmt(..)); @@ -123,36 +136,39 @@ impl MultiItemModifier for DeriveProcMacro { // altogether. See #73345. crate::base::ann_pretty_printing_compatibility_hack(&item, &ecx.sess.psess); let input = item.to_tokens(); - let stream = { - let _timer = - ecx.sess.prof.generic_activity_with_arg_recorder("expand_proc_macro", |recorder| { - recorder.record_arg_with_span( - ecx.sess.source_map(), - ecx.expansion_descr(), - span, - ); - }); - let proc_macro_backtrace = ecx.ecfg.proc_macro_backtrace; - let strategy = exec_strategy(ecx); - let server = proc_macro_server::Rustc::new(ecx); - match self.client.run(&strategy, server, input, proc_macro_backtrace) { - Ok(stream) => stream, - Err(e) => { - ecx.dcx().emit_err({ - errors::ProcMacroDerivePanicked { - span, - message: e.as_str().map(|message| { - errors::ProcMacroDerivePanickedHelp { message: message.into() } - }), - } - }); - return ExpandResult::Ready(vec![]); - } + let res = ty::tls::with(|tcx| { + let input = tcx.arena.alloc(input) as &TokenStream; + let invoc_id = ecx.current_expansion.id; + let invoc_expn_data = invoc_id.expn_data(); + + assert_eq!(invoc_expn_data.call_site, span); + + // FIXME(pr-time): Is this the correct way to check for incremental compilation (as + // well as for `cache_proc_macros`)? + if tcx.sess.opts.incremental.is_some() && tcx.sess.opts.unstable_opts.cache_proc_macros + { + // FIXME(pr-time): Just using the crate hash to notice when the proc-macro code has + // changed. How to *correctly* depend on exactly the macro definition? + // I.e., depending on the crate hash is just a HACK, and ideally the dependency would be + // more narrow. + let macro_def_id = invoc_expn_data.macro_def_id.unwrap(); + let proc_macro_crate_hash = tcx.crate_hash(macro_def_id.krate); + + let key = (invoc_id, proc_macro_crate_hash, input); + + enter_context((ecx, self.client), move || tcx.derive_macro_expansion(key).cloned()) + } else { + expand_derive_macro(tcx, invoc_id, input, ecx, self.client).cloned() } + }); + + let Ok(output) = res else { + // error will already have been emitted + return ExpandResult::Ready(vec![]); }; let error_count_before = ecx.dcx().err_count(); - let mut parser = Parser::new(&ecx.sess.psess, stream, Some("proc-macro derive")); + let mut parser = Parser::new(&ecx.sess.psess, output, Some("proc-macro derive")); let mut items = vec![]; loop { @@ -180,3 +196,102 @@ impl MultiItemModifier for DeriveProcMacro { ExpandResult::Ready(items) } } + +pub(super) fn provide_derive_macro_expansion<'tcx>( + tcx: TyCtxt<'tcx>, + key: (LocalExpnId, Svh, &'tcx TokenStream), +) -> Result<&'tcx TokenStream, ()> { + let (invoc_id, _macro_crate_hash, input) = key; + + with_context(|(ecx, client)| expand_derive_macro(tcx, invoc_id, input, ecx, *client)) +} + +type CLIENT = pm::bridge::client::Client; + +fn expand_derive_macro<'tcx>( + tcx: TyCtxt<'tcx>, + invoc_id: LocalExpnId, + input: &'tcx TokenStream, + ecx: &mut ExtCtxt<'_>, + client: CLIENT, +) -> Result<&'tcx TokenStream, ()> { + let invoc_expn_data = invoc_id.expn_data(); + let span = invoc_expn_data.call_site; + let event_arg = invoc_expn_data.kind.descr(); + let _timer = tcx.sess.prof.generic_activity_with_arg_recorder( + "expand_derive_proc_macro_inner", + |recorder| { + recorder.record_arg_with_span(tcx.sess.source_map(), event_arg.clone(), span); + }, + ); + + let proc_macro_backtrace = ecx.ecfg.proc_macro_backtrace; + let strategy = crate::proc_macro::exec_strategy(tcx.sess); + let server = crate::proc_macro_server::Rustc::new(ecx); + + match client.run(&strategy, server, input.clone(), proc_macro_backtrace) { + Ok(stream) => Ok(tcx.arena.alloc(stream) as &TokenStream), + Err(e) => { + tcx.dcx().emit_err({ + errors::ProcMacroDerivePanicked { + span, + message: e.as_str().map(|message| errors::ProcMacroDerivePanickedHelp { + message: message.into(), + }), + } + }); + Err(()) + } + } +} + +// based on rust/compiler/rustc_middle/src/ty/context/tls.rs +thread_local! { + /// A thread local variable that stores a pointer to the current `CONTEXT`. + static TLV: Cell<(*mut (), Option)> = const { Cell::new((std::ptr::null_mut(), None)) }; +} + +/// Sets `context` as the new current `CONTEXT` for the duration of the function `f`. +#[inline] +pub(crate) fn enter_context<'a, F, R>(context: (&mut ExtCtxt<'a>, CLIENT), f: F) -> R +where + F: FnOnce() -> R, +{ + let (ectx, client) = context; + let erased = (ectx as *mut _ as *mut (), Some(client)); + TLV.with(|tlv| { + let old = tlv.replace(erased); + let _reset = rustc_data_structures::defer(move || tlv.set(old)); + f() + }) +} + +/// Allows access to the current `CONTEXT`. +/// Panics if there is no `CONTEXT` available. +#[inline] +#[track_caller] +fn with_context(f: F) -> R +where + F: for<'a, 'b> FnOnce(&'b mut (&mut ExtCtxt<'a>, CLIENT)) -> R, +{ + let (ectx, client_opt) = TLV.get(); + let ectx = NonNull::new(ectx).expect("no CONTEXT stored in tls"); + + // We could get an `CONTEXT` pointer from another thread. + // Ensure that `CONTEXT` is `DynSync`. + // FIXME(pr-time): we should not be able to? + // sync::assert_dyn_sync::>(); + + // prevent double entering, as that would allow creating two `&mut ExtCtxt`s + // FIXME(pr-time): probably use a RefCell instead (which checks this properly)? + TLV.with(|tlv| { + let old = tlv.replace((std::ptr::null_mut(), None)); + let _reset = rustc_data_structures::defer(move || tlv.set(old)); + let ectx = { + let mut casted = ectx.cast::>(); + unsafe { casted.as_mut() } + }; + + f(&mut (ectx, client_opt.unwrap())) + }) +} diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index bc5ef04079ed4..b4d6c645a0920 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -881,6 +881,7 @@ pub static DEFAULT_QUERY_PROVIDERS: LazyLock = LazyLock::new(|| { providers.env_var_os = env_var_os; limits::provide(providers); proc_macro_decls::provide(providers); + rustc_expand::provide(providers); rustc_const_eval::provide(providers); rustc_middle::hir::provide(providers); rustc_borrowck::provide(providers); diff --git a/compiler/rustc_middle/src/arena.rs b/compiler/rustc_middle/src/arena.rs index 4b6e38cd52dd3..4f0af1a53dace 100644 --- a/compiler/rustc_middle/src/arena.rs +++ b/compiler/rustc_middle/src/arena.rs @@ -116,6 +116,7 @@ macro_rules! arena_types { [decode] specialization_graph: rustc_middle::traits::specialization_graph::Graph, [] crate_inherent_impls: rustc_middle::ty::CrateInherentImpls, [] hir_owner_nodes: rustc_hir::OwnerNodes<'tcx>, + [decode] token_stream: rustc_ast::tokenstream::TokenStream, ]); ) } diff --git a/compiler/rustc_middle/src/query/erase.rs b/compiler/rustc_middle/src/query/erase.rs index ea62461ebeb60..5cf759e796be2 100644 --- a/compiler/rustc_middle/src/query/erase.rs +++ b/compiler/rustc_middle/src/query/erase.rs @@ -2,6 +2,7 @@ use std::ffi::OsStr; use std::intrinsics::transmute_unchecked; use std::mem::MaybeUninit; +use rustc_ast::tokenstream::TokenStream; use rustc_span::ErrorGuaranteed; use crate::mir::interpret::EvalToValTreeResult; @@ -170,6 +171,10 @@ impl EraseType for Result>, CyclePlaceholder> { type Result = [u8; size_of::>, CyclePlaceholder>>()]; } +impl EraseType for Result<&'_ TokenStream, ()> { + type Result = [u8; size_of::>()]; +} + impl EraseType for Option<&'_ T> { type Result = [u8; size_of::>()]; } diff --git a/compiler/rustc_middle/src/query/keys.rs b/compiler/rustc_middle/src/query/keys.rs index 4d914c42cfc62..11f47fe8b42ae 100644 --- a/compiler/rustc_middle/src/query/keys.rs +++ b/compiler/rustc_middle/src/query/keys.rs @@ -2,11 +2,13 @@ use std::ffi::OsStr; +use rustc_ast::tokenstream::TokenStream; +use rustc_data_structures::svh::Svh; use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE, LocalDefId, LocalModDefId, ModDefId}; use rustc_hir::hir_id::{HirId, OwnerId}; use rustc_query_system::dep_graph::DepNodeIndex; use rustc_query_system::query::{DefIdCache, DefaultCache, SingleCache, VecCache}; -use rustc_span::{DUMMY_SP, Ident, Span, Symbol}; +use rustc_span::{DUMMY_SP, Ident, LocalExpnId, Span, Symbol}; use crate::infer::canonical::CanonicalQueryInput; use crate::mir::mono::CollectionMode; @@ -616,6 +618,19 @@ impl Key for (LocalDefId, HirId) { } } +impl<'tcx> Key for (LocalExpnId, Svh, &'tcx TokenStream) { + type Cache = DefaultCache; + + fn default_span(&self, _tcx: TyCtxt<'_>) -> Span { + self.0.expn_data().call_site + } + + #[inline(always)] + fn key_as_def_id(&self) -> Option { + None + } +} + impl<'tcx> Key for (ValidityRequirement, ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>) { type Cache = DefaultCache; diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 7bd8a0525a2cf..a0fb25d8689ef 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -70,6 +70,7 @@ use std::sync::Arc; use rustc_abi::Align; use rustc_arena::TypedArena; use rustc_ast::expand::allocator::AllocatorKind; +use rustc_ast::tokenstream::TokenStream; use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_data_structures::sorted_map::SortedMap; @@ -99,7 +100,7 @@ use rustc_session::cstore::{ use rustc_session::lint::LintExpectationId; use rustc_span::def_id::LOCAL_CRATE; use rustc_span::source_map::Spanned; -use rustc_span::{DUMMY_SP, Span, Symbol}; +use rustc_span::{DUMMY_SP, LocalExpnId, Span, Symbol}; use rustc_target::spec::{PanicStrategy, SanitizerSet}; use {rustc_abi as abi, rustc_ast as ast, rustc_hir as hir}; @@ -164,6 +165,13 @@ pub use plumbing::{IntoQueryParam, TyCtxtAt, TyCtxtEnsureDone, TyCtxtEnsureOk}; // Queries marked with `fatal_cycle` do not need the latter implementation, // as they will raise an fatal error on query cycles instead. rustc_queries! { + query derive_macro_expansion(key: (LocalExpnId, Svh, &'tcx TokenStream)) -> Result<&'tcx TokenStream, ()> { + // eval_always + // no_hash + desc { "expanding a derive (proc) macro" } + cache_on_disk_if { true } + } + /// This exists purely for testing the interactions between delayed bugs and incremental. query trigger_delayed_bug(key: DefId) { desc { "triggering a delayed bug for testing incremental" } diff --git a/compiler/rustc_middle/src/query/on_disk_cache.rs b/compiler/rustc_middle/src/query/on_disk_cache.rs index 546791135ba61..f73612dcbcb63 100644 --- a/compiler/rustc_middle/src/query/on_disk_cache.rs +++ b/compiler/rustc_middle/src/query/on_disk_cache.rs @@ -780,6 +780,13 @@ impl<'a, 'tcx> Decodable> } } +impl<'a, 'tcx> Decodable> for &'tcx rustc_ast::tokenstream::TokenStream { + #[inline] + fn decode(d: &mut CacheDecoder<'a, 'tcx>) -> Self { + RefDecodable::decode(d) + } +} + macro_rules! impl_ref_decoder { (<$tcx:tt> $($ty:ty,)*) => { $(impl<'a, $tcx> Decodable> for &$tcx [$ty] { diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 6d5be2d92cd2a..45d658354baa7 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -2157,6 +2157,8 @@ options! { "set options for branch target identification and pointer authentication on AArch64"), build_sdylib_interface: bool = (false, parse_bool, [UNTRACKED], "whether the stable interface is being built"), + cache_derive_macros: bool = (false, parse_bool, [TRACKED], + "cache the results of derive proc macro invocations (potentially unsound!) (default: no"), cf_protection: CFProtection = (CFProtection::None, parse_cfprotection, [TRACKED], "instrument control-flow architecture protection"), check_cfg_all_expected: bool = (false, parse_bool, [UNTRACKED], diff --git a/compiler/rustc_span/src/hygiene.rs b/compiler/rustc_span/src/hygiene.rs index 19494ffc37eaf..0c65943e30989 100644 --- a/compiler/rustc_span/src/hygiene.rs +++ b/compiler/rustc_span/src/hygiene.rs @@ -1558,3 +1558,9 @@ impl HashStable for ExpnId { hash.hash_stable(ctx, hasher); } } + +impl HashStable for LocalExpnId { + fn hash_stable(&self, hcx: &mut CTX, hasher: &mut StableHasher) { + self.to_expn_id().hash_stable(hcx, hasher); + } +} diff --git a/tests/incremental/derive_macro_expansion/auxiliary/derive_nothing.rs b/tests/incremental/derive_macro_expansion/auxiliary/derive_nothing.rs new file mode 100644 index 0000000000000..ee0fe7ea10023 --- /dev/null +++ b/tests/incremental/derive_macro_expansion/auxiliary/derive_nothing.rs @@ -0,0 +1,20 @@ +//@ force-host +//@ no-prefer-dynamic + +#![crate_type = "proc-macro"] + +extern crate proc_macro; +use proc_macro::TokenStream; + +#[proc_macro_derive(Nothing)] +pub fn derive(_input: TokenStream) -> TokenStream { + eprintln!("invoked"); + + return r#" + pub mod nothing_mod { + pub fn nothing() { + eprintln!("nothing"); + } + } + "#.parse().unwrap(); +} diff --git a/tests/incremental/derive_macro_expansion/proc_macro_unchanged.rs b/tests/incremental/derive_macro_expansion/proc_macro_unchanged.rs new file mode 100644 index 0000000000000..ad98c9f789f47 --- /dev/null +++ b/tests/incremental/derive_macro_expansion/proc_macro_unchanged.rs @@ -0,0 +1,39 @@ +// This test tests that derive-macro execution is cached. +// HOWEVER, this test can currently only be checked manually, +// by running it (through compiletest) with `-- --nocapture --verbose`. +// The proc-macro (for `Nothing`) prints a message to stderr when invoked, +// and this message should only be present during the first invocation, +// because the cached result should be used for the second invocation. +// FIXME(pr-time): Properly have the test check this, but how? UI-test that tests for `.stderr`? + +//@ aux-build:derive_nothing.rs +//@ revisions:cfail1 cfail2 +//@ compile-flags: -Z query-dep-graph -Zcache-proc-macros=true +//@ build-pass + +#![feature(rustc_attrs)] +#![feature(stmt_expr_attributes)] +#![allow(dead_code)] +#![crate_type = "rlib"] + +#![rustc_partition_codegened(module="proc_macro_unchanged-foo", cfg="cfail1")] +// #![rustc_partition_codegened(module="proc_macro_unchanged-foo", cfg="cfail2")] + +// `foo::nothing_mod` is created by the derive macro and doesn't change +// BUG: this yields the same result with `-Zcache-proc-macros=false` (i.e., uncached), +// not sure how to do this correctly. +#![rustc_partition_reused(module="proc_macro_unchanged-foo-nothing_mod", cfg="cfail2")] + + #[macro_use] + extern crate derive_nothing; + +pub mod foo { + #[derive(Nothing)] + pub struct Foo; + + pub fn use_foo(_f: Foo) { + nothing_mod::nothing(); + + eprintln!("foo used"); + } +} From af55d22f3eee60ad445d2e86fb262c5dca56e04a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Wed, 13 Aug 2025 13:14:33 +0200 Subject: [PATCH 3/7] Add some comments and rename the option to `-Zcache-derive-macros` --- compiler/rustc_expand/src/proc_macro.rs | 4 +++- compiler/rustc_middle/src/query/mod.rs | 9 +++++++-- .../derive_macro_expansion/proc_macro_unchanged.rs | 13 ++++++------- 3 files changed, 16 insertions(+), 10 deletions(-) diff --git a/compiler/rustc_expand/src/proc_macro.rs b/compiler/rustc_expand/src/proc_macro.rs index 110e7ce1c46ce..afd6e98b09e1d 100644 --- a/compiler/rustc_expand/src/proc_macro.rs +++ b/compiler/rustc_expand/src/proc_macro.rs @@ -145,7 +145,8 @@ impl MultiItemModifier for DeriveProcMacro { // FIXME(pr-time): Is this the correct way to check for incremental compilation (as // well as for `cache_proc_macros`)? - if tcx.sess.opts.incremental.is_some() && tcx.sess.opts.unstable_opts.cache_proc_macros + if tcx.sess.opts.incremental.is_some() + && tcx.sess.opts.unstable_opts.cache_derive_macros { // FIXME(pr-time): Just using the crate hash to notice when the proc-macro code has // changed. How to *correctly* depend on exactly the macro definition? @@ -197,6 +198,7 @@ impl MultiItemModifier for DeriveProcMacro { } } +/// Provide a query for computing the output of a derive macro. pub(super) fn provide_derive_macro_expansion<'tcx>( tcx: TyCtxt<'tcx>, key: (LocalExpnId, Svh, &'tcx TokenStream), diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index a0fb25d8689ef..5c902a629d925 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -165,9 +165,14 @@ pub use plumbing::{IntoQueryParam, TyCtxtAt, TyCtxtEnsureDone, TyCtxtEnsureOk}; // Queries marked with `fatal_cycle` do not need the latter implementation, // as they will raise an fatal error on query cycles instead. rustc_queries! { + /// Caches the expansion of a derive proc macro, e.g. `#[derive(Serialize)]`. + /// The key is: + /// - A unique key corresponding to the invocation of a macro. + /// - Strict Version Hash of a crate. + /// - Token stream which serves as an input to the macro. + /// + /// The output is the token stream generated by the proc macro. query derive_macro_expansion(key: (LocalExpnId, Svh, &'tcx TokenStream)) -> Result<&'tcx TokenStream, ()> { - // eval_always - // no_hash desc { "expanding a derive (proc) macro" } cache_on_disk_if { true } } diff --git a/tests/incremental/derive_macro_expansion/proc_macro_unchanged.rs b/tests/incremental/derive_macro_expansion/proc_macro_unchanged.rs index ad98c9f789f47..0cb3cd8ae3efe 100644 --- a/tests/incremental/derive_macro_expansion/proc_macro_unchanged.rs +++ b/tests/incremental/derive_macro_expansion/proc_macro_unchanged.rs @@ -8,24 +8,23 @@ //@ aux-build:derive_nothing.rs //@ revisions:cfail1 cfail2 -//@ compile-flags: -Z query-dep-graph -Zcache-proc-macros=true +//@ compile-flags: -Z query-dep-graph -Zcache-derive-macros=true //@ build-pass #![feature(rustc_attrs)] #![feature(stmt_expr_attributes)] #![allow(dead_code)] #![crate_type = "rlib"] - -#![rustc_partition_codegened(module="proc_macro_unchanged-foo", cfg="cfail1")] +#![rustc_partition_codegened(module = "proc_macro_unchanged-foo", cfg = "cfail1")] // #![rustc_partition_codegened(module="proc_macro_unchanged-foo", cfg="cfail2")] // `foo::nothing_mod` is created by the derive macro and doesn't change -// BUG: this yields the same result with `-Zcache-proc-macros=false` (i.e., uncached), +// BUG: this yields the same result with `-Zcache-derive-macros=false` (i.e., uncached), // not sure how to do this correctly. -#![rustc_partition_reused(module="proc_macro_unchanged-foo-nothing_mod", cfg="cfail2")] +#![rustc_partition_reused(module = "proc_macro_unchanged-foo-nothing_mod", cfg = "cfail2")] - #[macro_use] - extern crate derive_nothing; +#[macro_use] +extern crate derive_nothing; pub mod foo { #[derive(Nothing)] From 1d491e498dc349ffd72e8b9433f5721b09a7a604 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Wed, 13 Aug 2025 14:14:26 +0200 Subject: [PATCH 4/7] Refactor TLS access --- Cargo.lock | 1 + compiler/rustc_expand/Cargo.toml | 1 + compiler/rustc_expand/src/proc_macro.rs | 164 +++++++++++------------- 3 files changed, 80 insertions(+), 86 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d24dd3238fd0a..d750c279e654b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3754,6 +3754,7 @@ dependencies = [ "rustc_serialize", "rustc_session", "rustc_span", + "scoped-tls", "smallvec", "thin-vec", "tracing", diff --git a/compiler/rustc_expand/Cargo.toml b/compiler/rustc_expand/Cargo.toml index d4064d0b65a2b..6b04be021c74b 100644 --- a/compiler/rustc_expand/Cargo.toml +++ b/compiler/rustc_expand/Cargo.toml @@ -29,6 +29,7 @@ rustc_proc_macro = { path = "../rustc_proc_macro" } rustc_serialize = { path = "../rustc_serialize" } rustc_session = { path = "../rustc_session" } rustc_span = { path = "../rustc_span" } +scoped-tls = "1.0" smallvec = { version = "1.8.1", features = ["union", "may_dangle"] } thin-vec.workspace = true tracing.workspace = true diff --git a/compiler/rustc_expand/src/proc_macro.rs b/compiler/rustc_expand/src/proc_macro.rs index afd6e98b09e1d..3d303a702d5a1 100644 --- a/compiler/rustc_expand/src/proc_macro.rs +++ b/compiler/rustc_expand/src/proc_macro.rs @@ -1,6 +1,3 @@ -use std::cell::Cell; -use std::ptr::NonNull; - use rustc_ast::tokenstream::TokenStream; use rustc_data_structures::svh::Svh; use rustc_errors::ErrorGuaranteed; @@ -36,7 +33,7 @@ impl pm::bridge::server::MessagePipe for MessagePipe { } } -pub fn exec_strategy(sess: &Session) -> impl pm::bridge::server::ExecutionStrategy + 'static { +fn exec_strategy(sess: &Session) -> impl pm::bridge::server::ExecutionStrategy + 'static { pm::bridge::server::MaybeCrossThread::>::new( sess.opts.unstable_opts.proc_macro_execution_strategy == ProcMacroExecutionStrategy::CrossThread, @@ -107,7 +104,7 @@ impl base::AttrProcMacro for AttrProcMacro { } pub struct DeriveProcMacro { - pub client: pm::bridge::client::Client, + pub client: DeriveClient, } impl MultiItemModifier for DeriveProcMacro { @@ -136,32 +133,31 @@ impl MultiItemModifier for DeriveProcMacro { // altogether. See #73345. crate::base::ann_pretty_printing_compatibility_hack(&item, &ecx.sess.psess); let input = item.to_tokens(); - let res = ty::tls::with(|tcx| { - let input = tcx.arena.alloc(input) as &TokenStream; - let invoc_id = ecx.current_expansion.id; - let invoc_expn_data = invoc_id.expn_data(); - - assert_eq!(invoc_expn_data.call_site, span); - - // FIXME(pr-time): Is this the correct way to check for incremental compilation (as - // well as for `cache_proc_macros`)? - if tcx.sess.opts.incremental.is_some() - && tcx.sess.opts.unstable_opts.cache_derive_macros - { + + let invoc_id = ecx.current_expansion.id; + + let res = if ecx.sess.opts.incremental.is_some() + && ecx.sess.opts.unstable_opts.cache_derive_macros + { + ty::tls::with(|tcx| { // FIXME(pr-time): Just using the crate hash to notice when the proc-macro code has // changed. How to *correctly* depend on exactly the macro definition? // I.e., depending on the crate hash is just a HACK, and ideally the dependency would be // more narrow. + let invoc_expn_data = invoc_id.expn_data(); let macro_def_id = invoc_expn_data.macro_def_id.unwrap(); let proc_macro_crate_hash = tcx.crate_hash(macro_def_id.krate); + let input = tcx.arena.alloc(input) as &TokenStream; let key = (invoc_id, proc_macro_crate_hash, input); - enter_context((ecx, self.client), move || tcx.derive_macro_expansion(key).cloned()) - } else { - expand_derive_macro(tcx, invoc_id, input, ecx, self.client).cloned() - } - }); + QueryDeriveExpandCtx::enter(ecx, self.client, move || { + tcx.derive_macro_expansion(key).cloned() + }) + }) + } else { + expand_derive_macro(invoc_id, input, ecx, self.client) + }; let Ok(output) = res else { // error will already have been emitted @@ -205,36 +201,38 @@ pub(super) fn provide_derive_macro_expansion<'tcx>( ) -> Result<&'tcx TokenStream, ()> { let (invoc_id, _macro_crate_hash, input) = key; - with_context(|(ecx, client)| expand_derive_macro(tcx, invoc_id, input, ecx, *client)) + eprintln!("Expanding derive macro in a query"); + + QueryDeriveExpandCtx::with(|ecx, client| { + expand_derive_macro(invoc_id, input.clone(), ecx, client) + .map(|ts| tcx.arena.alloc(ts) as &TokenStream) + }) } -type CLIENT = pm::bridge::client::Client; +type DeriveClient = pm::bridge::client::Client; -fn expand_derive_macro<'tcx>( - tcx: TyCtxt<'tcx>, +fn expand_derive_macro( invoc_id: LocalExpnId, - input: &'tcx TokenStream, + input: TokenStream, ecx: &mut ExtCtxt<'_>, - client: CLIENT, -) -> Result<&'tcx TokenStream, ()> { + client: DeriveClient, +) -> Result { let invoc_expn_data = invoc_id.expn_data(); let span = invoc_expn_data.call_site; let event_arg = invoc_expn_data.kind.descr(); - let _timer = tcx.sess.prof.generic_activity_with_arg_recorder( - "expand_derive_proc_macro_inner", - |recorder| { - recorder.record_arg_with_span(tcx.sess.source_map(), event_arg.clone(), span); - }, - ); + let _timer = + ecx.sess.prof.generic_activity_with_arg_recorder("expand_proc_macro", |recorder| { + recorder.record_arg_with_span(ecx.sess.source_map(), event_arg.clone(), span); + }); let proc_macro_backtrace = ecx.ecfg.proc_macro_backtrace; - let strategy = crate::proc_macro::exec_strategy(tcx.sess); - let server = crate::proc_macro_server::Rustc::new(ecx); + let strategy = exec_strategy(ecx.sess); + let server = proc_macro_server::Rustc::new(ecx); - match client.run(&strategy, server, input.clone(), proc_macro_backtrace) { - Ok(stream) => Ok(tcx.arena.alloc(stream) as &TokenStream), + match client.run(&strategy, server, input, proc_macro_backtrace) { + Ok(stream) => Ok(stream), Err(e) => { - tcx.dcx().emit_err({ + ecx.dcx().emit_err({ errors::ProcMacroDerivePanicked { span, message: e.as_str().map(|message| errors::ProcMacroDerivePanickedHelp { @@ -247,53 +245,47 @@ fn expand_derive_macro<'tcx>( } } -// based on rust/compiler/rustc_middle/src/ty/context/tls.rs -thread_local! { - /// A thread local variable that stores a pointer to the current `CONTEXT`. - static TLV: Cell<(*mut (), Option)> = const { Cell::new((std::ptr::null_mut(), None)) }; -} - -/// Sets `context` as the new current `CONTEXT` for the duration of the function `f`. -#[inline] -pub(crate) fn enter_context<'a, F, R>(context: (&mut ExtCtxt<'a>, CLIENT), f: F) -> R -where - F: FnOnce() -> R, -{ - let (ectx, client) = context; - let erased = (ectx as *mut _ as *mut (), Some(client)); - TLV.with(|tlv| { - let old = tlv.replace(erased); - let _reset = rustc_data_structures::defer(move || tlv.set(old)); - f() - }) +/// Stores the context necessary to expand a derive proc macro via a query. +struct QueryDeriveExpandCtx { + /// Type-erased version of `&mut ExtCtxt` + expansion_ctx: *mut (), + client: DeriveClient, } -/// Allows access to the current `CONTEXT`. -/// Panics if there is no `CONTEXT` available. -#[inline] -#[track_caller] -fn with_context(f: F) -> R -where - F: for<'a, 'b> FnOnce(&'b mut (&mut ExtCtxt<'a>, CLIENT)) -> R, -{ - let (ectx, client_opt) = TLV.get(); - let ectx = NonNull::new(ectx).expect("no CONTEXT stored in tls"); - - // We could get an `CONTEXT` pointer from another thread. - // Ensure that `CONTEXT` is `DynSync`. - // FIXME(pr-time): we should not be able to? - // sync::assert_dyn_sync::>(); - - // prevent double entering, as that would allow creating two `&mut ExtCtxt`s - // FIXME(pr-time): probably use a RefCell instead (which checks this properly)? - TLV.with(|tlv| { - let old = tlv.replace((std::ptr::null_mut(), None)); - let _reset = rustc_data_structures::defer(move || tlv.set(old)); - let ectx = { - let mut casted = ectx.cast::>(); - unsafe { casted.as_mut() } - }; +impl QueryDeriveExpandCtx { + /// Store the extension context and the client into the thread local value. + /// It will be accessible via the `with` method while `f` is active. + fn enter(ecx: &mut ExtCtxt<'_>, client: DeriveClient, f: F) -> R + where + F: FnOnce() -> R, + { + // We need erasure to get rid of the lifetime + let ctx = Self { expansion_ctx: ecx as *mut _ as *mut (), client }; + DERIVE_EXPAND_CTX.set(&ctx, || f()) + } - f(&mut (ectx, client_opt.unwrap())) - }) + /// Accesses the thread local value of the derive expansion context. + /// Must be called while the `enter` function is active. + fn with(f: F) -> R + where + F: for<'a, 'b> FnOnce(&'b mut ExtCtxt<'a>, DeriveClient) -> R, + { + DERIVE_EXPAND_CTX.with(|ctx| { + let ectx = { + let casted = ctx.expansion_ctx.cast::>(); + // SAFETY: We can only get the value from `with` while the `enter` function + // is active (on the callstack), and that function's signature ensures that the + // lifetime is valid. + // If `with` is called at some other time, it will panic due to usage of + // `scoped_tls::with`. + unsafe { casted.as_mut().unwrap() } + }; + + f(ectx, ctx.client) + }) + } } + +// When we invoke a query to expand a derive proc macro, we need to provide it with the expansion +// context and derive Client. We do that using a thread-local. +scoped_tls::scoped_thread_local!(static DERIVE_EXPAND_CTX: QueryDeriveExpandCtx); From 2a0500801fcb23f246db9001f14cbc86651d33d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Wed, 13 Aug 2025 14:58:34 +0200 Subject: [PATCH 5/7] Update test --- .../auxiliary/derive_nothing.rs | 8 +---- .../proc_macro_unchanged.rs | 31 ++++++------------- 2 files changed, 11 insertions(+), 28 deletions(-) diff --git a/tests/incremental/derive_macro_expansion/auxiliary/derive_nothing.rs b/tests/incremental/derive_macro_expansion/auxiliary/derive_nothing.rs index ee0fe7ea10023..e75b78dd92460 100644 --- a/tests/incremental/derive_macro_expansion/auxiliary/derive_nothing.rs +++ b/tests/incremental/derive_macro_expansion/auxiliary/derive_nothing.rs @@ -10,11 +10,5 @@ use proc_macro::TokenStream; pub fn derive(_input: TokenStream) -> TokenStream { eprintln!("invoked"); - return r#" - pub mod nothing_mod { - pub fn nothing() { - eprintln!("nothing"); - } - } - "#.parse().unwrap(); + TokenStream::new() } diff --git a/tests/incremental/derive_macro_expansion/proc_macro_unchanged.rs b/tests/incremental/derive_macro_expansion/proc_macro_unchanged.rs index 0cb3cd8ae3efe..9f3be27001764 100644 --- a/tests/incremental/derive_macro_expansion/proc_macro_unchanged.rs +++ b/tests/incremental/derive_macro_expansion/proc_macro_unchanged.rs @@ -7,32 +7,21 @@ // FIXME(pr-time): Properly have the test check this, but how? UI-test that tests for `.stderr`? //@ aux-build:derive_nothing.rs -//@ revisions:cfail1 cfail2 -//@ compile-flags: -Z query-dep-graph -Zcache-derive-macros=true -//@ build-pass +//@ revisions:rpass1 rpass2 +//@ compile-flags: -Zquery-dep-graph -Zcache-derive-macros #![feature(rustc_attrs)] -#![feature(stmt_expr_attributes)] -#![allow(dead_code)] -#![crate_type = "rlib"] -#![rustc_partition_codegened(module = "proc_macro_unchanged-foo", cfg = "cfail1")] -// #![rustc_partition_codegened(module="proc_macro_unchanged-foo", cfg="cfail2")] - -// `foo::nothing_mod` is created by the derive macro and doesn't change -// BUG: this yields the same result with `-Zcache-derive-macros=false` (i.e., uncached), -// not sure how to do this correctly. -#![rustc_partition_reused(module = "proc_macro_unchanged-foo-nothing_mod", cfg = "cfail2")] #[macro_use] extern crate derive_nothing; -pub mod foo { - #[derive(Nothing)] - pub struct Foo; +#[cfg(rpass1)] +#[derive(Nothing)] +pub struct Foo; - pub fn use_foo(_f: Foo) { - nothing_mod::nothing(); +#[cfg(rpass2)] +#[derive(Nothing)] +#[rustc_clean(cfg = "rpass2", loaded_from_disk = "derive_macro_expansion")] +pub struct Foo; - eprintln!("foo used"); - } -} +fn main() {} From f35acb801795c4ad69f23d06b050dc43f3e5b764 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Wed, 13 Aug 2025 15:01:46 +0200 Subject: [PATCH 6/7] WIP: temporarily enable `cache-derive-macros` by default for rustc-perf --- compiler/rustc_session/src/options.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 45d658354baa7..8ccc7e5b4c577 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -2157,8 +2157,8 @@ options! { "set options for branch target identification and pointer authentication on AArch64"), build_sdylib_interface: bool = (false, parse_bool, [UNTRACKED], "whether the stable interface is being built"), - cache_derive_macros: bool = (false, parse_bool, [TRACKED], - "cache the results of derive proc macro invocations (potentially unsound!) (default: no"), + cache_derive_macros: bool = (true, parse_bool, [TRACKED], + "cache the results of derive proc macro invocations (potentially unsound!) (default: yes"), cf_protection: CFProtection = (CFProtection::None, parse_cfprotection, [TRACKED], "instrument control-flow architecture protection"), check_cfg_all_expected: bool = (false, parse_bool, [UNTRACKED], From 8540970efd1e2098d4f6886f0803b4932dc596a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Wed, 13 Aug 2025 16:14:27 +0200 Subject: [PATCH 7/7] Derive `Hash` for `TokenStream` --- compiler/rustc_ast/src/ast.rs | 13 +++++++++- compiler/rustc_ast/src/token.rs | 16 ++++++------ compiler/rustc_ast/src/tokenstream.rs | 34 ++++++++++++++------------ compiler/rustc_middle/src/query/mod.rs | 2 +- 4 files changed, 39 insertions(+), 26 deletions(-) diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index de3e0e0c87f5a..3af75ba9ab2a9 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -3243,7 +3243,18 @@ impl UseTree { /// Distinguishes between `Attribute`s that decorate items and Attributes that /// are contained as statements within items. These two cases need to be /// distinguished for pretty-printing. -#[derive(Clone, PartialEq, Encodable, Decodable, Debug, Copy, HashStable_Generic, Walkable)] +#[derive( + Clone, + PartialEq, + Eq, + Encodable, + Decodable, + Hash, + Debug, + Copy, + HashStable_Generic, + Walkable +)] pub enum AttrStyle { Outer, Inner, diff --git a/compiler/rustc_ast/src/token.rs b/compiler/rustc_ast/src/token.rs index 6dc6d1026f621..6c224a60e4fcf 100644 --- a/compiler/rustc_ast/src/token.rs +++ b/compiler/rustc_ast/src/token.rs @@ -16,13 +16,13 @@ use rustc_span::{Ident, Symbol}; use crate::ast; use crate::util::case::Case; -#[derive(Clone, Copy, PartialEq, Encodable, Decodable, Debug, HashStable_Generic)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, Encodable, Decodable, Debug, HashStable_Generic)] pub enum CommentKind { Line, Block, } -#[derive(Copy, Clone, PartialEq, Debug, Encodable, Decodable, HashStable_Generic)] +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Encodable, Decodable, HashStable_Generic)] pub enum InvisibleOrigin { // From the expansion of a metavariable in a declarative macro. MetaVar(MetaVarKind), @@ -99,7 +99,7 @@ impl fmt::Display for MetaVarKind { /// Describes how a sequence of token trees is delimited. /// Cannot use `proc_macro::Delimiter` directly because this /// structure should implement some additional traits. -#[derive(Copy, Clone, Debug, PartialEq, Encodable, Decodable, HashStable_Generic)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Encodable, Decodable, HashStable_Generic)] pub enum Delimiter { /// `( ... )` Parenthesis, @@ -162,7 +162,7 @@ impl Delimiter { // type. This means that float literals like `1f32` are classified by this type // as `Int`. Only upon conversion to `ast::LitKind` will such a literal be // given the `Float` kind. -#[derive(Clone, Copy, PartialEq, Encodable, Decodable, Debug, HashStable_Generic)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, Encodable, Decodable, Debug, HashStable_Generic)] pub enum LitKind { Bool, // AST only, must never appear in a `Token` Byte, @@ -179,7 +179,7 @@ pub enum LitKind { } /// A literal token. -#[derive(Clone, Copy, PartialEq, Encodable, Decodable, Debug, HashStable_Generic)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, Encodable, Decodable, Debug, HashStable_Generic)] pub struct Lit { pub kind: LitKind, pub symbol: Symbol, @@ -325,7 +325,7 @@ fn ident_can_begin_type(name: Symbol, span: Span, is_raw: IdentIsRaw) -> bool { .contains(&name) } -#[derive(PartialEq, Encodable, Decodable, Debug, Copy, Clone, HashStable_Generic)] +#[derive(PartialEq, Eq, Encodable, Decodable, Hash, Debug, Copy, Clone, HashStable_Generic)] pub enum IdentIsRaw { No, Yes, @@ -352,7 +352,7 @@ impl From for IdentIsRaw { } } -#[derive(Clone, Copy, PartialEq, Encodable, Decodable, Debug, HashStable_Generic)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, Encodable, Decodable, Debug, HashStable_Generic)] pub enum TokenKind { /* Expression-operator symbols. */ /// `=` @@ -502,7 +502,7 @@ pub enum TokenKind { Eof, } -#[derive(Clone, Copy, PartialEq, Encodable, Decodable, Debug, HashStable_Generic)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, Encodable, Decodable, Debug, HashStable_Generic)] pub struct Token { pub kind: TokenKind, pub span: Span, diff --git a/compiler/rustc_ast/src/tokenstream.rs b/compiler/rustc_ast/src/tokenstream.rs index 53f5cb52263f4..89b8c13257a55 100644 --- a/compiler/rustc_ast/src/tokenstream.rs +++ b/compiler/rustc_ast/src/tokenstream.rs @@ -13,9 +13,8 @@ use std::{cmp, fmt, iter, mem}; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_data_structures::sync; use rustc_macros::{Decodable, Encodable, HashStable_Generic, Walkable}; -use rustc_serialize::{Decodable, Encodable, Encoder}; -use rustc_span::def_id::{CrateNum, DefIndex}; -use rustc_span::{ByteSymbol, DUMMY_SP, Span, SpanDecoder, SpanEncoder, Symbol, sym}; +use rustc_serialize::{Decodable, Encodable}; +use rustc_span::{DUMMY_SP, Span, SpanDecoder, SpanEncoder, Symbol, sym}; use thin_vec::ThinVec; use crate::ast::AttrStyle; @@ -24,7 +23,7 @@ use crate::token::{self, Delimiter, Token, TokenKind}; use crate::{AttrVec, Attribute}; /// Part of a `TokenStream`. -#[derive(Debug, Clone, PartialEq, Encodable, Decodable, HashStable_Generic)] +#[derive(Debug, Clone, PartialEq, Eq, Hash, Encodable, Decodable, HashStable_Generic)] pub enum TokenTree { /// A single token. Should never be `OpenDelim` or `CloseDelim`, because /// delimiters are implicitly represented by `Delimited`. @@ -542,7 +541,7 @@ pub struct AttrsTarget { /// compound token. Used for conversions to `proc_macro::Spacing`. Also used to /// guide pretty-printing, which is where the `JointHidden` value (which isn't /// part of `proc_macro::Spacing`) comes in useful. -#[derive(Clone, Copy, Debug, PartialEq, Encodable, Decodable, HashStable_Generic)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Encodable, Decodable, HashStable_Generic)] pub enum Spacing { /// The token cannot join with the following token to form a compound /// token. @@ -599,7 +598,7 @@ pub enum Spacing { } /// A `TokenStream` is an abstract sequence of tokens, organized into [`TokenTree`]s. -#[derive(Clone, Debug, Default, Encodable, Decodable)] +#[derive(Clone, Debug, Default, PartialEq, Eq, Hash, Encodable, Decodable)] pub struct TokenStream(pub(crate) Arc>); impl TokenStream { @@ -815,14 +814,6 @@ impl TokenStream { } } -impl PartialEq for TokenStream { - fn eq(&self, other: &TokenStream) -> bool { - self.iter().eq(other.iter()) - } -} - -impl Eq for TokenStream {} - impl FromIterator for TokenStream { fn from_iter>(iter: I) -> Self { TokenStream::new(iter.into_iter().collect::>()) @@ -974,7 +965,18 @@ impl TokenCursor { } } -#[derive(Debug, Copy, Clone, PartialEq, Encodable, Decodable, HashStable_Generic, Walkable)] +#[derive( + Debug, + Copy, + Clone, + PartialEq, + Eq, + Hash, + Encodable, + Decodable, + HashStable_Generic, + Walkable +)] pub struct DelimSpan { pub open: Span, pub close: Span, @@ -998,7 +1000,7 @@ impl DelimSpan { } } -#[derive(Copy, Clone, Debug, PartialEq, Encodable, Decodable, HashStable_Generic)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Encodable, Decodable, HashStable_Generic)] pub struct DelimSpacing { pub open: Spacing, pub close: Spacing, diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 5c902a629d925..e7ad786344829 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -168,7 +168,7 @@ rustc_queries! { /// Caches the expansion of a derive proc macro, e.g. `#[derive(Serialize)]`. /// The key is: /// - A unique key corresponding to the invocation of a macro. - /// - Strict Version Hash of a crate. + /// - Strict Version Hash of the crate defining the proc macro. /// - Token stream which serves as an input to the macro. /// /// The output is the token stream generated by the proc macro.