1 // Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
11 //! Machinery for hygienic macros, inspired by the `MTWT[1]` paper.
13 //! `[1]` Matthew Flatt, Ryan Culpepper, David Darais, and Robert Bruce Findler. 2012.
14 //! *Macros that work together: Compile-time bindings, partial expansion,
15 //! and definition contexts*. J. Funct. Program. 22, 2 (March 2012), 181-216.
16 //! DOI=10.1017/S0956796812000093 <https://doi.org/10.1017/S0956796812000093>
21 use symbol::{Ident, Symbol};
23 use serialize::{Encodable, Decodable, Encoder, Decoder};
24 use std::collections::HashMap;
25 use rustc_data_structures::fx::FxHashSet;
28 /// A SyntaxContext represents a chain of macro expansions (represented by marks).
29 #[derive(Clone, Copy, PartialEq, Eq, Default, PartialOrd, Ord, Hash)]
30 pub struct SyntaxContext(pub(super) u32);
32 #[derive(Copy, Clone, Debug)]
33 pub struct SyntaxContextData {
35 pub prev_ctxt: SyntaxContext,
36 pub modern: SyntaxContext,
39 /// A mark is a unique id associated with a macro expansion.
40 #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, RustcEncodable, RustcDecodable)]
46 transparency: Transparency,
48 expn_info: Option<ExpnInfo>,
51 /// A property of a macro expansion that determines how identifiers
52 /// produced by that expansion are resolved.
53 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
54 pub enum Transparency {
55 /// Identifier produced by a transparent expansion is always resolved at call-site.
56 /// Call-site spans in procedural macros, hygiene opt-out in `macro` should use this.
59 /// Identifier produced by a semi-transparent expansion may be resolved
60 /// either at call-site or at definition-site.
61 /// If it's a local variable, label or `$crate` then it's resolved at def-site.
62 /// Otherwise it's resolved at call-site.
63 /// `macro_rules` macros behave like this, built-in macros currently behave like this too,
64 /// but that's an implementation detail.
66 /// Identifier produced by an opaque expansion is always resolved at definition-site.
67 /// Def-site spans in procedural macros, identifiers from `macro` by default use this.
72 pub fn fresh(parent: Mark) -> Self {
73 HygieneData::with(|data| {
74 data.marks.push(MarkData {
76 // By default expansions behave like `macro_rules`.
77 transparency: Transparency::SemiTransparent,
81 Mark(data.marks.len() as u32 - 1)
85 /// The mark of the theoretical expansion that generates freshly parsed, unexpanded AST.
87 pub fn root() -> Self {
92 pub fn as_u32(self) -> u32 {
97 pub fn from_u32(raw: u32) -> Mark {
102 pub fn expn_info(self) -> Option<ExpnInfo> {
103 HygieneData::with(|data| data.marks[self.0 as usize].expn_info.clone())
107 pub fn set_expn_info(self, info: ExpnInfo) {
108 HygieneData::with(|data| {
109 let old_info = &mut data.marks[self.0 as usize].expn_info;
110 if let Some(old_info) = old_info {
111 panic!("expansion info is reset for the mark {}\nold: {:#?}\nnew: {:#?}",
112 self.0, old_info, info);
114 *old_info = Some(info);
118 pub fn modern(mut self) -> Mark {
119 HygieneData::with(|data| {
120 while data.marks[self.0 as usize].transparency != Transparency::Opaque {
121 self = data.marks[self.0 as usize].parent;
128 pub fn transparency(self) -> Transparency {
129 assert_ne!(self, Mark::root());
130 HygieneData::with(|data| data.marks[self.0 as usize].transparency)
134 pub fn set_transparency(self, transparency: Transparency) {
135 assert_ne!(self, Mark::root());
136 HygieneData::with(|data| data.marks[self.0 as usize].transparency = transparency)
140 pub fn is_builtin(self) -> bool {
141 HygieneData::with(|data| data.marks[self.0 as usize].is_builtin)
145 pub fn set_is_builtin(self, is_builtin: bool) {
146 HygieneData::with(|data| data.marks[self.0 as usize].is_builtin = is_builtin)
149 pub fn is_descendant_of(mut self, ancestor: Mark) -> bool {
150 HygieneData::with(|data| {
151 while self != ancestor {
152 if self == Mark::root() {
155 self = data.marks[self.0 as usize].parent;
161 /// Computes a mark such that both input marks are descendants of (or equal to) the returned
162 /// mark. That is, the following holds:
165 /// let la = least_ancestor(a, b);
166 /// assert!(a.is_descendant_of(la))
167 /// assert!(b.is_descendant_of(la))
169 pub fn least_ancestor(mut a: Mark, mut b: Mark) -> Mark {
170 HygieneData::with(|data| {
171 // Compute the path from a to the root
172 let mut a_path = FxHashSet::<Mark>();
173 while a != Mark::root() {
175 a = data.marks[a.0 as usize].parent;
178 // While the path from b to the root hasn't intersected, move up the tree
179 while !a_path.contains(&b) {
180 b = data.marks[b.0 as usize].parent;
189 pub struct HygieneData {
190 marks: Vec<MarkData>,
191 syntax_contexts: Vec<SyntaxContextData>,
192 markings: HashMap<(SyntaxContext, Mark), SyntaxContext>,
193 gensym_to_ctxt: HashMap<Symbol, Span>,
194 default_edition: Edition,
198 pub fn new() -> Self {
200 marks: vec![MarkData {
201 parent: Mark::root(),
202 // If the root is opaque, then loops searching for an opaque mark
203 // will automatically stop after reaching it.
204 transparency: Transparency::Opaque,
208 syntax_contexts: vec![SyntaxContextData {
209 outer_mark: Mark::root(),
210 prev_ctxt: SyntaxContext(0),
211 modern: SyntaxContext(0),
213 markings: HashMap::new(),
214 gensym_to_ctxt: HashMap::new(),
215 default_edition: Edition::Edition2015,
219 fn with<T, F: FnOnce(&mut HygieneData) -> T>(f: F) -> T {
220 GLOBALS.with(|globals| f(&mut *globals.hygiene_data.borrow_mut()))
224 pub fn default_edition() -> Edition {
225 HygieneData::with(|data| data.default_edition)
228 pub fn set_default_edition(edition: Edition) {
229 HygieneData::with(|data| data.default_edition = edition);
232 pub fn clear_markings() {
233 HygieneData::with(|data| data.markings = HashMap::new());
237 pub const fn empty() -> Self {
241 // Allocate a new SyntaxContext with the given ExpnInfo. This is used when
242 // deserializing Spans from the incr. comp. cache.
243 // FIXME(mw): This method does not restore MarkData::parent or
244 // SyntaxContextData::prev_ctxt or SyntaxContextData::modern. These things
245 // don't seem to be used after HIR lowering, so everything should be fine
246 // as long as incremental compilation does not kick in before that.
247 pub fn allocate_directly(expansion_info: ExpnInfo) -> Self {
248 HygieneData::with(|data| {
249 data.marks.push(MarkData {
250 parent: Mark::root(),
251 transparency: Transparency::SemiTransparent,
253 expn_info: Some(expansion_info),
256 let mark = Mark(data.marks.len() as u32 - 1);
258 data.syntax_contexts.push(SyntaxContextData {
260 prev_ctxt: SyntaxContext::empty(),
261 modern: SyntaxContext::empty(),
263 SyntaxContext(data.syntax_contexts.len() as u32 - 1)
267 /// Extend a syntax context with a given mark
268 pub fn apply_mark(self, mark: Mark) -> SyntaxContext {
269 if mark.transparency() == Transparency::Opaque {
270 return self.apply_mark_internal(mark);
274 mark.expn_info().map_or(SyntaxContext::empty(), |info| info.call_site.ctxt()).modern();
275 if call_site_ctxt == SyntaxContext::empty() {
276 return self.apply_mark_internal(mark);
279 // Otherwise, `mark` is a macros 1.0 definition and the call site is in a
280 // macros 2.0 expansion, i.e. a macros 1.0 invocation is in a macros 2.0 definition.
282 // In this case, the tokens from the macros 1.0 definition inherit the hygiene
283 // at their invocation. That is, we pretend that the macros 1.0 definition
284 // was defined at its invocation (i.e. inside the macros 2.0 definition)
285 // so that the macros 2.0 definition remains hygienic.
287 // See the example at `test/run-pass/hygiene/legacy_interaction.rs`.
288 let mut ctxt = call_site_ctxt;
289 for mark in self.marks() {
290 ctxt = ctxt.apply_mark_internal(mark);
292 ctxt.apply_mark_internal(mark)
295 fn apply_mark_internal(self, mark: Mark) -> SyntaxContext {
296 HygieneData::with(|data| {
297 let syntax_contexts = &mut data.syntax_contexts;
298 let mut modern = syntax_contexts[self.0 as usize].modern;
299 if data.marks[mark.0 as usize].transparency == Transparency::Opaque {
300 modern = *data.markings.entry((modern, mark)).or_insert_with(|| {
301 let len = syntax_contexts.len() as u32;
302 syntax_contexts.push(SyntaxContextData {
305 modern: SyntaxContext(len),
311 *data.markings.entry((self, mark)).or_insert_with(|| {
312 syntax_contexts.push(SyntaxContextData {
317 SyntaxContext(syntax_contexts.len() as u32 - 1)
322 /// Pulls a single mark off of the syntax context. This effectively moves the
323 /// context up one macro definition level. That is, if we have a nested macro
324 /// definition as follows:
334 /// and we have a SyntaxContext that is referring to something declared by an invocation
335 /// of g (call it g1), calling remove_mark will result in the SyntaxContext for the
336 /// invocation of f that created g1.
337 /// Returns the mark that was removed.
338 pub fn remove_mark(&mut self) -> Mark {
339 HygieneData::with(|data| {
340 let outer_mark = data.syntax_contexts[self.0 as usize].outer_mark;
341 *self = data.syntax_contexts[self.0 as usize].prev_ctxt;
346 pub fn marks(mut self) -> Vec<Mark> {
347 HygieneData::with(|data| {
348 let mut marks = Vec::new();
349 while self != SyntaxContext::empty() {
350 marks.push(data.syntax_contexts[self.0 as usize].outer_mark);
351 self = data.syntax_contexts[self.0 as usize].prev_ctxt;
358 /// Adjust this context for resolution in a scope created by the given expansion.
359 /// For example, consider the following three resolutions of `f`:
362 /// mod foo { pub fn f() {} } // `f`'s `SyntaxContext` is empty.
364 /// macro m($f:ident) {
366 /// pub fn f() {} // `f`'s `SyntaxContext` has a single `Mark` from `m`.
367 /// pub fn $f() {} // `$f`'s `SyntaxContext` is empty.
369 /// foo::f(); // `f`'s `SyntaxContext` has a single `Mark` from `m`
370 /// //^ Since `mod foo` is outside this expansion, `adjust` removes the mark from `f`,
371 /// //| and it resolves to `::foo::f`.
372 /// bar::f(); // `f`'s `SyntaxContext` has a single `Mark` from `m`
373 /// //^ Since `mod bar` not outside this expansion, `adjust` does not change `f`,
374 /// //| and it resolves to `::bar::f`.
375 /// bar::$f(); // `f`'s `SyntaxContext` is empty.
376 /// //^ Since `mod bar` is not outside this expansion, `adjust` does not change `$f`,
377 /// //| and it resolves to `::bar::$f`.
380 /// This returns the expansion whose definition scope we use to privacy check the resolution,
381 /// or `None` if we privacy check as usual (i.e. not w.r.t. a macro definition scope).
382 pub fn adjust(&mut self, expansion: Mark) -> Option<Mark> {
383 let mut scope = None;
384 while !expansion.is_descendant_of(self.outer()) {
385 scope = Some(self.remove_mark());
390 /// Adjust this context for resolution in a scope created by the given expansion
391 /// via a glob import with the given `SyntaxContext`.
396 /// macro m($i:ident) {
398 /// pub fn f() {} // `f`'s `SyntaxContext` has a single `Mark` from `m`.
399 /// pub fn $i() {} // `$i`'s `SyntaxContext` is empty.
402 /// macro n($j:ident) {
404 /// f(); // `f`'s `SyntaxContext` has a mark from `m` and a mark from `n`
405 /// //^ `glob_adjust` removes the mark from `n`, so this resolves to `foo::f`.
406 /// $i(); // `$i`'s `SyntaxContext` has a mark from `n`
407 /// //^ `glob_adjust` removes the mark from `n`, so this resolves to `foo::$i`.
408 /// $j(); // `$j`'s `SyntaxContext` has a mark from `m`
409 /// //^ This cannot be glob-adjusted, so this is a resolution error.
413 /// This returns `None` if the context cannot be glob-adjusted.
414 /// Otherwise, it returns the scope to use when privacy checking (see `adjust` for details).
415 pub fn glob_adjust(&mut self, expansion: Mark, mut glob_ctxt: SyntaxContext)
416 -> Option<Option<Mark>> {
417 let mut scope = None;
418 while !expansion.is_descendant_of(glob_ctxt.outer()) {
419 scope = Some(glob_ctxt.remove_mark());
420 if self.remove_mark() != scope.unwrap() {
424 if self.adjust(expansion).is_some() {
430 /// Undo `glob_adjust` if possible:
433 /// if let Some(privacy_checking_scope) = self.reverse_glob_adjust(expansion, glob_ctxt) {
434 /// assert!(self.glob_adjust(expansion, glob_ctxt) == Some(privacy_checking_scope));
437 pub fn reverse_glob_adjust(&mut self, expansion: Mark, mut glob_ctxt: SyntaxContext)
438 -> Option<Option<Mark>> {
439 if self.adjust(expansion).is_some() {
443 let mut marks = Vec::new();
444 while !expansion.is_descendant_of(glob_ctxt.outer()) {
445 marks.push(glob_ctxt.remove_mark());
448 let scope = marks.last().cloned();
449 while let Some(mark) = marks.pop() {
450 *self = self.apply_mark(mark);
456 pub fn modern(self) -> SyntaxContext {
457 HygieneData::with(|data| data.syntax_contexts[self.0 as usize].modern)
461 pub fn outer(self) -> Mark {
462 HygieneData::with(|data| data.syntax_contexts[self.0 as usize].outer_mark)
466 impl fmt::Debug for SyntaxContext {
467 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
468 write!(f, "#{}", self.0)
472 /// Extra information for tracking spans of macro and syntax sugar expansion
473 #[derive(Clone, Hash, Debug, RustcEncodable, RustcDecodable)]
474 pub struct ExpnInfo {
475 /// The location of the actual macro invocation or syntax sugar , e.g.
476 /// `let x = foo!();` or `if let Some(y) = x {}`
478 /// This may recursively refer to other macro invocations, e.g. if
479 /// `foo!()` invoked `bar!()` internally, and there was an
480 /// expression inside `bar!`; the call_site of the expression in
481 /// the expansion would point to the `bar!` invocation; that
482 /// call_site span would have its own ExpnInfo, with the call_site
483 /// pointing to the `foo!` invocation.
485 /// The span of the macro definition itself. The macro may not
486 /// have a sensible definition span (e.g. something defined
487 /// completely inside libsyntax) in which case this is None.
488 /// This span serves only informational purpose and is not used for resolution.
489 pub def_site: Option<Span>,
490 /// The format with which the macro was invoked.
491 pub format: ExpnFormat,
492 /// Whether the macro is allowed to use #[unstable]/feature-gated
493 /// features internally without forcing the whole crate to opt-in
495 pub allow_internal_unstable: bool,
496 /// Whether the macro is allowed to use `unsafe` internally
497 /// even if the user crate has `#![forbid(unsafe_code)]`.
498 pub allow_internal_unsafe: bool,
499 /// Edition of the crate in which the macro is defined.
500 pub edition: Edition,
503 /// The source of expansion.
504 #[derive(Clone, Hash, Debug, PartialEq, Eq, RustcEncodable, RustcDecodable)]
505 pub enum ExpnFormat {
506 /// e.g. #[derive(...)] <item>
507 MacroAttribute(Symbol),
510 /// Desugaring done by the compiler during HIR lowering.
511 CompilerDesugaring(CompilerDesugaringKind)
515 pub fn name(&self) -> Symbol {
517 ExpnFormat::MacroBang(name) | ExpnFormat::MacroAttribute(name) => name,
518 ExpnFormat::CompilerDesugaring(kind) => kind.name(),
523 /// The kind of compiler desugaring.
524 #[derive(Clone, Copy, Hash, Debug, PartialEq, Eq, RustcEncodable, RustcDecodable)]
525 pub enum CompilerDesugaringKind {
529 /// Desugaring of an `impl Trait` in return type position
530 /// to an `existential type Foo: Trait;` + replacing the
531 /// `impl Trait` with `Foo`.
532 ExistentialReturnType,
536 impl CompilerDesugaringKind {
537 pub fn name(self) -> Symbol {
538 Symbol::intern(match self {
539 CompilerDesugaringKind::Async => "async",
540 CompilerDesugaringKind::DotFill => "...",
541 CompilerDesugaringKind::QuestionMark => "?",
542 CompilerDesugaringKind::Catch => "do catch",
543 CompilerDesugaringKind::ExistentialReturnType => "existental type",
548 impl Encodable for SyntaxContext {
549 fn encode<E: Encoder>(&self, _: &mut E) -> Result<(), E::Error> {
550 Ok(()) // FIXME(jseyfried) intercrate hygiene
554 impl Decodable for SyntaxContext {
555 fn decode<D: Decoder>(_: &mut D) -> Result<SyntaxContext, D::Error> {
556 Ok(SyntaxContext::empty()) // FIXME(jseyfried) intercrate hygiene
561 pub fn from_ident(ident: Ident) -> Symbol {
562 HygieneData::with(|data| {
563 let gensym = ident.name.gensymed();
564 data.gensym_to_ctxt.insert(gensym, ident.span);
569 pub fn to_ident(self) -> Ident {
570 HygieneData::with(|data| {
571 match data.gensym_to_ctxt.get(&self) {
572 Some(&span) => Ident::new(self.interned(), span),
573 None => Ident::with_empty_ctxt(self),