Repository navigation
Tracking issue for cfg_select (formerly cfg_match) #115585
Copy link
Copy link
Closed
Labels
A-cfg_selectArea: `cfg_select!`Area: `cfg_select!`C-tracking-issueCategory: An issue tracking the progress of sth. like the implementation of an RFCCategory: An issue tracking the progress of sth. like the implementation of an RFCI-lang-radarItems that are on lang's radar and will need eventual work or consideration.Items that are on lang's radar and will need eventual work or consideration.T-langRelevant to the language teamRelevant to the language teamT-libs-api[DEPRECATED; DO NOT USE][DEPRECATED; DO NOT USE]
Description
Provides a native way to easily manage multiple conditional flags without having to rewrite each clause multiple times.
Public API
cfg_select! {
unix => {
fn foo() { /* unix specific functionality */ }
}
target_pointer_width = "32" => {
fn foo() { /* non-unix, 32-bit functionality */ }
}
_ => {
fn foo() { /* fallback implementation */ }
}
}
let is_unix_str = cfg_select! {
unix => "unix",
_ => "not unix",
};
println!("{is_unix_str}");Steps / History
- Implementation (Add the
cfg_match!macro #115416)- Rename from
cfg_matchtocfg_selectRenamecfg_match!tocfg_select!#137198 - Support unbraced expressions: cfg_select: Support unbraced expressions #145233
- Rename from
- Final comment period
- Stabilization PR
Unresolved Questions
What should the final syntax be? A match-like syntax feels more natural in the sense that each macro fragment resembles an arm.Should the macro be supported directly by a language feature?What should the feature name be?cfg_matchconflicts with the already existingcfg_matchcrate.How can we support usage in both expression-position and item position?Support trailing commas to have similar grammar as(cfg_select: Support unbraced expressions #145233)matchstatements.
References
- https://crates.io/crates/cfg-match
- https://crates.io/crates/cfg-if
- Tracking issue for
cfg_if#59442 - https://internals.rust-lang.org/t/is-cfg-if-going-to-be-pulled-into-std/13316
Implementation history
- Initiate the inner usage of
cfg_match(Compiler) #116312 - Turn
cfg_matchinto a builtin #116323 - Initiate the inner usage of
cfg_match(Library) #116342 - Remove
cfg_matchfrom the prelude #117162 - [
cfg_match] Generalize inputs #130313 - [cfg_match] Adjust syntax #133720
- [cfg_match] Document the use of expressions. #135625
- [cfg_match] Library edition #136261
- Rename
cfg_match!tocfg_select!#137198 - Make
cfg_match!a semitransparent macro #138993 - Use
cfg_match!in core #138996 - make
cfg_selecta builtin macro #143461 - update
cfg_select!documentation #143941 - cfg_select: Support unbraced expressions #145233
Tooling support
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
A-cfg_selectArea: `cfg_select!`Area: `cfg_select!`C-tracking-issueCategory: An issue tracking the progress of sth. like the implementation of an RFCCategory: An issue tracking the progress of sth. like the implementation of an RFCI-lang-radarItems that are on lang's radar and will need eventual work or consideration.Items that are on lang's radar and will need eventual work or consideration.T-langRelevant to the language teamRelevant to the language teamT-libs-api[DEPRECATED; DO NOT USE][DEPRECATED; DO NOT USE]
Activity
madsmtm commentedon Sep 12, 2023
I think it needs to be considered how this will interact with
doc_auto_cfg?Also, if this is intended to be
match-like, then it seems weird to me that you can omit a fallback implementation? Unless of course all the possible combinations are statically known to be accounted for (which is hard, I know).CAD97 commentedon Sep 25, 2023
Modulo (auto) doc(cfg), this implementation is more complex than it needs to be, since it's only supporting items.
simpler impl
No internal matching arms necessary; just a normal inductive definition over the input syntax. The one tricky difference is that without the extra
#[cfg] constitem in the positive arm, later cfg predicates wouldn't be validated where they would be with the current implementation which always builds the full#[cfg(all(yes, not(any(...))))]matrix out.This can be rather simply adapted to expression position by matching a block and using
breakwith value.It's unfortunate though that implemented without compiler support
cfg_match!is effectively limited to either supporting item position or expression position. Supporting both needn't block supporting only item position (the more necessary position, since expression context can useif cfg!() else ifchains most of the time), but it'd be nice to ensure there's a path forward for a compiler enhanced version which can expand differently between item and expression position, so the macro can be made to just work on both.madsmtm commentedon Sep 25, 2023
Something else to consider: This should be able to detect invalid cfgs like the following (ideally regardless of whether the
cfgis currently enabled or not):thomcc commentedon Sep 25, 2023
I would prefer if it could support both items and expressions. It being part of the stdlib means that implementing it in the compiler would be completely reasonable, and would also make a lint that warns on redundant cfg arms pretty doable.
jhpratt commentedon Sep 25, 2023
For detecting duplicates, it's important to note that this is more difficult than it seems at first. rustdoc has been dealing with this for
doc(cfg).CAD97 commentedon Sep 30, 2023
I tested the current (1.74.0-nightly (e6aabe8b3 2023-09-26)) behavior of doc_auto_cfg: items defined in
cfg_match!do not receive a portability info box. Theoretically cfg dependencies could be tracked through macro expansion, but that's not done currently; cfg dependencies are only recorded on items.Fixing this is actually fairly simple once you understand why this is: instead of expanding to
#[cfg($cfg)] identity! { $($items)* }, splat the cfg directly onto each item with$(#[cfg($cfg)] $items)*. Thematch_cfgcrate does this; thecfg_ifcrate doesn't. A quick scan through the library directory didn't reveal any places where splatting cfg throughcfg_if!would change documentation.I want to additionally point out that doc_auto_cfg fundamentally can only see what items are defined on the current cfg and mark them as only available on whatever cfg this specific implementation is gated to. If you want to expose a portable API, either define a single portable public version of the API (and use private cfg gates to implement it) or use
doc(cfg())to explicitly tell rustdoc what gate it should display as.So what I personally think the macro definition wants to be:
in item position
in expression position
in statement position
This technically is the most flexible spelling just passing through tt sequences; it works perfectly for items and statements (modulo preserving doc_auto_cfg) and can even be used for expressions if wrapped inside an outer block (e.g.
{ cfg_match! { ... } }).There's also the further question of how much syntax checking is expected for inactive arms. The current implementation that matches everything as
$($:item)*does full syntax validation, but a naive implementation forwarding tokens context-antagonistically as$($:tt)*can easily cfg out tokens without ever placing them in a syntax-validated position -- in fact the above statement macro form does this.A far too overly clever implementation:
This does almost always behave as desired by using the arm bodies to try to guess how to expand, but is unreasonably complicated by attempting to do so and as a result causes some very poor errors for reasonable syntax errors.
c410-f3r commentedon Sep 30, 2023
Well, since #115416 (comment) I have been working in transposing
cfg_matchto a builtin in order to easily allow single elements in an arm without brackets. Such thing will also permit any other feature that is being discussed here AFAICT.It is worth noting that the current implementation mimics
cfg_ifand allows the expansion of token trees becausecfgs are applied to macro calls as @CAD97 previously commented.That said, changing the strategy to apply
cfgon each element inside a block will triggerstmt_expr_attributes(https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=4cf4facc19b052053cca655b2b42f782) for expressions and conceivably/possibly/maybe/perhaps, a large set of elements along a large set ofcfgs could affect compilation times. But, it seems to be the correct approach due todoc_auto_cfg.petrochenkov commentedon Oct 11, 2023
FWIW,
#[cfg(TRUE)]not being removed from items during expansion is a bug, and reliance of rustdoc on that bug is very unfortunate.What we need to do is to keep expansion backtraces for
cfgs like we do for all other macros.In that case it won't be important whether a
cfgis placed directly on an item, or somewhere higher in the expansion tree (like on a macro producing that item), it will be available from the macro backtrace anyway.Rollup merge of rust-lang#116312 - c410-f3r:try, r=b-naber
Rollup merge of rust-lang#116312 - c410-f3r:try, r=b-naber
Rollup merge of rust-lang#116312 - c410-f3r:try, r=Mark-Simulacrum
Unrolled build for rust-lang#116312
Auto merge of rust-lang#116342 - c410-f3r:t3st3ss, r=Mark-Simulacrum
148 remaining items
Unrolled build for #149783
Rollup merge of #149783 - folkertdev:stabilize-cfg-select, r=Jonathan…
CI: bump pinned GitHub Actions to latest majors
fix(deps): pin rusqlite to 0.39 to restore MSRV 1.88 / stable build
fix(fuzz): make libsqlite3-sys 0.38 cfg_select error go away
chore: OSSF best-practices compliance fixes + CIFuzz repair
ci(security): ignore RUSTSEC-2026-0176/-0177 via audit.toml — pyo3 0.…
build(toolchain): pin Rust 1.96.1 for stable cfg_select! (sqlite deps)
Rollup merge of rust-lang#154202 - ytmimi:format_cfg_select, r=jieyouxu
Rollup merge of rust-lang#154202 - ytmimi:format_cfg_select, r=jieyouxu
Rollup merge of rust-lang#154202 - ytmimi:format_cfg_select, r=jieyouxu
Rollup merge of rust-lang#154202 - ytmimi:format_cfg_select, r=jieyouxu
Unrolled build for #154202
Rollup merge of #154202 - ytmimi:format_cfg_select, r=jieyouxu
blyxyas commentedon Aug 12, 2026
Should
cfg_selectkeep their expansion markers? We're having problems at Clippy becausecfg_selectresults are indistinguishable of real code (and theSyntaxContextis identical), producing weird warnings.See rust-lang/rust-clippy#17498