Skip to content

Commit d75c64d

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 e1d0de8 + 400a4f2 commit d75c64d

File tree

12 files changed

+895
-755
lines changed

12 files changed

+895
-755
lines changed

src/librustc_driver/driver.rs

+4-16
Original file line numberDiff line numberDiff line change
@@ -551,7 +551,7 @@ pub struct ExpansionResult<'a> {
551551
/// Returns `None` if we're aborting after handling -W help.
552552
pub fn phase_2_configure_and_expand<'a, F>(sess: &Session,
553553
cstore: &CStore,
554-
mut krate: ast::Crate,
554+
krate: ast::Crate,
555555
registry: Option<Registry>,
556556
crate_name: &'a str,
557557
addl_plugins: Option<Vec<String>>,
@@ -562,21 +562,9 @@ pub fn phase_2_configure_and_expand<'a, F>(sess: &Session,
562562
{
563563
let time_passes = sess.time_passes();
564564

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

581569
*sess.crate_types.borrow_mut() = collect_crate_types(sess, &krate.attrs);
582570
*sess.crate_disambiguator.borrow_mut() =

src/libsyntax/config.rs

+63-105
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,13 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11-
use attr::HasAttrs;
11+
use attr::{self, HasAttrs};
1212
use feature_gate::{emit_feature_err, EXPLAIN_STMT_ATTR_SYNTAX, Features, get_features, GateIssue};
13-
use fold::Folder;
14-
use {fold, attr};
1513
use ast;
1614
use codemap::{Spanned, respan};
17-
use parse::{ParseSess, token};
15+
use parse::ParseSess;
1816
use ptr::P;
1917

20-
use util::small_vector::SmallVector;
21-
2218
/// A folder that strips out items that do not belong in the current configuration.
2319
pub struct StripUnconfigured<'a> {
2420
pub config: &'a ast::CrateConfig,
@@ -27,8 +23,42 @@ pub struct StripUnconfigured<'a> {
2723
pub features: Option<&'a Features>,
2824
}
2925

