Skip to content

Commit 552a887

Browse files
committed
expand: Implement support for retrying macro expansions
1 parent eafeb9a commit 552a887

File tree

4 files changed

+124
-54
lines changed

4 files changed

+124
-54
lines changed

src/librustc_builtin_macros/deriving/mod.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
33
use rustc_ast::ast::{self, ItemKind, MetaItem};
44
use rustc_ast::ptr::P;
5-
use rustc_expand::base::{Annotatable, ExtCtxt, MultiItemModifier};
5+
use rustc_expand::base::{Annotatable, ExpandResult, ExtCtxt, MultiItemModifier};
66
use rustc_span::symbol::{sym, Symbol};
77
use rustc_span::Span;
88

@@ -48,13 +48,13 @@ impl MultiItemModifier for BuiltinDerive {
4848
span: Span,
4949
meta_item: &MetaItem,
5050
item: Annotatable,
51-
) -> Vec<Annotatable> {
51+
) -> ExpandResult<Vec<Annotatable>, Annotatable> {
5252
// FIXME: Built-in derives often forget to give spans contexts,
5353
// so we are doing it here in a centralized way.
5454
let span = ecx.with_def_site_ctxt(span);
5555
let mut items = Vec::new();
5656
(self.0)(ecx, span, meta_item, &item, &mut |a| items.push(a));
57-
items
57+
ExpandResult::Ready(items)
5858
}
5959
}
6060

src/librustc_expand/base.rs

+14-5
Original file line numberDiff line numberDiff line change
@@ -258,16 +258,25 @@ impl Annotatable {
258258
}
259259
}
260260

261-
// `meta_item` is the annotation, and `item` is the item being modified.
262-
// FIXME Decorators should follow the same pattern too.
261+
/// Result of an expansion that may need to be retried.
262+
/// Consider using this for non-`MultiItemModifier` expanders as well.
263+
pub enum ExpandResult<T, U> {
264+
/// Expansion produced a result (possibly dummy).
265+
Ready(T),
266+
/// Expansion could not produce a result and needs to be retried.
267+
/// The string is an explanation that will be printed if we are stuck in an infinite retry loop.
268+
Retry(U, String),
269+
}
270+
271+
// `meta_item` is the attribute, and `item` is the item being modified.
263272
pub trait MultiItemModifier {
264273
fn expand(
265274
&self,
266275
ecx: &mut ExtCtxt<'_>,
267276
span: Span,
268277
meta_item: &ast::MetaItem,
269278
item: Annotatable,
270-
) -> Vec<Annotatable>;
279+
) -> ExpandResult<Vec<Annotatable>, Annotatable>;
271280
}
272281

273282
impl<F> MultiItemModifier for F
@@ -280,8 +289,8 @@ where
280289
span: Span,
281290
meta_item: &ast::MetaItem,
282291
item: Annotatable,
283-
) -> Vec<Annotatable> {
284-
self(ecx, span, meta_item, item)
292+
) -> ExpandResult<Vec<Annotatable>, Annotatable> {
293+
ExpandResult::Ready(self(ecx, span, meta_item, item))
285294
}
286295
}
287296

src/librustc_expand/expand.rs

