From cff0ea5f88cf926a4cb326545ec63ccd27afb846 Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Tue, 22 Jun 2021 19:16:18 +0200 Subject: [PATCH 1/2] Move HashStable implementations. --- compiler/rustc_span/src/hygiene.rs | 60 ++++++++++++++++++++++++++++- compiler/rustc_span/src/lib.rs | 61 +----------------------------- 2 files changed, 60 insertions(+), 61 deletions(-) diff --git a/compiler/rustc_span/src/hygiene.rs b/compiler/rustc_span/src/hygiene.rs index 78b181aa3300a..daa331fe0522b 100644 --- a/compiler/rustc_span/src/hygiene.rs +++ b/compiler/rustc_span/src/hygiene.rs @@ -27,7 +27,7 @@ use crate::edition::Edition; use crate::symbol::{kw, sym, Symbol}; use crate::with_session_globals; -use crate::{BytePos, CachingSourceMapView, ExpnIdCache, SourceFile, Span, DUMMY_SP}; +use crate::{BytePos, CachingSourceMapView, HashStableContext, SourceFile, Span, DUMMY_SP}; use crate::def_id::{CrateNum, DefId, DefPathHash, CRATE_DEF_INDEX, LOCAL_CRATE}; use rustc_data_structures::fingerprint::Fingerprint; @@ -36,6 +36,7 @@ use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_data_structures::sync::{Lock, Lrc}; use rustc_macros::HashStable_Generic; use rustc_serialize::{Decodable, Decoder, Encodable, Encoder}; +use std::cell::RefCell; use std::fmt; use std::hash::Hash; use std::thread::LocalKey; @@ -1407,3 +1408,60 @@ fn update_disambiguator(expn_id: ExpnId) { }; } } + +impl HashStable for SyntaxContext { + fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) { + const TAG_EXPANSION: u8 = 0; + const TAG_NO_EXPANSION: u8 = 1; + + if *self == SyntaxContext::root() { + TAG_NO_EXPANSION.hash_stable(ctx, hasher); + } else { + TAG_EXPANSION.hash_stable(ctx, hasher); + let (expn_id, transparency) = self.outer_mark(); + expn_id.hash_stable(ctx, hasher); + transparency.hash_stable(ctx, hasher); + } + } +} + +pub type ExpnIdCache = RefCell>>; + +impl HashStable for ExpnId { + fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) { + const TAG_ROOT: u8 = 0; + const TAG_NOT_ROOT: u8 = 1; + + if *self == ExpnId::root() { + TAG_ROOT.hash_stable(ctx, hasher); + return; + } + + // Since the same expansion context is usually referenced many + // times, we cache a stable hash of it and hash that instead of + // recursing every time. + let index = self.as_u32() as usize; + let res = CTX::expn_id_cache().with(|cache| cache.borrow().get(index).copied().flatten()); + + if let Some(res) = res { + res.hash_stable(ctx, hasher); + } else { + let new_len = index + 1; + + let mut sub_hasher = StableHasher::new(); + TAG_NOT_ROOT.hash_stable(ctx, &mut sub_hasher); + self.expn_data().hash_stable(ctx, &mut sub_hasher); + let sub_hash: Fingerprint = sub_hasher.finish(); + + CTX::expn_id_cache().with(|cache| { + let mut cache = cache.borrow_mut(); + if cache.len() < new_len { + cache.resize(new_len, None); + } + let prev = cache[index].replace(sub_hash); + assert_eq!(prev, None, "Cache slot was filled"); + }); + sub_hash.hash_stable(ctx, hasher); + } + } +} diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs index 84bef4b113c15..fa591521dbc8d 100644 --- a/compiler/rustc_span/src/lib.rs +++ b/compiler/rustc_span/src/lib.rs @@ -36,9 +36,9 @@ use source_map::SourceMap; pub mod edition; use edition::Edition; pub mod hygiene; -pub use hygiene::SyntaxContext; use hygiene::Transparency; pub use hygiene::{DesugaringKind, ExpnData, ExpnId, ExpnKind, ForLoopLoc, MacroKind}; +pub use hygiene::{ExpnIdCache, SyntaxContext}; pub mod def_id; use def_id::{CrateNum, DefId, DefPathHash, LOCAL_CRATE}; pub mod lev_distance; @@ -51,12 +51,10 @@ pub use symbol::{sym, Symbol}; mod analyze_source_file; pub mod fatal_error; -use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_data_structures::sync::{Lock, Lrc}; use std::borrow::Cow; -use std::cell::RefCell; use std::cmp::{self, Ordering}; use std::fmt; use std::hash::Hash; @@ -2036,60 +2034,3 @@ where Hash::hash(&len, hasher); } } - -impl HashStable for SyntaxContext { - fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) { - const TAG_EXPANSION: u8 = 0; - const TAG_NO_EXPANSION: u8 = 1; - - if *self == SyntaxContext::root() { - TAG_NO_EXPANSION.hash_stable(ctx, hasher); - } else { - TAG_EXPANSION.hash_stable(ctx, hasher); - let (expn_id, transparency) = self.outer_mark(); - expn_id.hash_stable(ctx, hasher); - transparency.hash_stable(ctx, hasher); - } - } -} - -pub type ExpnIdCache = RefCell>>; - -impl HashStable for ExpnId { - fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) { - const TAG_ROOT: u8 = 0; - const TAG_NOT_ROOT: u8 = 1; - - if *self == ExpnId::root() { - TAG_ROOT.hash_stable(ctx, hasher); - return; - } - - // Since the same expansion context is usually referenced many - // times, we cache a stable hash of it and hash that instead of - // recursing every time. - let index = self.as_u32() as usize; - let res = CTX::expn_id_cache().with(|cache| cache.borrow().get(index).copied().flatten()); - - if let Some(res) = res { - res.hash_stable(ctx, hasher); - } else { - let new_len = index + 1; - - let mut sub_hasher = StableHasher::new(); - TAG_NOT_ROOT.hash_stable(ctx, &mut sub_hasher); - self.expn_data().hash_stable(ctx, &mut sub_hasher); - let sub_hash: Fingerprint = sub_hasher.finish(); - - CTX::expn_id_cache().with(|cache| { - let mut cache = cache.borrow_mut(); - if cache.len() < new_len { - cache.resize(new_len, None); - } - let prev = cache[index].replace(sub_hash); - assert_eq!(prev, None, "Cache slot was filled"); - }); - sub_hash.hash_stable(ctx, hasher); - } - } -} From 616ce3c5c029446ef8b0d7a3525f96b2e451840c Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Tue, 22 Jun 2021 19:20:25 +0200 Subject: [PATCH 2/2] Cache expansion hash. --- compiler/rustc_ast_lowering/src/lib.rs | 46 ++- compiler/rustc_expand/src/expand.rs | 4 +- compiler/rustc_metadata/src/rmeta/decoder.rs | 11 +- compiler/rustc_metadata/src/rmeta/encoder.rs | 19 +- compiler/rustc_metadata/src/rmeta/mod.rs | 4 +- compiler/rustc_middle/src/ich/hcx.rs | 8 - .../src/ty/query/on_disk_cache.rs | 8 +- compiler/rustc_mir/src/transform/inline.rs | 3 +- compiler/rustc_resolve/src/lib.rs | 44 ++- compiler/rustc_resolve/src/macros.rs | 40 +-- compiler/rustc_span/src/hygiene.rs | 283 ++++++++---------- compiler/rustc_span/src/lib.rs | 10 +- 12 files changed, 263 insertions(+), 217 deletions(-) diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 7a4e39376a86d..b787158c34368 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -48,7 +48,7 @@ use rustc_data_structures::sync::Lrc; use rustc_errors::{struct_span_err, Applicability}; use rustc_hir as hir; use rustc_hir::def::{DefKind, Namespace, PartialRes, PerNS, Res}; -use rustc_hir::def_id::{DefId, DefIdMap, LocalDefId, CRATE_DEF_ID}; +use rustc_hir::def_id::{DefId, DefIdMap, DefPathHash, LocalDefId, CRATE_DEF_ID}; use rustc_hir::definitions::{DefKey, DefPathData, Definitions}; use rustc_hir::intravisit; use rustc_hir::{ConstArg, GenericArg, ParamName}; @@ -59,7 +59,7 @@ use rustc_session::utils::{FlattenNonterminals, NtToTokenstream}; use rustc_session::Session; use rustc_span::edition::Edition; use rustc_span::hygiene::ExpnId; -use rustc_span::source_map::{respan, DesugaringKind}; +use rustc_span::source_map::{respan, CachingSourceMapView, DesugaringKind}; use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::{Span, DUMMY_SP}; use rustc_target::spec::abi::Abi; @@ -204,6 +204,8 @@ pub trait ResolverAstLowering { fn local_def_id(&self, node: NodeId) -> LocalDefId; + fn def_path_hash(&self, def_id: DefId) -> DefPathHash; + fn create_def( &mut self, parent: LocalDefId, @@ -214,6 +216,32 @@ pub trait ResolverAstLowering { ) -> LocalDefId; } +struct LoweringHasher<'a> { + source_map: CachingSourceMapView<'a>, + resolver: &'a dyn ResolverAstLowering, +} + +impl<'a> rustc_span::HashStableContext for LoweringHasher<'a> { + #[inline] + fn hash_spans(&self) -> bool { + true + } + + #[inline] + fn def_path_hash(&self, def_id: DefId) -> DefPathHash { + self.resolver.def_path_hash(def_id) + } + + #[inline] + fn span_data_to_lines_and_cols( + &mut self, + span: &rustc_span::SpanData, + ) -> Option<(Lrc, usize, rustc_span::BytePos, usize, rustc_span::BytePos)> + { + self.source_map.span_data_to_lines_and_cols(span) + } +} + /// Context of `impl Trait` in code, which determines whether it is allowed in an HIR subtree, /// and if so, what meaning it has. #[derive(Debug)] @@ -565,6 +593,13 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { lowered } + fn create_stable_hashing_context(&self) -> LoweringHasher<'_> { + LoweringHasher { + source_map: CachingSourceMapView::new(self.sess.source_map()), + resolver: self.resolver, + } + } + fn lower_node_id_generic( &mut self, ast_node_id: NodeId, @@ -684,7 +719,12 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { span: Span, allow_internal_unstable: Option>, ) -> Span { - span.mark_with_reason(allow_internal_unstable, reason, self.sess.edition()) + span.mark_with_reason( + allow_internal_unstable, + reason, + self.sess.edition(), + self.create_stable_hashing_context(), + ) } fn with_anonymous_lifetime_mode( diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index f8a12ef8a2081..df673c6084b96 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -506,7 +506,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { .map(|(path, item, _exts)| { // FIXME: Consider using the derive resolutions (`_exts`) // instead of enqueuing the derives to be resolved again later. - let expn_id = ExpnId::fresh(None); + let expn_id = ExpnId::fresh_empty(); derive_invocations.push(( Invocation { kind: InvocationKind::Derive { path, item }, @@ -989,7 +989,7 @@ struct InvocationCollector<'a, 'b> { impl<'a, 'b> InvocationCollector<'a, 'b> { fn collect(&mut self, fragment_kind: AstFragmentKind, kind: InvocationKind) -> AstFragment { - let expn_id = ExpnId::fresh(None); + let expn_id = ExpnId::fresh_empty(); let vis = kind.placeholder_visibility(); self.invocations.push(( Invocation { diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs index 6ecd4a512e9b4..e9773bac819bd 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder.rs @@ -393,12 +393,19 @@ impl<'a, 'tcx> Decodable> for ExpnId { } else { local_cdata.cstore.get_crate_data(cnum) }; - Ok(crate_data + let expn_data = crate_data .root .expn_data .get(&crate_data, index) .unwrap() - .decode((&crate_data, sess))) + .decode((&crate_data, sess)); + let expn_hash = crate_data + .root + .expn_hashes + .get(&crate_data, index) + .unwrap() + .decode((&crate_data, sess)); + Ok((expn_data, expn_hash)) }, ) } diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index 0e924d644353c..6fac8e595d0ad 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -653,7 +653,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { // Therefore, we need to encode the hygiene data last to ensure that we encode // any `SyntaxContext`s that might be used. i = self.position(); - let (syntax_contexts, expn_data) = self.encode_hygiene(); + let (syntax_contexts, expn_data, expn_hashes) = self.encode_hygiene(); let hygiene_bytes = self.position() - i; // Encode source_map. This needs to be done last, @@ -701,6 +701,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { tables, syntax_contexts, expn_data, + expn_hashes, }); let total_bytes = self.position(); @@ -1578,23 +1579,29 @@ impl EncodeContext<'a, 'tcx> { self.lazy(foreign_modules.iter().map(|(_, m)| m).cloned()) } - fn encode_hygiene(&mut self) -> (SyntaxContextTable, ExpnDataTable) { + fn encode_hygiene(&mut self) -> (SyntaxContextTable, ExpnDataTable, ExpnHashTable) { let mut syntax_contexts: TableBuilder<_, _> = Default::default(); let mut expn_data_table: TableBuilder<_, _> = Default::default(); + let mut expn_hash_table: TableBuilder<_, _> = Default::default(); let _: Result<(), !> = self.hygiene_ctxt.encode( - &mut (&mut *self, &mut syntax_contexts, &mut expn_data_table), - |(this, syntax_contexts, _), index, ctxt_data| { + &mut (&mut *self, &mut syntax_contexts, &mut expn_data_table, &mut expn_hash_table), + |(this, syntax_contexts, _, _), index, ctxt_data| { syntax_contexts.set(index, this.lazy(ctxt_data)); Ok(()) }, - |(this, _, expn_data_table), index, expn_data| { + |(this, _, expn_data_table, expn_hash_table), index, expn_data, hash| { expn_data_table.set(index, this.lazy(expn_data)); + expn_hash_table.set(index, this.lazy(hash)); Ok(()) }, ); - (syntax_contexts.encode(&mut self.opaque), expn_data_table.encode(&mut self.opaque)) + ( + syntax_contexts.encode(&mut self.opaque), + expn_data_table.encode(&mut self.opaque), + expn_hash_table.encode(&mut self.opaque), + ) } fn encode_proc_macros(&mut self) -> Option { diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs index 3793058062347..8cc1935951fe3 100644 --- a/compiler/rustc_metadata/src/rmeta/mod.rs +++ b/compiler/rustc_metadata/src/rmeta/mod.rs @@ -21,7 +21,7 @@ use rustc_session::config::SymbolManglingVersion; use rustc_span::edition::Edition; use rustc_span::hygiene::MacroKind; use rustc_span::symbol::{Ident, Symbol}; -use rustc_span::{self, ExpnData, ExpnId, Span}; +use rustc_span::{self, ExpnData, ExpnHash, ExpnId, Span}; use rustc_target::spec::{PanicStrategy, TargetTriple}; use std::marker::PhantomData; @@ -171,6 +171,7 @@ macro_rules! Lazy { type SyntaxContextTable = Lazy>>; type ExpnDataTable = Lazy>>; +type ExpnHashTable = Lazy>>; #[derive(MetadataEncodable, MetadataDecodable)] crate struct ProcMacroData { @@ -226,6 +227,7 @@ crate struct CrateRoot<'tcx> { syntax_contexts: SyntaxContextTable, expn_data: ExpnDataTable, + expn_hashes: ExpnHashTable, source_map: Lazy<[rustc_span::SourceFile]>, diff --git a/compiler/rustc_middle/src/ich/hcx.rs b/compiler/rustc_middle/src/ich/hcx.rs index f1c4529b8552b..32ccdafaeb48c 100644 --- a/compiler/rustc_middle/src/ich/hcx.rs +++ b/compiler/rustc_middle/src/ich/hcx.rs @@ -16,7 +16,6 @@ use rustc_span::{BytePos, CachingSourceMapView, SourceFile, SpanData}; use smallvec::SmallVec; use std::cmp::Ord; -use std::thread::LocalKey; fn compute_ignored_attr_names() -> FxHashSet { debug_assert!(!ich::IGNORED_ATTRIBUTES.is_empty()); @@ -230,13 +229,6 @@ impl<'a> rustc_span::HashStableContext for StableHashingContext<'a> { self.def_path_hash(def_id) } - fn expn_id_cache() -> &'static LocalKey { - thread_local! { - static CACHE: rustc_span::ExpnIdCache = Default::default(); - } - &CACHE - } - fn span_data_to_lines_and_cols( &mut self, span: &SpanData, diff --git a/compiler/rustc_middle/src/ty/query/on_disk_cache.rs b/compiler/rustc_middle/src/ty/query/on_disk_cache.rs index 15188643d6631..e3db0d2cf30a6 100644 --- a/compiler/rustc_middle/src/ty/query/on_disk_cache.rs +++ b/compiler/rustc_middle/src/ty/query/on_disk_cache.rs @@ -25,7 +25,7 @@ use rustc_span::hygiene::{ }; use rustc_span::source_map::{SourceMap, StableSourceFileId}; use rustc_span::CachingSourceMapView; -use rustc_span::{BytePos, ExpnData, SourceFile, Span, DUMMY_SP}; +use rustc_span::{BytePos, ExpnData, ExpnHash, SourceFile, Span, DUMMY_SP}; use std::collections::hash_map::Entry; use std::mem; @@ -364,9 +364,9 @@ impl<'sess> OnDiskCache<'sess> { syntax_contexts.insert(index, pos); Ok(()) }, - |encoder, index, expn_data| -> FileEncodeResult { + |encoder, index, expn_data, hash| -> FileEncodeResult { let pos = AbsoluteBytePos::new(encoder.position()); - encoder.encode_tagged(TAG_EXPN_DATA, expn_data)?; + encoder.encode_tagged(TAG_EXPN_DATA, &(expn_data, hash))?; expn_ids.insert(index, pos); Ok(()) }, @@ -804,7 +804,7 @@ impl<'a, 'tcx> Decodable> for ExpnId { .unwrap_or_else(|| panic!("Bad index {:?} (map {:?})", index, expn_data)); this.with_position(pos.to_usize(), |decoder| { - let data: ExpnData = decode_tagged(decoder, TAG_EXPN_DATA)?; + let data: (ExpnData, ExpnHash) = decode_tagged(decoder, TAG_EXPN_DATA)?; Ok(data) }) }, diff --git a/compiler/rustc_mir/src/transform/inline.rs b/compiler/rustc_mir/src/transform/inline.rs index 703ac39dc3080..7d765cec57578 100644 --- a/compiler/rustc_mir/src/transform/inline.rs +++ b/compiler/rustc_mir/src/transform/inline.rs @@ -839,7 +839,8 @@ impl<'a, 'tcx> MutVisitor<'tcx> for Integrator<'a, 'tcx> { ExpnData::default(ExpnKind::Inlined, *span, self.tcx.sess.edition(), None, None); expn_data.def_site = self.body_span; // Make sure that all spans track the fact that they were inlined. - *span = self.callsite_span.fresh_expansion(expn_data); + *span = + self.callsite_span.fresh_expansion(expn_data, self.tcx.create_stable_hashing_context()); } fn visit_place(&mut self, place: &mut Place<'tcx>, context: PlaceContext, location: Location) { diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index bcdae1cb43dbd..4d12415215194 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -39,7 +39,7 @@ use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder}; use rustc_expand::base::{DeriveResolutions, SyntaxExtension, SyntaxExtensionKind}; use rustc_hir::def::Namespace::*; use rustc_hir::def::{self, CtorOf, DefKind, NonMacroAttrKind, PartialRes}; -use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LocalDefId, CRATE_DEF_INDEX}; +use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, DefPathHash, LocalDefId, CRATE_DEF_INDEX}; use rustc_hir::definitions::{DefKey, DefPathData, Definitions}; use rustc_hir::TraitCandidate; use rustc_index::vec::IndexVec; @@ -54,7 +54,7 @@ use rustc_session::lint::{BuiltinLintDiagnostics, LintBuffer}; use rustc_session::Session; use rustc_span::edition::Edition; use rustc_span::hygiene::{ExpnId, ExpnKind, MacroKind, SyntaxContext, Transparency}; -use rustc_span::source_map::Spanned; +use rustc_span::source_map::{CachingSourceMapView, Spanned}; use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::{Span, DUMMY_SP}; @@ -1149,6 +1149,13 @@ impl ResolverAstLowering for Resolver<'_> { self.opt_local_def_id(node).unwrap_or_else(|| panic!("no entry for node id: `{:?}`", node)) } + fn def_path_hash(&self, def_id: DefId) -> DefPathHash { + match def_id.as_local() { + Some(def_id) => self.definitions.def_path_hash(def_id), + None => self.cstore().def_path_hash(def_id), + } + } + /// Adds a definition with a parent definition. fn create_def( &mut self, @@ -1192,6 +1199,32 @@ impl ResolverAstLowering for Resolver<'_> { } } +struct ExpandHasher<'a, 'b> { + source_map: CachingSourceMapView<'a>, + resolver: &'a Resolver<'b>, +} + +impl<'a, 'b> rustc_span::HashStableContext for ExpandHasher<'a, 'b> { + #[inline] + fn hash_spans(&self) -> bool { + true + } + + #[inline] + fn def_path_hash(&self, def_id: DefId) -> DefPathHash { + self.resolver.def_path_hash(def_id) + } + + #[inline] + fn span_data_to_lines_and_cols( + &mut self, + span: &rustc_span::SpanData, + ) -> Option<(Lrc, usize, rustc_span::BytePos, usize, rustc_span::BytePos)> + { + self.source_map.span_data_to_lines_and_cols(span) + } +} + impl<'a> Resolver<'a> { pub fn new( session: &'a Session, @@ -1364,6 +1397,13 @@ impl<'a> Resolver<'a> { resolver } + fn create_stable_hashing_context(&self) -> ExpandHasher<'_, 'a> { + ExpandHasher { + source_map: CachingSourceMapView::new(self.session.source_map()), + resolver: self, + } + } + pub fn next_node_id(&mut self) -> NodeId { let next = self .next_node_id diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index e024ade7b3c84..8686704388fee 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -218,14 +218,17 @@ impl<'a> ResolverExpand for Resolver<'a> { parent_module_id: Option, ) -> ExpnId { let parent_module = parent_module_id.map(|module_id| self.local_def_id(module_id)); - let expn_id = ExpnId::fresh(Some(ExpnData::allow_unstable( - ExpnKind::AstPass(pass), - call_site, - self.session.edition(), - features.into(), - None, - parent_module.map(LocalDefId::to_def_id), - ))); + let expn_id = ExpnId::fresh( + ExpnData::allow_unstable( + ExpnKind::AstPass(pass), + call_site, + self.session.edition(), + features.into(), + None, + parent_module.map(LocalDefId::to_def_id), + ), + self.create_stable_hashing_context(), + ); let parent_scope = parent_module .map_or(self.empty_module, |parent_def_id| self.module_map[&parent_def_id]); @@ -287,15 +290,18 @@ impl<'a> ResolverExpand for Resolver<'a> { )?; let span = invoc.span(); - invoc_id.set_expn_data(ext.expn_data( - parent_scope.expansion, - span, - fast_print_path(path), - res.opt_def_id(), - res.opt_def_id().map(|macro_def_id| { - self.macro_def_scope_from_def_id(macro_def_id).nearest_parent_mod - }), - )); + invoc_id.set_expn_data( + ext.expn_data( + parent_scope.expansion, + span, + fast_print_path(path), + res.opt_def_id(), + res.opt_def_id().map(|macro_def_id| { + self.macro_def_scope_from_def_id(macro_def_id).nearest_parent_mod + }), + ), + self.create_stable_hashing_context(), + ); if let Res::Def(_, _) = res { // Gate macro attributes in `#[derive]` output. diff --git a/compiler/rustc_span/src/hygiene.rs b/compiler/rustc_span/src/hygiene.rs index daa331fe0522b..9a101169d20b6 100644 --- a/compiler/rustc_span/src/hygiene.rs +++ b/compiler/rustc_span/src/hygiene.rs @@ -27,19 +27,18 @@ use crate::edition::Edition; use crate::symbol::{kw, sym, Symbol}; use crate::with_session_globals; -use crate::{BytePos, CachingSourceMapView, HashStableContext, SourceFile, Span, DUMMY_SP}; +use crate::{HashStableContext, Span, DUMMY_SP}; -use crate::def_id::{CrateNum, DefId, DefPathHash, CRATE_DEF_INDEX, LOCAL_CRATE}; +use crate::def_id::{CrateNum, DefId, CRATE_DEF_INDEX, LOCAL_CRATE}; use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_data_structures::sync::{Lock, Lrc}; +use rustc_data_structures::unhash::UnhashMap; use rustc_macros::HashStable_Generic; use rustc_serialize::{Decodable, Decoder, Encodable, Encoder}; -use std::cell::RefCell; use std::fmt; use std::hash::Hash; -use std::thread::LocalKey; use tracing::*; /// A `SyntaxContext` represents a chain of pairs `(ExpnId, Transparency)` named "marks". @@ -63,6 +62,10 @@ pub struct SyntaxContextData { #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] pub struct ExpnId(u32); +/// A unique hash value associated to an expansion. +#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Encodable, Decodable, HashStable_Generic)] +pub struct ExpnHash(Fingerprint); + /// A property of a macro expansion that determines how identifiers /// produced by that expansion are resolved. #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Hash, Debug, Encodable, Decodable)] @@ -84,12 +87,13 @@ pub enum Transparency { } impl ExpnId { - pub fn fresh(expn_data: Option) -> Self { - let has_data = expn_data.is_some(); - let expn_id = HygieneData::with(|data| data.fresh_expn(expn_data)); - if has_data { - update_disambiguator(expn_id); - } + pub fn fresh_empty() -> Self { + HygieneData::with(|data| data.fresh_expn(None)) + } + + pub fn fresh(expn_data: ExpnData, ctx: impl HashStableContext) -> Self { + let expn_id = HygieneData::with(|data| data.fresh_expn(Some(expn_data))); + update_disambiguator(expn_id, ctx); expn_id } @@ -109,13 +113,23 @@ impl ExpnId { ExpnId(raw) } + #[inline] + pub fn expn_hash(self) -> ExpnHash { + HygieneData::with(|data| data.expn_hash(self)) + } + + #[inline] + pub fn from_hash(hash: ExpnHash) -> Option { + HygieneData::with(|data| data.expn_hash_to_expn_id.get(&hash).copied()) + } + #[inline] pub fn expn_data(self) -> ExpnData { HygieneData::with(|data| data.expn_data(self).clone()) } #[inline] - pub fn set_expn_data(self, mut expn_data: ExpnData) { + pub fn set_expn_data(self, mut expn_data: ExpnData, ctx: impl HashStableContext) { HygieneData::with(|data| { let old_expn_data = &mut data.expn_data[self.0 as usize]; assert!(old_expn_data.is_none(), "expansion data is reset for an expansion ID"); @@ -123,7 +137,7 @@ impl ExpnId { expn_data.orig_id = Some(self.as_u32()); *old_expn_data = Some(expn_data); }); - update_disambiguator(self) + update_disambiguator(self, ctx) } pub fn is_descendant_of(self, ancestor: ExpnId) -> bool { @@ -162,6 +176,8 @@ pub struct HygieneData { /// between creation of an expansion ID and obtaining its data (e.g. macros are collected /// first and then resolved later), so we use an `Option` here. expn_data: Vec>, + expn_hashes: Vec, + expn_hash_to_expn_id: UnhashMap, syntax_context_data: Vec, syntax_context_map: FxHashMap<(SyntaxContext, ExpnId, Transparency), SyntaxContext>, /// Maps the `Fingerprint` of an `ExpnData` to the next disambiguator value. @@ -185,6 +201,9 @@ impl HygieneData { HygieneData { expn_data: vec![Some(root_data)], + expn_hashes: vec![ExpnHash(Fingerprint::ZERO)], + expn_hash_to_expn_id: std::iter::once((ExpnHash(Fingerprint::ZERO), ExpnId(0))) + .collect(), syntax_context_data: vec![SyntaxContextData { outer_expn: ExpnId::root(), outer_transparency: Transparency::Opaque, @@ -209,9 +228,15 @@ impl HygieneData { data.orig_id = Some(raw_id); } self.expn_data.push(expn_data); + self.expn_hashes.push(ExpnHash(Fingerprint::ZERO)); ExpnId(raw_id) } + #[inline] + fn expn_hash(&self, expn_id: ExpnId) -> ExpnHash { + self.expn_hashes[expn_id.0 as usize] + } + fn expn_data(&self, expn_id: ExpnId) -> &ExpnData { self.expn_data[expn_id.0 as usize].as_ref().expect("no expansion data for an expansion ID") } @@ -661,16 +686,17 @@ impl Span { /// other compiler-generated code to set per-span properties like allowed unstable features. /// The returned span belongs to the created expansion and has the new properties, /// but its location is inherited from the current span. - pub fn fresh_expansion(self, expn_data: ExpnData) -> Span { - self.fresh_expansion_with_transparency(expn_data, Transparency::Transparent) + pub fn fresh_expansion(self, expn_data: ExpnData, ctx: impl HashStableContext) -> Span { + self.fresh_expansion_with_transparency(expn_data, Transparency::Transparent, ctx) } pub fn fresh_expansion_with_transparency( self, expn_data: ExpnData, transparency: Transparency, + ctx: impl HashStableContext, ) -> Span { - let expn_id = ExpnId::fresh(Some(expn_data)); + let expn_id = ExpnId::fresh(expn_data, ctx); HygieneData::with(|data| { self.with_ctxt(data.apply_mark(SyntaxContext::root(), expn_id, transparency)) }) @@ -683,11 +709,13 @@ impl Span { allow_internal_unstable: Option>, reason: DesugaringKind, edition: Edition, + ctx: impl HashStableContext, ) -> Span { - self.fresh_expansion(ExpnData { + let expn_data = ExpnData { allow_internal_unstable, ..ExpnData::default(ExpnKind::Desugaring(reason), self, edition, None, None) - }) + }; + self.fresh_expansion(expn_data, ctx) } } @@ -839,6 +867,13 @@ impl ExpnData { pub fn is_root(&self) -> bool { matches!(self.kind, ExpnKind::Root) } + + #[inline] + fn hash_expn(&self, ctx: &mut impl HashStableContext) -> Fingerprint { + let mut hasher = StableHasher::new(); + self.hash_stable(ctx, &mut hasher); + hasher.finish() + } } /// Expansion kind. @@ -985,16 +1020,11 @@ pub struct HygieneEncodeContext { } impl HygieneEncodeContext { - pub fn encode< - T, - R, - F: FnMut(&mut T, u32, &SyntaxContextData) -> Result<(), R>, - G: FnMut(&mut T, u32, &ExpnData) -> Result<(), R>, - >( + pub fn encode( &self, encoder: &mut T, - mut encode_ctxt: F, - mut encode_expn: G, + mut encode_ctxt: impl FnMut(&mut T, u32, &SyntaxContextData) -> Result<(), R>, + mut encode_expn: impl FnMut(&mut T, u32, ExpnData, ExpnHash) -> Result<(), R>, ) -> Result<(), R> { // When we serialize a `SyntaxContextData`, we may end up serializing // a `SyntaxContext` that we haven't seen before @@ -1012,7 +1042,7 @@ impl HygieneEncodeContext { // It's fine to iterate over a HashMap, because the serialization // of the table that we insert data into doesn't depend on insertion // order - for_all_ctxts_in(latest_ctxts.into_iter(), |(index, ctxt, data)| { + for_all_ctxts_in(latest_ctxts.into_iter(), |index, ctxt, data| { if self.serialized_ctxts.lock().insert(ctxt) { encode_ctxt(encoder, index, data)?; } @@ -1021,9 +1051,9 @@ impl HygieneEncodeContext { let latest_expns = { std::mem::take(&mut *self.latest_expns.lock()) }; - for_all_expns_in(latest_expns.into_iter(), |index, expn, data| { + for_all_expns_in(latest_expns.into_iter(), |index, expn, data, hash| { if self.serialized_expns.lock().insert(expn) { - encode_expn(encoder, index, data)?; + encode_expn(encoder, index, data, hash)?; } Ok(()) })?; @@ -1046,16 +1076,14 @@ pub struct HygieneDecodeContext { remapped_expns: Lock>>, } -pub fn decode_expn_id< - 'a, - D: Decoder, - F: FnOnce(&mut D, u32) -> Result, - G: FnOnce(CrateNum) -> &'a HygieneDecodeContext, ->( +pub fn decode_expn_id<'a, D: Decoder, G>( d: &mut D, mode: ExpnDataDecodeMode<'a, G>, - decode_data: F, -) -> Result { + decode_data: impl FnOnce(&mut D, u32) -> Result<(ExpnData, ExpnHash), D::Error>, +) -> Result +where + G: FnOnce(CrateNum) -> &'a HygieneDecodeContext, +{ let index = u32::decode(d)?; let context = match mode { ExpnDataDecodeMode::IncrComp(context) => context, @@ -1083,9 +1111,13 @@ pub fn decode_expn_id< // Don't decode the data inside `HygieneData::with`, since we need to recursively decode // other ExpnIds - let mut expn_data = decode_data(d, index)?; + let (mut expn_data, hash) = decode_data(d, index)?; let expn_id = HygieneData::with(|hygiene_data| { + if let Some(&expn_id) = hygiene_data.expn_hash_to_expn_id.get(&hash) { + return expn_id; + } + let expn_id = ExpnId(hygiene_data.expn_data.len() as u32); // If we just deserialized an `ExpnData` owned by @@ -1098,6 +1130,9 @@ pub fn decode_expn_id< } hygiene_data.expn_data.push(Some(expn_data)); + hygiene_data.expn_hashes.push(hash); + let _old_id = hygiene_data.expn_hash_to_expn_id.insert(hash, expn_id); + debug_assert!(_old_id.is_none()); let mut expns = outer_expns.lock(); let new_len = index as usize + 1; @@ -1184,7 +1219,7 @@ pub fn decode_syntax_context< Ok(new_ctxt) } -fn for_all_ctxts_in Result<(), E>>( +fn for_all_ctxts_in Result<(), E>>( ctxts: impl Iterator, mut f: F, ) -> Result<(), E> { @@ -1192,20 +1227,26 @@ fn for_all_ctxts_in Res ctxts.map(|ctxt| (ctxt, data.syntax_context_data[ctxt.0 as usize].clone())).collect() }); for (ctxt, data) in all_data.into_iter() { - f((ctxt.0, ctxt, &data))?; + f(ctxt.0, ctxt, &data)?; } Ok(()) } -fn for_all_expns_in Result<(), E>>( +fn for_all_expns_in( expns: impl Iterator, - mut f: F, + mut f: impl FnMut(u32, ExpnId, ExpnData, ExpnHash) -> Result<(), E>, ) -> Result<(), E> { let all_data: Vec<_> = HygieneData::with(|data| { - expns.map(|expn| (expn, data.expn_data[expn.0 as usize].clone())).collect() + expns + .map(|expn| { + let idx = expn.0 as usize; + (expn, data.expn_data[idx].clone(), data.expn_hashes[idx].clone()) + }) + .collect() }); - for (expn, data) in all_data.into_iter() { - f(expn.0, expn, &data.unwrap_or_else(|| panic!("Missing data for {:?}", expn)))?; + for (expn, data, hash) in all_data.into_iter() { + let data = data.unwrap_or_else(|| panic!("Missing data for {:?}", expn)); + f(expn.0, expn, data, hash)?; } Ok(()) } @@ -1306,107 +1347,51 @@ impl Decodable for SyntaxContext { /// `set_expn_data`). It is *not* called for foreign `ExpnId`s deserialized /// from another crate's metadata - since `ExpnData` includes a `krate` field, /// collisions are only possible between `ExpnId`s within the same crate. -fn update_disambiguator(expn_id: ExpnId) { - /// A `HashStableContext` which hashes the raw id values for `DefId` - /// and `CrateNum`, rather than using their computed stable hash. - /// - /// This allows us to use the `HashStable` implementation on `ExpnId` - /// early on in compilation, before we've constructed a `TyCtxt`. - /// The `Fingerprint`s created by this context are not 'stable', since - /// the raw `CrateNum` and `DefId` values for an item may change between - /// sessions due to unrelated changes (e.g. adding/removing an different item). - /// - /// However, this is fine for our purposes - we only need to detect - /// when two `ExpnData`s have the same `Fingerprint`. Since the hashes produced - /// by this context still obey the properties of `HashStable`, we have - /// that - /// `hash_stable(expn1, DummyHashStableContext) == hash_stable(expn2, DummyHashStableContext)` - /// iff `hash_stable(expn1, StableHashingContext) == hash_stable(expn2, StableHasingContext)`. - /// - /// This is sufficient for determining when we need to update the disambiguator. - struct DummyHashStableContext<'a> { - caching_source_map: CachingSourceMapView<'a>, - } - - impl<'a> crate::HashStableContext for DummyHashStableContext<'a> { - #[inline] - fn def_path_hash(&self, def_id: DefId) -> DefPathHash { - DefPathHash(Fingerprint::new( - def_id.krate.as_u32().into(), - def_id.index.as_u32().into(), - )) - } - - fn expn_id_cache() -> &'static LocalKey { - // This cache is only used by `DummyHashStableContext`, - // so we won't pollute the cache values of the normal `StableHashingContext` - thread_local! { - static CACHE: ExpnIdCache = const { ExpnIdCache::new(Vec::new()) }; - } - - &CACHE - } - - fn hash_spans(&self) -> bool { - true - } - fn span_data_to_lines_and_cols( - &mut self, - span: &crate::SpanData, - ) -> Option<(Lrc, usize, BytePos, usize, BytePos)> { - self.caching_source_map.span_data_to_lines_and_cols(span) - } - } - - let source_map = with_session_globals(|session_globals| { - session_globals.source_map.borrow().as_ref().unwrap().clone() - }); - - let mut ctx = - DummyHashStableContext { caching_source_map: CachingSourceMapView::new(&source_map) }; - - let mut hasher = StableHasher::new(); - - let expn_data = expn_id.expn_data(); +fn update_disambiguator(expn_id: ExpnId, mut ctx: impl HashStableContext) { + let mut expn_data = expn_id.expn_data(); // This disambiguator should not have been set yet. assert_eq!( expn_data.disambiguator, 0, "Already set disambiguator for ExpnData: {:?}", expn_data ); - expn_data.hash_stable(&mut ctx, &mut hasher); - let first_hash = hasher.finish(); + let mut expn_hash = expn_data.hash_expn(&mut ctx); - let modified = HygieneData::with(|data| { + let disambiguator = HygieneData::with(|data| { // If this is the first ExpnData with a given hash, then keep our // disambiguator at 0 (the default u32 value) - let disambig = data.expn_data_disambiguators.entry(first_hash).or_default(); - data.expn_data[expn_id.0 as usize].as_mut().unwrap().disambiguator = *disambig; + let disambig = data.expn_data_disambiguators.entry(expn_hash).or_default(); + let disambiguator = *disambig; *disambig += 1; - - *disambig != 1 + disambiguator }); - if modified { - debug!("Set disambiguator for {:?} (hash {:?})", expn_id, first_hash); - debug!("expn_data = {:?}", expn_id.expn_data()); + if disambiguator != 0 { + debug!("Set disambiguator for {:?} (hash {:?})", expn_id, expn_hash); + debug!("expn_data = {:?}", expn_data); + + expn_data.disambiguator = disambiguator; + expn_hash = expn_data.hash_expn(&mut ctx); // Verify that the new disambiguator makes the hash unique #[cfg(debug_assertions)] - { - hasher = StableHasher::new(); - expn_id.expn_data().hash_stable(&mut ctx, &mut hasher); - let new_hash: Fingerprint = hasher.finish(); - - HygieneData::with(|data| { - assert_eq!( - data.expn_data_disambiguators.get(&new_hash), - None, - "Hash collision after disambiguator update!", - ); - }); - }; + HygieneData::with(|data| { + assert_eq!( + data.expn_data_disambiguators.get(&expn_hash), + None, + "Hash collision after disambiguator update!", + ); + }); } + + let expn_hash = ExpnHash(expn_hash); + HygieneData::with(|data| { + data.expn_data[expn_id.0 as usize].as_mut().unwrap().disambiguator = disambiguator; + debug_assert_eq!(data.expn_hashes[expn_id.0 as usize].0, Fingerprint::ZERO); + data.expn_hashes[expn_id.0 as usize] = expn_hash; + let _old_id = data.expn_hash_to_expn_id.insert(expn_hash, expn_id); + debug_assert!(_old_id.is_none()); + }); } impl HashStable for SyntaxContext { @@ -1425,43 +1410,15 @@ impl HashStable for SyntaxContext { } } -pub type ExpnIdCache = RefCell>>; - impl HashStable for ExpnId { fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) { - const TAG_ROOT: u8 = 0; - const TAG_NOT_ROOT: u8 = 1; - - if *self == ExpnId::root() { - TAG_ROOT.hash_stable(ctx, hasher); - return; - } - - // Since the same expansion context is usually referenced many - // times, we cache a stable hash of it and hash that instead of - // recursing every time. - let index = self.as_u32() as usize; - let res = CTX::expn_id_cache().with(|cache| cache.borrow().get(index).copied().flatten()); - - if let Some(res) = res { - res.hash_stable(ctx, hasher); + let hash = if *self == ExpnId::root() { + // Avoid fetching TLS storage for a trivial often-used value. + Fingerprint::ZERO } else { - let new_len = index + 1; - - let mut sub_hasher = StableHasher::new(); - TAG_NOT_ROOT.hash_stable(ctx, &mut sub_hasher); - self.expn_data().hash_stable(ctx, &mut sub_hasher); - let sub_hash: Fingerprint = sub_hasher.finish(); + self.expn_hash().0 + }; - CTX::expn_id_cache().with(|cache| { - let mut cache = cache.borrow_mut(); - if cache.len() < new_len { - cache.resize(new_len, None); - } - let prev = cache[index].replace(sub_hash); - assert_eq!(prev, None, "Cache slot was filled"); - }); - sub_hash.hash_stable(ctx, hasher); - } + hash.hash_stable(ctx, hasher); } } diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs index fa591521dbc8d..7a1ee20ee7951 100644 --- a/compiler/rustc_span/src/lib.rs +++ b/compiler/rustc_span/src/lib.rs @@ -37,8 +37,8 @@ pub mod edition; use edition::Edition; pub mod hygiene; use hygiene::Transparency; -pub use hygiene::{DesugaringKind, ExpnData, ExpnId, ExpnKind, ForLoopLoc, MacroKind}; -pub use hygiene::{ExpnIdCache, SyntaxContext}; +pub use hygiene::{DesugaringKind, ExpnKind, ForLoopLoc, MacroKind}; +pub use hygiene::{ExpnData, ExpnHash, ExpnId, SyntaxContext}; pub mod def_id; use def_id::{CrateNum, DefId, DefPathHash, LOCAL_CRATE}; pub mod lev_distance; @@ -61,7 +61,6 @@ use std::hash::Hash; use std::ops::{Add, Range, Sub}; use std::path::{Path, PathBuf}; use std::str::FromStr; -use std::thread::LocalKey; use md5::Md5; use sha1::Digest; @@ -1956,11 +1955,6 @@ impl InnerSpan { /// instead of implementing everything in rustc_middle. pub trait HashStableContext { fn def_path_hash(&self, def_id: DefId) -> DefPathHash; - /// Obtains a cache for storing the `Fingerprint` of an `ExpnId`. - /// This method allows us to have multiple `HashStableContext` implementations - /// that hash things in a different way, without the results of one polluting - /// the cache of the other. - fn expn_id_cache() -> &'static LocalKey; fn hash_spans(&self) -> bool; fn span_data_to_lines_and_cols( &mut self,