26+
// `cfg_attr`-process the crate's attributes and compute the crate's features.
27+
pub fn features(mut krate: ast::Crate, sess: &ParseSess, should_test: bool)
28+
-> (ast::Crate, Features) {
29+
let features;
30+
{
31+
let mut strip_unconfigured = StripUnconfigured {
32+
config: &krate.config.clone(),
33+
should_test: should_test,
34+
sess: sess,
35+
features: None,
36+
};
37+
38+
let unconfigured_attrs = krate.attrs.clone();
39+
let err_count = sess.span_diagnostic.err_count();
40+
if let Some(attrs) = strip_unconfigured.configure(krate.attrs) {
41+
krate.attrs = attrs;
42+
} else { // the entire crate is unconfigured
43+
krate.attrs = Vec::new();
44+
krate.module.items = Vec::new();
45+
return (krate, Features::new());
46+
}
47+
48+
features = get_features(&sess.span_diagnostic, &krate.attrs);
49+
50+
// Avoid reconfiguring malformed `cfg_attr`s
51+
if err_count == sess.span_diagnostic.err_count() {
52+
strip_unconfigured.features = Some(&features);
53+
strip_unconfigured.configure(unconfigured_attrs);
54+
}
55+
}
56+
57+
(krate, features)
58+
}
59+
3060
impl<'a> StripUnconfigured<'a> {
31-
fn configure<T: HasAttrs>(&mut self, node: T) -> Option<T> {
61+
pub fn configure<T: HasAttrs>(&mut self, node: T) -> Option<T> {
3262
let node = self.process_cfg_attrs(node);
3363
if self.in_cfg(node.attrs()) { Some(node) } else { None }
3464
}
@@ -123,65 +153,35 @@ impl<'a> StripUnconfigured<'a> {
123153
}
124154
}
125155
}
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-
};
140156

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-
}
152-
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 {
157+
pub fn configure_foreign_mod(&mut self, foreign_mod: ast::ForeignMod) -> ast::ForeignMod {
158158
ast::ForeignMod {
159159
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(),
160+
items: foreign_mod.items.into_iter().filter_map(|item| self.configure(item)).collect(),
163161
}
164162
}
165163

166-
fn fold_item_kind(&mut self, item: ast::ItemKind) -> ast::ItemKind {
167-
let fold_struct = |this: &mut Self, vdata| match vdata {
164+
fn configure_variant_data(&mut self, vdata: ast::VariantData) -> ast::VariantData {
165+
match vdata {
168166
ast::VariantData::Struct(fields, id) => {
169-
let fields = fields.into_iter().filter_map(|field| this.configure(field));
167+
let fields = fields.into_iter().filter_map(|field| self.configure(field));
170168
ast::VariantData::Struct(fields.collect(), id)
171169
}
172170
ast::VariantData::Tuple(fields, id) => {
173-
let fields = fields.into_iter().filter_map(|field| this.configure(field));
171+
let fields = fields.into_iter().filter_map(|field| self.configure(field));
174172
ast::VariantData::Tuple(fields.collect(), id)
175173
}
176174
ast::VariantData::Unit(id) => ast::VariantData::Unit(id)
177-
};
175+
}
176+
}
178177

179-
let item = match item {
178+
pub fn configure_item_kind(&mut self, item: ast::ItemKind) -> ast::ItemKind {
179+
match item {
180180
ast::ItemKind::Struct(def, generics) => {
181-
ast::ItemKind::Struct(fold_struct(self, def), generics)
181+
ast::ItemKind::Struct(self.configure_variant_data(def), generics)
182182
}
183183
ast::ItemKind::Union(def, generics) => {
184-
ast::ItemKind::Union(fold_struct(self, def), generics)
184+
ast::ItemKind::Union(self.configure_variant_data(def), generics)
185185
}
186186
ast::ItemKind::Enum(def, generics) => {
187187
let variants = def.variants.into_iter().filter_map(|v| {
@@ -190,7 +190,7 @@ impl<'a> fold::Folder for StripUnconfigured<'a> {
190190
node: ast::Variant_ {
191191
name: v.node.name,
192192
attrs: v.node.attrs,
193-
data: fold_struct(self, v.node.data),
193+
data: self.configure_variant_data(v.node.data),
194194
disr_expr: v.node.disr_expr,
195195
},
196196
span: v.span
@@ -202,12 +202,19 @@ impl<'a> fold::Folder for StripUnconfigured<'a> {
202202
}, generics)
203203
}
204204
item => item,
205-
};
205+
}
206+
}
206207

207-
fold::noop_fold_item_kind(item, self)
208+
pub fn configure_expr_kind(&mut self, expr_kind: ast::ExprKind) -> ast::ExprKind {
209+
if let ast::ExprKind::Match(m, arms) = expr_kind {
210+
let arms = arms.into_iter().filter_map(|a| self.configure(a)).collect();
211+
ast::ExprKind::Match(m, arms)
212+
} else {
213+
expr_kind
214+
}
208215
}
209216

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

213220
// If an expr is valid to cfg away it will have been removed by the
@@ -222,62 +229,13 @@ impl<'a> fold::Folder for StripUnconfigured<'a> {
222229
self.sess.span_diagnostic.span_err(attr.span, msg);
223230
}
224231

225-
let expr = self.process_cfg_attrs(expr);
226-
fold_expr(self, expr)
232+
self.process_cfg_attrs(expr)
227233
}
228234

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))
231-
}
232-
233-
fn fold_stmt(&mut self, stmt: ast::Stmt) -> SmallVector<ast::Stmt> {
235+
pub fn configure_stmt(&mut self, stmt: ast::Stmt) -> Option<ast::Stmt> {
234236
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())
237-
}
238-
239-
fn fold_mac(&mut self, mac: ast::Mac) -> ast::Mac {
240-
fold::noop_fold_mac(mac, self)
241-
}
242-
243-
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())
246-
}
247-
248-
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())
251-
}
252-
253-
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())
237+
self.configure(stmt)
256238
}
257-
258-
fn fold_interpolated(&mut self, nt: token::Nonterminal) -> token::Nonterminal {
259-
// Don't configure interpolated AST (c.f. #34171).
260-
// Interpolated AST will get configured once the surrounding tokens are parsed.
261-
nt
262-
}
263-
}
264-
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-
})
281239
}
282240

283241
fn is_cfg(attr: &ast::Attribute) -> bool {

0 commit comments

Comments
 (0)