expand: Tell built-in macros whether we are currently in forced expansion mode

This commit is contained in:
Vadim Petrochenkov 2020-11-14 14:47:14 +03:00
parent 7e2032390c
commit b49fbc9432
5 changed files with 42 additions and 40 deletions

View File

@ -1,7 +1,7 @@
//! Implementation of the `#[cfg_accessible(path)]` attribute macro. //! Implementation of the `#[cfg_accessible(path)]` attribute macro.
use rustc_ast as ast; use rustc_ast as ast;
use rustc_expand::base::{Annotatable, ExpandResult, ExtCtxt, MultiItemModifier}; use rustc_expand::base::{Annotatable, ExpandResult, ExtCtxt, Indeterminate, MultiItemModifier};
use rustc_feature::AttributeTemplate; use rustc_feature::AttributeTemplate;
use rustc_parse::validate_attr; use rustc_parse::validate_attr;
use rustc_span::symbol::sym; use rustc_span::symbol::sym;
@ -31,7 +31,7 @@ impl MultiItemModifier for Expander {
fn expand( fn expand(
&self, &self,
ecx: &mut ExtCtxt<'_>, ecx: &mut ExtCtxt<'_>,
_span: Span, span: Span,
meta_item: &ast::MetaItem, meta_item: &ast::MetaItem,
item: Annotatable, item: Annotatable,
) -> ExpandResult<Vec<Annotatable>, Annotatable> { ) -> ExpandResult<Vec<Annotatable>, Annotatable> {
@ -49,11 +49,14 @@ impl MultiItemModifier for Expander {
None => return ExpandResult::Ready(Vec::new()), None => return ExpandResult::Ready(Vec::new()),
}; };
let failure_msg = "cannot determine whether the path is accessible or not";
match ecx.resolver.cfg_accessible(ecx.current_expansion.id, path) { match ecx.resolver.cfg_accessible(ecx.current_expansion.id, path) {
Ok(true) => ExpandResult::Ready(vec![item]), Ok(true) => ExpandResult::Ready(vec![item]),
Ok(false) => ExpandResult::Ready(Vec::new()), Ok(false) => ExpandResult::Ready(Vec::new()),
Err(_) => ExpandResult::Retry(item, failure_msg.into()), Err(Indeterminate) if ecx.force_mode => {
ecx.span_err(span, "cannot determine whether the path is accessible or not");
ExpandResult::Ready(vec![item])
}
Err(Indeterminate) => ExpandResult::Retry(item),
} }
} }
} }

View File

@ -251,8 +251,7 @@ pub enum ExpandResult<T, U> {
/// Expansion produced a result (possibly dummy). /// Expansion produced a result (possibly dummy).
Ready(T), Ready(T),
/// Expansion could not produce a result and needs to be retried. /// Expansion could not produce a result and needs to be retried.
/// The string is an explanation that will be printed if we are stuck in an infinite retry loop. Retry(U),
Retry(U, String),
} }
// `meta_item` is the attribute, and `item` is the item being modified. // `meta_item` is the attribute, and `item` is the item being modified.
@ -919,6 +918,9 @@ pub struct ExtCtxt<'a> {
pub root_path: PathBuf, pub root_path: PathBuf,
pub resolver: &'a mut dyn ResolverExpand, pub resolver: &'a mut dyn ResolverExpand,
pub current_expansion: ExpansionData, pub current_expansion: ExpansionData,
/// Error recovery mode entered when expansion is stuck
/// (or during eager expansion, but that's a hack).
pub force_mode: bool,
pub expansions: FxHashMap<Span, Vec<String>>, pub expansions: FxHashMap<Span, Vec<String>>,
/// Called directly after having parsed an external `mod foo;` in expansion. /// Called directly after having parsed an external `mod foo;` in expansion.
pub(super) extern_mod_loaded: Option<&'a dyn Fn(&ast::Crate)>, pub(super) extern_mod_loaded: Option<&'a dyn Fn(&ast::Crate)>,
@ -945,6 +947,7 @@ impl<'a> ExtCtxt<'a> {
directory_ownership: DirectoryOwnership::Owned { relative: None }, directory_ownership: DirectoryOwnership::Owned { relative: None },
prior_type_ascription: None, prior_type_ascription: None,
}, },
force_mode: false,
expansions: FxHashMap::default(), expansions: FxHashMap::default(),
} }
} }

View File

