Skip to content

Commit 4a26286

Browse files
authored
Auto merge of #36214 - jseyfried:stackless_expansion, r=nrc
macros: stackless expansion After this PR, macro expansion cannot overflow the stack unless the expanded crate is too deep to fold. Everything but the stackless placeholder expansion commit is also groundwork for macro modularization. r? @nrc or @eddyb
2 parents a7b2232 + 9ac91fa commit 4a26286

File tree

12 files changed

+941
-733
lines changed

12 files changed

+941
-733
lines changed

Diff for: src/librustc_driver/driver.rs

+4-16
Original file line numberDiff line numberDiff line change
@@ -555,7 +555,7 @@ pub struct ExpansionResult<'a> {
555555
/// Returns `None` if we're aborting after handling -W help.
556556
pub fn phase_2_configure_and_expand<'a, F>(sess: &Session,
557557
cstore: &CStore,
558-
mut krate: ast::Crate,
558+
krate: ast::Crate,
559559
registry: Option<Registry>,
560560
crate_name: &'a str,
561561
addl_plugins: Option<Vec<String>>,
@@ -566,21 +566,9 @@ pub fn phase_2_configure_and_expand<'a, F>(sess: &Session,
566566
{
567567
let time_passes = sess.time_passes();
568568

569-
// strip before anything else because crate metadata may use #[cfg_attr]
570-
// and so macros can depend on configuration variables, such as
571-
//
572-
// #[macro_use] #[cfg(foo)]
573-
// mod bar { macro_rules! baz!(() => {{}}) }
574-
//
575-
// baz! should not use this definition unless foo is enabled.
576-
577-
krate = time(time_passes, "configuration", || {
578-
let (krate, features) =
579-
syntax::config::strip_unconfigured_items(krate, &sess.parse_sess, sess.opts.test);
580-
// these need to be set "early" so that expansion sees `quote` if enabled.
581-
*sess.features.borrow_mut() = features;
582-
krate
583-
});
569+
let (mut krate, features) = syntax::config::features(krate, &sess.parse_sess, sess.opts.test);
570+
// these need to be set "early" so that expansion sees `quote` if enabled.
571+
*sess.features.borrow_mut() = features;
584572

585573
*sess.crate_types.borrow_mut() = collect_crate_types(sess, &krate.attrs);
586574
*sess.crate_disambiguator.borrow_mut() =

Diff for: src/libsyntax/config.rs

+104-85
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,10 @@
1010

1111
use attr::HasAttrs;
1212
use feature_gate::{emit_feature_err, EXPLAIN_STMT_ATTR_SYNTAX, Features, get_features, GateIssue};
13-
use fold::Folder;
1413
use {fold, attr};
1514
use ast;
1615
use codemap::{Spanned, respan};
17-
use parse::{ParseSess, token};
16+
use parse::ParseSess;
1817
use ptr::P;
1918

2019
use util::small_vector::SmallVector;
@@ -27,8 +26,51 @@ pub struct StripUnconfigured<'a> {
2726
pub features: Option<&'a Features>,
2827
}
2928

