rustc_interface: Add a new query pre_configure

It partially expands crate attributes before the main expansion pass (without modifying the crate), and the produced preliminary crate attribute list is used for querying a few attributes that are required very early.

Crate-level cfg attributes are then expanded normally during the main expansion pass, like attributes on any other nodes.
This commit is contained in:
Vadim Petrochenkov 2023-03-14 16:53:04 +04:00
parent f26da39e04
commit aca1b1e0b3
12 changed files with 108 additions and 84 deletions

View File

@ -8,14 +8,20 @@ use rustc_span::symbol::{kw, sym, Ident, Symbol};
use rustc_span::DUMMY_SP;
use thin_vec::thin_vec;
pub fn inject(krate: &mut ast::Crate, resolver: &mut dyn ResolverExpand, sess: &Session) {
pub fn inject(
krate: &mut ast::Crate,
pre_configured_attrs: &[ast::Attribute],
resolver: &mut dyn ResolverExpand,
sess: &Session,
) -> usize {
let orig_num_items = krate.items.len();
let edition = sess.parse_sess.edition;
// the first name in this list is the crate name of the crate with the prelude
let names: &[Symbol] = if attr::contains_name(&krate.attrs, sym::no_core) {
return;
} else if attr::contains_name(&krate.attrs, sym::no_std) {
if attr::contains_name(&krate.attrs, sym::compiler_builtins) {
let names: &[Symbol] = if attr::contains_name(pre_configured_attrs, sym::no_core) {
return 0;
} else if attr::contains_name(pre_configured_attrs, sym::no_std) {
if attr::contains_name(pre_configured_attrs, sym::compiler_builtins) {
&[sym::core]
} else {
&[sym::core, sym::compiler_builtins]
@ -84,4 +90,5 @@ pub fn inject(krate: &mut ast::Crate, resolver: &mut dyn ResolverExpand, sess: &
);
krate.items.insert(0, use_item);
krate.items.len() - orig_num_items
}

View File

@ -353,7 +353,7 @@ fn run_compiler(
{
let plugins = queries.register_plugins()?;
let (_, lint_store) = &*plugins.borrow();
let (.., lint_store) = &*plugins.borrow();
// Lint plugins are registered; now we can process command line flags.
if sess.opts.describe_lints {

View File

@ -1002,6 +1002,7 @@ pub struct ExpansionData {
pub struct ExtCtxt<'a> {
pub sess: &'a Session,
pub ecfg: expand::ExpansionConfig<'a>,
pub num_standard_library_imports: usize,
pub reduced_recursion_limit: Option<Limit>,
pub root_path: PathBuf,
pub resolver: &'a mut dyn ResolverExpand,
@ -1030,6 +1031,7 @@ impl<'a> ExtCtxt<'a> {
ExtCtxt {
sess,
ecfg,
num_standard_library_imports: 0,
reduced_recursion_limit: None,
resolver,
lint_store,

View File

@ -24,7 +24,6 @@ use rustc_session::Session;
use rustc_span::edition::{Edition, ALL_EDITIONS};
use rustc_span::symbol::{sym, Symbol};
use rustc_span::{Span, DUMMY_SP};
use thin_vec::ThinVec;
/// A folder that strips out items that do not belong in the current configuration.
pub struct StripUnconfigured<'a> {
@ -37,7 +36,7 @@ pub struct StripUnconfigured<'a> {
pub lint_node_id: NodeId,
}
fn get_features(sess: &Session, krate_attrs: &[ast::Attribute]) -> Features {
pub fn features(sess: &Session, krate_attrs: &[Attribute]) -> Features {
fn feature_removed(sess: &Session, span: Span, reason: Option<&str>) {
sess.emit_err(FeatureRemoved {
span,
@ -191,33 +190,16 @@ fn get_features(sess: &Session, krate_attrs: &[ast::Attribute]) -> Features {
features
}
/// `cfg_attr`-process the crate's attributes and compute the crate's features.
pub fn features(sess: &Session, krate: &mut ast::Crate, lint_node_id: NodeId) -> Features {
let mut strip_unconfigured =
StripUnconfigured { sess, features: None, config_tokens: false, lint_node_id };
let mut unconfigured_attrs = krate.attrs.clone();
let diag = &sess.parse_sess.span_diagnostic;
let err_count = diag.err_count();
krate.attrs.flat_map_in_place(|attr| strip_unconfigured.process_cfg_attr(&attr));
if !strip_unconfigured.in_cfg(&krate.attrs) {
// The entire crate is unconfigured.
krate.attrs = ast::AttrVec::new();
krate.items = ThinVec::new();
Features::default()
} else {
let features = get_features(sess, &krate.attrs);
if err_count == diag.err_count() {
// Avoid reconfiguring malformed `cfg_attr`s.
strip_unconfigured.features = Some(&features);
// Run configuration again, this time with features available
// so that we can perform feature-gating.
unconfigured_attrs.flat_map_in_place(|attr| strip_unconfigured.process_cfg_attr(&attr));
strip_unconfigured.in_cfg(&unconfigured_attrs);
}
features
}
pub fn pre_configure_attrs(sess: &Session, attrs: &[Attribute]) -> ast::AttrVec {
let strip_unconfigured = StripUnconfigured {
sess,
features: None,
config_tokens: false,
lint_node_id: ast::CRATE_NODE_ID,
};
let attrs: ast::AttrVec =
attrs.iter().flat_map(|attr| strip_unconfigured.process_cfg_attr(attr)).collect();
if strip_unconfigured.in_cfg(&attrs) { attrs } else { ast::AttrVec::new() }
}
#[macro_export]

View File

@ -1038,6 +1038,9 @@ trait InvocationCollectorNode: HasAttrs + HasNodeId + Sized {
) -> Result<Self::OutputTy, Self> {
Ok(noop_flat_map(node, collector))
}
fn expand_cfg_false(&mut self, collector: &mut InvocationCollector<'_, '_>, span: Span) {
collector.cx.emit_err(RemoveNodeNotSupported { span, descr: Self::descr() });
}
}
impl InvocationCollectorNode for P<ast::Item> {
@ -1378,6 +1381,11 @@ impl InvocationCollectorNode for ast::Crate {
fn noop_visit<V: MutVisitor>(&mut self, visitor: &mut V) {
noop_visit_crate(self, visitor)
}
fn expand_cfg_false(&mut self, collector: &mut InvocationCollector<'_, '_>, _span: Span) {
self.attrs.clear();
// Standard prelude imports are left in the crate for backward compatibility.
self.items.truncate(collector.cx.num_standard_library_imports);
}
}
impl InvocationCollectorNode for P<ast::Ty> {
@ -1756,7 +1764,7 @@ impl<'a, 'b> InvocationCollector<'a, 'b> {
continue;
}
self.cx.emit_err(RemoveNodeNotSupported { span, descr: Node::descr() });
node.expand_cfg_false(self, span);
continue;
}
sym::cfg_attr => {

View File

@ -3,7 +3,6 @@ use crate::interface::{Compiler, Result};
use crate::proc_macro_decls;
use crate::util;
use ast::CRATE_NODE_ID;
use rustc_ast::{self as ast, visit};
use rustc_borrowck as mir_borrowck;
use rustc_codegen_ssa::traits::CodegenBackend;
@ -76,22 +75,14 @@ pub fn register_plugins<'a>(
sess: &'a Session,
metadata_loader: &'a dyn MetadataLoader,
register_lints: impl Fn(&Session, &mut LintStore),
krate: &mut ast::Crate,
pre_configured_attrs: &[ast::Attribute],
crate_name: Symbol,
) -> Result<LintStore> {
sess.time("attributes_injection", || {
rustc_builtin_macros::cmdline_attrs::inject(
krate,
&sess.parse_sess,
&sess.opts.unstable_opts.crate_attr,
)
});
let features = rustc_expand::config::features(sess, krate, CRATE_NODE_ID);
// these need to be set "early" so that expansion sees `quote` if enabled.
let features = rustc_expand::config::features(sess, pre_configured_attrs);
sess.init_features(features);
let crate_types = util::collect_crate_types(sess, &krate.attrs);
let crate_types = util::collect_crate_types(sess, pre_configured_attrs);
sess.init_crate_types(crate_types);
let stable_crate_id = StableCrateId::new(
@ -117,8 +108,9 @@ pub fn register_plugins<'a>(
let mut lint_store = rustc_lint::new_lint_store(sess.enable_internal_lints());
register_lints(sess, &mut lint_store);
let registrars = sess
.time("plugin_loading", || plugin::load::load_plugins(sess, metadata_loader, &krate.attrs));
let registrars = sess.time("plugin_loading", || {
plugin::load::load_plugins(sess, metadata_loader, pre_configured_attrs)
});
sess.time("plugin_registration", || {
let mut registry = plugin::Registry { lint_store: &mut lint_store };
for registrar in registrars {
@ -173,19 +165,29 @@ impl LintStoreExpand for LintStoreExpandImpl<'_> {
/// harness if one is to be provided, injection of a dependency on the
/// standard library and prelude, and name resolution.
#[instrument(level = "trace", skip(krate, resolver))]
fn configure_and_expand(mut krate: ast::Crate, resolver: &mut Resolver<'_, '_>) -> ast::Crate {
fn configure_and_expand(
mut krate: ast::Crate,
pre_configured_attrs: &[ast::Attribute],
resolver: &mut Resolver<'_, '_>,
) -> ast::Crate {
let tcx = resolver.tcx();
let sess = tcx.sess;
let lint_store = unerased_lint_store(tcx);
let crate_name = tcx.crate_name(LOCAL_CRATE);
pre_expansion_lint(sess, lint_store, tcx.registered_tools(()), &krate, crate_name);
let lint_check_node = (&krate, pre_configured_attrs);
pre_expansion_lint(sess, lint_store, tcx.registered_tools(()), lint_check_node, crate_name);
rustc_builtin_macros::register_builtin_macros(resolver);
sess.time("crate_injection", || {
rustc_builtin_macros::standard_library_imports::inject(&mut krate, resolver, sess)
let num_standard_library_imports = sess.time("crate_injection", || {
rustc_builtin_macros::standard_library_imports::inject(
&mut krate,
pre_configured_attrs,
resolver,
sess,
)
});
util::check_attr_crate_type(sess, &krate.attrs, &mut resolver.lint_buffer());
util::check_attr_crate_type(sess, pre_configured_attrs, &mut resolver.lint_buffer());
// Expand all macros
krate = sess.time("macro_expand_crate", || {
@ -222,7 +224,7 @@ fn configure_and_expand(mut krate: ast::Crate, resolver: &mut Resolver<'_, '_>)
// Create the config for macro expansion
let features = sess.features_untracked();
let recursion_limit = get_recursion_limit(&krate.attrs, sess);
let recursion_limit = get_recursion_limit(pre_configured_attrs, sess);
let cfg = rustc_expand::expand::ExpansionConfig {
features: Some(features),
recursion_limit,
@ -235,6 +237,7 @@ fn configure_and_expand(mut krate: ast::Crate, resolver: &mut Resolver<'_, '_>)
let lint_store = LintStoreExpandImpl(lint_store);
let mut ecx = ExtCtxt::new(sess, cfg, resolver, Some(&lint_store));
ecx.num_standard_library_imports = num_standard_library_imports;
// Expand macros now!
let krate = sess.time("expand_crate", || ecx.monotonic_expander().expand_crate(krate));
@ -356,7 +359,7 @@ fn early_lint_checks(tcx: TyCtxt<'_>, (): ()) {
tcx.registered_tools(()),
Some(lint_buffer),
rustc_lint::BuiltinCombinedEarlyLintPass::new(),
&**krate,
(&**krate, &*krate.attrs),
)
}
@ -557,9 +560,9 @@ fn resolver_for_lowering<'tcx>(
) -> &'tcx Steal<(ty::ResolverAstLowering, Lrc<ast::Crate>)> {
let arenas = Resolver::arenas();
let _ = tcx.registered_tools(()); // Uses `crate_for_resolver`.
let krate = tcx.crate_for_resolver(()).steal();
let mut resolver = Resolver::new(tcx, &krate, &arenas);
let krate = configure_and_expand(krate, &mut resolver);
let (krate, pre_configured_attrs) = tcx.crate_for_resolver(()).steal();
let mut resolver = Resolver::new(tcx, &pre_configured_attrs, krate.spans.inner_span, &arenas);
let krate = configure_and_expand(krate, &pre_configured_attrs, &mut resolver);
// Make sure we don't mutate the cstore from here on.
tcx.untracked().cstore.leak();

View File

@ -88,8 +88,9 @@ pub struct Queries<'tcx> {
dep_graph_future: Query<Option<DepGraphFuture>>,
parse: Query<ast::Crate>,
pre_configure: Query<(ast::Crate, ast::AttrVec)>,
crate_name: Query<Symbol>,
register_plugins: Query<(ast::Crate, Lrc<LintStore>)>,
register_plugins: Query<(ast::Crate, ast::AttrVec, Lrc<LintStore>)>,
dep_graph: Query<DepGraph>,
// This just points to what's in `gcx_cell`.
gcx: Query<&'tcx GlobalCtxt<'tcx>>,
@ -106,6 +107,7 @@ impl<'tcx> Queries<'tcx> {
hir_arena: WorkerLocal::new(|_| rustc_hir::Arena::default()),
dep_graph_future: Default::default(),
parse: Default::default(),
pre_configure: Default::default(),
crate_name: Default::default(),
register_plugins: Default::default(),
dep_graph: Default::default(),
@ -133,17 +135,36 @@ impl<'tcx> Queries<'tcx> {
.compute(|| passes::parse(self.session()).map_err(|mut parse_error| parse_error.emit()))
}
pub fn register_plugins(&self) -> Result<QueryResult<'_, (ast::Crate, Lrc<LintStore>)>> {
pub fn pre_configure(&self) -> Result<QueryResult<'_, (ast::Crate, ast::AttrVec)>> {
self.pre_configure.compute(|| {
let mut krate = self.parse()?.steal();
let sess = self.session();
rustc_builtin_macros::cmdline_attrs::inject(
&mut krate,
&sess.parse_sess,
&sess.opts.unstable_opts.crate_attr,
);
let pre_configured_attrs =
rustc_expand::config::pre_configure_attrs(sess, &krate.attrs);
Ok((krate, pre_configured_attrs))
})
}
pub fn register_plugins(
&self,
) -> Result<QueryResult<'_, (ast::Crate, ast::AttrVec, Lrc<LintStore>)>> {
self.register_plugins.compute(|| {
let crate_name = *self.crate_name()?.borrow();
let mut krate = self.parse()?.steal();
let (krate, pre_configured_attrs) = self.pre_configure()?.steal();
let empty: &(dyn Fn(&Session, &mut LintStore) + Sync + Send) = &|_, _| {};
let lint_store = passes::register_plugins(
self.session(),
&*self.codegen_backend().metadata_loader(),
self.compiler.register_lints.as_deref().unwrap_or_else(|| empty),
&mut krate,
&pre_configured_attrs,
crate_name,
)?;
@ -154,17 +175,17 @@ impl<'tcx> Queries<'tcx> {
// called, which happens within passes::register_plugins().
self.dep_graph_future().ok();
Ok((krate, Lrc::new(lint_store)))
Ok((krate, pre_configured_attrs, Lrc::new(lint_store)))
})
}
fn crate_name(&self) -> Result<QueryResult<'_, Symbol>> {
self.crate_name.compute(|| {
Ok({
let parse_result = self.parse()?;
let krate = parse_result.borrow();
let pre_configure_result = self.pre_configure()?;
let (_, pre_configured_attrs) = &*pre_configure_result.borrow();
// parse `#[crate_name]` even if `--crate-name` was passed, to make sure it matches.
find_crate_name(self.session(), &krate.attrs)
find_crate_name(self.session(), pre_configured_attrs)
})
})
}
@ -188,7 +209,7 @@ impl<'tcx> Queries<'tcx> {
pub fn global_ctxt(&'tcx self) -> Result<QueryResult<'_, &'tcx GlobalCtxt<'tcx>>> {
self.gcx.compute(|| {
let crate_name = *self.crate_name()?.borrow();
let (krate, lint_store) = self.register_plugins()?.steal();
let (krate, pre_configured_attrs, lint_store) = self.register_plugins()?.steal();
let sess = self.session();
@ -215,7 +236,7 @@ impl<'tcx> Queries<'tcx> {
feed.crate_name(crate_name);
let feed = tcx.feed_unit_query();
feed.crate_for_resolver(tcx.arena.alloc(Steal::new(krate)));
feed.crate_for_resolver(tcx.arena.alloc(Steal::new((krate, pre_configured_attrs))));
feed.metadata_loader(
tcx.arena.alloc(Steal::new(self.codegen_backend().metadata_loader())),
);

View File

@ -341,7 +341,7 @@ pub trait EarlyCheckNode<'a>: Copy {
'a: 'b;
}
impl<'a> EarlyCheckNode<'a> for &'a ast::Crate {
impl<'a> EarlyCheckNode<'a> for (&'a ast::Crate, &'a [ast::Attribute]) {
fn id(self) -> ast::NodeId {
ast::CRATE_NODE_ID
}
@ -349,15 +349,15 @@ impl<'a> EarlyCheckNode<'a> for &'a ast::Crate {
where
'a: 'b,
{
&self.attrs
&self.1
}
fn check<'b, T: EarlyLintPass>(self, cx: &mut EarlyContextAndPass<'b, T>)
where
'a: 'b,
{
lint_callback!(cx, check_crate, self);
ast_visit::walk_crate(cx, self);
lint_callback!(cx, check_crate_post, self);
lint_callback!(cx, check_crate, self.0);
ast_visit::walk_crate(cx, self.0);
lint_callback!(cx, check_crate_post, self.0);
}
}

View File

@ -36,7 +36,7 @@ macro_rules! arena_types {
)>,
[] output_filenames: std::sync::Arc<rustc_session::config::OutputFilenames>,
[] metadata_loader: rustc_data_structures::steal::Steal<Box<rustc_session::cstore::MetadataLoaderDyn>>,
[] crate_for_resolver: rustc_data_structures::steal::Steal<rustc_ast::ast::Crate>,
[] crate_for_resolver: rustc_data_structures::steal::Steal<(rustc_ast::Crate, rustc_ast::AttrVec)>,
[] resolutions: rustc_middle::ty::ResolverGlobalCtxt,
[decode] unsafety_check_result: rustc_middle::mir::UnsafetyCheckResult,
[decode] code_region: rustc_middle::mir::coverage::CodeRegion,

View File

@ -2116,7 +2116,7 @@ rustc_queries! {
desc { "raw operations for metadata file access" }
}
query crate_for_resolver((): ()) -> &'tcx Steal<rustc_ast::ast::Crate> {
query crate_for_resolver((): ()) -> &'tcx Steal<(rustc_ast::Crate, rustc_ast::AttrVec)> {
feedable
no_hash
desc { "the ast before macro expansion and name resolution" }

View File

@ -1180,7 +1180,8 @@ impl<'tcx> Resolver<'_, 'tcx> {
impl<'a, 'tcx> Resolver<'a, 'tcx> {
pub fn new(
tcx: TyCtxt<'tcx>,
krate: &Crate,
attrs: &[ast::Attribute],
crate_span: Span,
arenas: &'a ResolverArenas<'a>,
) -> Resolver<'a, 'tcx> {
let root_def_id = CRATE_DEF_ID.to_def_id();
@ -1189,8 +1190,8 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
None,
ModuleKind::Def(DefKind::Mod, root_def_id, kw::Empty),
ExpnId::root(),
krate.spans.inner_span,
attr::contains_name(&krate.attrs, sym::no_implicit_prelude),
crate_span,
attr::contains_name(attrs, sym::no_implicit_prelude),
&mut module_map,
);
let empty_module = arenas.new_module(
@ -1222,9 +1223,9 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
.map(|(name, _)| (Ident::from_str(name), Default::default()))
.collect();
if !attr::contains_name(&krate.attrs, sym::no_core) {
if !attr::contains_name(attrs, sym::no_core) {
extern_prelude.insert(Ident::with_dummy_span(sym::core), Default::default());
if !attr::contains_name(&krate.attrs, sym::no_std) {
if !attr::contains_name(attrs, sym::no_std) {
extern_prelude.insert(Ident::with_dummy_span(sym::std), Default::default());
}
}

View File

@ -112,8 +112,8 @@ fn fast_print_path(path: &ast::Path) -> Symbol {
pub(crate) fn registered_tools(tcx: TyCtxt<'_>, (): ()) -> RegisteredTools {
let mut registered_tools = RegisteredTools::default();
let krate = tcx.crate_for_resolver(()).borrow();
for attr in attr::filter_by_name(&krate.attrs, sym::register_tool) {
let (_, pre_configured_attrs) = &*tcx.crate_for_resolver(()).borrow();
for attr in attr::filter_by_name(pre_configured_attrs, sym::register_tool) {
for nested_meta in attr.meta_item_list().unwrap_or_default() {
match nested_meta.ident() {
Some(ident) => {