Skip to content
63 changes: 61 additions & 2 deletions src/conditional-compilation.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ r[cfg.intro]
*Conditionally compiled source code* is source code that is compiled only under certain conditions.

r[cfg.attributes-macro]
Source code can be made conditionally compiled using the [`cfg`] and [`cfg_attr`] [attributes] and the built-in [`cfg` macro].
Source code can be made conditionally compiled using the [`cfg`] and [`cfg_attr`] [attributes] and the built-in [`cfg!`] and [`cfg_select!`] [macros].

r[cfg.conditional]
Whether to compile can depend on the target architecture of the compiled crate, arbitrary values passed to the compiler, and other things further described below.
Expand Down Expand Up @@ -436,17 +436,76 @@ let machine_kind = if cfg!(unix) {
println!("I'm running on a {} machine!", machine_kind);
```

r[cfg.cfg_select]
### The `cfg_select` macro

r[cfg.cfg_select.intro]
The built-in [`cfg_select!`][std::cfg_select] macro can be used to select code at compile-time based on multiple configuration predicates.

> [!EXAMPLE]
> ```rust
> 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",
> };
> ```

r[cfg.cfg_select.syntax]
```grammar,configuration
@root CfgSelect -> CfgSelectArms?

CfgSelectArms ->
CfgSelectConfigurationPredicate `=>`
(
`{` ^ TokenTree `}` `,`? CfgSelectArms?
| ExpressionWithBlockNoAttrs `,`? CfgSelectArms?
| ExpressionWithoutBlockNoAttrs ( `,` CfgSelectArms? )?
)

CfgSelectConfigurationPredicate ->
ConfigurationPredicate | `_`
```
Comment on lines 466 to 479
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This grammar doesn't quite look correct to me.

Some comments:

  • We don't have a precedent for documenting macro inputs. I don't think we can express it like `cfg_select!` `{` … `}` because the braces can also be () or [] (also ! is a separate token), and the macro name can be renamed. I would suggest just documenting what the input to the macro is, and not the surrounding invocation syntax.
  • This doesn't handle the difference in behavior of the last branch.
  • This doesn't specify that the non-braced value needs to be an expression of a certain kind.
  • This doesn't handle the optional , behavior.

I'm thinking the grammar would be something closer to:

@root CfgSelect ->
    CfgSelectBranch*
    LastCfgSelectBranch?

CfgSelectBranch ->
    CfgSelectConfigurationPredicate `=>` `{` TokenTree `}`
  | CfgSelectConfigurationPredicate `=>` ExpressionWithBlockX `,`?
  | CfgSelectConfigurationPredicate `=>` ExpressionWithoutBlockX `,`

ExpressionWithBlockX ->
      BlockExpression
    | ConstBlockExpression
    | UnsafeBlockExpression
    | LoopExpression
    | IfExpression
    | MatchExpression

ExpressionWithoutBlockX ->
      LiteralExpression
    | PathExpression
    | OperatorExpression
    | GroupedExpression
    | ArrayExpression
    | AwaitExpression
    | IndexExpression
    | TupleExpression
    | TupleIndexingExpression
    | StructExpression
    | CallExpression
    | MethodCallExpression
    | FieldExpression
    | ClosureExpression
    | AsyncBlockExpression
    | ContinueExpression
    | BreakExpression
    | RangeExpression
    | ReturnExpression
    | UnderscoreExpression
    | MacroInvocation

LastCfgSelectBranch ->
    CfgSelectConfigurationPredicate => ExpressionX `,`?

ExpressionX -> ExpressionWithBlockX | ExpressionWithoutBlockX

CfgSelectConfigurationPredicate ->
    ConfigurationPredicate | `_`

Where the X expressions are from the Expression grammar, but without the outer attributes. If this is correct, we'll need to rework Expression to support that.

Does that make sense?

Copy link
Contributor

@ehuss ehuss Jan 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Though looking again, my suggestion above won't work because we are moving towards a grammar that does not have infinite lookahead for disambiguation. So the LastCfgSelectBranch won't work. I offhand can't think of a way to actually express that...

(Maybe lookahead is required?)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pushed something that might be closer.


r[cfg.cfg_select.first-arm]
`cfg_select` expands to the payload of the first arm whose configuration predicate evaluates to true.

r[cfg.cfg_select.braces]
If the entire payload is wrapped in curly braces, the braces are removed during expansion.

r[cfg.cfg_select.wildcard]
The configuration predicate `_` always evaluates to true.

r[cfg.cfg_select.fallthrough]
It is a compile error if none of the predicates evaluate to true.

r[cfg.cfg_select.well-formed]
Each right-hand side must be a syntactically valid expansion for the position in which the macro is invoked.

[Testing]: attributes/testing.md
[`--cfg`]: ../rustc/command-line-arguments.html#--cfg-configure-the-compilation-environment
[`--test`]: ../rustc/command-line-arguments.html#--test-build-a-test-harness
[`cfg`]: #the-cfg-attribute
[`cfg` macro]: #the-cfg-macro
[`cfg!`]: #the-cfg-macro
[`cfg_attr`]: #the-cfg_attr-attribute
[`cfg_select!`]: #the-cfg_select-macro
[`crate_name`]: crates-and-source-files.md#the-crate_name-attribute
[`crate_type`]: linkage.md
[`target_feature` attribute]: attributes/codegen.md#the-target_feature-attribute
[attribute]: attributes.md
[attributes]: attributes.md
[cargo-feature]: ../cargo/reference/features.html
[crate type]: linkage.md
[macros]: macros.md
[static C runtime]: linkage.md#static-and-dynamic-c-runtimes
66 changes: 33 additions & 33 deletions src/expressions.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,41 +8,41 @@ Expression ->
| ExpressionWithBlock

ExpressionWithoutBlock ->
OuterAttribute*
(
LiteralExpression
| PathExpression
| OperatorExpression
| GroupedExpression
| ArrayExpression
| AwaitExpression
| IndexExpression
| TupleExpression
| TupleIndexingExpression
| StructExpression
| CallExpression
| MethodCallExpression
| FieldExpression
| ClosureExpression
| AsyncBlockExpression
| ContinueExpression
| BreakExpression
| RangeExpression
| ReturnExpression
| UnderscoreExpression
| MacroInvocation
)
OuterAttribute* ExpressionWithoutBlockNoAttrs

ExpressionWithoutBlockNoAttrs ->
LiteralExpression
| PathExpression
| OperatorExpression
| GroupedExpression
| ArrayExpression
| AwaitExpression
| IndexExpression
| TupleExpression
| TupleIndexingExpression
| StructExpression
| CallExpression
| MethodCallExpression
| FieldExpression
| ClosureExpression
| AsyncBlockExpression
| ContinueExpression
| BreakExpression
| RangeExpression
| ReturnExpression
| UnderscoreExpression
| MacroInvocation

ExpressionWithBlock ->
OuterAttribute*
(
BlockExpression
| ConstBlockExpression
| UnsafeBlockExpression
| LoopExpression
| IfExpression
| MatchExpression
)
OuterAttribute* ExpressionWithBlockNoAttrs

ExpressionWithBlockNoAttrs ->
BlockExpression
| ConstBlockExpression
| UnsafeBlockExpression
| LoopExpression
| IfExpression
| MatchExpression
```

r[expr.intro]
Expand Down
Loading