]>
Commit | Line | Data |
---|---|---|
f035d41b | 1 | //! Machinery for hygienic macros. |
5bcae85e | 2 | //! |
f035d41b XL |
3 | //! Inspired by Matthew Flatt et al., “Macros That Work Together: Compile-Time Bindings, Partial |
4 | //! Expansion, and Definition Contexts,” *Journal of Functional Programming* 22, no. 2 | |
5 | //! (March 1, 2012): 181–216, <https://doi.org/10.1017/S0956796812000093>. | |
5bcae85e | 6 | |
dc9dc135 XL |
7 | // Hygiene data is stored in a global variable and accessed via TLS, which |
8 | // means that accesses are somewhat expensive. (`HygieneData::with` | |
9 | // encapsulates a single access.) Therefore, on hot code paths it is worth | |
10 | // ensuring that multiple HygieneData accesses are combined into a single | |
11 | // `HygieneData::with`. | |
12 | // | |
416331ca | 13 | // This explains why `HygieneData`, `SyntaxContext` and `ExpnId` have interfaces |
dc9dc135 | 14 | // with a certain amount of redundancy in them. For example, |
e1599b0c XL |
15 | // `SyntaxContext::outer_expn_data` combines `SyntaxContext::outer` and |
16 | // `ExpnId::expn_data` so that two `HygieneData` accesses can be performed within | |
dc9dc135 XL |
17 | // a single `HygieneData::with` call. |
18 | // | |
19 | // It also explains why many functions appear in `HygieneData` and again in | |
416331ca | 20 | // `SyntaxContext` or `ExpnId`. For example, `HygieneData::outer` and |
dc9dc135 XL |
21 | // `SyntaxContext::outer` do the same thing, but the former is for use within a |
22 | // `HygieneData::with` call while the latter is for use outside such a call. | |
23 | // When modifying this file it is important to understand this distinction, | |
24 | // because getting it wrong can lead to nested `HygieneData::with` calls that | |
25 | // trigger runtime aborts. (Fortunately these are obvious and easy to fix.) | |
26 | ||
dc9dc135 | 27 | use crate::edition::Edition; |
60c5eb7d | 28 | use crate::symbol::{kw, sym, Symbol}; |
f035d41b | 29 | use crate::SESSION_GLOBALS; |
5869c6ff | 30 | use crate::{BytePos, CachingSourceMapView, ExpnIdCache, SourceFile, Span, DUMMY_SP}; |
cc61c64b | 31 | |
3dfed10e | 32 | use crate::def_id::{CrateNum, DefId, CRATE_DEF_INDEX, LOCAL_CRATE}; |
5869c6ff | 33 | use rustc_data_structures::fingerprint::Fingerprint; |
3dfed10e | 34 | use rustc_data_structures::fx::{FxHashMap, FxHashSet}; |
5869c6ff | 35 | use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; |
3dfed10e | 36 | use rustc_data_structures::sync::{Lock, Lrc}; |
dfeec247 XL |
37 | use rustc_macros::HashStable_Generic; |
38 | use rustc_serialize::{Decodable, Decoder, Encodable, Encoder}; | |
416331ca | 39 | use std::fmt; |
5869c6ff XL |
40 | use std::hash::Hash; |
41 | use std::thread::LocalKey; | |
3dfed10e | 42 | use tracing::*; |
5bcae85e | 43 | |
416331ca XL |
44 | /// A `SyntaxContext` represents a chain of pairs `(ExpnId, Transparency)` named "marks". |
45 | #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] | |
8faf50e0 XL |
46 | pub struct SyntaxContext(u32); |
47 | ||
3dfed10e XL |
48 | #[derive(Debug, Encodable, Decodable, Clone)] |
49 | pub struct SyntaxContextData { | |
416331ca XL |
50 | outer_expn: ExpnId, |
51 | outer_transparency: Transparency, | |
52 | parent: SyntaxContext, | |
53 | /// This context, but with all transparent and semi-transparent expansions filtered away. | |
8faf50e0 | 54 | opaque: SyntaxContext, |
416331ca | 55 | /// This context, but with all transparent expansions filtered away. |
8faf50e0 | 56 | opaque_and_semitransparent: SyntaxContext, |
48663c56 | 57 | /// Name of the crate to which `$crate` with this context would resolve. |
0731742a | 58 | dollar_crate_name: Symbol, |
5bcae85e SL |
59 | } |
60 | ||
416331ca XL |
61 | /// A unique ID associated with a macro invocation and expansion. |
62 | #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] | |
63 | pub struct ExpnId(u32); | |
5bcae85e | 64 | |
8faf50e0 XL |
65 | /// A property of a macro expansion that determines how identifiers |
66 | /// produced by that expansion are resolved. | |
3dfed10e | 67 | #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Hash, Debug, Encodable, Decodable)] |
ba9703b0 | 68 | #[derive(HashStable_Generic)] |
8faf50e0 XL |
69 | pub enum Transparency { |
70 | /// Identifier produced by a transparent expansion is always resolved at call-site. | |
71 | /// Call-site spans in procedural macros, hygiene opt-out in `macro` should use this. | |
72 | Transparent, | |
73 | /// Identifier produced by a semi-transparent expansion may be resolved | |
74 | /// either at call-site or at definition-site. | |
75 | /// If it's a local variable, label or `$crate` then it's resolved at def-site. | |
76 | /// Otherwise it's resolved at call-site. | |
77 | /// `macro_rules` macros behave like this, built-in macros currently behave like this too, | |
78 | /// but that's an implementation detail. | |
79 | SemiTransparent, | |
80 | /// Identifier produced by an opaque expansion is always resolved at definition-site. | |
81 | /// Def-site spans in procedural macros, identifiers from `macro` by default use this. | |
82 | Opaque, | |
ff7c6d11 XL |
83 | } |
84 | ||
416331ca | 85 | impl ExpnId { |
e1599b0c | 86 | pub fn fresh(expn_data: Option<ExpnData>) -> Self { |
5869c6ff XL |
87 | let has_data = expn_data.is_some(); |
88 | let expn_id = HygieneData::with(|data| data.fresh_expn(expn_data)); | |
89 | if has_data { | |
90 | update_disambiguator(expn_id); | |
91 | } | |
92 | expn_id | |
5bcae85e | 93 | } |
9e0c209e | 94 | |
416331ca | 95 | /// The ID of the theoretical expansion that generates freshly parsed, unexpanded AST. |
ff7c6d11 | 96 | #[inline] |
9e0c209e | 97 | pub fn root() -> Self { |
416331ca | 98 | ExpnId(0) |
9e0c209e SL |
99 | } |
100 | ||
ff7c6d11 | 101 | #[inline] |
cc61c64b XL |
102 | pub fn as_u32(self) -> u32 { |
103 | self.0 | |
c30ab7b3 SL |
104 | } |
105 | ||
ff7c6d11 | 106 | #[inline] |
416331ca XL |
107 | pub fn from_u32(raw: u32) -> ExpnId { |
108 | ExpnId(raw) | |
32a655c1 SL |
109 | } |
110 | ||
b7449926 | 111 | #[inline] |
e1599b0c XL |
112 | pub fn expn_data(self) -> ExpnData { |
113 | HygieneData::with(|data| data.expn_data(self).clone()) | |
b7449926 XL |
114 | } |
115 | ||
ff7c6d11 | 116 | #[inline] |
3dfed10e | 117 | pub fn set_expn_data(self, mut expn_data: ExpnData) { |
416331ca | 118 | HygieneData::with(|data| { |
e1599b0c XL |
119 | let old_expn_data = &mut data.expn_data[self.0 as usize]; |
120 | assert!(old_expn_data.is_none(), "expansion data is reset for an expansion ID"); | |
6a06907d XL |
121 | assert_eq!(expn_data.orig_id, None); |
122 | expn_data.orig_id = Some(self.as_u32()); | |
e1599b0c | 123 | *old_expn_data = Some(expn_data); |
5869c6ff XL |
124 | }); |
125 | update_disambiguator(self) | |
7cac9316 XL |
126 | } |
127 | ||
416331ca | 128 | pub fn is_descendant_of(self, ancestor: ExpnId) -> bool { |
dc9dc135 | 129 | HygieneData::with(|data| data.is_descendant_of(self, ancestor)) |
7cac9316 XL |
130 | } |
131 | ||
416331ca XL |
132 | /// `expn_id.outer_expn_is_descendant_of(ctxt)` is equivalent to but faster than |
133 | /// `expn_id.is_descendant_of(ctxt.outer_expn())`. | |
134 | pub fn outer_expn_is_descendant_of(self, ctxt: SyntaxContext) -> bool { | |
135 | HygieneData::with(|data| data.is_descendant_of(self, data.outer_expn(ctxt))) | |
83c7162d | 136 | } |
60c5eb7d XL |
137 | |
138 | /// Returns span for the macro which originally caused this expansion to happen. | |
139 | /// | |
140 | /// Stops backtracing at include! boundary. | |
141 | pub fn expansion_cause(mut self) -> Option<Span> { | |
142 | let mut last_macro = None; | |
143 | loop { | |
144 | let expn_data = self.expn_data(); | |
145 | // Stop going up the backtrace once include! is encountered | |
dfeec247 XL |
146 | if expn_data.is_root() |
147 | || expn_data.kind == ExpnKind::Macro(MacroKind::Bang, sym::include) | |
148 | { | |
60c5eb7d XL |
149 | break; |
150 | } | |
151 | self = expn_data.call_site.ctxt().outer_expn(); | |
152 | last_macro = Some(expn_data.call_site); | |
153 | } | |
154 | last_macro | |
155 | } | |
5bcae85e SL |
156 | } |
157 | ||
8faf50e0 | 158 | #[derive(Debug)] |
3dfed10e | 159 | pub struct HygieneData { |
e1599b0c XL |
160 | /// Each expansion should have an associated expansion data, but sometimes there's a delay |
161 | /// between creation of an expansion ID and obtaining its data (e.g. macros are collected | |
162 | /// first and then resolved later), so we use an `Option` here. | |
163 | expn_data: Vec<Option<ExpnData>>, | |
416331ca XL |
164 | syntax_context_data: Vec<SyntaxContextData>, |
165 | syntax_context_map: FxHashMap<(SyntaxContext, ExpnId, Transparency), SyntaxContext>, | |
5869c6ff XL |
166 | /// Maps the `Fingerprint` of an `ExpnData` to the next disambiguator value. |
167 | /// This is used by `update_disambiguator` to keep track of which `ExpnData`s | |
168 | /// would have collisions without a disambiguator. | |
169 | /// The keys of this map are always computed with `ExpnData.disambiguator` | |
170 | /// set to 0. | |
171 | expn_data_disambiguators: FxHashMap<Fingerprint, u32>, | |
5bcae85e SL |
172 | } |
173 | ||
174 | impl HygieneData { | |
416331ca | 175 | crate fn new(edition: Edition) -> Self { |
3dfed10e XL |
176 | let mut root_data = ExpnData::default( |
177 | ExpnKind::Root, | |
178 | DUMMY_SP, | |
179 | edition, | |
180 | Some(DefId::local(CRATE_DEF_INDEX)), | |
181 | ); | |
182 | root_data.orig_id = Some(0); | |
183 | ||
5bcae85e | 184 | HygieneData { |
3dfed10e | 185 | expn_data: vec![Some(root_data)], |
416331ca XL |
186 | syntax_context_data: vec![SyntaxContextData { |
187 | outer_expn: ExpnId::root(), | |
188 | outer_transparency: Transparency::Opaque, | |
189 | parent: SyntaxContext(0), | |
8faf50e0 XL |
190 | opaque: SyntaxContext(0), |
191 | opaque_and_semitransparent: SyntaxContext(0), | |
dc9dc135 | 192 | dollar_crate_name: kw::DollarCrate, |
ff7c6d11 | 193 | }], |
416331ca | 194 | syntax_context_map: FxHashMap::default(), |
5869c6ff | 195 | expn_data_disambiguators: FxHashMap::default(), |
5bcae85e SL |
196 | } |
197 | } | |
198 | ||
3dfed10e | 199 | pub fn with<T, F: FnOnce(&mut HygieneData) -> T>(f: F) -> T { |
f035d41b | 200 | SESSION_GLOBALS.with(|session_globals| f(&mut *session_globals.hygiene_data.borrow_mut())) |
5bcae85e | 201 | } |
5bcae85e | 202 | |
3dfed10e XL |
203 | fn fresh_expn(&mut self, mut expn_data: Option<ExpnData>) -> ExpnId { |
204 | let raw_id = self.expn_data.len() as u32; | |
205 | if let Some(data) = expn_data.as_mut() { | |
6a06907d XL |
206 | assert_eq!(data.orig_id, None); |
207 | data.orig_id = Some(raw_id); | |
3dfed10e | 208 | } |
e1599b0c | 209 | self.expn_data.push(expn_data); |
3dfed10e | 210 | ExpnId(raw_id) |
dc9dc135 XL |
211 | } |
212 | ||
e1599b0c | 213 | fn expn_data(&self, expn_id: ExpnId) -> &ExpnData { |
dfeec247 | 214 | self.expn_data[expn_id.0 as usize].as_ref().expect("no expansion data for an expansion ID") |
416331ca XL |
215 | } |
216 | ||
217 | fn is_descendant_of(&self, mut expn_id: ExpnId, ancestor: ExpnId) -> bool { | |
218 | while expn_id != ancestor { | |
219 | if expn_id == ExpnId::root() { | |
dc9dc135 XL |
220 | return false; |
221 | } | |
e1599b0c | 222 | expn_id = self.expn_data(expn_id).parent; |
dc9dc135 XL |
223 | } |
224 | true | |
225 | } | |
226 | ||
ba9703b0 | 227 | fn normalize_to_macros_2_0(&self, ctxt: SyntaxContext) -> SyntaxContext { |
416331ca | 228 | self.syntax_context_data[ctxt.0 as usize].opaque |
dc9dc135 XL |
229 | } |
230 | ||
ba9703b0 | 231 | fn normalize_to_macro_rules(&self, ctxt: SyntaxContext) -> SyntaxContext { |
416331ca | 232 | self.syntax_context_data[ctxt.0 as usize].opaque_and_semitransparent |
dc9dc135 XL |
233 | } |
234 | ||
416331ca XL |
235 | fn outer_expn(&self, ctxt: SyntaxContext) -> ExpnId { |
236 | self.syntax_context_data[ctxt.0 as usize].outer_expn | |
dc9dc135 XL |
237 | } |
238 | ||
e1599b0c XL |
239 | fn outer_mark(&self, ctxt: SyntaxContext) -> (ExpnId, Transparency) { |
240 | let data = &self.syntax_context_data[ctxt.0 as usize]; | |
241 | (data.outer_expn, data.outer_transparency) | |
dc9dc135 XL |
242 | } |
243 | ||
416331ca XL |
244 | fn parent_ctxt(&self, ctxt: SyntaxContext) -> SyntaxContext { |
245 | self.syntax_context_data[ctxt.0 as usize].parent | |
dc9dc135 XL |
246 | } |
247 | ||
e1599b0c XL |
248 | fn remove_mark(&self, ctxt: &mut SyntaxContext) -> (ExpnId, Transparency) { |
249 | let outer_mark = self.outer_mark(*ctxt); | |
416331ca | 250 | *ctxt = self.parent_ctxt(*ctxt); |
e1599b0c | 251 | outer_mark |
dc9dc135 XL |
252 | } |
253 | ||
416331ca | 254 | fn marks(&self, mut ctxt: SyntaxContext) -> Vec<(ExpnId, Transparency)> { |
dc9dc135 | 255 | let mut marks = Vec::new(); |
e1599b0c | 256 | while ctxt != SyntaxContext::root() { |
3dfed10e | 257 | debug!("marks: getting parent of {:?}", ctxt); |
e1599b0c | 258 | marks.push(self.outer_mark(ctxt)); |
416331ca | 259 | ctxt = self.parent_ctxt(ctxt); |
dc9dc135 XL |
260 | } |
261 | marks.reverse(); | |
262 | marks | |
263 | } | |
264 | ||
265 | fn walk_chain(&self, mut span: Span, to: SyntaxContext) -> Span { | |
3dfed10e XL |
266 | debug!("walk_chain({:?}, {:?})", span, to); |
267 | debug!("walk_chain: span ctxt = {:?}", span.ctxt()); | |
e1599b0c | 268 | while span.from_expansion() && span.ctxt() != to { |
3dfed10e XL |
269 | let outer_expn = self.outer_expn(span.ctxt()); |
270 | debug!("walk_chain({:?}): outer_expn={:?}", span, outer_expn); | |
271 | let expn_data = self.expn_data(outer_expn); | |
272 | debug!("walk_chain({:?}): expn_data={:?}", span, expn_data); | |
273 | span = expn_data.call_site; | |
dc9dc135 XL |
274 | } |
275 | span | |
276 | } | |
277 | ||
416331ca | 278 | fn adjust(&self, ctxt: &mut SyntaxContext, expn_id: ExpnId) -> Option<ExpnId> { |
dc9dc135 | 279 | let mut scope = None; |
416331ca | 280 | while !self.is_descendant_of(expn_id, self.outer_expn(*ctxt)) { |
e1599b0c | 281 | scope = Some(self.remove_mark(ctxt).0); |
dc9dc135 XL |
282 | } |
283 | scope | |
284 | } | |
285 | ||
e1599b0c | 286 | fn apply_mark( |
dfeec247 XL |
287 | &mut self, |
288 | ctxt: SyntaxContext, | |
289 | expn_id: ExpnId, | |
290 | transparency: Transparency, | |
e1599b0c | 291 | ) -> SyntaxContext { |
416331ca | 292 | assert_ne!(expn_id, ExpnId::root()); |
dc9dc135 | 293 | if transparency == Transparency::Opaque { |
416331ca | 294 | return self.apply_mark_internal(ctxt, expn_id, transparency); |
dc9dc135 XL |
295 | } |
296 | ||
e1599b0c | 297 | let call_site_ctxt = self.expn_data(expn_id).call_site.ctxt(); |
dc9dc135 | 298 | let mut call_site_ctxt = if transparency == Transparency::SemiTransparent { |
ba9703b0 | 299 | self.normalize_to_macros_2_0(call_site_ctxt) |
dc9dc135 | 300 | } else { |
ba9703b0 | 301 | self.normalize_to_macro_rules(call_site_ctxt) |
dc9dc135 XL |
302 | }; |
303 | ||
e1599b0c | 304 | if call_site_ctxt == SyntaxContext::root() { |
416331ca | 305 | return self.apply_mark_internal(ctxt, expn_id, transparency); |
dc9dc135 XL |
306 | } |
307 | ||
416331ca | 308 | // Otherwise, `expn_id` is a macros 1.0 definition and the call site is in a |
dc9dc135 XL |
309 | // macros 2.0 expansion, i.e., a macros 1.0 invocation is in a macros 2.0 definition. |
310 | // | |
311 | // In this case, the tokens from the macros 1.0 definition inherit the hygiene | |
312 | // at their invocation. That is, we pretend that the macros 1.0 definition | |
313 | // was defined at its invocation (i.e., inside the macros 2.0 definition) | |
314 | // so that the macros 2.0 definition remains hygienic. | |
315 | // | |
416331ca XL |
316 | // See the example at `test/ui/hygiene/legacy_interaction.rs`. |
317 | for (expn_id, transparency) in self.marks(ctxt) { | |
318 | call_site_ctxt = self.apply_mark_internal(call_site_ctxt, expn_id, transparency); | |
dc9dc135 | 319 | } |
416331ca | 320 | self.apply_mark_internal(call_site_ctxt, expn_id, transparency) |
dc9dc135 XL |
321 | } |
322 | ||
416331ca | 323 | fn apply_mark_internal( |
dfeec247 XL |
324 | &mut self, |
325 | ctxt: SyntaxContext, | |
326 | expn_id: ExpnId, | |
327 | transparency: Transparency, | |
416331ca XL |
328 | ) -> SyntaxContext { |
329 | let syntax_context_data = &mut self.syntax_context_data; | |
330 | let mut opaque = syntax_context_data[ctxt.0 as usize].opaque; | |
dc9dc135 | 331 | let mut opaque_and_semitransparent = |
416331ca | 332 | syntax_context_data[ctxt.0 as usize].opaque_and_semitransparent; |
dc9dc135 XL |
333 | |
334 | if transparency >= Transparency::Opaque { | |
416331ca | 335 | let parent = opaque; |
dfeec247 XL |
336 | opaque = *self |
337 | .syntax_context_map | |
338 | .entry((parent, expn_id, transparency)) | |
339 | .or_insert_with(|| { | |
340 | let new_opaque = SyntaxContext(syntax_context_data.len() as u32); | |
341 | syntax_context_data.push(SyntaxContextData { | |
342 | outer_expn: expn_id, | |
343 | outer_transparency: transparency, | |
344 | parent, | |
345 | opaque: new_opaque, | |
346 | opaque_and_semitransparent: new_opaque, | |
347 | dollar_crate_name: kw::DollarCrate, | |
348 | }); | |
349 | new_opaque | |
dc9dc135 | 350 | }); |
dc9dc135 XL |
351 | } |
352 | ||
353 | if transparency >= Transparency::SemiTransparent { | |
416331ca | 354 | let parent = opaque_and_semitransparent; |
dfeec247 XL |
355 | opaque_and_semitransparent = *self |
356 | .syntax_context_map | |
357 | .entry((parent, expn_id, transparency)) | |
358 | .or_insert_with(|| { | |
359 | let new_opaque_and_semitransparent = | |
360 | SyntaxContext(syntax_context_data.len() as u32); | |
361 | syntax_context_data.push(SyntaxContextData { | |
362 | outer_expn: expn_id, | |
363 | outer_transparency: transparency, | |
364 | parent, | |
365 | opaque, | |
366 | opaque_and_semitransparent: new_opaque_and_semitransparent, | |
367 | dollar_crate_name: kw::DollarCrate, | |
368 | }); | |
369 | new_opaque_and_semitransparent | |
dc9dc135 | 370 | }); |
dc9dc135 XL |
371 | } |
372 | ||
416331ca XL |
373 | let parent = ctxt; |
374 | *self.syntax_context_map.entry((parent, expn_id, transparency)).or_insert_with(|| { | |
dc9dc135 | 375 | let new_opaque_and_semitransparent_and_transparent = |
416331ca XL |
376 | SyntaxContext(syntax_context_data.len() as u32); |
377 | syntax_context_data.push(SyntaxContextData { | |
378 | outer_expn: expn_id, | |
379 | outer_transparency: transparency, | |
380 | parent, | |
dc9dc135 XL |
381 | opaque, |
382 | opaque_and_semitransparent, | |
383 | dollar_crate_name: kw::DollarCrate, | |
384 | }); | |
385 | new_opaque_and_semitransparent_and_transparent | |
386 | }) | |
387 | } | |
94b46f34 XL |
388 | } |
389 | ||
416331ca XL |
390 | pub fn clear_syntax_context_map() { |
391 | HygieneData::with(|data| data.syntax_context_map = FxHashMap::default()); | |
5bcae85e SL |
392 | } |
393 | ||
dc9dc135 XL |
394 | pub fn walk_chain(span: Span, to: SyntaxContext) -> Span { |
395 | HygieneData::with(|data| data.walk_chain(span, to)) | |
396 | } | |
397 | ||
416331ca XL |
398 | pub fn update_dollar_crate_names(mut get_name: impl FnMut(SyntaxContext) -> Symbol) { |
399 | // The new contexts that need updating are at the end of the list and have `$crate` as a name. | |
dfeec247 XL |
400 | let (len, to_update) = HygieneData::with(|data| { |
401 | ( | |
402 | data.syntax_context_data.len(), | |
403 | data.syntax_context_data | |
404 | .iter() | |
405 | .rev() | |
406 | .take_while(|scdata| scdata.dollar_crate_name == kw::DollarCrate) | |
407 | .count(), | |
408 | ) | |
409 | }); | |
416331ca XL |
410 | // The callback must be called from outside of the `HygieneData` lock, |
411 | // since it will try to acquire it too. | |
dfeec247 | 412 | let range_to_update = len - to_update..len; |
416331ca XL |
413 | let names: Vec<_> = |
414 | range_to_update.clone().map(|idx| get_name(SyntaxContext::from_u32(idx as u32))).collect(); | |
dfeec247 XL |
415 | HygieneData::with(|data| { |
416 | range_to_update.zip(names.into_iter()).for_each(|(idx, name)| { | |
417 | data.syntax_context_data[idx].dollar_crate_name = name; | |
418 | }) | |
419 | }) | |
416331ca XL |
420 | } |
421 | ||
e1599b0c XL |
422 | pub fn debug_hygiene_data(verbose: bool) -> String { |
423 | HygieneData::with(|data| { | |
424 | if verbose { | |
425 | format!("{:#?}", data) | |
426 | } else { | |
427 | let mut s = String::from(""); | |
428 | s.push_str("Expansions:"); | |
429 | data.expn_data.iter().enumerate().for_each(|(id, expn_info)| { | |
430 | let expn_info = expn_info.as_ref().expect("no expansion data for an expansion ID"); | |
431 | s.push_str(&format!( | |
f035d41b | 432 | "\n{}: parent: {:?}, call_site_ctxt: {:?}, def_site_ctxt: {:?}, kind: {:?}", |
e1599b0c XL |
433 | id, |
434 | expn_info.parent, | |
435 | expn_info.call_site.ctxt(), | |
f035d41b | 436 | expn_info.def_site.ctxt(), |
e1599b0c XL |
437 | expn_info.kind, |
438 | )); | |
439 | }); | |
440 | s.push_str("\n\nSyntaxContexts:"); | |
441 | data.syntax_context_data.iter().enumerate().for_each(|(id, ctxt)| { | |
442 | s.push_str(&format!( | |
443 | "\n#{}: parent: {:?}, outer_mark: ({:?}, {:?})", | |
dfeec247 | 444 | id, ctxt.parent, ctxt.outer_expn, ctxt.outer_transparency, |
e1599b0c XL |
445 | )); |
446 | }); | |
447 | s | |
448 | } | |
449 | }) | |
450 | } | |
451 | ||
5bcae85e | 452 | impl SyntaxContext { |
48663c56 | 453 | #[inline] |
e1599b0c | 454 | pub const fn root() -> Self { |
5bcae85e SL |
455 | SyntaxContext(0) |
456 | } | |
457 | ||
48663c56 | 458 | #[inline] |
8faf50e0 XL |
459 | crate fn as_u32(self) -> u32 { |
460 | self.0 | |
461 | } | |
462 | ||
48663c56 | 463 | #[inline] |
8faf50e0 XL |
464 | crate fn from_u32(raw: u32) -> SyntaxContext { |
465 | SyntaxContext(raw) | |
466 | } | |
467 | ||
416331ca | 468 | /// Extend a syntax context with a given expansion and transparency. |
e1599b0c XL |
469 | crate fn apply_mark(self, expn_id: ExpnId, transparency: Transparency) -> SyntaxContext { |
470 | HygieneData::with(|data| data.apply_mark(self, expn_id, transparency)) | |
5bcae85e | 471 | } |
cc61c64b | 472 | |
83c7162d XL |
473 | /// Pulls a single mark off of the syntax context. This effectively moves the |
474 | /// context up one macro definition level. That is, if we have a nested macro | |
475 | /// definition as follows: | |
476 | /// | |
477 | /// ```rust | |
478 | /// macro_rules! f { | |
479 | /// macro_rules! g { | |
480 | /// ... | |
481 | /// } | |
482 | /// } | |
483 | /// ``` | |
484 | /// | |
485 | /// and we have a SyntaxContext that is referring to something declared by an invocation | |
486 | /// of g (call it g1), calling remove_mark will result in the SyntaxContext for the | |
487 | /// invocation of f that created g1. | |
488 | /// Returns the mark that was removed. | |
416331ca | 489 | pub fn remove_mark(&mut self) -> ExpnId { |
e1599b0c | 490 | HygieneData::with(|data| data.remove_mark(self).0) |
7cac9316 XL |
491 | } |
492 | ||
416331ca | 493 | pub fn marks(self) -> Vec<(ExpnId, Transparency)> { |
dc9dc135 | 494 | HygieneData::with(|data| data.marks(self)) |
2c00a5a8 XL |
495 | } |
496 | ||
7cac9316 XL |
497 | /// Adjust this context for resolution in a scope created by the given expansion. |
498 | /// For example, consider the following three resolutions of `f`: | |
ff7c6d11 | 499 | /// |
7cac9316 XL |
500 | /// ```rust |
501 | /// mod foo { pub fn f() {} } // `f`'s `SyntaxContext` is empty. | |
502 | /// m!(f); | |
503 | /// macro m($f:ident) { | |
504 | /// mod bar { | |
416331ca | 505 | /// pub fn f() {} // `f`'s `SyntaxContext` has a single `ExpnId` from `m`. |
7cac9316 XL |
506 | /// pub fn $f() {} // `$f`'s `SyntaxContext` is empty. |
507 | /// } | |
416331ca | 508 | /// foo::f(); // `f`'s `SyntaxContext` has a single `ExpnId` from `m` |
7cac9316 XL |
509 | /// //^ Since `mod foo` is outside this expansion, `adjust` removes the mark from `f`, |
510 | /// //| and it resolves to `::foo::f`. | |
416331ca | 511 | /// bar::f(); // `f`'s `SyntaxContext` has a single `ExpnId` from `m` |
7cac9316 XL |
512 | /// //^ Since `mod bar` not outside this expansion, `adjust` does not change `f`, |
513 | /// //| and it resolves to `::bar::f`. | |
514 | /// bar::$f(); // `f`'s `SyntaxContext` is empty. | |
515 | /// //^ Since `mod bar` is not outside this expansion, `adjust` does not change `$f`, | |
516 | /// //| and it resolves to `::bar::$f`. | |
517 | /// } | |
518 | /// ``` | |
519 | /// This returns the expansion whose definition scope we use to privacy check the resolution, | |
0731742a | 520 | /// or `None` if we privacy check as usual (i.e., not w.r.t. a macro definition scope). |
416331ca XL |
521 | pub fn adjust(&mut self, expn_id: ExpnId) -> Option<ExpnId> { |
522 | HygieneData::with(|data| data.adjust(self, expn_id)) | |
dc9dc135 XL |
523 | } |
524 | ||
ba9703b0 XL |
525 | /// Like `SyntaxContext::adjust`, but also normalizes `self` to macros 2.0. |
526 | pub fn normalize_to_macros_2_0_and_adjust(&mut self, expn_id: ExpnId) -> Option<ExpnId> { | |
dc9dc135 | 527 | HygieneData::with(|data| { |
ba9703b0 | 528 | *self = data.normalize_to_macros_2_0(*self); |
416331ca | 529 | data.adjust(self, expn_id) |
dc9dc135 | 530 | }) |
7cac9316 XL |
531 | } |
532 | ||
533 | /// Adjust this context for resolution in a scope created by the given expansion | |
534 | /// via a glob import with the given `SyntaxContext`. | |
ff7c6d11 XL |
535 | /// For example: |
536 | /// | |
7cac9316 XL |
537 | /// ```rust |
538 | /// m!(f); | |
539 | /// macro m($i:ident) { | |
540 | /// mod foo { | |
416331ca | 541 | /// pub fn f() {} // `f`'s `SyntaxContext` has a single `ExpnId` from `m`. |
7cac9316 XL |
542 | /// pub fn $i() {} // `$i`'s `SyntaxContext` is empty. |
543 | /// } | |
544 | /// n(f); | |
545 | /// macro n($j:ident) { | |
546 | /// use foo::*; | |
547 | /// f(); // `f`'s `SyntaxContext` has a mark from `m` and a mark from `n` | |
548 | /// //^ `glob_adjust` removes the mark from `n`, so this resolves to `foo::f`. | |
549 | /// $i(); // `$i`'s `SyntaxContext` has a mark from `n` | |
550 | /// //^ `glob_adjust` removes the mark from `n`, so this resolves to `foo::$i`. | |
551 | /// $j(); // `$j`'s `SyntaxContext` has a mark from `m` | |
552 | /// //^ This cannot be glob-adjusted, so this is a resolution error. | |
553 | /// } | |
554 | /// } | |
555 | /// ``` | |
556 | /// This returns `None` if the context cannot be glob-adjusted. | |
557 | /// Otherwise, it returns the scope to use when privacy checking (see `adjust` for details). | |
416331ca | 558 | pub fn glob_adjust(&mut self, expn_id: ExpnId, glob_span: Span) -> Option<Option<ExpnId>> { |
dc9dc135 XL |
559 | HygieneData::with(|data| { |
560 | let mut scope = None; | |
ba9703b0 | 561 | let mut glob_ctxt = data.normalize_to_macros_2_0(glob_span.ctxt()); |
416331ca | 562 | while !data.is_descendant_of(expn_id, data.outer_expn(glob_ctxt)) { |
e1599b0c XL |
563 | scope = Some(data.remove_mark(&mut glob_ctxt).0); |
564 | if data.remove_mark(self).0 != scope.unwrap() { | |
dc9dc135 XL |
565 | return None; |
566 | } | |
567 | } | |
416331ca | 568 | if data.adjust(self, expn_id).is_some() { |
7cac9316 XL |
569 | return None; |
570 | } | |
dc9dc135 XL |
571 | Some(scope) |
572 | }) | |
7cac9316 XL |
573 | } |
574 | ||
575 | /// Undo `glob_adjust` if possible: | |
ff7c6d11 | 576 | /// |
7cac9316 XL |
577 | /// ```rust |
578 | /// if let Some(privacy_checking_scope) = self.reverse_glob_adjust(expansion, glob_ctxt) { | |
579 | /// assert!(self.glob_adjust(expansion, glob_ctxt) == Some(privacy_checking_scope)); | |
580 | /// } | |
581 | /// ``` | |
dfeec247 XL |
582 | pub fn reverse_glob_adjust( |
583 | &mut self, | |
584 | expn_id: ExpnId, | |
585 | glob_span: Span, | |
586 | ) -> Option<Option<ExpnId>> { | |
dc9dc135 | 587 | HygieneData::with(|data| { |
416331ca | 588 | if data.adjust(self, expn_id).is_some() { |
dc9dc135 XL |
589 | return None; |
590 | } | |
7cac9316 | 591 | |
ba9703b0 | 592 | let mut glob_ctxt = data.normalize_to_macros_2_0(glob_span.ctxt()); |
dc9dc135 | 593 | let mut marks = Vec::new(); |
416331ca | 594 | while !data.is_descendant_of(expn_id, data.outer_expn(glob_ctxt)) { |
dc9dc135 XL |
595 | marks.push(data.remove_mark(&mut glob_ctxt)); |
596 | } | |
7cac9316 | 597 | |
e1599b0c XL |
598 | let scope = marks.last().map(|mark| mark.0); |
599 | while let Some((expn_id, transparency)) = marks.pop() { | |
600 | *self = data.apply_mark(*self, expn_id, transparency); | |
dc9dc135 XL |
601 | } |
602 | Some(scope) | |
603 | }) | |
604 | } | |
605 | ||
416331ca | 606 | pub fn hygienic_eq(self, other: SyntaxContext, expn_id: ExpnId) -> bool { |
dc9dc135 | 607 | HygieneData::with(|data| { |
ba9703b0 XL |
608 | let mut self_normalized = data.normalize_to_macros_2_0(self); |
609 | data.adjust(&mut self_normalized, expn_id); | |
610 | self_normalized == data.normalize_to_macros_2_0(other) | |
dc9dc135 | 611 | }) |
7cac9316 XL |
612 | } |
613 | ||
ff7c6d11 | 614 | #[inline] |
ba9703b0 XL |
615 | pub fn normalize_to_macros_2_0(self) -> SyntaxContext { |
616 | HygieneData::with(|data| data.normalize_to_macros_2_0(self)) | |
8faf50e0 XL |
617 | } |
618 | ||
619 | #[inline] | |
ba9703b0 XL |
620 | pub fn normalize_to_macro_rules(self) -> SyntaxContext { |
621 | HygieneData::with(|data| data.normalize_to_macro_rules(self)) | |
7cac9316 XL |
622 | } |
623 | ||
ff7c6d11 | 624 | #[inline] |
416331ca XL |
625 | pub fn outer_expn(self) -> ExpnId { |
626 | HygieneData::with(|data| data.outer_expn(self)) | |
dc9dc135 XL |
627 | } |
628 | ||
e1599b0c XL |
629 | /// `ctxt.outer_expn_data()` is equivalent to but faster than |
630 | /// `ctxt.outer_expn().expn_data()`. | |
dc9dc135 | 631 | #[inline] |
e1599b0c XL |
632 | pub fn outer_expn_data(self) -> ExpnData { |
633 | HygieneData::with(|data| data.expn_data(data.outer_expn(self)).clone()) | |
dc9dc135 XL |
634 | } |
635 | ||
3dfed10e XL |
636 | #[inline] |
637 | pub fn outer_mark(self) -> (ExpnId, Transparency) { | |
638 | HygieneData::with(|data| data.outer_mark(self)) | |
639 | } | |
640 | ||
0731742a | 641 | pub fn dollar_crate_name(self) -> Symbol { |
416331ca | 642 | HygieneData::with(|data| data.syntax_context_data[self.0 as usize].dollar_crate_name) |
0731742a | 643 | } |
5869c6ff XL |
644 | |
645 | pub fn edition(self) -> Edition { | |
646 | self.outer_expn_data().edition | |
647 | } | |
5bcae85e SL |
648 | } |
649 | ||
650 | impl fmt::Debug for SyntaxContext { | |
9fa01778 | 651 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
5bcae85e SL |
652 | write!(f, "#{}", self.0) |
653 | } | |
654 | } | |
cc61c64b | 655 | |
416331ca XL |
656 | impl Span { |
657 | /// Creates a fresh expansion with given properties. | |
658 | /// Expansions are normally created by macros, but in some cases expansions are created for | |
659 | /// other compiler-generated code to set per-span properties like allowed unstable features. | |
660 | /// The returned span belongs to the created expansion and has the new properties, | |
661 | /// but its location is inherited from the current span. | |
e1599b0c XL |
662 | pub fn fresh_expansion(self, expn_data: ExpnData) -> Span { |
663 | self.fresh_expansion_with_transparency(expn_data, Transparency::Transparent) | |
664 | } | |
665 | ||
666 | pub fn fresh_expansion_with_transparency( | |
dfeec247 XL |
667 | self, |
668 | expn_data: ExpnData, | |
669 | transparency: Transparency, | |
e1599b0c | 670 | ) -> Span { |
5869c6ff | 671 | let expn_id = ExpnId::fresh(Some(expn_data)); |
416331ca | 672 | HygieneData::with(|data| { |
e1599b0c | 673 | self.with_ctxt(data.apply_mark(SyntaxContext::root(), expn_id, transparency)) |
416331ca XL |
674 | }) |
675 | } | |
5869c6ff XL |
676 | |
677 | /// Reuses the span but adds information like the kind of the desugaring and features that are | |
678 | /// allowed inside this span. | |
679 | pub fn mark_with_reason( | |
680 | self, | |
681 | allow_internal_unstable: Option<Lrc<[Symbol]>>, | |
682 | reason: DesugaringKind, | |
683 | edition: Edition, | |
684 | ) -> Span { | |
685 | self.fresh_expansion(ExpnData { | |
686 | allow_internal_unstable, | |
687 | ..ExpnData::default(ExpnKind::Desugaring(reason), self, edition, None) | |
688 | }) | |
689 | } | |
416331ca XL |
690 | } |
691 | ||
692 | /// A subset of properties from both macro definition and macro call available through global data. | |
693 | /// Avoid using this if you have access to the original definition or call structures. | |
3dfed10e | 694 | #[derive(Clone, Debug, Encodable, Decodable, HashStable_Generic)] |
e1599b0c | 695 | pub struct ExpnData { |
dc9dc135 | 696 | // --- The part unique to each expansion. |
e1599b0c XL |
697 | /// The kind of this expansion - macro or compiler desugaring. |
698 | pub kind: ExpnKind, | |
699 | /// The expansion that produced this expansion. | |
700 | pub parent: ExpnId, | |
cc61c64b XL |
701 | /// The location of the actual macro invocation or syntax sugar , e.g. |
702 | /// `let x = foo!();` or `if let Some(y) = x {}` | |
703 | /// | |
0731742a | 704 | /// This may recursively refer to other macro invocations, e.g., if |
cc61c64b XL |
705 | /// `foo!()` invoked `bar!()` internally, and there was an |
706 | /// expression inside `bar!`; the call_site of the expression in | |
707 | /// the expansion would point to the `bar!` invocation; that | |
e1599b0c | 708 | /// call_site span would have its own ExpnData, with the call_site |
cc61c64b XL |
709 | /// pointing to the `foo!` invocation. |
710 | pub call_site: Span, | |
dc9dc135 XL |
711 | |
712 | // --- The part specific to the macro/desugaring definition. | |
e1599b0c XL |
713 | // --- It may be reasonable to share this part between expansions with the same definition, |
714 | // --- but such sharing is known to bring some minor inconveniences without also bringing | |
715 | // --- noticeable perf improvements (PR #62898). | |
416331ca | 716 | /// The span of the macro definition (possibly dummy). |
8faf50e0 | 717 | /// This span serves only informational purpose and is not used for resolution. |
416331ca | 718 | pub def_site: Span, |
f9f354fc | 719 | /// List of `#[unstable]`/feature-gated features that the macro is allowed to use |
9fa01778 | 720 | /// internally without forcing the whole crate to opt-in |
cc61c64b | 721 | /// to them. |
9fa01778 | 722 | pub allow_internal_unstable: Option<Lrc<[Symbol]>>, |
3b2f2976 XL |
723 | /// Whether the macro is allowed to use `unsafe` internally |
724 | /// even if the user crate has `#![forbid(unsafe_code)]`. | |
725 | pub allow_internal_unsafe: bool, | |
8faf50e0 XL |
726 | /// Enables the macro helper hack (`ident!(...)` -> `$crate::ident!(...)`) |
727 | /// for a given macro. | |
728 | pub local_inner_macros: bool, | |
94b46f34 XL |
729 | /// Edition of the crate in which the macro is defined. |
730 | pub edition: Edition, | |
f9f354fc XL |
731 | /// The `DefId` of the macro being invoked, |
732 | /// if this `ExpnData` corresponds to a macro invocation | |
733 | pub macro_def_id: Option<DefId>, | |
1b1a35ee | 734 | /// The crate that originally created this `ExpnData`. During |
3dfed10e XL |
735 | /// metadata serialization, we only encode `ExpnData`s that were |
736 | /// created locally - when our serialized metadata is decoded, | |
737 | /// foreign `ExpnId`s will have their `ExpnData` looked up | |
738 | /// from the crate specified by `Crate | |
5869c6ff | 739 | krate: CrateNum, |
3dfed10e XL |
740 | /// The raw that this `ExpnData` had in its original crate. |
741 | /// An `ExpnData` can be created before being assigned an `ExpnId`, | |
742 | /// so this might be `None` until `set_expn_data` is called | |
743 | // This is used only for serialization/deserialization purposes: | |
744 | // two `ExpnData`s that differ only in their `orig_id` should | |
745 | // be considered equivalent. | |
746 | #[stable_hasher(ignore)] | |
5869c6ff XL |
747 | orig_id: Option<u32>, |
748 | ||
749 | /// Used to force two `ExpnData`s to have different `Fingerprint`s. | |
750 | /// Due to macro expansion, it's possible to end up with two `ExpnId`s | |
751 | /// that have identical `ExpnData`s. This violates the constract of `HashStable` | |
752 | /// - the two `ExpnId`s are not equal, but their `Fingerprint`s are equal | |
753 | /// (since the numerical `ExpnId` value is not considered by the `HashStable` | |
754 | /// implementation). | |
755 | /// | |
756 | /// The `disambiguator` field is set by `update_disambiguator` when two distinct | |
757 | /// `ExpnId`s would end up with the same `Fingerprint`. Since `ExpnData` includes | |
758 | /// a `krate` field, this value only needs to be unique within a single crate. | |
759 | disambiguator: u32, | |
cc61c64b XL |
760 | } |
761 | ||
5869c6ff | 762 | // These would require special handling of `orig_id`. |
3dfed10e | 763 | impl !PartialEq for ExpnData {} |
5869c6ff | 764 | impl !Hash for ExpnData {} |
3dfed10e | 765 | |
e1599b0c | 766 | impl ExpnData { |
5869c6ff XL |
767 | pub fn new( |
768 | kind: ExpnKind, | |
769 | parent: ExpnId, | |
770 | call_site: Span, | |
771 | def_site: Span, | |
772 | allow_internal_unstable: Option<Lrc<[Symbol]>>, | |
773 | allow_internal_unsafe: bool, | |
774 | local_inner_macros: bool, | |
775 | edition: Edition, | |
776 | macro_def_id: Option<DefId>, | |
777 | ) -> ExpnData { | |
778 | ExpnData { | |
779 | kind, | |
780 | parent, | |
781 | call_site, | |
782 | def_site, | |
783 | allow_internal_unstable, | |
784 | allow_internal_unsafe, | |
785 | local_inner_macros, | |
786 | edition, | |
787 | macro_def_id, | |
788 | krate: LOCAL_CRATE, | |
789 | orig_id: None, | |
790 | disambiguator: 0, | |
791 | } | |
792 | } | |
793 | ||
e1599b0c | 794 | /// Constructs expansion data with default properties. |
f9f354fc XL |
795 | pub fn default( |
796 | kind: ExpnKind, | |
797 | call_site: Span, | |
798 | edition: Edition, | |
799 | macro_def_id: Option<DefId>, | |
800 | ) -> ExpnData { | |
e1599b0c | 801 | ExpnData { |
416331ca | 802 | kind, |
e1599b0c XL |
803 | parent: ExpnId::root(), |
804 | call_site, | |
416331ca | 805 | def_site: DUMMY_SP, |
dc9dc135 XL |
806 | allow_internal_unstable: None, |
807 | allow_internal_unsafe: false, | |
808 | local_inner_macros: false, | |
809 | edition, | |
f9f354fc | 810 | macro_def_id, |
3dfed10e XL |
811 | krate: LOCAL_CRATE, |
812 | orig_id: None, | |
5869c6ff | 813 | disambiguator: 0, |
dc9dc135 XL |
814 | } |
815 | } | |
816 | ||
dfeec247 XL |
817 | pub fn allow_unstable( |
818 | kind: ExpnKind, | |
819 | call_site: Span, | |
820 | edition: Edition, | |
821 | allow_internal_unstable: Lrc<[Symbol]>, | |
f9f354fc | 822 | macro_def_id: Option<DefId>, |
dfeec247 | 823 | ) -> ExpnData { |
e1599b0c | 824 | ExpnData { |
416331ca | 825 | allow_internal_unstable: Some(allow_internal_unstable), |
f9f354fc | 826 | ..ExpnData::default(kind, call_site, edition, macro_def_id) |
dc9dc135 XL |
827 | } |
828 | } | |
e1599b0c XL |
829 | |
830 | #[inline] | |
831 | pub fn is_root(&self) -> bool { | |
1b1a35ee | 832 | matches!(self.kind, ExpnKind::Root) |
e1599b0c | 833 | } |
dc9dc135 XL |
834 | } |
835 | ||
416331ca | 836 | /// Expansion kind. |
3dfed10e | 837 | #[derive(Clone, Debug, PartialEq, Encodable, Decodable, HashStable_Generic)] |
416331ca XL |
838 | pub enum ExpnKind { |
839 | /// No expansion, aka root expansion. Only `ExpnId::root()` has this kind. | |
840 | Root, | |
841 | /// Expansion produced by a macro. | |
416331ca | 842 | Macro(MacroKind, Symbol), |
e1599b0c XL |
843 | /// Transform done by the compiler on the AST. |
844 | AstPass(AstPass), | |
cc61c64b | 845 | /// Desugaring done by the compiler during HIR lowering. |
dfeec247 | 846 | Desugaring(DesugaringKind), |
29967ef6 XL |
847 | /// MIR inlining |
848 | Inlined, | |
3b2f2976 XL |
849 | } |
850 | ||
416331ca | 851 | impl ExpnKind { |
dfeec247 | 852 | pub fn descr(&self) -> String { |
8faf50e0 | 853 | match *self { |
dfeec247 XL |
854 | ExpnKind::Root => kw::PathRoot.to_string(), |
855 | ExpnKind::Macro(macro_kind, name) => match macro_kind { | |
856 | MacroKind::Bang => format!("{}!", name), | |
857 | MacroKind::Attr => format!("#[{}]", name), | |
858 | MacroKind::Derive => format!("#[derive({})]", name), | |
859 | }, | |
860 | ExpnKind::AstPass(kind) => kind.descr().to_string(), | |
861 | ExpnKind::Desugaring(kind) => format!("desugaring of {}", kind.descr()), | |
29967ef6 | 862 | ExpnKind::Inlined => "inlined source".to_string(), |
416331ca XL |
863 | } |
864 | } | |
865 | } | |
866 | ||
867 | /// The kind of macro invocation or definition. | |
3dfed10e | 868 | #[derive(Clone, Copy, PartialEq, Eq, Encodable, Decodable, Hash, Debug)] |
ba9703b0 | 869 | #[derive(HashStable_Generic)] |
416331ca XL |
870 | pub enum MacroKind { |
871 | /// A bang macro `foo!()`. | |
872 | Bang, | |
873 | /// An attribute macro `#[foo]`. | |
874 | Attr, | |
875 | /// A derive macro `#[derive(Foo)]` | |
876 | Derive, | |
877 | } | |
878 | ||
879 | impl MacroKind { | |
880 | pub fn descr(self) -> &'static str { | |
881 | match self { | |
882 | MacroKind::Bang => "macro", | |
883 | MacroKind::Attr => "attribute macro", | |
884 | MacroKind::Derive => "derive macro", | |
885 | } | |
886 | } | |
887 | ||
e1599b0c XL |
888 | pub fn descr_expected(self) -> &'static str { |
889 | match self { | |
890 | MacroKind::Attr => "attribute", | |
891 | _ => self.descr(), | |
892 | } | |
893 | } | |
894 | ||
416331ca XL |
895 | pub fn article(self) -> &'static str { |
896 | match self { | |
897 | MacroKind::Attr => "an", | |
898 | _ => "a", | |
8faf50e0 XL |
899 | } |
900 | } | |
901 | } | |
902 | ||
e1599b0c | 903 | /// The kind of AST transform. |
3dfed10e | 904 | #[derive(Clone, Copy, Debug, PartialEq, Encodable, Decodable, HashStable_Generic)] |
e1599b0c XL |
905 | pub enum AstPass { |
906 | StdImports, | |
907 | TestHarness, | |
908 | ProcMacroHarness, | |
e1599b0c XL |
909 | } |
910 | ||
911 | impl AstPass { | |
912 | fn descr(self) -> &'static str { | |
913 | match self { | |
914 | AstPass::StdImports => "standard library imports", | |
915 | AstPass::TestHarness => "test harness", | |
916 | AstPass::ProcMacroHarness => "proc macro harness", | |
e1599b0c XL |
917 | } |
918 | } | |
919 | } | |
920 | ||
3b2f2976 | 921 | /// The kind of compiler desugaring. |
3dfed10e | 922 | #[derive(Clone, Copy, PartialEq, Debug, Encodable, Decodable, HashStable_Generic)] |
416331ca | 923 | pub enum DesugaringKind { |
48663c56 XL |
924 | /// We desugar `if c { i } else { e }` to `match $ExprKind::Use(c) { true => i, _ => e }`. |
925 | /// However, we do not want to blame `c` for unreachability but rather say that `i` | |
926 | /// is unreachable. This desugaring kind allows us to avoid blaming `c`. | |
416331ca XL |
927 | /// This also applies to `while` loops. |
928 | CondTemporary, | |
3b2f2976 | 929 | QuestionMark, |
b7449926 | 930 | TryBlock, |
94b46f34 | 931 | /// Desugaring of an `impl Trait` in return type position |
416331ca | 932 | /// to an `type Foo = impl Trait;` and replacing the |
94b46f34 | 933 | /// `impl Trait` with `Foo`. |
416331ca | 934 | OpaqueTy, |
8faf50e0 | 935 | Async, |
48663c56 | 936 | Await, |
f035d41b XL |
937 | ForLoop(ForLoopLoc), |
938 | } | |
939 | ||
940 | /// A location in the desugaring of a `for` loop | |
3dfed10e | 941 | #[derive(Clone, Copy, PartialEq, Debug, Encodable, Decodable, HashStable_Generic)] |
f035d41b XL |
942 | pub enum ForLoopLoc { |
943 | Head, | |
944 | IntoIter, | |
3b2f2976 XL |
945 | } |
946 | ||
416331ca XL |
947 | impl DesugaringKind { |
948 | /// The description wording should combine well with "desugaring of {}". | |
949 | fn descr(self) -> &'static str { | |
950 | match self { | |
951 | DesugaringKind::CondTemporary => "`if` or `while` condition", | |
952 | DesugaringKind::Async => "`async` block or function", | |
953 | DesugaringKind::Await => "`await` expression", | |
954 | DesugaringKind::QuestionMark => "operator `?`", | |
955 | DesugaringKind::TryBlock => "`try` block", | |
956 | DesugaringKind::OpaqueTy => "`impl Trait`", | |
f035d41b | 957 | DesugaringKind::ForLoop(_) => "`for` loop", |
416331ca | 958 | } |
3b2f2976 | 959 | } |
cc61c64b XL |
960 | } |
961 | ||
3dfed10e XL |
962 | #[derive(Default)] |
963 | pub struct HygieneEncodeContext { | |
964 | /// All `SyntaxContexts` for which we have written `SyntaxContextData` into crate metadata. | |
965 | /// This is `None` after we finish encoding `SyntaxContexts`, to ensure | |
966 | /// that we don't accidentally try to encode any more `SyntaxContexts` | |
967 | serialized_ctxts: Lock<FxHashSet<SyntaxContext>>, | |
968 | /// The `SyntaxContexts` that we have serialized (e.g. as a result of encoding `Spans`) | |
969 | /// in the most recent 'round' of serializnig. Serializing `SyntaxContextData` | |
970 | /// may cause us to serialize more `SyntaxContext`s, so serialize in a loop | |
971 | /// until we reach a fixed point. | |
972 | latest_ctxts: Lock<FxHashSet<SyntaxContext>>, | |
973 | ||
974 | serialized_expns: Lock<FxHashSet<ExpnId>>, | |
975 | ||
976 | latest_expns: Lock<FxHashSet<ExpnId>>, | |
977 | } | |
978 | ||
979 | impl HygieneEncodeContext { | |
980 | pub fn encode< | |
981 | T, | |
982 | R, | |
983 | F: FnMut(&mut T, u32, &SyntaxContextData) -> Result<(), R>, | |
984 | G: FnMut(&mut T, u32, &ExpnData) -> Result<(), R>, | |
985 | >( | |
986 | &self, | |
987 | encoder: &mut T, | |
988 | mut encode_ctxt: F, | |
989 | mut encode_expn: G, | |
990 | ) -> Result<(), R> { | |
991 | // When we serialize a `SyntaxContextData`, we may end up serializing | |
992 | // a `SyntaxContext` that we haven't seen before | |
993 | while !self.latest_ctxts.lock().is_empty() || !self.latest_expns.lock().is_empty() { | |
994 | debug!( | |
995 | "encode_hygiene: Serializing a round of {:?} SyntaxContextDatas: {:?}", | |
996 | self.latest_ctxts.lock().len(), | |
997 | self.latest_ctxts | |
998 | ); | |
999 | ||
1000 | // Consume the current round of SyntaxContexts. | |
1001 | // Drop the lock() temporary early | |
1002 | let latest_ctxts = { std::mem::take(&mut *self.latest_ctxts.lock()) }; | |
1003 | ||
1004 | // It's fine to iterate over a HashMap, because the serialization | |
1005 | // of the table that we insert data into doesn't depend on insertion | |
1006 | // order | |
1007 | for_all_ctxts_in(latest_ctxts.into_iter(), |(index, ctxt, data)| { | |
1008 | if self.serialized_ctxts.lock().insert(ctxt) { | |
1009 | encode_ctxt(encoder, index, data)?; | |
1010 | } | |
1011 | Ok(()) | |
1012 | })?; | |
1013 | ||
1014 | let latest_expns = { std::mem::take(&mut *self.latest_expns.lock()) }; | |
1015 | ||
1016 | for_all_expns_in(latest_expns.into_iter(), |index, expn, data| { | |
1017 | if self.serialized_expns.lock().insert(expn) { | |
1018 | encode_expn(encoder, index, data)?; | |
1019 | } | |
1020 | Ok(()) | |
1021 | })?; | |
1022 | } | |
1023 | debug!("encode_hygiene: Done serializing SyntaxContextData"); | |
1024 | Ok(()) | |
1025 | } | |
1026 | } | |
1027 | ||
1028 | #[derive(Default)] | |
1029 | /// Additional information used to assist in decoding hygiene data | |
1030 | pub struct HygieneDecodeContext { | |
1031 | // Maps serialized `SyntaxContext` ids to a `SyntaxContext` in the current | |
1032 | // global `HygieneData`. When we deserialize a `SyntaxContext`, we need to create | |
1033 | // a new id in the global `HygieneData`. This map tracks the ID we end up picking, | |
1034 | // so that multiple occurrences of the same serialized id are decoded to the same | |
1035 | // `SyntaxContext` | |
1036 | remapped_ctxts: Lock<Vec<Option<SyntaxContext>>>, | |
1037 | // The same as `remapepd_ctxts`, but for `ExpnId`s | |
1038 | remapped_expns: Lock<Vec<Option<ExpnId>>>, | |
1039 | } | |
1040 | ||
1041 | pub fn decode_expn_id< | |
1042 | 'a, | |
1043 | D: Decoder, | |
1044 | F: FnOnce(&mut D, u32) -> Result<ExpnData, D::Error>, | |
1045 | G: FnOnce(CrateNum) -> &'a HygieneDecodeContext, | |
1046 | >( | |
1047 | d: &mut D, | |
1048 | mode: ExpnDataDecodeMode<'a, G>, | |
1049 | decode_data: F, | |
1050 | ) -> Result<ExpnId, D::Error> { | |
1051 | let index = u32::decode(d)?; | |
1052 | let context = match mode { | |
1053 | ExpnDataDecodeMode::IncrComp(context) => context, | |
1054 | ExpnDataDecodeMode::Metadata(get_context) => { | |
1055 | let krate = CrateNum::decode(d)?; | |
1056 | get_context(krate) | |
1057 | } | |
1058 | }; | |
1059 | ||
1060 | // Do this after decoding, so that we decode a `CrateNum` | |
1061 | // if necessary | |
1062 | if index == ExpnId::root().as_u32() { | |
1063 | debug!("decode_expn_id: deserialized root"); | |
1064 | return Ok(ExpnId::root()); | |
1065 | } | |
1066 | ||
1067 | let outer_expns = &context.remapped_expns; | |
1068 | ||
1069 | // Ensure that the lock() temporary is dropped early | |
1070 | { | |
1071 | if let Some(expn_id) = outer_expns.lock().get(index as usize).copied().flatten() { | |
1072 | return Ok(expn_id); | |
1073 | } | |
1074 | } | |
1075 | ||
1076 | // Don't decode the data inside `HygieneData::with`, since we need to recursively decode | |
1077 | // other ExpnIds | |
1078 | let mut expn_data = decode_data(d, index)?; | |
1079 | ||
1080 | let expn_id = HygieneData::with(|hygiene_data| { | |
1081 | let expn_id = ExpnId(hygiene_data.expn_data.len() as u32); | |
1082 | ||
1083 | // If we just deserialized an `ExpnData` owned by | |
1084 | // the local crate, its `orig_id` will be stale, | |
1085 | // so we need to update it to its own value. | |
1086 | // This only happens when we deserialize the incremental cache, | |
1087 | // since a crate will never decode its own metadata. | |
1088 | if expn_data.krate == LOCAL_CRATE { | |
1089 | expn_data.orig_id = Some(expn_id.0); | |
1090 | } | |
1091 | ||
1092 | hygiene_data.expn_data.push(Some(expn_data)); | |
1093 | ||
1094 | let mut expns = outer_expns.lock(); | |
1095 | let new_len = index as usize + 1; | |
1096 | if expns.len() < new_len { | |
1097 | expns.resize(new_len, None); | |
1098 | } | |
1099 | expns[index as usize] = Some(expn_id); | |
1100 | drop(expns); | |
1101 | expn_id | |
1102 | }); | |
1103 | Ok(expn_id) | |
1104 | } | |
1105 | ||
1106 | // Decodes `SyntaxContext`, using the provided `HygieneDecodeContext` | |
1107 | // to track which `SyntaxContext`s we have already decoded. | |
1108 | // The provided closure will be invoked to deserialize a `SyntaxContextData` | |
1109 | // if we haven't already seen the id of the `SyntaxContext` we are deserializing. | |
1110 | pub fn decode_syntax_context< | |
1111 | D: Decoder, | |
1112 | F: FnOnce(&mut D, u32) -> Result<SyntaxContextData, D::Error>, | |
1113 | >( | |
1114 | d: &mut D, | |
1115 | context: &HygieneDecodeContext, | |
1116 | decode_data: F, | |
1117 | ) -> Result<SyntaxContext, D::Error> { | |
1118 | let raw_id: u32 = Decodable::decode(d)?; | |
1119 | if raw_id == 0 { | |
1120 | debug!("decode_syntax_context: deserialized root"); | |
1121 | // The root is special | |
1122 | return Ok(SyntaxContext::root()); | |
1123 | } | |
1124 | ||
1125 | let outer_ctxts = &context.remapped_ctxts; | |
1126 | ||
1127 | // Ensure that the lock() temporary is dropped early | |
1128 | { | |
1129 | if let Some(ctxt) = outer_ctxts.lock().get(raw_id as usize).copied().flatten() { | |
1130 | return Ok(ctxt); | |
1131 | } | |
1132 | } | |
1133 | ||
1134 | // Allocate and store SyntaxContext id *before* calling the decoder function, | |
1135 | // as the SyntaxContextData may reference itself. | |
1136 | let new_ctxt = HygieneData::with(|hygiene_data| { | |
1137 | let new_ctxt = SyntaxContext(hygiene_data.syntax_context_data.len() as u32); | |
1138 | // Push a dummy SyntaxContextData to ensure that nobody else can get the | |
1139 | // same ID as us. This will be overwritten after call `decode_Data` | |
1140 | hygiene_data.syntax_context_data.push(SyntaxContextData { | |
1141 | outer_expn: ExpnId::root(), | |
1142 | outer_transparency: Transparency::Transparent, | |
1143 | parent: SyntaxContext::root(), | |
1144 | opaque: SyntaxContext::root(), | |
1145 | opaque_and_semitransparent: SyntaxContext::root(), | |
5869c6ff | 1146 | dollar_crate_name: kw::Empty, |
3dfed10e XL |
1147 | }); |
1148 | let mut ctxts = outer_ctxts.lock(); | |
1149 | let new_len = raw_id as usize + 1; | |
1150 | if ctxts.len() < new_len { | |
1151 | ctxts.resize(new_len, None); | |
1152 | } | |
1153 | ctxts[raw_id as usize] = Some(new_ctxt); | |
1154 | drop(ctxts); | |
1155 | new_ctxt | |
1156 | }); | |
1157 | ||
1158 | // Don't try to decode data while holding the lock, since we need to | |
1159 | // be able to recursively decode a SyntaxContext | |
1160 | let mut ctxt_data = decode_data(d, raw_id)?; | |
1161 | // Reset `dollar_crate_name` so that it will be updated by `update_dollar_crate_names` | |
1162 | // We don't care what the encoding crate set this to - we want to resolve it | |
1163 | // from the perspective of the current compilation session | |
1164 | ctxt_data.dollar_crate_name = kw::DollarCrate; | |
1165 | ||
1166 | // Overwrite the dummy data with our decoded SyntaxContextData | |
1167 | HygieneData::with(|hygiene_data| { | |
1168 | let dummy = std::mem::replace( | |
1169 | &mut hygiene_data.syntax_context_data[new_ctxt.as_u32() as usize], | |
1170 | ctxt_data, | |
1171 | ); | |
1172 | // Make sure nothing weird happening while `decode_data` was running | |
5869c6ff | 1173 | assert_eq!(dummy.dollar_crate_name, kw::Empty); |
3dfed10e XL |
1174 | }); |
1175 | ||
1176 | Ok(new_ctxt) | |
1177 | } | |
1178 | ||
1179 | pub fn num_syntax_ctxts() -> usize { | |
1180 | HygieneData::with(|data| data.syntax_context_data.len()) | |
1181 | } | |
1182 | ||
1183 | pub fn for_all_ctxts_in<E, F: FnMut((u32, SyntaxContext, &SyntaxContextData)) -> Result<(), E>>( | |
1184 | ctxts: impl Iterator<Item = SyntaxContext>, | |
1185 | mut f: F, | |
1186 | ) -> Result<(), E> { | |
1187 | let all_data: Vec<_> = HygieneData::with(|data| { | |
1188 | ctxts.map(|ctxt| (ctxt, data.syntax_context_data[ctxt.0 as usize].clone())).collect() | |
1189 | }); | |
1190 | for (ctxt, data) in all_data.into_iter() { | |
1191 | f((ctxt.0, ctxt, &data))?; | |
1192 | } | |
1193 | Ok(()) | |
1194 | } | |
1195 | ||
1196 | pub fn for_all_expns_in<E, F: FnMut(u32, ExpnId, &ExpnData) -> Result<(), E>>( | |
1197 | expns: impl Iterator<Item = ExpnId>, | |
1198 | mut f: F, | |
1199 | ) -> Result<(), E> { | |
1200 | let all_data: Vec<_> = HygieneData::with(|data| { | |
1201 | expns.map(|expn| (expn, data.expn_data[expn.0 as usize].clone())).collect() | |
1202 | }); | |
1203 | for (expn, data) in all_data.into_iter() { | |
1204 | f(expn.0, expn, &data.unwrap_or_else(|| panic!("Missing data for {:?}", expn)))?; | |
1205 | } | |
1206 | Ok(()) | |
1207 | } | |
1208 | ||
1209 | pub fn for_all_data<E, F: FnMut((u32, SyntaxContext, &SyntaxContextData)) -> Result<(), E>>( | |
1210 | mut f: F, | |
1211 | ) -> Result<(), E> { | |
1212 | let all_data = HygieneData::with(|data| data.syntax_context_data.clone()); | |
1213 | for (i, data) in all_data.into_iter().enumerate() { | |
1214 | f((i as u32, SyntaxContext(i as u32), &data))?; | |
1215 | } | |
1216 | Ok(()) | |
1217 | } | |
1218 | ||
1219 | impl<E: Encoder> Encodable<E> for ExpnId { | |
1220 | default fn encode(&self, _: &mut E) -> Result<(), E::Error> { | |
1221 | panic!("cannot encode `ExpnId` with `{}`", std::any::type_name::<E>()); | |
1222 | } | |
1223 | } | |
1224 | ||
1225 | impl<D: Decoder> Decodable<D> for ExpnId { | |
1226 | default fn decode(_: &mut D) -> Result<Self, D::Error> { | |
1227 | panic!("cannot decode `ExpnId` with `{}`", std::any::type_name::<D>()); | |
1228 | } | |
1229 | } | |
1230 | ||
1231 | pub fn for_all_expn_data<E, F: FnMut(u32, &ExpnData) -> Result<(), E>>(mut f: F) -> Result<(), E> { | |
1232 | let all_data = HygieneData::with(|data| data.expn_data.clone()); | |
1233 | for (i, data) in all_data.into_iter().enumerate() { | |
1234 | f(i as u32, &data.unwrap_or_else(|| panic!("Missing ExpnData!")))?; | |
1235 | } | |
1236 | Ok(()) | |
1237 | } | |
1238 | ||
1239 | pub fn raw_encode_syntax_context<E: Encoder>( | |
1240 | ctxt: SyntaxContext, | |
1241 | context: &HygieneEncodeContext, | |
1242 | e: &mut E, | |
1243 | ) -> Result<(), E::Error> { | |
1244 | if !context.serialized_ctxts.lock().contains(&ctxt) { | |
1245 | context.latest_ctxts.lock().insert(ctxt); | |
1246 | } | |
1247 | ctxt.0.encode(e) | |
1248 | } | |
1249 | ||
1250 | pub fn raw_encode_expn_id<E: Encoder>( | |
1251 | expn: ExpnId, | |
1252 | context: &HygieneEncodeContext, | |
1253 | mode: ExpnDataEncodeMode, | |
1254 | e: &mut E, | |
1255 | ) -> Result<(), E::Error> { | |
1256 | // Record the fact that we need to serialize the corresponding | |
1257 | // `ExpnData` | |
1258 | let needs_data = || { | |
1259 | if !context.serialized_expns.lock().contains(&expn) { | |
1260 | context.latest_expns.lock().insert(expn); | |
1261 | } | |
1262 | }; | |
1263 | ||
1264 | match mode { | |
1265 | ExpnDataEncodeMode::IncrComp => { | |
1266 | // Always serialize the `ExpnData` in incr comp mode | |
1267 | needs_data(); | |
1268 | expn.0.encode(e) | |
1269 | } | |
1270 | ExpnDataEncodeMode::Metadata => { | |
1271 | let data = expn.expn_data(); | |
1272 | // We only need to serialize the ExpnData | |
1273 | // if it comes from this crate. | |
1274 | // We currently don't serialize any hygiene information data for | |
1275 | // proc-macro crates: see the `SpecializedEncoder<Span>` impl | |
1276 | // for crate metadata. | |
1277 | if data.krate == LOCAL_CRATE { | |
1278 | needs_data(); | |
1279 | } | |
1280 | data.orig_id.expect("Missing orig_id").encode(e)?; | |
1281 | data.krate.encode(e) | |
1282 | } | |
1283 | } | |
1284 | } | |
1285 | ||
1286 | pub enum ExpnDataEncodeMode { | |
1287 | IncrComp, | |
1288 | Metadata, | |
1289 | } | |
1290 | ||
1291 | pub enum ExpnDataDecodeMode<'a, F: FnOnce(CrateNum) -> &'a HygieneDecodeContext> { | |
1292 | IncrComp(&'a HygieneDecodeContext), | |
1293 | Metadata(F), | |
1294 | } | |
1295 | ||
1296 | impl<'a> ExpnDataDecodeMode<'a, Box<dyn FnOnce(CrateNum) -> &'a HygieneDecodeContext>> { | |
1297 | pub fn incr_comp(ctxt: &'a HygieneDecodeContext) -> Self { | |
1298 | ExpnDataDecodeMode::IncrComp(ctxt) | |
1299 | } | |
1300 | } | |
1301 | ||
1302 | impl<E: Encoder> Encodable<E> for SyntaxContext { | |
1303 | default fn encode(&self, _: &mut E) -> Result<(), E::Error> { | |
1304 | panic!("cannot encode `SyntaxContext` with `{}`", std::any::type_name::<E>()); | |
cc61c64b XL |
1305 | } |
1306 | } | |
1307 | ||
3dfed10e XL |
1308 | impl<D: Decoder> Decodable<D> for SyntaxContext { |
1309 | default fn decode(_: &mut D) -> Result<Self, D::Error> { | |
1310 | panic!("cannot decode `SyntaxContext` with `{}`", std::any::type_name::<D>()); | |
cc61c64b XL |
1311 | } |
1312 | } | |
5869c6ff XL |
1313 | |
1314 | /// Updates the `disambiguator` field of the corresponding `ExpnData` | |
1315 | /// such that the `Fingerprint` of the `ExpnData` does not collide with | |
1316 | /// any other `ExpnIds`. | |
1317 | /// | |
1318 | /// This method is called only when an `ExpnData` is first associated | |
1319 | /// with an `ExpnId` (when the `ExpnId` is initially constructed, or via | |
1320 | /// `set_expn_data`). It is *not* called for foreign `ExpnId`s deserialized | |
1321 | /// from another crate's metadata - since `ExpnData` includes a `krate` field, | |
1322 | /// collisions are only possible between `ExpnId`s within the same crate. | |
1323 | fn update_disambiguator(expn_id: ExpnId) { | |
1324 | /// A `HashStableContext` which hashes the raw id values for `DefId` | |
1325 | /// and `CrateNum`, rather than using their computed stable hash. | |
1326 | /// | |
1327 | /// This allows us to use the `HashStable` implementation on `ExpnId` | |
1328 | /// early on in compilation, before we've constructed a `TyCtxt`. | |
1329 | /// The `Fingerprint`s created by this context are not 'stable', since | |
1330 | /// the raw `CrateNum` and `DefId` values for an item may change between | |
1331 | /// sessions due to unrelated changes (e.g. adding/removing an different item). | |
1332 | /// | |
1333 | /// However, this is fine for our purposes - we only need to detect | |
1334 | /// when two `ExpnData`s have the same `Fingerprint`. Since the hashes produced | |
1335 | /// by this context still obey the properties of `HashStable`, we have | |
1336 | /// that | |
1337 | /// `hash_stable(expn1, DummyHashStableContext) == hash_stable(expn2, DummyHashStableContext)` | |
1338 | /// iff `hash_stable(expn1, StableHashingContext) == hash_stable(expn2, StableHasingContext)`. | |
1339 | /// | |
1340 | /// This is sufficient for determining when we need to update the disambiguator. | |
1341 | struct DummyHashStableContext<'a> { | |
1342 | caching_source_map: CachingSourceMapView<'a>, | |
1343 | } | |
1344 | ||
1345 | impl<'a> crate::HashStableContext for DummyHashStableContext<'a> { | |
1346 | fn hash_def_id(&mut self, def_id: DefId, hasher: &mut StableHasher) { | |
1347 | def_id.krate.as_u32().hash_stable(self, hasher); | |
1348 | def_id.index.as_u32().hash_stable(self, hasher); | |
1349 | } | |
1350 | ||
1351 | fn expn_id_cache() -> &'static LocalKey<ExpnIdCache> { | |
1352 | // This cache is only used by `DummyHashStableContext`, | |
1353 | // so we won't pollute the cache values of the normal `StableHashingContext` | |
1354 | thread_local! { | |
1355 | static CACHE: ExpnIdCache = Default::default(); | |
1356 | } | |
1357 | ||
1358 | &CACHE | |
1359 | } | |
1360 | ||
1361 | fn hash_crate_num(&mut self, krate: CrateNum, hasher: &mut StableHasher) { | |
1362 | krate.as_u32().hash_stable(self, hasher); | |
1363 | } | |
1364 | fn hash_spans(&self) -> bool { | |
1365 | true | |
1366 | } | |
5869c6ff XL |
1367 | fn span_data_to_lines_and_cols( |
1368 | &mut self, | |
1369 | span: &crate::SpanData, | |
1370 | ) -> Option<(Lrc<SourceFile>, usize, BytePos, usize, BytePos)> { | |
1371 | self.caching_source_map.span_data_to_lines_and_cols(span) | |
1372 | } | |
1373 | } | |
1374 | ||
1375 | let source_map = SESSION_GLOBALS | |
1376 | .with(|session_globals| session_globals.source_map.borrow().as_ref().unwrap().clone()); | |
1377 | ||
1378 | let mut ctx = | |
1379 | DummyHashStableContext { caching_source_map: CachingSourceMapView::new(&source_map) }; | |
1380 | ||
1381 | let mut hasher = StableHasher::new(); | |
1382 | ||
1383 | let expn_data = expn_id.expn_data(); | |
1384 | // This disambiguator should not have been set yet. | |
1385 | assert_eq!( | |
1386 | expn_data.disambiguator, 0, | |
1387 | "Already set disambiguator for ExpnData: {:?}", | |
1388 | expn_data | |
1389 | ); | |
1390 | expn_data.hash_stable(&mut ctx, &mut hasher); | |
1391 | let first_hash = hasher.finish(); | |
1392 | ||
1393 | let modified = HygieneData::with(|data| { | |
1394 | // If this is the first ExpnData with a given hash, then keep our | |
1395 | // disambiguator at 0 (the default u32 value) | |
1396 | let disambig = data.expn_data_disambiguators.entry(first_hash).or_default(); | |
1397 | data.expn_data[expn_id.0 as usize].as_mut().unwrap().disambiguator = *disambig; | |
1398 | *disambig += 1; | |
1399 | ||
1400 | *disambig != 1 | |
1401 | }); | |
1402 | ||
1403 | if modified { | |
6a06907d XL |
1404 | debug!("Set disambiguator for {:?} (hash {:?})", expn_id, first_hash); |
1405 | debug!("expn_data = {:?}", expn_id.expn_data()); | |
5869c6ff XL |
1406 | |
1407 | // Verify that the new disambiguator makes the hash unique | |
1408 | #[cfg(debug_assertions)] | |
1409 | { | |
1410 | hasher = StableHasher::new(); | |
1411 | expn_id.expn_data().hash_stable(&mut ctx, &mut hasher); | |
1412 | let new_hash: Fingerprint = hasher.finish(); | |
1413 | ||
1414 | HygieneData::with(|data| { | |
6a06907d XL |
1415 | assert_eq!( |
1416 | data.expn_data_disambiguators.get(&new_hash), | |
1417 | None, | |
1418 | "Hash collision after disambiguator update!", | |
1419 | ); | |
5869c6ff XL |
1420 | }); |
1421 | }; | |
1422 | } | |
1423 | } |