@ -404,6 +404,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
// Recursively expand all macro invocations in this AST fragment. // Recursively expand all macro invocations in this AST fragment.
pub fn fully_expand_fragment(&mut self, input_fragment: AstFragment) -> AstFragment { pub fn fully_expand_fragment(&mut self, input_fragment: AstFragment) -> AstFragment {
let orig_expansion_data = self.cx.current_expansion.clone(); let orig_expansion_data = self.cx.current_expansion.clone();
let orig_force_mode = self.cx.force_mode;
self.cx.current_expansion.depth = 0; self.cx.current_expansion.depth = 0;
// Collect all macro invocations and replace them with placeholders. // Collect all macro invocations and replace them with placeholders.
@ -432,6 +433,12 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
} }
invocations = mem::take(&mut undetermined_invocations); invocations = mem::take(&mut undetermined_invocations);
force = !mem::replace(&mut progress, false); force = !mem::replace(&mut progress, false);
if force && self.monotonic {
self.cx.sess.delay_span_bug(
invocations.last().unwrap().0.span(),
"expansion entered force mode without producing any errors",
);
}
continue; continue;
}; };
@ -460,18 +467,18 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
let ExpansionData { depth, id: expn_id, .. } = invoc.expansion_data; let ExpansionData { depth, id: expn_id, .. } = invoc.expansion_data;
self.cx.current_expansion = invoc.expansion_data.clone(); self.cx.current_expansion = invoc.expansion_data.clone();
self.cx.force_mode = force;
// FIXME(jseyfried): Refactor out the following logic // FIXME(jseyfried): Refactor out the following logic
let (expanded_fragment, new_invocations) = match res { let (expanded_fragment, new_invocations) = match res {
InvocationRes::Single(ext) => match self.expand_invoc(invoc, &ext.kind) { InvocationRes::Single(ext) => match self.expand_invoc(invoc, &ext.kind) {
ExpandResult::Ready(fragment) => self.collect_invocations(fragment, &[]), ExpandResult::Ready(fragment) => self.collect_invocations(fragment, &[]),
ExpandResult::Retry(invoc, explanation) => { ExpandResult::Retry(invoc) => {
if force { if force {
// We are stuck, stop retrying and produce a dummy fragment. self.cx.span_bug(
let span = invoc.span(); invoc.span(),
self.cx.span_err(span, &explanation); "expansion entered force mode but is still stuck",
let fragment = invoc.fragment_kind.dummy(span); );
self.collect_invocations(fragment, &[])
} else { } else {
// Cannot expand, will retry this invocation later. // Cannot expand, will retry this invocation later.
undetermined_invocations undetermined_invocations
@ -526,6 +533,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
} }
self.cx.current_expansion = orig_expansion_data; self.cx.current_expansion = orig_expansion_data;
self.cx.force_mode = orig_force_mode;
// Finally incorporate all the expanded macros into the input AST fragment. // Finally incorporate all the expanded macros into the input AST fragment.
let mut placeholder_expander = PlaceholderExpander::new(self.cx, self.monotonic); let mut placeholder_expander = PlaceholderExpander::new(self.cx, self.monotonic);
@ -735,20 +743,17 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
Ok(meta) => { Ok(meta) => {
let items = match expander.expand(self.cx, span, &meta, item) { let items = match expander.expand(self.cx, span, &meta, item) {
ExpandResult::Ready(items) => items, ExpandResult::Ready(items) => items,
ExpandResult::Retry(item, explanation) => { ExpandResult::Retry(item) => {
// Reassemble the original invocation for retrying. // Reassemble the original invocation for retrying.
return ExpandResult::Retry( return ExpandResult::Retry(Invocation {
Invocation { kind: InvocationKind::Attr {
kind: InvocationKind::Attr { attr,
attr, item,
item, derives,
derives, after_derive,
after_derive,
},
..invoc
}, },
explanation, ..invoc
); });
} }
}; };
fragment_kind.expect_from_annotatables(items) fragment_kind.expect_from_annotatables(items)
@ -781,15 +786,12 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
let meta = ast::MetaItem { kind: ast::MetaItemKind::Word, span, path }; let meta = ast::MetaItem { kind: ast::MetaItemKind::Word, span, path };
let items = match expander.expand(self.cx, span, &meta, item) { let items = match expander.expand(self.cx, span, &meta, item) {
ExpandResult::Ready(items) => items, ExpandResult::Ready(items) => items,
ExpandResult::Retry(item, explanation) => { ExpandResult::Retry(item) => {
// Reassemble the original invocation for retrying. // Reassemble the original invocation for retrying.
return ExpandResult::Retry( return ExpandResult::Retry(Invocation {
Invocation { kind: InvocationKind::Derive { path: meta.path, item },
kind: InvocationKind::Derive { path: meta.path, item }, ..invoc
..invoc });
},
explanation,
);
} }
}; };
fragment_kind.expect_from_annotatables(items) fragment_kind.expect_from_annotatables(items)

View File

@ -1,6 +1,6 @@
#![feature(cfg_accessible)] #![feature(cfg_accessible)]
#[cfg_accessible(Z)] //~ ERROR cannot determine whether the path is accessible or not #[cfg_accessible(Z)] // OK, recovered after the other `cfg_accessible` produces an error.
struct S; struct S;
#[cfg_accessible(S)] //~ ERROR cannot determine whether the path is accessible or not #[cfg_accessible(S)] //~ ERROR cannot determine whether the path is accessible or not

View File

@ -4,11 +4,5 @@ error: cannot determine whether the path is accessible or not
LL | #[cfg_accessible(S)] LL | #[cfg_accessible(S)]
| ^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^
error: cannot determine whether the path is accessible or not error: aborting due to previous error
--> $DIR/cfg_accessible-stuck.rs:3:1
|
LL | #[cfg_accessible(Z)]
| ^^^^^^^^^^^^^^^^^^^^
error: aborting due to 2 previous errors