2014-02-25 04:47:19 +08:00
|
|
|
// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
|
|
|
|
// file at the top-level directory of this distribution and at
|
|
|
|
// http://rust-lang.org/COPYRIGHT.
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
|
|
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
|
|
|
// option. This file may not be copied, modified, or distributed
|
|
|
|
// except according to those terms.
|
|
|
|
|
2016-07-16 19:11:28 +00:00
|
|
|
//! Machinery for hygienic macros, inspired by the MTWT[1] paper.
|
2014-02-25 04:47:19 +08:00
|
|
|
//!
|
|
|
|
//! [1] Matthew Flatt, Ryan Culpepper, David Darais, and Robert Bruce Findler.
|
|
|
|
//! 2012. *Macros that work together: Compile-time bindings, partial expansion,
|
|
|
|
//! and definition contexts*. J. Funct. Program. 22, 2 (March 2012), 181-216.
|
|
|
|
//! DOI=10.1017/S0956796812000093 http://dx.doi.org/10.1017/S0956796812000093
|
|
|
|
|
2017-03-17 04:04:41 +00:00
|
|
|
use Span;
|
|
|
|
use symbol::Symbol;
|
|
|
|
|
|
|
|
use serialize::{Encodable, Decodable, Encoder, Decoder};
|
2014-02-25 04:47:19 +08:00
|
|
|
use std::cell::RefCell;
|
2014-05-29 19:03:06 -07:00
|
|
|
use std::collections::HashMap;
|
2016-06-22 08:03:42 +00:00
|
|
|
use std::fmt;
|
2014-02-25 04:47:19 +08:00
|
|
|
|
2016-06-22 08:03:42 +00:00
|
|
|
/// A SyntaxContext represents a chain of macro expansions (represented by marks).
|
2017-03-17 04:04:41 +00:00
|
|
|
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
2016-06-22 08:03:42 +00:00
|
|
|
pub struct SyntaxContext(u32);
|
2014-02-25 04:47:19 +08:00
|
|
|
|
2016-06-22 08:03:42 +00:00
|
|
|
#[derive(Copy, Clone)]
|
|
|
|
pub struct SyntaxContextData {
|
|
|
|
pub outer_mark: Mark,
|
|
|
|
pub prev_ctxt: SyntaxContext,
|
2014-02-25 04:47:19 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 23:21:59 +00:00
|
|
|
/// A mark is a unique id associated with a macro expansion.
|
2017-03-05 05:15:58 +00:00
|
|
|
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Default, RustcEncodable, RustcDecodable)]
|
2016-06-22 08:03:42 +00:00
|
|
|
pub struct Mark(u32);
|
2014-02-25 04:47:19 +08:00
|
|
|
|
2016-06-22 08:03:42 +00:00
|
|
|
impl Mark {
|
|
|
|
pub fn fresh() -> Self {
|
|
|
|
HygieneData::with(|data| {
|
2017-03-17 04:04:41 +00:00
|
|
|
data.marks.push(None);
|
|
|
|
Mark(data.marks.len() as u32 - 1)
|
2016-06-22 08:03:42 +00:00
|
|
|
})
|
2016-06-20 10:28:32 +00:00
|
|
|
}
|
2016-09-02 09:12:47 +00:00
|
|
|
|
2016-09-07 23:21:59 +00:00
|
|
|
/// The mark of the theoretical expansion that generates freshly parsed, unexpanded AST.
|
|
|
|
pub fn root() -> Self {
|
|
|
|
Mark(0)
|
|
|
|
}
|
|
|
|
|
2016-12-01 22:49:32 +00:00
|
|
|
pub fn as_u32(self) -> u32 {
|
2016-09-02 09:12:47 +00:00
|
|
|
self.0
|
|
|
|
}
|
2017-03-16 10:23:33 +00:00
|
|
|
|
|
|
|
pub fn from_u32(raw: u32) -> Mark {
|
|
|
|
Mark(raw)
|
|
|
|
}
|
2017-03-17 04:04:41 +00:00
|
|
|
|
|
|
|
pub fn expn_info(self) -> Option<ExpnInfo> {
|
|
|
|
HygieneData::with(|data| data.marks[self.0 as usize].clone())
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn set_expn_info(self, info: ExpnInfo) {
|
|
|
|
HygieneData::with(|data| data.marks[self.0 as usize] = Some(info))
|
|
|
|
}
|
2014-02-25 04:47:19 +08:00
|
|
|
}
|
|
|
|
|
2016-06-22 08:03:42 +00:00
|
|
|
struct HygieneData {
|
2017-03-17 04:04:41 +00:00
|
|
|
marks: Vec<Option<ExpnInfo>>,
|
2016-06-22 08:03:42 +00:00
|
|
|
syntax_contexts: Vec<SyntaxContextData>,
|
|
|
|
markings: HashMap<(SyntaxContext, Mark), SyntaxContext>,
|
2014-02-25 04:47:19 +08:00
|
|
|
}
|
|
|
|
|
2016-06-22 08:03:42 +00:00
|
|
|
impl HygieneData {
|
|
|
|
fn new() -> Self {
|
|
|
|
HygieneData {
|
2017-03-17 04:04:41 +00:00
|
|
|
marks: vec![None],
|
2016-06-22 08:03:42 +00:00
|
|
|
syntax_contexts: vec![SyntaxContextData {
|
2016-09-07 23:21:59 +00:00
|
|
|
outer_mark: Mark::root(),
|
|
|
|
prev_ctxt: SyntaxContext::empty(),
|
2016-06-22 08:03:42 +00:00
|
|
|
}],
|
|
|
|
markings: HashMap::new(),
|
|
|
|
}
|
2014-02-25 04:47:19 +08:00
|
|
|
}
|
|
|
|
|
2016-06-22 08:03:42 +00:00
|
|
|
fn with<T, F: FnOnce(&mut HygieneData) -> T>(f: F) -> T {
|
|
|
|
thread_local! {
|
|
|
|
static HYGIENE_DATA: RefCell<HygieneData> = RefCell::new(HygieneData::new());
|
|
|
|
}
|
|
|
|
HYGIENE_DATA.with(|data| f(&mut *data.borrow_mut()))
|
|
|
|
}
|
2014-03-09 00:18:58 +02:00
|
|
|
}
|
2014-02-25 04:47:19 +08:00
|
|
|
|
2017-03-17 04:04:41 +00:00
|
|
|
pub fn clear_markings() {
|
|
|
|
HygieneData::with(|data| data.markings = HashMap::new());
|
2014-11-28 21:56:09 -07:00
|
|
|
}
|
|
|
|
|
2016-06-22 08:03:42 +00:00
|
|
|
impl SyntaxContext {
|
|
|
|
pub const fn empty() -> Self {
|
|
|
|
SyntaxContext(0)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn data(self) -> SyntaxContextData {
|
|
|
|
HygieneData::with(|data| data.syntax_contexts[self.0 as usize])
|
|
|
|
}
|
2014-02-25 04:47:19 +08:00
|
|
|
|
2016-06-22 08:03:42 +00:00
|
|
|
/// Extend a syntax context with a given mark
|
|
|
|
pub fn apply_mark(self, mark: Mark) -> SyntaxContext {
|
|
|
|
// Applying the same mark twice is a no-op
|
|
|
|
let ctxt_data = self.data();
|
|
|
|
if mark == ctxt_data.outer_mark {
|
|
|
|
return ctxt_data.prev_ctxt;
|
2014-02-25 04:47:19 +08:00
|
|
|
}
|
2016-06-22 08:03:42 +00:00
|
|
|
|
|
|
|
HygieneData::with(|data| {
|
|
|
|
let syntax_contexts = &mut data.syntax_contexts;
|
|
|
|
*data.markings.entry((self, mark)).or_insert_with(|| {
|
|
|
|
syntax_contexts.push(SyntaxContextData {
|
|
|
|
outer_mark: mark,
|
|
|
|
prev_ctxt: self,
|
|
|
|
});
|
|
|
|
SyntaxContext(syntax_contexts.len() as u32 - 1)
|
|
|
|
})
|
|
|
|
})
|
|
|
|
}
|
2017-03-17 04:04:41 +00:00
|
|
|
|
|
|
|
pub fn outer(self) -> Mark {
|
|
|
|
HygieneData::with(|data| data.syntax_contexts[self.0 as usize].outer_mark)
|
|
|
|
}
|
2014-02-25 04:47:19 +08:00
|
|
|
}
|
|
|
|
|
2016-06-22 08:03:42 +00:00
|
|
|
impl fmt::Debug for SyntaxContext {
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
|
|
write!(f, "#{}", self.0)
|
|
|
|
}
|
2016-06-26 03:32:45 +00:00
|
|
|
}
|
2017-03-17 04:04:41 +00:00
|
|
|
|
|
|
|
/// Extra information for tracking spans of macro and syntax sugar expansion
|
|
|
|
#[derive(Clone, Hash, Debug)]
|
|
|
|
pub struct ExpnInfo {
|
|
|
|
/// The location of the actual macro invocation or syntax sugar , e.g.
|
|
|
|
/// `let x = foo!();` or `if let Some(y) = x {}`
|
|
|
|
///
|
|
|
|
/// This may recursively refer to other macro invocations, e.g. if
|
|
|
|
/// `foo!()` invoked `bar!()` internally, and there was an
|
|
|
|
/// expression inside `bar!`; the call_site of the expression in
|
|
|
|
/// the expansion would point to the `bar!` invocation; that
|
|
|
|
/// call_site span would have its own ExpnInfo, with the call_site
|
|
|
|
/// pointing to the `foo!` invocation.
|
|
|
|
pub call_site: Span,
|
|
|
|
/// Information about the expansion.
|
|
|
|
pub callee: NameAndSpan
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Clone, Hash, Debug)]
|
|
|
|
pub struct NameAndSpan {
|
|
|
|
/// The format with which the macro was invoked.
|
|
|
|
pub format: ExpnFormat,
|
|
|
|
/// Whether the macro is allowed to use #[unstable]/feature-gated
|
|
|
|
/// features internally without forcing the whole crate to opt-in
|
|
|
|
/// to them.
|
|
|
|
pub allow_internal_unstable: bool,
|
|
|
|
/// The span of the macro definition itself. The macro may not
|
|
|
|
/// have a sensible definition span (e.g. something defined
|
|
|
|
/// completely inside libsyntax) in which case this is None.
|
|
|
|
pub span: Option<Span>
|
|
|
|
}
|
|
|
|
|
|
|
|
impl NameAndSpan {
|
|
|
|
pub fn name(&self) -> Symbol {
|
|
|
|
match self.format {
|
|
|
|
ExpnFormat::MacroAttribute(s) |
|
|
|
|
ExpnFormat::MacroBang(s) |
|
|
|
|
ExpnFormat::CompilerDesugaring(s) => s,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// The source of expansion.
|
|
|
|
#[derive(Clone, Hash, Debug, PartialEq, Eq)]
|
|
|
|
pub enum ExpnFormat {
|
|
|
|
/// e.g. #[derive(...)] <item>
|
|
|
|
MacroAttribute(Symbol),
|
|
|
|
/// e.g. `format!()`
|
|
|
|
MacroBang(Symbol),
|
|
|
|
/// Desugaring done by the compiler during HIR lowering.
|
|
|
|
CompilerDesugaring(Symbol)
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Encodable for SyntaxContext {
|
|
|
|
fn encode<E: Encoder>(&self, _: &mut E) -> Result<(), E::Error> {
|
|
|
|
Ok(()) // FIXME(jseyfried) intercrate hygiene
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Decodable for SyntaxContext {
|
|
|
|
fn decode<D: Decoder>(_: &mut D) -> Result<SyntaxContext, D::Error> {
|
|
|
|
Ok(SyntaxContext::empty()) // FIXME(jseyfried) intercrate hygiene
|
|
|
|
}
|
|
|
|
}
|