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