diff --git a/compiler/rustc_ast/src/node_id.rs b/compiler/rustc_ast/src/node_id.rs index d20bace6088..e1e7d757d7e 100644 --- a/compiler/rustc_ast/src/node_id.rs +++ b/compiler/rustc_ast/src/node_id.rs @@ -1,4 +1,4 @@ -use rustc_span::ExpnId; +use rustc_span::{ExpnId, LocalExpnId}; use std::fmt; rustc_index::newtype_index! { @@ -25,11 +25,11 @@ pub const DUMMY_NODE_ID: NodeId = NodeId::MAX; impl NodeId { pub fn placeholder_from_expn_id(expn_id: ExpnId) -> Self { - NodeId::from_u32(expn_id.as_u32()) + NodeId::from_u32(expn_id.expect_local().as_u32()) } pub fn placeholder_to_expn_id(self) -> ExpnId { - ExpnId::from_u32(self.as_u32()) + LocalExpnId::from_u32(self.as_u32()).to_expn_id() } } diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs index e2203fffd45..4b72ac86957 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder.rs @@ -30,9 +30,10 @@ use rustc_middle::ty::codec::TyDecoder; use rustc_middle::ty::{self, Ty, TyCtxt, Visibility}; use rustc_serialize::{opaque, Decodable, Decoder}; use rustc_session::Session; +use rustc_span::hygiene::{ExpnIndex, MacroKind}; use rustc_span::source_map::{respan, Spanned}; use rustc_span::symbol::{sym, Ident, Symbol}; -use rustc_span::{self, hygiene::MacroKind, BytePos, ExpnId, Pos, Span, SyntaxContext, DUMMY_SP}; +use rustc_span::{self, BytePos, ExpnId, Pos, Span, SyntaxContext, DUMMY_SP}; use proc_macro::bridge::client::ProcMacro; use std::io; @@ -348,6 +349,12 @@ impl<'a, 'tcx> Decodable> for DefIndex { } } +impl<'a, 'tcx> Decodable> for ExpnIndex { + fn decode(d: &mut DecodeContext<'a, 'tcx>) -> Result { + Ok(ExpnIndex::from_u32(d.read_u32()?)) + } +} + impl<'a, 'tcx> Decodable> for SyntaxContext { fn decode(decoder: &mut DecodeContext<'a, 'tcx>) -> Result { let cdata = decoder.cdata(); diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs index ce8dfeae076..67023e9e84e 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs @@ -18,11 +18,11 @@ use rustc_middle::ty::query::Providers; use rustc_middle::ty::{self, TyCtxt, Visibility}; use rustc_session::utils::NativeLibKind; use rustc_session::{Session, StableCrateId}; +use rustc_span::hygiene::{ExpnData, ExpnHash, ExpnId}; use rustc_span::source_map::{Span, Spanned}; use rustc_span::symbol::Symbol; use rustc_data_structures::sync::Lrc; -use rustc_span::ExpnId; use smallvec::SmallVec; use std::any::Any; @@ -494,6 +494,23 @@ impl CrateStore for CStore { fn as_any(&self) -> &dyn Any { self } + fn decode_expn_data(&self, sess: &Session, expn_id: ExpnId) -> (ExpnData, ExpnHash) { + let crate_data = self.get_crate_data(expn_id.krate); + ( + crate_data + .root + .expn_data + .get(&crate_data, expn_id.local_id) + .unwrap() + .decode((&crate_data, sess)), + crate_data + .root + .expn_hashes + .get(&crate_data, expn_id.local_id) + .unwrap() + .decode((&crate_data, sess)), + ) + } fn crate_name(&self, cnum: CrateNum) -> Symbol { self.get_crate_data(cnum).root.name diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index ba6d2d74aa7..4684daef4a1 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -31,7 +31,7 @@ use rustc_session::config::CrateType; use rustc_span::symbol::{sym, Ident, Symbol}; use rustc_span::{self, ExternalSource, FileName, SourceFile, Span, SyntaxContext}; use rustc_span::{ - hygiene::{HygieneEncodeContext, MacroKind}, + hygiene::{ExpnIndex, HygieneEncodeContext, MacroKind}, RealFileName, }; use rustc_target::abi::VariantIdx; @@ -168,6 +168,12 @@ impl<'a, 'tcx> Encodable> for DefIndex { } } +impl<'a, 'tcx> Encodable> for ExpnIndex { + fn encode(&self, s: &mut EncodeContext<'a, 'tcx>) -> opaque::EncodeResult { + s.emit_u32(self.as_u32()) + } +} + impl<'a, 'tcx> Encodable> for SyntaxContext { fn encode(&self, s: &mut EncodeContext<'a, 'tcx>) -> opaque::EncodeResult { rustc_span::hygiene::raw_encode_syntax_context(*self, &s.hygiene_ctxt, s) @@ -1588,8 +1594,10 @@ impl EncodeContext<'a, 'tcx> { Ok(()) }, |(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)); + if let Some(index) = index.as_local() { + expn_data_table.set(index.as_raw(), this.lazy(expn_data)); + expn_hash_table.set(index.as_raw(), this.lazy(hash)); + } Ok(()) }, ); diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs index f2ebfb9b725..a487753f462 100644 --- a/compiler/rustc_metadata/src/rmeta/mod.rs +++ b/compiler/rustc_metadata/src/rmeta/mod.rs @@ -19,7 +19,7 @@ use rustc_middle::ty::{self, ReprOptions, Ty}; use rustc_serialize::opaque::Encoder; use rustc_session::config::SymbolManglingVersion; use rustc_span::edition::Edition; -use rustc_span::hygiene::MacroKind; +use rustc_span::hygiene::{ExpnIndex, MacroKind}; use rustc_span::symbol::{Ident, Symbol}; use rustc_span::{self, ExpnData, ExpnHash, ExpnId, Span}; use rustc_target::spec::{PanicStrategy, TargetTriple}; @@ -170,8 +170,8 @@ macro_rules! Lazy { } type SyntaxContextTable = Lazy>>; -type ExpnDataTable = Lazy>>; -type ExpnHashTable = Lazy>>; +type ExpnDataTable = Lazy>>; +type ExpnHashTable = Lazy>>; #[derive(MetadataEncodable, MetadataDecodable)] crate struct ProcMacroData { diff --git a/compiler/rustc_middle/src/middle/cstore.rs b/compiler/rustc_middle/src/middle/cstore.rs index 7efe8e061e8..c10fcc2e90c 100644 --- a/compiler/rustc_middle/src/middle/cstore.rs +++ b/compiler/rustc_middle/src/middle/cstore.rs @@ -6,12 +6,13 @@ use crate::ty::TyCtxt; use rustc_ast as ast; use rustc_data_structures::sync::{self, MetadataRef}; -use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE}; +use rustc_hir::def_id::{CrateNum, DefId, StableCrateId, LOCAL_CRATE}; use rustc_hir::definitions::{DefKey, DefPath, DefPathHash}; use rustc_macros::HashStable; use rustc_session::search_paths::PathKind; use rustc_session::utils::NativeLibKind; -use rustc_session::StableCrateId; +use rustc_session::Session; +use rustc_span::hygiene::{ExpnData, ExpnHash, ExpnId}; use rustc_span::symbol::Symbol; use rustc_span::Span; use rustc_target::spec::Target; @@ -187,6 +188,7 @@ pub type MetadataLoaderDyn = dyn MetadataLoader + Sync; /// during resolve) pub trait CrateStore: std::fmt::Debug { fn as_any(&self) -> &dyn Any; + fn decode_expn_data(&self, sess: &Session, expn_id: ExpnId) -> (ExpnData, ExpnHash); // Foreign definitions. // This information is safe to access, since it's hashed as part of the DefPathHash, which incr. 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 85e84d6a0f4..358d016368e 100644 --- a/compiler/rustc_middle/src/ty/query/on_disk_cache.rs +++ b/compiler/rustc_middle/src/ty/query/on_disk_cache.rs @@ -364,9 +364,12 @@ impl<'sess> OnDiskCache<'sess> { Ok(()) }, |encoder, index, expn_data, hash| -> FileEncodeResult { - let pos = AbsoluteBytePos::new(encoder.position()); - encoder.encode_tagged(TAG_EXPN_DATA, &(expn_data, hash))?; - expn_ids.insert(index, pos); + if index.krate == LOCAL_CRATE { + let pos = AbsoluteBytePos::new(encoder.position()); + encoder.encode_tagged(TAG_EXPN_DATA, &(expn_data, hash))?; + expn_ids.insert(index.local_id.as_u32(), pos); + } + // TODO Handle foreign expansions. Ok(()) }, )?; @@ -807,6 +810,9 @@ impl<'a, 'tcx> Decodable> for ExpnId { Ok(data) }) }, + |this, expn_id| { + Ok(this.tcx.untracked_resolutions.cstore.decode_expn_data(this.tcx.sess, expn_id)) + }, ) } } diff --git a/compiler/rustc_span/src/hygiene.rs b/compiler/rustc_span/src/hygiene.rs index e3174b47f8d..14b68112982 100644 --- a/compiler/rustc_span/src/hygiene.rs +++ b/compiler/rustc_span/src/hygiene.rs @@ -29,12 +29,13 @@ use crate::symbol::{kw, sym, Symbol}; use crate::with_session_globals; use crate::{HashStableContext, Span, DUMMY_SP}; -use crate::def_id::{CrateNum, DefId, CRATE_DEF_INDEX, LOCAL_CRATE}; +use crate::def_id::{CrateNum, DefId, CRATE_DEF_ID, 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_index::vec::IndexVec; use rustc_macros::HashStable_Generic; use rustc_serialize::{Decodable, Decoder, Encodable, Encoder}; use std::fmt; @@ -58,9 +59,34 @@ pub struct SyntaxContextData { dollar_crate_name: Symbol, } +rustc_index::newtype_index! { + /// A unique ID associated with a macro invocation and expansion. + pub struct ExpnIndex { + ENCODABLE = custom + } +} + /// A unique ID associated with a macro invocation and expansion. -#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] -pub struct ExpnId(u32); +#[derive(Clone, Copy, PartialEq, Eq, Hash)] +pub struct ExpnId { + pub krate: CrateNum, + pub local_id: ExpnIndex, +} + +impl fmt::Debug for ExpnId { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // Generate crate_::{{expn_}}. + write!(f, "{:?}::{{{{expn{}}}}}", self.krate, self.local_id.private) + } +} + +rustc_index::newtype_index! { + /// A unique ID associated with a macro invocation and expansion. + pub struct LocalExpnId { + ENCODABLE = custom + DEBUG_FORMAT = "expn{}" + } +} /// A unique hash value associated to an expansion. #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Encodable, Decodable, HashStable_Generic)] @@ -86,31 +112,103 @@ pub enum Transparency { Opaque, } -impl ExpnId { - pub fn fresh_empty() -> Self { +impl LocalExpnId { + /// The ID of the theoretical expansion that generates freshly parsed, unexpanded AST. + pub const ROOT: LocalExpnId = LocalExpnId::from_u32(0); + + pub fn from_raw(idx: ExpnIndex) -> LocalExpnId { + LocalExpnId::from_u32(idx.as_u32()) + } + + pub fn as_raw(self) -> ExpnIndex { + ExpnIndex::from_u32(self.as_u32()) + } + + pub fn fresh_empty() -> LocalExpnId { HygieneData::with(|data| data.fresh_expn(None)) } - pub fn fresh(expn_data: ExpnData, ctx: impl HashStableContext) -> Self { + pub fn fresh(expn_data: ExpnData, ctx: impl HashStableContext) -> LocalExpnId { + debug_assert_eq!(expn_data.parent.krate, LOCAL_CRATE); let expn_id = HygieneData::with(|data| data.fresh_expn(Some(expn_data))); update_disambiguator(expn_id, ctx); expn_id } + #[inline] + pub fn expn_hash(self) -> ExpnHash { + HygieneData::with(|data| data.local_expn_hash(self)) + } + + #[inline] + pub fn expn_data(self) -> ExpnData { + HygieneData::with(|data| data.local_expn_data(self).clone()) + } + + #[inline] + pub fn to_expn_id(self) -> ExpnId { + ExpnId { krate: LOCAL_CRATE, local_id: self.as_raw() } + } + + #[inline] + pub fn set_expn_data(self, mut expn_data: ExpnData, ctx: impl HashStableContext) { + debug_assert_eq!(expn_data.parent.krate, LOCAL_CRATE); + HygieneData::with(|data| { + let old_expn_data = &mut data.local_expn_data[self]; + assert!(old_expn_data.is_none(), "expansion data is reset for an expansion ID"); + assert_eq!(expn_data.orig_id, None); + debug_assert_eq!(expn_data.krate, LOCAL_CRATE); + expn_data.orig_id = Some(self.as_u32()); + *old_expn_data = Some(expn_data); + }); + update_disambiguator(self, ctx) + } + + #[inline] + pub fn is_descendant_of(self, ancestor: LocalExpnId) -> bool { + self.to_expn_id().is_descendant_of(ancestor.to_expn_id()) + } + + /// `expn_id.outer_expn_is_descendant_of(ctxt)` is equivalent to but faster than + /// `expn_id.is_descendant_of(ctxt.outer_expn())`. + #[inline] + pub fn outer_expn_is_descendant_of(self, ctxt: SyntaxContext) -> bool { + self.to_expn_id().outer_expn_is_descendant_of(ctxt) + } + + /// Returns span for the macro which originally caused this expansion to happen. + /// + /// Stops backtracing at include! boundary. + #[inline] + pub fn expansion_cause(self) -> Option { + self.to_expn_id().expansion_cause() + } + + #[inline] + #[track_caller] + pub fn parent(self) -> LocalExpnId { + self.expn_data().parent.as_local().unwrap() + } +} + +impl ExpnId { /// The ID of the theoretical expansion that generates freshly parsed, unexpanded AST. - #[inline] - pub fn root() -> Self { - ExpnId(0) + /// Invariant: we do not create any ExpnId with local_id == 0 and krate != 0. + pub const fn root() -> ExpnId { + ExpnId { krate: LOCAL_CRATE, local_id: ExpnIndex::from_u32(0) } + } + + pub fn fresh_empty() -> ExpnId { + LocalExpnId::fresh_empty().to_expn_id() + } + + pub fn fresh(expn_data: ExpnData, ctx: impl HashStableContext) -> ExpnId { + LocalExpnId::fresh(expn_data, ctx).to_expn_id() } #[inline] - pub fn as_u32(self) -> u32 { - self.0 - } - - #[inline] - pub fn from_u32(raw: u32) -> ExpnId { - ExpnId(raw) + pub fn set_expn_data(self, expn_data: ExpnData, ctx: impl HashStableContext) { + self.expect_local().set_expn_data(expn_data, ctx) } #[inline] @@ -124,20 +222,19 @@ impl ExpnId { } #[inline] - pub fn expn_data(self) -> ExpnData { - HygieneData::with(|data| data.expn_data(self).clone()) + pub fn as_local(self) -> Option { + if self.krate == LOCAL_CRATE { Some(LocalExpnId::from_raw(self.local_id)) } else { None } } #[inline] - 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"); - assert_eq!(expn_data.orig_id, None); - expn_data.orig_id = Some(self.as_u32()); - *old_expn_data = Some(expn_data); - }); - update_disambiguator(self, ctx) + #[track_caller] + pub fn expect_local(self) -> LocalExpnId { + self.as_local().unwrap() + } + + #[inline] + pub fn expn_data(self) -> ExpnData { + HygieneData::with(|data| data.expn_data(self).clone()) } pub fn is_descendant_of(self, ancestor: ExpnId) -> bool { @@ -175,8 +272,10 @@ pub struct HygieneData { /// Each expansion should have an associated expansion data, but sometimes there's a delay /// 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, + local_expn_data: IndexVec>, + local_expn_hashes: IndexVec, + foreign_expn_data: FxHashMap, + foreign_expn_hashes: FxHashMap, expn_hash_to_expn_id: UnhashMap, syntax_context_data: Vec, syntax_context_map: FxHashMap<(SyntaxContext, ExpnId, Transparency), SyntaxContext>, @@ -194,15 +293,17 @@ impl HygieneData { ExpnKind::Root, DUMMY_SP, edition, - Some(DefId::local(CRATE_DEF_INDEX)), + Some(CRATE_DEF_ID.to_def_id()), None, ); root_data.orig_id = Some(0); 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))) + local_expn_data: IndexVec::from_elem_n(Some(root_data), 1), + local_expn_hashes: IndexVec::from_elem_n(ExpnHash(Fingerprint::ZERO), 1), + foreign_expn_data: FxHashMap::default(), + foreign_expn_hashes: FxHashMap::default(), + expn_hash_to_expn_id: std::iter::once((ExpnHash(Fingerprint::ZERO), ExpnId::root())) .collect(), syntax_context_data: vec![SyntaxContextData { outer_expn: ExpnId::root(), @@ -221,24 +322,42 @@ impl HygieneData { with_session_globals(|session_globals| f(&mut *session_globals.hygiene_data.borrow_mut())) } - fn fresh_expn(&mut self, mut expn_data: Option) -> ExpnId { - let raw_id = self.expn_data.len() as u32; + fn fresh_expn(&mut self, mut expn_data: Option) -> LocalExpnId { + let expn_id = self.local_expn_data.next_index(); if let Some(data) = expn_data.as_mut() { + debug_assert_eq!(data.krate, LOCAL_CRATE); assert_eq!(data.orig_id, None); - data.orig_id = Some(raw_id); + data.orig_id = Some(expn_id.as_u32()); } - self.expn_data.push(expn_data); - self.expn_hashes.push(ExpnHash(Fingerprint::ZERO)); - ExpnId(raw_id) + self.local_expn_data.push(expn_data); + let _eid = self.local_expn_hashes.push(ExpnHash(Fingerprint::ZERO)); + debug_assert_eq!(expn_id, _eid); + expn_id + } + + #[inline] + fn local_expn_hash(&self, expn_id: LocalExpnId) -> ExpnHash { + self.local_expn_hashes[expn_id] } #[inline] fn expn_hash(&self, expn_id: ExpnId) -> ExpnHash { - self.expn_hashes[expn_id.0 as usize] + match expn_id.as_local() { + Some(expn_id) => self.local_expn_hashes[expn_id], + None => self.foreign_expn_hashes[&expn_id], + } + } + + fn local_expn_data(&self, expn_id: LocalExpnId) -> &ExpnData { + self.local_expn_data[expn_id].as_ref().expect("no expansion data for an expansion ID") } 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") + if let Some(expn_id) = expn_id.as_local() { + self.local_expn_data[expn_id].as_ref().expect("no expansion data for an expansion ID") + } else { + &self.foreign_expn_data[&expn_id] + } } fn is_descendant_of(&self, mut expn_id: ExpnId, ancestor: ExpnId) -> bool { @@ -453,17 +572,21 @@ pub fn debug_hygiene_data(verbose: bool) -> String { } else { let mut s = String::from(""); s.push_str("Expansions:"); - data.expn_data.iter().enumerate().for_each(|(id, expn_info)| { - let expn_info = expn_info.as_ref().expect("no expansion data for an expansion ID"); + let mut debug_expn_data = |(id, expn_info): (&ExpnId, &ExpnData)| { s.push_str(&format!( - "\n{}: parent: {:?}, call_site_ctxt: {:?}, def_site_ctxt: {:?}, kind: {:?}", + "\n{:?}: parent: {:?}, call_site_ctxt: {:?}, def_site_ctxt: {:?}, kind: {:?}", id, expn_info.parent, expn_info.call_site.ctxt(), expn_info.def_site.ctxt(), expn_info.kind, - )); + )) + }; + data.local_expn_data.iter_enumerated().for_each(|(id, expn_info)| { + let expn_info = expn_info.as_ref().expect("no expansion data for an expansion ID"); + debug_expn_data((&id.to_expn_id(), expn_info)) }); + data.foreign_expn_data.iter().for_each(debug_expn_data); s.push_str("\n\nSyntaxContexts:"); data.syntax_context_data.iter().enumerate().for_each(|(id, ctxt)| { s.push_str(&format!( @@ -1024,7 +1147,7 @@ impl HygieneEncodeContext { &self, encoder: &mut T, mut encode_ctxt: impl FnMut(&mut T, u32, &SyntaxContextData) -> Result<(), R>, - mut encode_expn: impl FnMut(&mut T, u32, ExpnData, ExpnHash) -> Result<(), R>, + mut encode_expn: impl FnMut(&mut T, ExpnId, ExpnData, ExpnHash) -> Result<(), R>, ) -> Result<(), R> { // When we serialize a `SyntaxContextData`, we may end up serializing // a `SyntaxContext` that we haven't seen before @@ -1051,9 +1174,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, hash| { + for_all_expns_in(latest_expns.into_iter(), |expn, data, hash| { if self.serialized_expns.lock().insert(expn) { - encode_expn(encoder, index, data, hash)?; + encode_expn(encoder, expn, data, hash)?; } Ok(()) })?; @@ -1073,56 +1196,75 @@ pub struct HygieneDecodeContext { // `SyntaxContext` remapped_ctxts: Lock>>, // The same as `remapepd_ctxts`, but for `ExpnId`s - remapped_expns: Lock>>, + remapped_expns: Lock>>, } pub fn decode_expn_id_incrcomp( d: &mut D, context: &HygieneDecodeContext, decode_data: impl FnOnce(&mut D, u32) -> Result<(ExpnData, ExpnHash), D::Error>, + decode_foreign: impl FnOnce(&mut D, ExpnId) -> Result<(ExpnData, ExpnHash), D::Error>, ) -> Result { + let krate = CrateNum::decode(d)?; let index = u32::decode(d)?; // Do this after decoding, so that we decode a `CrateNum` // if necessary - if index == ExpnId::root().as_u32() { + if index == 0 { debug!("decode_expn_id: deserialized root"); return Ok(ExpnId::root()); } + if krate != LOCAL_CRATE { + let expn_id = ExpnId { krate, local_id: ExpnIndex::from_u32(index) }; + if HygieneData::with(|hygiene_data| hygiene_data.foreign_expn_data.contains_key(&expn_id)) { + return Ok(expn_id); + } + let (expn_data, hash) = decode_foreign(d, expn_id)?; + debug_assert_eq!(krate, expn_data.krate); + debug_assert_eq!(expn_data.orig_id, Some(index)); + let expn_id = HygieneData::with(|hygiene_data| { + debug_assert_eq!(expn_data.orig_id, Some(index)); + let _old_data = hygiene_data.foreign_expn_data.insert(expn_id, expn_data); + debug_assert!(_old_data.is_none()); + let _old_hash = hygiene_data.foreign_expn_hashes.insert(expn_id, hash); + debug_assert!(_old_hash.is_none()); + let _old_id = hygiene_data.expn_hash_to_expn_id.insert(hash, expn_id); + debug_assert!(_old_id.is_none()); + expn_id + }); + return Ok(expn_id); + } + let outer_expns = &context.remapped_expns; // Ensure that the lock() temporary is dropped early { if let Some(expn_id) = outer_expns.lock().get(index as usize).copied().flatten() { - return Ok(expn_id); + return Ok(expn_id.to_expn_id()); } } // Don't decode the data inside `HygieneData::with`, since we need to recursively decode // other ExpnIds let (mut expn_data, hash) = decode_data(d, index)?; + debug_assert_eq!(krate, expn_data.krate); let expn_id = HygieneData::with(|hygiene_data| { - if let Some(&expn_id) = hygiene_data.expn_hash_to_expn_id.get(&hash) { - return expn_id; + 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 // the local crate, its `orig_id` will be stale, // so we need to update it to its own value. // This only happens when we deserialize the incremental cache, // since a crate will never decode its own metadata. - if expn_data.krate == LOCAL_CRATE { - expn_data.orig_id = Some(expn_id.0); - } - - 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 expn_id = hygiene_data.local_expn_data.next_index(); + expn_data.orig_id = Some(expn_id.as_u32()); + hygiene_data.local_expn_data.push(Some(expn_data)); + let _eid = hygiene_data.local_expn_hashes.push(hash); + debug_assert_eq!(expn_id, _eid); let mut expns = outer_expns.lock(); let new_len = index as usize + 1; @@ -1131,6 +1273,10 @@ pub fn decode_expn_id_incrcomp( } expns[index as usize] = Some(expn_id); drop(expns); + let expn_id = expn_id.to_expn_id(); + + let _old_id = hygiene_data.expn_hash_to_expn_id.insert(hash, expn_id); + debug_assert!(_old_id.is_none()); expn_id }); Ok(expn_id) @@ -1138,39 +1284,42 @@ pub fn decode_expn_id_incrcomp( pub fn decode_expn_id( d: &mut D, - decode_data: impl FnOnce(CrateNum, u32) -> (ExpnData, ExpnHash), + decode_data: impl FnOnce(CrateNum, ExpnIndex) -> (ExpnData, ExpnHash), ) -> Result { - let index = u32::decode(d)?; let krate = CrateNum::decode(d)?; + let index = u32::decode(d)?; // Do this after decoding, so that we decode a `CrateNum` // if necessary - if index == ExpnId::root().as_u32() { + if index == 0 { debug!("decode_expn_id: deserialized root"); return Ok(ExpnId::root()); } + let index = ExpnIndex::from_u32(index); + // This function is used to decode metadata, so it cannot decode information about LOCAL_CRATE. debug_assert_ne!(krate, LOCAL_CRATE); + let expn_id = ExpnId { krate, local_id: index }; + + // Fast path if the expansion has already been decoded. + if HygieneData::with(|hygiene_data| hygiene_data.foreign_expn_data.contains_key(&expn_id)) { + return Ok(expn_id); + } // Don't decode the data inside `HygieneData::with`, since we need to recursively decode // other ExpnIds let (expn_data, hash) = decode_data(krate, index); debug_assert_eq!(krate, expn_data.krate); - debug_assert_eq!(expn_data.orig_id, Some(index)); + debug_assert_eq!(Some(index.as_u32()), expn_data.orig_id); - 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); - hygiene_data.expn_data.push(Some(expn_data)); - hygiene_data.expn_hashes.push(hash); + HygieneData::with(|hygiene_data| { + let _old_data = hygiene_data.foreign_expn_data.insert(expn_id, expn_data); + debug_assert!(_old_data.is_none()); + let _old_hash = hygiene_data.foreign_expn_hashes.insert(expn_id, hash); + debug_assert!(_old_hash.is_none()); let _old_id = hygiene_data.expn_hash_to_expn_id.insert(hash, expn_id); debug_assert!(_old_id.is_none()); - - expn_id }); Ok(expn_id) @@ -1264,29 +1413,37 @@ fn for_all_ctxts_in Resul fn for_all_expns_in( expns: impl Iterator, - mut f: impl FnMut(u32, ExpnId, ExpnData, ExpnHash) -> Result<(), E>, + mut f: impl FnMut(ExpnId, ExpnData, ExpnHash) -> Result<(), E>, ) -> Result<(), E> { let all_data: Vec<_> = HygieneData::with(|data| { expns - .map(|expn| { - let idx = expn.0 as usize; - (expn, data.expn_data[idx].clone(), data.expn_hashes[idx].clone()) - }) + .map(|expn| (expn, data.expn_data(expn).clone(), data.expn_hash(expn).clone())) .collect() }); 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)?; + f(expn, data, hash)?; } Ok(()) } +impl Encodable for LocalExpnId { + fn encode(&self, e: &mut E) -> Result<(), E::Error> { + self.to_expn_id().encode(e) + } +} + impl Encodable for ExpnId { default fn encode(&self, _: &mut E) -> Result<(), E::Error> { panic!("cannot encode `ExpnId` with `{}`", std::any::type_name::()); } } +impl Decodable for LocalExpnId { + fn decode(d: &mut D) -> Result { + ExpnId::decode(d).map(ExpnId::expect_local) + } +} + impl Decodable for ExpnId { default fn decode(_: &mut D) -> Result { panic!("cannot decode `ExpnId` with `{}`", std::any::type_name::()); @@ -1309,12 +1466,12 @@ pub fn raw_encode_expn_id_incrcomp( context: &HygieneEncodeContext, e: &mut E, ) -> Result<(), E::Error> { - // Record the fact that we need to serialize the corresponding - // `ExpnData` + // Record the fact that we need to serialize the corresponding `ExpnData` if !context.serialized_expns.lock().contains(&expn) { context.latest_expns.lock().insert(expn); } - expn.0.encode(e) + expn.krate.encode(e)?; + expn.local_id.as_u32().encode(e) } pub fn raw_encode_expn_id( @@ -1322,21 +1479,19 @@ pub fn raw_encode_expn_id( context: &HygieneEncodeContext, e: &mut E, ) -> Result<(), E::Error> { - let data = expn.expn_data(); // We only need to serialize the ExpnData // if it comes from this crate. // We currently don't serialize any hygiene information data for // proc-macro crates: see the `SpecializedEncoder` impl // for crate metadata. - if data.krate == LOCAL_CRATE { - // Record the fact that we need to serialize the corresponding - // `ExpnData` + // Record the fact that we need to serialize the corresponding `ExpnData` + if expn.krate == LOCAL_CRATE { if !context.serialized_expns.lock().contains(&expn) { context.latest_expns.lock().insert(expn); } } - data.orig_id.expect("Missing orig_id").encode(e)?; - data.krate.encode(e) + expn.krate.encode(e)?; + expn.local_id.as_u32().encode(e) } impl Encodable for SyntaxContext { @@ -1360,7 +1515,7 @@ 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, mut ctx: impl HashStableContext) { +fn update_disambiguator(expn_id: LocalExpnId, mut ctx: impl HashStableContext) { let mut expn_data = expn_id.expn_data(); // This disambiguator should not have been set yet. assert_eq!( @@ -1399,10 +1554,10 @@ fn update_disambiguator(expn_id: ExpnId, mut ctx: impl HashStableContext) { 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); + data.local_expn_data[expn_id].as_mut().unwrap().disambiguator = disambiguator; + debug_assert_eq!(data.local_expn_hashes[expn_id].0, Fingerprint::ZERO); + data.local_expn_hashes[expn_id] = expn_hash; + let _old_id = data.expn_hash_to_expn_id.insert(expn_hash, expn_id.to_expn_id()); debug_assert!(_old_id.is_none()); }); } diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs index 7a1ee20ee79..1c95cc91208 100644 --- a/compiler/rustc_span/src/lib.rs +++ b/compiler/rustc_span/src/lib.rs @@ -38,7 +38,7 @@ use edition::Edition; pub mod hygiene; use hygiene::Transparency; pub use hygiene::{DesugaringKind, ExpnKind, ForLoopLoc, MacroKind}; -pub use hygiene::{ExpnData, ExpnHash, ExpnId, SyntaxContext}; +pub use hygiene::{ExpnData, ExpnHash, ExpnId, LocalExpnId, SyntaxContext}; pub mod def_id; use def_id::{CrateNum, DefId, DefPathHash, LOCAL_CRATE}; pub mod lev_distance;