Skip to content

Commit 6cc49e5

Browse files
authored
Auto merge of #34860 - jseyfried:encapsulate_hygiene, r=nrc
Clean up and encapsulate `syntax::ext::mtwt`, rename `mtwt` to `hygiene` r? @nrc
2 parents f441bca + 275d321 commit 6cc49e5

File tree

11 files changed

+157
-233
lines changed

11 files changed

+157
-233
lines changed

src/librustc/session/config.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -735,8 +735,8 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options,
735735
"for every macro invocation, print its name and arguments"),
736736
enable_nonzeroing_move_hints: bool = (false, parse_bool,
737737
"force nonzeroing move optimization on"),
738-
keep_mtwt_tables: bool = (false, parse_bool,
739-
"don't clear the resolution tables after analysis"),
738+
keep_hygiene_data: bool = (false, parse_bool,
739+
"don't clear the hygiene data after analysis"),
740740
keep_ast: bool = (false, parse_bool,
741741
"keep the AST after lowering it to HIR"),
742742
show_span: Option<String> = (None, parse_opt_string,

src/librustc_driver/driver.rs

+7-8
Original file line numberDiff line numberDiff line change
@@ -236,8 +236,8 @@ pub fn compile_input(sess: &Session,
236236
Ok(())
237237
}
238238

239-
fn keep_mtwt_tables(sess: &Session) -> bool {
240-
sess.opts.debugging_opts.keep_mtwt_tables
239+
fn keep_hygiene_data(sess: &Session) -> bool {
240+
sess.opts.debugging_opts.keep_hygiene_data
241241
}
242242

243243
fn keep_ast(sess: &Session) -> bool {
@@ -479,9 +479,8 @@ pub fn phase_1_parse_input<'a>(sess: &'a Session,
479479
input: &Input)
480480
-> PResult<'a, ast::Crate> {
481481
// These may be left in an incoherent state after a previous compile.
482-
// `clear_tables` and `clear_ident_interner` can be used to free
483-
// memory, but they do not restore the initial state.
484-
syntax::ext::mtwt::reset_tables();
482+
syntax::ext::hygiene::reset_hygiene_data();
483+
// `clear_ident_interner` can be used to free memory, but it does not restore the initial state.
485484
token::reset_ident_interner();
486485
let continue_after_error = sess.opts.continue_parse_after_error;
487486
sess.diagnostic().set_continue_after_error(continue_after_error);
@@ -761,9 +760,9 @@ pub fn phase_2_configure_and_expand<'a, F>(sess: &Session,
761760
hir_map::Forest::new(lower_crate(sess, &krate, &mut resolver), &sess.dep_graph)
762761
});
763762

