Repository navigation
How do crate-level macro attributes deal with standard library prelude? #110082
Description
Activity
- addedA-resolveArea: Name/path resolution done by `rustc_resolve` specificallyArea: Name/path resolution done by `rustc_resolve` specificallyA-macrosArea: All kinds of macros (custom derive, macro_rules!, proc macros, ..)Area: All kinds of macros (custom derive, macro_rules!, proc macros, ..)A-linkageArea: linking into static, shared libraries and binariesArea: linking into static, shared libraries and binaries
on Apr 8, 2023 Right now the behavior on stable is that
#![cfg(FALSE)]keeps the standard library prelude in place, and the fully unconfigured crate is not ano_stdcrate, and it links to standard library.
Moreover, nominallyno_stdcrate with explicitly written#![no_std]attribute starts linking to standard library if fully unconfigured.#108221 accidentally broke this behavior causing regressions in some
no_stdcrates targeting embedded platforms.I think we can break this behavior intentionally as well, because the crater regression were obtained in a very unnatural setup - crater compiles those crates for x86 host instead of their expected embedded targets making them fully unconfigured.
What is injected right now (edition 2021):
#[prelude_import] use std::prelude::rust_2021::*; #[macro_use] extern crate std; // this std is hygienic
// no_std #[prelude_import] use core::prelude::rust_2021::*; #[macro_use] extern crate core; // this core is hygienic #[macro_use] extern crate compiler_builtins; // this compiler_builtins is hygienic
// no_coreI think a reasonable behavior would be to inject the standard library prelude once after all the crate-level attributes are expanded and the root AST is fixed.
Alternative 1: what exactly is injected would be determined by crate attributes at that point (remaining on the expanded root).
That would remove at least one attribute from the "very early" list at #108221 (comment).For fully unconfigured crates that would mean that they always link
std, even if they are explicitly#![no_std]before configuration.
Alternative 2: what exactly is injected would be determined by the "pre-configured attribute list" determined before any macro expansion (see #108221 for details).
All the relevant attributes would stay on the "very early" list in that case.This would give a choice to fully unconfigured crates to control their
no_stdstatus, if we do the thing that #104633 wants.
#[no_std] #![cfg(FALSE)]- no-std crate,#![cfg(FALSE)] #[no_std]- std crate.Another possibility - change how
#![cfg(FALSE)]works when applied to crate root specifically (both during pre-configuration and regular expansion) - make it remove everything below itself, but not above itself.Crate-level
cfgis already special - it cannot remove the node entirely, unlike with all other locations, it can only cleanup it contents. So this change wouldn't make it significantly more special.
Here I'll talk about hightly unstable crate-level macro attributes first, but this issue is relevant even on stable because crate level
#![cfg(...)]is also such a macro (sort of), and it is supported since Rust 1.0.This case caused issues in #108221.
We assume that macros work with token streams.
Implicitly injected extern prelude does not have any token representation, so I conclude that crate-level macro attributes should only receive explicitly written user code as input (collected during token collection as usual).
Now the question is how and when standard library prelude is added to output of a crate level attribute (including
#![cfg(FALSE)]) because eventually it should be added there.Note: #104633 is a similar issue discussing the fate of
#![feature]attributes on a fully unconfigured crate.