Conditional compilation & Const Ord

I'm working on something which has to translate between c-preprocessor definitions and rust cfg()'s. It occurred to me that rust doesn't have any Ord based equivalent to the cpp #if FOO > 1. With the likely explanation that Ord isn't const because const trait i.e. RFC 2632.

Adding const traits alone though wouldn't automatically allow cfg to use it. There would seem to be need for a further addition before const expressions could result in conditional compilation.

I was curious but hadn't found any RFC's regarding this, or whether limiting cfgs to boolean expressions and string comparison was a conscious decision, or currently just a limitation.

1 Like

At least at the moment, limiting the capability of cfg to simple booleans and string equality checks was to my knowledge intentional. I do think we could expand that, with caution, but I'm not sure if we need or want full numeric expressions for instance.

Also, as far as I know, compile-time constant evaluation happens at a later point than cfg evaluation; you can, for instance, use cfg to define different constants. So making cfg depend on constant evaluation could lead to cycles.

Could you elaborate further on the use case for numeric comparisons in cfg?

1 Like

Yes, essentially at least for the moment this is just used in creating an enum describing system calls, depending upon different kernel configurations, different system calls are supported.

enum Syscalls {
    #[cfg(feature = "foo")]
    Foo = -1,
    #[cfg(feature = "bar")]
    Bar = -2,
    #[cfg(feature = "baz")]
    Baz = -3
   #[cfg(NUM_CPUS > 1)]
   SomeSMPThing = -4
}

It appears that there is already a #define ENABLED_SMP_SUPPORT which is an alias for the numeric support which we can use to expose rust in cfg. But there is discussion of expanding this usage to other things which aren't currently exposed to the syscall layer.

If we expanded this beyond just system calls there would be more which I'm uncertain of their ease of replacing them with a boolean define.

It is more a question that I wanted to be prepared if I need to defend the thought that we should limit this particular c-preprocessor usage to using boolean expressions (at least for the time being unless we do get numeric expressions eventually).

Edit: I should perhaps comment on the expanded use case, which goes beyond what is just selection of what is external public facing API, which is where most of the numeric expressions come into play. In particular it is stuff in rust which you would probably use const generics for, e.g. affecting the size of internal structures and the like I haven't delved deeply enough into it be able to give concrete use cases.

It's also worth noting that what is often done in preprocessor integer comparisons can and is done by build.rs today.

The big one being of course version/feature detection with e.g. autocfg, which turns the integer comparisons into booleans for cfg. This works for anything which is knowable by build.rs.

3 Likes

It would also be useful to be able to specify a minimum Rust version:

#[cfg(rustc_version >= "1.36")]
// or
#[cfg(at_least(rustc_version, "1.36"))]
1 Like

Strings sound to be a bad representation for this. 1.100 is a different comparison if treated as a string or a "version" (unless all strings are to be considered versions). The strategies from rustversion seem to be much more reliable IMO.

5 Likes

The comparison doesn't need to be character-wise. If rustc_version has the hypothetical type SemanticVersion, that type could implement PartialCmp<str> which parses the string prior to comparison. Though I guess that is not how this would be implemented, because macros work a bit differently than normal Rust code.

Note there's already an accepted RFC to put that in cargo toml RFC: Minimum Supported Rust Version by newpavlov · Pull Request #2495 · rust-lang/rfcs · GitHub as well as a way to write it in cfg RFC: #[cfg(accessible(..) / version(..))] by Centril · Pull Request #2523 · rust-lang/rfcs · GitHub.

What I described is a slightly different use case: It's for crates that want to use a Rust feature if it is available and fall back to a different behavior otherwise.

How is that different?

The RFC is about the minimum Rust version that is required for the crate to compile. When you try to compile it with an older compiler, it will will issue an error. With a #[cfg(...)] option, you could do something else instead.

That's #[cfg(version(…))], which was linked to in the same comment.

2 Likes

Sorry, I didn't see the second link.