+103-42
Original file line numberDiff line numberDiff line change
@@ -408,7 +408,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
408408
let mut undetermined_invocations = Vec::new();
409409
let (mut progress, mut force) = (false, !self.monotonic);
410410
loop {
411-
let invoc = if let Some(invoc) = invocations.pop() {
411+
let (invoc, res) = if let Some(invoc) = invocations.pop() {
412412
invoc
413413
} else {
414414
self.resolve_imports();
@@ -420,30 +420,51 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
420420
continue;
421421
};
422422

423-
let eager_expansion_root =
424-
if self.monotonic { invoc.expansion_data.id } else { orig_expansion_data.id };
425-
let res = match self.cx.resolver.resolve_macro_invocation(
426-
&invoc,
427-
eager_expansion_root,
428-
force,
429-
) {
430-
Ok(res) => res,
431-
Err(Indeterminate) => {
432-
undetermined_invocations.push(invoc);
433-
continue;
423+
let res = match res {
424+
Some(res) => res,
425+
None => {
426+
let eager_expansion_root = if self.monotonic {
427+
invoc.expansion_data.id
428+
} else {
429+
orig_expansion_data.id
430+
};
431+
match self.cx.resolver.resolve_macro_invocation(
432+
&invoc,
433+
eager_expansion_root,
434+
force,
435+
) {
436+
Ok(res) => res,
437+
Err(Indeterminate) => {
438+
// Cannot resolve, will retry this invocation later.
439+
undetermined_invocations.push((invoc, None));
440+
continue;
441+
}
442+
}
434443
}
435444
};
436445

437-
progress = true;
438446
let ExpansionData { depth, id: expn_id, .. } = invoc.expansion_data;
439447
self.cx.current_expansion = invoc.expansion_data.clone();
440448

441449
// FIXME(jseyfried): Refactor out the following logic
442450
let (expanded_fragment, new_invocations) = match res {
443-
InvocationRes::Single(ext) => {
444-
let fragment = self.expand_invoc(invoc, &ext.kind);
445-
self.collect_invocations(fragment, &[])
446-
}
451+
InvocationRes::Single(ext) => match self.expand_invoc(invoc, &ext.kind) {
452+
ExpandResult::Ready(fragment) => self.collect_invocations(fragment, &[]),
453+
ExpandResult::Retry(invoc, explanation) => {
454+
if force {
455+
// We are stuck, stop retrying and produce a dummy fragment.
456+
let span = invoc.span();
457+
self.cx.span_err(span, &explanation);
458+
let fragment = invoc.fragment_kind.dummy(span);
459+
self.collect_invocations(fragment, &[])
460+
} else {
461+
// Cannot expand, will retry this invocation later.
462+
undetermined_invocations
463+
.push((invoc, Some(InvocationRes::Single(ext))));
464+
continue;
465+
}
466+
}
467+
},
447468
InvocationRes::DeriveContainer(_exts) => {
448469
// FIXME: Consider using the derive resolutions (`_exts`) immediately,
449470
// instead of enqueuing the derives to be resolved again later.
@@ -463,21 +484,25 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
463484
for path in derives {
464485
let expn_id = ExpnId::fresh(None);
465486
derive_placeholders.push(NodeId::placeholder_from_expn_id(expn_id));
466-
invocations.push(Invocation {
467-
kind: InvocationKind::Derive { path, item: item.clone() },
468-
fragment_kind: invoc.fragment_kind,
469-
expansion_data: ExpansionData {
470-
id: expn_id,
471-
..invoc.expansion_data.clone()
487+
invocations.push((
488+
Invocation {
489+
kind: InvocationKind::Derive { path, item: item.clone() },
490+
fragment_kind: invoc.fragment_kind,
491+
expansion_data: ExpansionData {
492+
id: expn_id,
493+
..invoc.expansion_data.clone()
494+
},
472495
},
473-
});
496+
None,
497+
));
474498
}
475499
let fragment =
476500
invoc.fragment_kind.expect_from_annotatables(::std::iter::once(item));
477501
self.collect_invocations(fragment, &derive_placeholders)
478502
}
479503
};
480504

505+
progress = true;
481506
if expanded_fragments.len() < depth {
482507
expanded_fragments.push(Vec::new());
483508
}
@@ -535,7 +560,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
535560
&mut self,
536561
mut fragment: AstFragment,
537562
extra_placeholders: &[NodeId],
538-
) -> (AstFragment, Vec<Invocation>) {
563+
) -> (AstFragment, Vec<(Invocation, Option<InvocationRes>)>) {
539564
// Resolve `$crate`s in the fragment for pretty-printing.
540565
self.cx.resolver.resolve_dollar_crates();
541566

@@ -635,13 +660,17 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
635660
self.cx.trace_macros_diag();
636661
}
637662

638-
fn expand_invoc(&mut self, invoc: Invocation, ext: &SyntaxExtensionKind) -> AstFragment {
663+
fn expand_invoc(
664+
&mut self,
665+
invoc: Invocation,
666+
ext: &SyntaxExtensionKind,
667+
) -> ExpandResult<AstFragment, Invocation> {
639668
if self.cx.current_expansion.depth > self.cx.ecfg.recursion_limit {
640669
self.error_recursion_limit_reached();
641670
}
642671

643672
let (fragment_kind, span) = (invoc.fragment_kind, invoc.span());
644-
match invoc.kind {
673+
ExpandResult::Ready(match invoc.kind {
645674
InvocationKind::Bang { mac, .. } => match ext {
646675
SyntaxExtensionKind::Bang(expander) => {
647676
self.gate_proc_macro_expansion_kind(span, fragment_kind);
@@ -663,7 +692,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
663692
}
664693
_ => unreachable!(),
665694
},
666-
InvocationKind::Attr { attr, mut item, .. } => match ext {
695+
InvocationKind::Attr { attr, mut item, derives, after_derive } => match ext {
667696
SyntaxExtensionKind::Attr(expander) => {
668697
self.gate_proc_macro_input(&item);
669698
self.gate_proc_macro_attr_item(span, &item);
@@ -679,8 +708,25 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
679708
SyntaxExtensionKind::LegacyAttr(expander) => {
680709
match validate_attr::parse_meta(self.cx.parse_sess, &attr) {
681710
Ok(meta) => {
682-
let item = expander.expand(self.cx, span, &meta, item);
683-
fragment_kind.expect_from_annotatables(item)
711+
let items = match expander.expand(self.cx, span, &meta, item) {
712+
ExpandResult::Ready(items) => items,
713+
ExpandResult::Retry(item, explanation) => {
714+
// Reassemble the original invocation for retrying.
715+
return ExpandResult::Retry(
716+
Invocation {
717+
kind: InvocationKind::Attr {
718+
attr,
719+
item,
720+
derives,
721+
after_derive,
722+
},
723+
..invoc
724+
},
725+
explanation,
726+
);
727+
}
728+
};
729+
fragment_kind.expect_from_annotatables(items)
684730
}
685731
Err(mut err) => {
686732
err.emit();
@@ -702,19 +748,31 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
702748
SyntaxExtensionKind::Derive(expander)
703749
| SyntaxExtensionKind::LegacyDerive(expander) => {
704750
if !item.derive_allowed() {
705-
return fragment_kind.dummy(span);
751+
return ExpandResult::Ready(fragment_kind.dummy(span));
706752
}
707753
if let SyntaxExtensionKind::Derive(..) = ext {
708754
self.gate_proc_macro_input(&item);
709755
}
710756
let meta = ast::MetaItem { kind: ast::MetaItemKind::Word, span, path };
711-
let items = expander.expand(self.cx, span, &meta, item);
757+
let items = match expander.expand(self.cx, span, &meta, item) {
758+
ExpandResult::Ready(items) => items,
759+
ExpandResult::Retry(item, explanation) => {
760+
// Reassemble the original invocation for retrying.
761+
return ExpandResult::Retry(
762+
Invocation {
763+
kind: InvocationKind::Derive { path: meta.path, item },
764+
..invoc
765+
},
766+
explanation,
767+
);
768+
}
769+
};
712770
fragment_kind.expect_from_annotatables(items)
713771
}
714772
_ => unreachable!(),
715773
},
716774
InvocationKind::DeriveContainer { .. } => unreachable!(),
717-
}
775+
})
718776
}
719777

720778
fn gate_proc_macro_attr_item(&self, span: Span, item: &Annotatable) {
@@ -933,7 +991,7 @@ pub fn ensure_complete_parse<'a>(
933991
struct InvocationCollector<'a, 'b> {
934992
cx: &'a mut ExtCtxt<'b>,
935993
cfg: StripUnconfigured<'a>,
936-
invocations: Vec<Invocation>,
994+
invocations: Vec<(Invocation, Option<InvocationRes>)>,
937995
monotonic: bool,
938996
}
939997

@@ -955,15 +1013,18 @@ impl<'a, 'b> InvocationCollector<'a, 'b> {
9551013
};
9561014
let expn_id = ExpnId::fresh(expn_data);
9571015
let vis = kind.placeholder_visibility();
958-
self.invocations.push(Invocation {
959-
kind,
960-
fragment_kind,
961-
expansion_data: ExpansionData {
962-
id: expn_id,
963-
depth: self.cx.current_expansion.depth + 1,
964-
..self.cx.current_expansion.clone()
1016+
self.invocations.push((
1017+
Invocation {
1018+
kind,
1019+
fragment_kind,
1020+
expansion_data: ExpansionData {
1021+
id: expn_id,
1022+
depth: self.cx.current_expansion.depth + 1,
1023+
..self.cx.current_expansion.clone()
1024+
},
9651025
},
966-
});
1026+
None,
1027+
));
9671028
placeholder(fragment_kind, NodeId::placeholder_from_expn_id(expn_id), vis)
9681029
}
9691030

src/librustc_expand/proc_macro.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ impl MultiItemModifier for ProcMacroDerive {
7979
span: Span,
8080
_meta_item: &ast::MetaItem,
8181
item: Annotatable,
82-
) -> Vec<Annotatable> {
82+
) -> ExpandResult<Vec<Annotatable>, Annotatable> {
8383
let item = match item {
8484
Annotatable::Arm(..)
8585
| Annotatable::Field(..)
@@ -99,7 +99,7 @@ impl MultiItemModifier for ProcMacroDerive {
9999
"proc-macro derives may only be \
100100
applied to a struct, enum, or union",
101101
);
102-
return Vec::new();
102+
return ExpandResult::Ready(Vec::new());
103103
}
104104
};
105105
match item.kind {
@@ -110,7 +110,7 @@ impl MultiItemModifier for ProcMacroDerive {
110110
"proc-macro derives may only be \
111111
applied to a struct, enum, or union",
112112
);
113-
return Vec::new();
113+
return ExpandResult::Ready(Vec::new());
114114
}
115115
}
116116

@@ -158,7 +158,7 @@ impl MultiItemModifier for ProcMacroDerive {
158158
FatalError.raise();
159159
}
160160

161-
items
161+
ExpandResult::Ready(items)
162162
}
163163
}
164164

0 commit comments

Comments
 (0)