Conditional compilation on compiler features

I have been working on a trait that I want to implement for the f128 type and I want it to compile if and only if the f128 feature flag is set. I can't use a crate feature as I don't want the f128 type to not have my trait implemented. I want it to look something like this:

pub trait MyTrait {
    fn my_method(&self) -> MyType;
}

#[cfg(feature = "f128")]
impl MyTrait for f128 {
    fn my_method(&self) -> MyType {
        // ...
    }
}

However this does not work, I found out that the f128 feature is a compiler feature and doesn't work with the #[cfg(...)] attribute macro. I tried some workarounds however they proved effective. I first thought I could construct a proc_macro_attribute that would function similarly to the cfg one, however I was not able to make it work and most similar solutions online (like this one) used the build.rs instead of a custom attribute macro so I tried using that.

Turning to generative ai for help it told me to make a build.rs script using the autocfg crate however it hallucinated a function that don't exist (it told me to use the AutoCfg::probe_feature method which doesn't exist) and upon correction it gave me more hacky solutions which I've learned mean that the model is at a dead end. But I looked into the crate and found the AutoCfg::emit_has_type method that I tried:

// build.rs
fn main() {
    let ac = autocfg::new();

    ac.emit_has_type("f128");
}
// main.rs
fn main() {
    dbg!(cfg!(has_f128));
}

however whether or not I run this with #![feature(f128)] I get the result

cfg!(has_f128) = false

The last thing I tried was I stumbled across the cfg_rust_features crate which gave me hope. So I tried

// build.rs
fn main() {
    let of_interest = ["f128"];
    cfg_rust_features::emit!(of_interest).unwrap();
}

However I get the error:

thread 'main' (786015) panicked at build.rs:5:43:
called Result::unwrap() on an Err value: UnsupportedFeatureTodoError("To request support for feature "f128", open an issue at: GitHub - DerickEddington/cfg_rust_features: Set cfg options according to Rust compiler, language, and library features. ยท GitHub")

Which lead me to inspect the crate source code where I found that it hasn't been updated in 2 years and that the functionality (here) was mostly a list containing named features and a script that autocfg can run to detect the feature.

I don't really know where to go from here, I am pretty familiar with most of the rust programming language however, when it comes to meta-programming I am still rather new to it, as the documentation / support is more sparse. I have done procedural macros but not much past that.

For autocfg, it isn't looking whether your own code has #![feature(f128)], and if that's not in the test code that it uses itself, then the type probe will fail. You can use probe_raw to write your own test code that does include the feature attribute.

I was able to find a solution. I stumbled across the version_check crate which has a method supports_feature which is exactly what I need. My solution has four parts:

// build.rs
fn main() {
    let features = &["f128", "f16"];
    for feature in features {
        if let Some(true) = dbg!(version_check::supports_feature(*feature)) {
            autocfg::emit(*feature);
        } else {
            autocfg::emit_possibility(*feature); // this is supposed to get rid of the 'unexpected `cfg`' warnings but for whatever reason it doesn't
        }
    }
}
// lib.rs
#![cfg_attr(f128, feature(f128))]
#![cfg_attr(f16, feature(f16))]
// ...
// mod.rs
#[cfg(f128)]
impl MyTrait for f128 {
    fn my_method(&self) -> MyType {
        // ...
    }
}
# Cargo.toml
[lints.rust]
unexpected_cfgs = { level = "warn", check-cfg = ['cfg(f128)', 'cfg(f16)'] } # shouldn't need but will still get warnings without

Make that unconditional, rather than only in the else branch.

Do not do this. Users of your library should have control over whether your library uses unstable features. If the build is being performed using a nightly toolchain, that could be for many reasons โ€” such as access to additional compiler flags for debugging โ€” which have nothing to do with use of unstable features. Your library should not magically change its behavior when compiled on a different toolchain.

11 Likes