764-
// Discard MTWT tables that aren't required past lowering to HIR.
765-
if !keep_mtwt_tables(sess) {
766-
syntax::ext::mtwt::clear_tables();
763+
// Discard hygiene data, which isn't required past lowering to HIR.
764+
if !keep_hygiene_data(sess) {
765+
syntax::ext::hygiene::reset_hygiene_data();
767766
}
768767

769768
Ok(ExpansionResult {

src/librustc_driver/pretty.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -456,7 +456,7 @@ impl<'ast> pprust::PpAnn for HygieneAnnotation<'ast> {
456456
pp::space(&mut s.s)?;
457457
// FIXME #16420: this doesn't display the connections
458458
// between syntax contexts
459-
s.synth_comment(format!("{}#{}", nm, ctxt.0))
459+
s.synth_comment(format!("{}{:?}", nm, ctxt))
460460
}
461461
pprust::NodeName(&ast::Name(nm)) => {
462462
pp::space(&mut s.s)?;

src/librustc_resolve/assign_ids.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
use Resolver;
1212
use rustc::session::Session;
1313
use syntax::ast;
14-
use syntax::ext::mtwt;
14+
use syntax::ext::hygiene::Mark;
1515
use syntax::fold::{self, Folder};
1616
use syntax::ptr::P;
1717
use syntax::util::move_map::MoveMap;
@@ -31,7 +31,7 @@ impl<'a> Resolver<'a> {
3131

3232
struct NodeIdAssigner<'a> {
3333
sess: &'a Session,
34-
macros_at_scope: &'a mut HashMap<ast::NodeId, Vec<ast::Mrk>>,
34+
macros_at_scope: &'a mut HashMap<ast::NodeId, Vec<Mark>>,
3535
}
3636

3737
impl<'a> Folder for NodeIdAssigner<'a> {
@@ -49,7 +49,7 @@ impl<'a> Folder for NodeIdAssigner<'a> {
4949
block.stmts = block.stmts.move_flat_map(|stmt| {
5050
if let ast::StmtKind::Item(ref item) = stmt.node {
5151
if let ast::ItemKind::Mac(..) = item.node {
52-
macros.push(mtwt::outer_mark(item.ident.ctxt));
52+
macros.push(item.ident.ctxt.data().outer_mark);
5353
return None;
5454
}
5555
}

src/librustc_resolve/lib.rs

+9-11
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ use rustc::ty::subst::{ParamSpace, FnSpace, TypeSpace};
5353
use rustc::hir::{Freevar, FreevarMap, TraitCandidate, TraitMap, GlobMap};
5454
use rustc::util::nodemap::{NodeMap, NodeSet, FnvHashMap, FnvHashSet};
5555

56-
use syntax::ext::mtwt;
56+
use syntax::ext::hygiene::Mark;
5757
use syntax::ast::{self, FloatTy};
5858
use syntax::ast::{CRATE_NODE_ID, Name, NodeId, CrateNum, IntTy, UintTy};
5959
use syntax::parse::token::{self, keywords};
@@ -654,7 +654,7 @@ enum RibKind<'a> {
654654
ModuleRibKind(Module<'a>),
655655

656656
// We passed through a `macro_rules!` statement with the given expansion
657-
MacroDefinition(ast::Mrk),
657+
MacroDefinition(Mark),
658658
}
659659

660660
#[derive(Copy, Clone)]
@@ -933,7 +933,7 @@ pub struct Resolver<'a> {
933933

934934
// Maps the node id of a statement to the expansions of the `macro_rules!`s
935935
// immediately above the statement (if appropriate).
936-
macros_at_scope: HashMap<NodeId, Vec<ast::Mrk>>,
936+
macros_at_scope: HashMap<NodeId, Vec<Mark>>,
937937

938938
graph_root: Module<'a>,
939939

@@ -1434,10 +1434,9 @@ impl<'a> Resolver<'a> {
14341434
if let MacroDefinition(mac) = self.get_ribs(ns)[i].kind {
14351435
// If an invocation of this macro created `ident`, give up on `ident`
14361436
// and switch to `ident`'s source from the macro definition.
1437-
if let Some((source_ident, source_macro)) = mtwt::source(ident) {
1438-
if mac == source_macro {
1439-
ident = source_ident;
1440-
}
1437+
let (source_ctxt, source_macro) = ident.ctxt.source();
1438+
if source_macro == mac {
1439+
ident.ctxt = source_ctxt;
14411440
}
14421441
}
14431442
}
@@ -1585,10 +1584,9 @@ impl<'a> Resolver<'a> {
15851584
MacroDefinition(mac) => {
15861585
// If an invocation of this macro created `ident`, give up on `ident`
15871586
// and switch to `ident`'s source from the macro definition.
1588-
if let Some((source_ident, source_macro)) = mtwt::source(ident) {
1589-
if mac == source_macro {
1590-
ident = source_ident;
1591-
}
1587+
let (source_ctxt, source_macro) = ident.ctxt.source();
1588+
if source_macro == mac {
1589+
ident.ctxt = source_ctxt;
15921590
}
15931591
}
15941592
_ => {

src/libsyntax/ast.rs

+3-19
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ pub use util::ThinVec;
1919
use syntax_pos::{mk_sp, Span, DUMMY_SP, ExpnId};
2020
use codemap::{respan, Spanned};
2121
use abi::Abi;
22+
use ext::hygiene::SyntaxContext;
2223
use parse::token::{self, keywords, InternedString};
2324
use print::pprust;
2425
use ptr::P;
@@ -33,15 +34,6 @@ use serialize::{Encodable, Decodable, Encoder, Decoder};
3334
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
3435
pub struct Name(pub u32);
3536

36-
/// A SyntaxContext represents a chain of macro-expandings
37-
/// and renamings. Each macro expansion corresponds to
38-
/// a fresh u32. This u32 is a reference to a table stored
39-
/// in thread-local storage.
40-
/// The special value EMPTY_CTXT is used to indicate an empty
41-
/// syntax context.
42-
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, RustcEncodable, RustcDecodable)]
43-
pub struct SyntaxContext(pub u32);
44-
4537
/// An identifier contains a Name (index into the interner
4638
/// table) and a SyntaxContext to track renaming and
4739
/// macro expansion per Flatt et al., "Macros That Work Together"
@@ -81,20 +73,15 @@ impl Decodable for Name {
8173
}
8274
}
8375

84-
pub const EMPTY_CTXT : SyntaxContext = SyntaxContext(0);
85-
8676
impl Ident {
87-
pub fn new(name: Name, ctxt: SyntaxContext) -> Ident {
88-
Ident {name: name, ctxt: ctxt}
89-
}
9077
pub const fn with_empty_ctxt(name: Name) -> Ident {
91-
Ident {name: name, ctxt: EMPTY_CTXT}
78+
Ident { name: name, ctxt: SyntaxContext::empty() }
9279
}
9380
}
9481

9582
impl fmt::Debug for Ident {
9683
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
97-
write!(f, "{}#{}", self.name, self.ctxt.0)
84+
write!(f, "{}{:?}", self.name, self.ctxt)
9885
}
9986
}
10087

@@ -116,9 +103,6 @@ impl Decodable for Ident {
116103
}
117104
}
118105

119-
/// A mark represents a unique id associated with a macro expansion
120-
pub type Mrk = u32;
121-
122106
#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Copy)]
123107
pub struct Lifetime {
124108
pub id: NodeId,

src/libsyntax/ext/expand.rs

+15-29
Original file line numberDiff line numberDiff line change
@@ -9,20 +9,19 @@
99
// except according to those terms.
1010

1111
use ast::{Block, Crate, Ident, Mac_, Name, PatKind};
12-
use ast::{MacStmtStyle, Mrk, Stmt, StmtKind, ItemKind};
12+
use ast::{MacStmtStyle, Stmt, StmtKind, ItemKind};
1313
use ast;
14-
use attr::HasAttrs;
15-
use ext::mtwt;
16-
use attr;
14+
use ext::hygiene::Mark;
15+
use attr::{self, HasAttrs};
1716
use attr::AttrMetaMethods;
18-
use codemap::{dummy_spanned, Spanned, ExpnInfo, NameAndSpan, MacroBang, MacroAttribute};
17+
use codemap::{dummy_spanned, ExpnInfo, NameAndSpan, MacroBang, MacroAttribute};
1918
use syntax_pos::{self, Span, ExpnId};
2019
use config::StripUnconfigured;
2120
use ext::base::*;
2221
use feature_gate::{self, Features};
2322
use fold;
2423
use fold::*;
25-
use parse::token::{fresh_mark, intern, keywords};
24+
use parse::token::{intern, keywords};
2625
use ptr::P;
2726
use tokenstream::TokenTree;
2827
use util::small_vector::SmallVector;
@@ -130,9 +129,9 @@ fn expand_mac_invoc<T>(mac: ast::Mac, ident: Option<Ident>, attrs: Vec<ast::Attr
130129
// It would almost certainly be cleaner to pass the whole macro invocation in,
131130
// rather than pulling it apart and marking the tts and the ctxt separately.
132131
let Mac_ { path, tts, .. } = mac.node;
133-
let mark = fresh_mark();
132+
let mark = Mark::fresh();
134133

135-
fn mac_result<'a>(path: &ast::Path, ident: Option<Ident>, tts: Vec<TokenTree>, mark: Mrk,
134+
fn mac_result<'a>(path: &ast::Path, ident: Option<Ident>, tts: Vec<TokenTree>, mark: Mark,
136135
attrs: Vec<ast::Attribute>, call_site: Span, fld: &'a mut MacroExpander)
137136
-> Option<Box<MacResult + 'a>> {
138137
// Detect use of feature-gated or invalid attributes on macro invoations
@@ -708,30 +707,17 @@ pub fn expand_crate(mut cx: ExtCtxt,
708707
return (ret, cx.syntax_env.names);
709708
}
710709

711-
// HYGIENIC CONTEXT EXTENSION:
712-
// all of these functions are for walking over
713-
// ASTs and making some change to the context of every
714-
// element that has one. a CtxtFn is a trait-ified
715-
// version of a closure in (SyntaxContext -> SyntaxContext).
716-
// the ones defined here include:
717-
// Marker - add a mark to a context
718-
719710
// A Marker adds the given mark to the syntax context and
720711
// sets spans' `expn_id` to the given expn_id (unless it is `None`).
721-
struct Marker { mark: Mrk, expn_id: Option<ExpnId> }
712+
struct Marker { mark: Mark, expn_id: Option<ExpnId> }
722713

723714
impl Folder for Marker {
724-
fn fold_ident(&mut self, id: Ident) -> Ident {
725-
ast::Ident::new(id.name, mtwt::apply_mark(self.mark, id.ctxt))
726-
}
727-
fn fold_mac(&mut self, Spanned {node, span}: ast::Mac) -> ast::Mac {
728-
Spanned {
729-
node: Mac_ {
730-
path: self.fold_path(node.path),
731-
tts: self.fold_tts(&node.tts),
732-
},
733-
span: self.new_span(span),
734-
}
715+
fn fold_ident(&mut self, mut ident: Ident) -> Ident {
716+
ident.ctxt = ident.ctxt.apply_mark(self.mark);
717+
ident
718+
}
719+
fn fold_mac(&mut self, mac: ast::Mac) -> ast::Mac {
720+
noop_fold_mac(mac, self)
735721
}
736722

737723
fn new_span(&mut self, mut span: Span) -> Span {
@@ -743,7 +729,7 @@ impl Folder for Marker {
743729
}
744730

745731
// apply a given mark to the given token trees. Used prior to expansion of a macro.
746-
fn mark_tts(tts: &[TokenTree], m: Mrk) -> Vec<TokenTree> {
732+
fn mark_tts(tts: &[TokenTree], m: Mark) -> Vec<TokenTree> {
747733
noop_fold_tts(tts, &mut Marker{mark:m, expn_id: None})
748734
}
749735

src/libsyntax/ext/hygiene.rs

+116
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
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.
4+
//
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.
10+
11+
//! Machinery for hygienic macros, inspired by the MTWT[1] paper.
12+
//!
13+
//! [1] Matthew Flatt, Ryan Culpepper, David Darais, and Robert Bruce Findler.
14+
//! 2012. *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 http://dx.doi.org/10.1017/S0956796812000093
17+
18+
use std::cell::RefCell;
19+
use std::collections::HashMap;
20+
use std::fmt;
21+
22+
/// A SyntaxContext represents a chain of macro expansions (represented by marks).
23+
#[derive(Clone, Copy, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable, Default)]
24+
pub struct SyntaxContext(u32);
25+
26+
#[derive(Copy, Clone)]
27+
pub struct SyntaxContextData {
28+
pub outer_mark: Mark,
29+
pub prev_ctxt: SyntaxContext,
30+
}
31+
32+
/// A mark represents a unique id associated with a macro expansion.
33+
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Default)]
34+
pub struct Mark(u32);
35+
36+
impl Mark {
37+
pub fn fresh() -> Self {
38+
HygieneData::with(|data| {
39+
let next_mark = Mark(data.next_mark.0 + 1);
40+
::std::mem::replace(&mut data.next_mark, next_mark)
41+
})
42+
}
43+
}
44+
45+
struct HygieneData {
46+
syntax_contexts: Vec<SyntaxContextData>,
47+
markings: HashMap<(SyntaxContext, Mark), SyntaxContext>,
48+
next_mark: Mark,
49+
}
50+
51+
impl HygieneData {
52+
fn new() -> Self {
53+
HygieneData {
54+
syntax_contexts: vec![SyntaxContextData {
55+
outer_mark: Mark(0), // the null mark
56+
prev_ctxt: SyntaxContext(0), // the empty context
57+
}],
58+
markings: HashMap::new(),
59+
next_mark: Mark(1),
60+
}
61+
}
62+
63+
fn with<T, F: FnOnce(&mut HygieneData) -> T>(f: F) -> T {
64+
thread_local! {
65+
static HYGIENE_DATA: RefCell<HygieneData> = RefCell::new(HygieneData::new());
66+
}
67+
HYGIENE_DATA.with(|data| f(&mut *data.borrow_mut()))
68+
}
69+
}
70+
71+
pub fn reset_hygiene_data() {
72+
HygieneData::with(|data| *data = HygieneData::new())
73+
}
74+
75+
impl SyntaxContext {
76+
pub const fn empty() -> Self {
77+
SyntaxContext(0)
78+
}
79+
80+
pub fn data(self) -> SyntaxContextData {
81+
HygieneData::with(|data| data.syntax_contexts[self.0 as usize])
82+
}
83+
84+
/// Extend a syntax context with a given mark
85+
pub fn apply_mark(self, mark: Mark) -> SyntaxContext {
86+
// Applying the same mark twice is a no-op
87+
let ctxt_data = self.data();
88+
if mark == ctxt_data.outer_mark {
89+
return ctxt_data.prev_ctxt;
90+
}
91+
92+
HygieneData::with(|data| {
93+
let syntax_contexts = &mut data.syntax_contexts;
94+
*data.markings.entry((self, mark)).or_insert_with(|| {
95+
syntax_contexts.push(SyntaxContextData {
96+
outer_mark: mark,
97+
prev_ctxt: self,
98+
});
99+
SyntaxContext(syntax_contexts.len() as u32 - 1)
100+
})
101+
})
102+
}
103+
104+
/// If `ident` is macro expanded, return the source ident from the macro definition
105+
/// and the mark of the expansion that created the macro definition.
106+
pub fn source(self) -> (Self /* source context */, Mark /* source macro */) {
107+
let macro_def_ctxt = self.data().prev_ctxt.data();
108+
(macro_def_ctxt.prev_ctxt, macro_def_ctxt.outer_mark)
109+
}
110+
}
111+
112+
impl fmt::Debug for SyntaxContext {
113+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
114+
write!(f, "#{}", self.0)
115+
}
116+
}

0 commit comments

Comments
 (0)