29+
// `cfg_attr`-process the crate's attributes and compute the crate's features.
30+
pub fn features(mut krate: ast::Crate, sess: &ParseSess, should_test: bool)
31+
-> (ast::Crate, Features) {
32+
let features;
33+
{
34+
let mut strip_unconfigured = StripUnconfigured {
35+
config: &krate.config.clone(),
36+
should_test: should_test,
37+
sess: sess,
38+
features: None,
39+
};
40+
41+
let unconfigured_attrs = krate.attrs.clone();
42+
let err_count = sess.span_diagnostic.err_count();
43+
if let Some(attrs) = strip_unconfigured.configure(krate.attrs) {
44+
krate.attrs = attrs;
45+
} else { // the entire crate is unconfigured
46+
krate.attrs = Vec::new();
47+
krate.module.items = Vec::new();
48+
return (krate, Features::new());
49+
}
50+
51+
features = get_features(&sess.span_diagnostic, &krate.attrs);
52+
53+
// Avoid reconfiguring malformed `cfg_attr`s
54+
if err_count == sess.span_diagnostic.err_count() {
55+
strip_unconfigured.features = Some(&features);
56+
strip_unconfigured.configure(unconfigured_attrs);
57+
}
58+
}
59+
60+
(krate, features)
61+
}
62+
63+
macro_rules! configure {
64+
($this:ident, $node:ident) => {
65+
match $this.configure($node) {
66+
Some(node) => node,
67+
None => return Default::default(),
68+
}
69+
}
70+
}
71+
3072
impl<'a> StripUnconfigured<'a> {
31-
fn configure<T: HasAttrs>(&mut self, node: T) -> Option<T> {
73+
pub fn configure<T: HasAttrs>(&mut self, node: T) -> Option<T> {
3274
let node = self.process_cfg_attrs(node);
3375
if self.in_cfg(node.attrs()) { Some(node) } else { None }
3476
}
@@ -123,65 +165,35 @@ impl<'a> StripUnconfigured<'a> {
123165
}
124166
}
125167
}
126-
}
127-
128-
// Support conditional compilation by transforming the AST, stripping out
129-
// any items that do not belong in the current configuration
130-
pub fn strip_unconfigured_items(mut krate: ast::Crate, sess: &ParseSess, should_test: bool)
131-
-> (ast::Crate, Features) {
132-
let features;
133-
{
134-
let mut strip_unconfigured = StripUnconfigured {
135-
config: &krate.config.clone(),
136-
should_test: should_test,
137-
sess: sess,
138-
features: None,
139-
};
140-
141-
let err_count = sess.span_diagnostic.err_count();
142-
let krate_attrs = strip_unconfigured.configure(krate.attrs.clone()).unwrap_or_default();
143-
features = get_features(&sess.span_diagnostic, &krate_attrs);
144-
if err_count < sess.span_diagnostic.err_count() {
145-
krate.attrs = krate_attrs.clone(); // Avoid reconfiguring malformed `cfg_attr`s
146-
}
147-
148-
strip_unconfigured.features = Some(&features);
149-
krate = strip_unconfigured.fold_crate(krate);
150-
krate.attrs = krate_attrs;
151-
}
152168

153-
(krate, features)
154-
}
155-
156-
impl<'a> fold::Folder for StripUnconfigured<'a> {
157-
fn fold_foreign_mod(&mut self, foreign_mod: ast::ForeignMod) -> ast::ForeignMod {
169+
pub fn configure_foreign_mod(&mut self, foreign_mod: ast::ForeignMod) -> ast::ForeignMod {
158170
ast::ForeignMod {
159171
abi: foreign_mod.abi,
160-
items: foreign_mod.items.into_iter().filter_map(|item| {
161-
self.configure(item).map(|item| fold::noop_fold_foreign_item(item, self))
162-
}).collect(),
172+
items: foreign_mod.items.into_iter().filter_map(|item| self.configure(item)).collect(),
163173
}
164174
}
165175

166-
fn fold_item_kind(&mut self, item: ast::ItemKind) -> ast::ItemKind {
167-
let fold_struct = |this: &mut Self, vdata| match vdata {
176+
fn configure_variant_data(&mut self, vdata: ast::VariantData) -> ast::VariantData {
177+
match vdata {
168178
ast::VariantData::Struct(fields, id) => {
169-
let fields = fields.into_iter().filter_map(|field| this.configure(field));
179+
let fields = fields.into_iter().filter_map(|field| self.configure(field));
170180
ast::VariantData::Struct(fields.collect(), id)
171181
}
172182
ast::VariantData::Tuple(fields, id) => {
173-
let fields = fields.into_iter().filter_map(|field| this.configure(field));
183+
let fields = fields.into_iter().filter_map(|field| self.configure(field));
174184
ast::VariantData::Tuple(fields.collect(), id)
175185
}
176186
ast::VariantData::Unit(id) => ast::VariantData::Unit(id)
177-
};
187+
}
188+
}
178189

179-
let item = match item {
190+
pub fn configure_item_kind(&mut self, item: ast::ItemKind) -> ast::ItemKind {
191+
match item {
180192
ast::ItemKind::Struct(def, generics) => {
181-
ast::ItemKind::Struct(fold_struct(self, def), generics)
193+
ast::ItemKind::Struct(self.configure_variant_data(def), generics)
182194
}
183195
ast::ItemKind::Union(def, generics) => {
184-
ast::ItemKind::Union(fold_struct(self, def), generics)
196+
ast::ItemKind::Union(self.configure_variant_data(def), generics)
185197
}
186198
ast::ItemKind::Enum(def, generics) => {
187199
let variants = def.variants.into_iter().filter_map(|v| {
@@ -190,7 +202,7 @@ impl<'a> fold::Folder for StripUnconfigured<'a> {
190202
node: ast::Variant_ {
191203
name: v.node.name,
192204
attrs: v.node.attrs,
193-
data: fold_struct(self, v.node.data),
205+
data: self.configure_variant_data(v.node.data),
194206
disr_expr: v.node.disr_expr,
195207
},
196208
span: v.span
@@ -202,12 +214,19 @@ impl<'a> fold::Folder for StripUnconfigured<'a> {
202214
}, generics)
203215
}
204216
item => item,
205-
};
217+
}
218+
}
206219

207-
fold::noop_fold_item_kind(item, self)
220+
pub fn configure_expr_kind(&mut self, expr_kind: ast::ExprKind) -> ast::ExprKind {
221+
if let ast::ExprKind::Match(m, arms) = expr_kind {
222+
let arms = arms.into_iter().filter_map(|a| self.configure(a)).collect();
223+
ast::ExprKind::Match(m, arms)
224+
} else {
225+
expr_kind
226+
}
208227
}
209228

210-
fn fold_expr(&mut self, expr: P<ast::Expr>) -> P<ast::Expr> {
229+
pub fn configure_expr(&mut self, expr: P<ast::Expr>) -> P<ast::Expr> {
211230
self.visit_stmt_or_expr_attrs(expr.attrs());
212231

213232
// If an expr is valid to cfg away it will have been removed by the
@@ -222,64 +241,64 @@ impl<'a> fold::Folder for StripUnconfigured<'a> {
222241
self.sess.span_diagnostic.span_err(attr.span, msg);
223242
}
224243

225-
let expr = self.process_cfg_attrs(expr);
226-
fold_expr(self, expr)
244+
self.process_cfg_attrs(expr)
227245
}
228246

229-
fn fold_opt_expr(&mut self, expr: P<ast::Expr>) -> Option<P<ast::Expr>> {
230-
self.configure(expr).map(|expr| fold_expr(self, expr))
247+
pub fn configure_stmt(&mut self, stmt: ast::Stmt) -> Option<ast::Stmt> {
248+
self.visit_stmt_or_expr_attrs(stmt.attrs());
249+
self.configure(stmt)
231250
}
251+
}
232252

233-
fn fold_stmt(&mut self, stmt: ast::Stmt) -> SmallVector<ast::Stmt> {
234-
self.visit_stmt_or_expr_attrs(stmt.attrs());
235-
self.configure(stmt).map(|stmt| fold::noop_fold_stmt(stmt, self))
236-
.unwrap_or(SmallVector::zero())
253+
impl<'a> fold::Folder for StripUnconfigured<'a> {
254+
fn fold_foreign_mod(&mut self, foreign_mod: ast::ForeignMod) -> ast::ForeignMod {
255+
let foreign_mod = self.configure_foreign_mod(foreign_mod);
256+
fold::noop_fold_foreign_mod(foreign_mod, self)
237257
}
238258

239-
fn fold_mac(&mut self, mac: ast::Mac) -> ast::Mac {
240-
fold::noop_fold_mac(mac, self)
259+
fn fold_item_kind(&mut self, item: ast::ItemKind) -> ast::ItemKind {
260+
let item = self.configure_item_kind(item);
261+
fold::noop_fold_item_kind(item, self)
262+
}
263+
264+
fn fold_expr(&mut self, expr: P<ast::Expr>) -> P<ast::Expr> {
265+
let mut expr = self.configure_expr(expr).unwrap();
266+
expr.node = self.configure_expr_kind(expr.node);
267+
P(fold::noop_fold_expr(expr, self))
268+
}
269+
270+
fn fold_opt_expr(&mut self, expr: P<ast::Expr>) -> Option<P<ast::Expr>> {
271+
let mut expr = configure!(self, expr).unwrap();
272+
expr.node = self.configure_expr_kind(expr.node);
273+
Some(P(fold::noop_fold_expr(expr, self)))
274+
}
275+
276+
fn fold_stmt(&mut self, stmt: ast::Stmt) -> SmallVector<ast::Stmt> {
277+
match self.configure_stmt(stmt) {
278+
Some(stmt) => fold::noop_fold_stmt(stmt, self),
279+
None => return SmallVector::zero(),
280+
}
241281
}
242282

243283
fn fold_item(&mut self, item: P<ast::Item>) -> SmallVector<P<ast::Item>> {
244-
self.configure(item).map(|item| fold::noop_fold_item(item, self))
245-
.unwrap_or(SmallVector::zero())
284+
fold::noop_fold_item(configure!(self, item), self)
246285
}
247286

248287
fn fold_impl_item(&mut self, item: ast::ImplItem) -> SmallVector<ast::ImplItem> {
249-
self.configure(item).map(|item| fold::noop_fold_impl_item(item, self))
250-
.unwrap_or(SmallVector::zero())
288+
fold::noop_fold_impl_item(configure!(self, item), self)
251289
}
252290

253291
fn fold_trait_item(&mut self, item: ast::TraitItem) -> SmallVector<ast::TraitItem> {
254-
self.configure(item).map(|item| fold::noop_fold_trait_item(item, self))
255-
.unwrap_or(SmallVector::zero())
292+
fold::noop_fold_trait_item(configure!(self, item), self)
256293
}
257294

258-
fn fold_interpolated(&mut self, nt: token::Nonterminal) -> token::Nonterminal {
295+
fn fold_mac(&mut self, mac: ast::Mac) -> ast::Mac {
259296
// Don't configure interpolated AST (c.f. #34171).
260297
// Interpolated AST will get configured once the surrounding tokens are parsed.
261-
nt
298+
mac
262299
}
263300
}
264301

265-
fn fold_expr(folder: &mut StripUnconfigured, expr: P<ast::Expr>) -> P<ast::Expr> {
266-
expr.map(|ast::Expr {id, span, node, attrs}| {
267-
fold::noop_fold_expr(ast::Expr {
268-
id: id,
269-
node: match node {
270-
ast::ExprKind::Match(m, arms) => {
271-
ast::ExprKind::Match(m, arms.into_iter()
272-
.filter_map(|a| folder.configure(a))
273-
.collect())
274-
}
275-
_ => node
276-
},
277-
span: span,
278-
attrs: attrs,
279-
}, folder)
280-
})
281-
}
282-
283302
fn is_cfg(attr: &ast::Attribute) -> bool {
284303
attr.check_name("cfg")
285304
}

0 commit comments

Comments
 (0)