Repository navigation
Description
I tried this code: playpen
#[cfg(target_feature="mmx")]
compile_fail!("we have mmx");I expected to see this happen:
This code uses a feature-gated cfg(, without enabling the feature. I would expect this to produce a build warning about using an unstable cfg gate, and produce no build error on all channels.
(target_feature="mmx" is currently gated behind mmx_target_feature as far as I can tell)
| ("mmx", Some(sym::mmx_target_feature)), |
Instead, this happened:
On stable, the gate was ignored as expected, but on the nightly channel the cfg check passed, causing the compile_fail! to be included in the source. The lack of a feature gate enabling mmx_target_feature was ignored, only considering the current release channel.
I believe this is occurring due to the feature gate logic for the target_feature config flag not being checked within the compiler. This mismatch lead me to believe that an unstable target_feature flag was actually stable, and write code using it, before realizing it won't function outside of the nightly channel.
Meta
rustc --version --verbose:
stable:
rustc 1.46.0 (04488afe3 2020-08-24)
binary: rustc
commit-hash: 04488afe34512aa4c33566eb16d8c912a3ae04f9
commit-date: 2020-08-24
host: x86_64-unknown-linux-gnu
release: 1.46.0
LLVM version: 10.0
nightly:
rustc 1.47.0-nightly (bf4342114 2020-08-25)
binary: rustc
commit-hash: bf4342114e357f2934d59e12e31e94532ddb2adf
commit-date: 2020-08-25
host: x86_64-unknown-linux-gnu
release: 1.47.0-nightly
LLVM version: 11.0
(this also occurs on rust-playpen at time of filing: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=4c58960ac6fc536a2da930d6c0b3d8e9)
Activity
m-ou-se commentedon Sep 17, 2020
Looks like all feature gated target_features are made available on nightly regardless of enabled features, here:
rust/compiler/rustc_codegen_llvm/src/llvm_util.rs
Lines 292 to 300 in 7bdb5de
[-]Feature gate for unstable target_feature values not being checked[/-][+]Feature gate for unstable target_feature values not being checked on nightly[/+]m-ou-se commentedon Sep 17, 2020
It seems like all target_features were always available on nightly when
cfg_target_featurewas still unstable. So,cfg(target_feature = "mmx")was not gated behindmmx_target_feature, but only behindcfg_target_feature. #49664 stabilizedcfg_target_feature, and added the lines I quoted above to let only stable target_features (notmmx) through on stable, but didn't change the behaviour on nightly. So that's when the 'unstable'cfg(target_feature = "mmx")suddenly became ungated.RalfJung commentedon Oct 23, 2026
On nightly rustc, all unstable target features get added to
cfg(target_feature), and-Ctarget-featureaccepts all unstable target features, without any kind of opt-in via-Zor#![feature]or so.This is tricky to fix because
#![feature]is parsed too late to affectcfgor-Cparsing. Do we have any othercfgwhere the values of thecfgshould differ based on whether one uses a nightly rustc? This is quite different from requiring nightly to use a particularcfgvariable at all -- usingtarget_featureis obviously fine, but whether or not it is populated with unstable target features should somehow be opt-in. Not sure how that could be done.Cc @Urgau @bjorn3
[-]Feature gate for unstable target_feature values not being checked on nightly[/-][+]unstable target_feature can be used in `cfg(target_feature)` and `-Ctarget-feature` on nightly without any kind of opt-in / feature gate[/+]Urgau commentedon Oct 23, 2026
We have multiple cfgs who are only set on nightly (not sure if they count towards your question):
fmt_debug,target_has_atomic/target_has_atomic_load_storeandrelocation_model.RalfJung commentedon Oct 24, 2026
Looking at fmt_debug I see that's guarded by
-Zfmt-debug. So maybe we should just have-Zunstable-target-featureswhich then has two effectscfg(target_feature)-Ctarget-featurewith unstable target featuresThis would ignore the actual nightly feature that is associated with that target feature. We'd still require that for
#[target_feature]. We could have people say-Zunstable-target-features=riscv_target_featurebut that seems kind of annoying.@rust-lang/lang does that seem reasonable to you? And would you prefer the variant with
-Zunstable-target-featuresunlocking all unstable target features or with having to list feature gates?RalfJung commentedon Oct 24, 2026
OTOH for
target_has_atomic, just the mere fact that one uses nightly already changes the value of thecfg. That does not seem great? EDIT: Ah, that is #133295.relocation_modelseems to have a check elsewhere that rejects looking at it withoutfeature(cfg_relocation_model). So while the coe makes no justification for why it's okay to have nightly-dependent behavior here, it seems to be fine.Mark-Simulacrum commentedon Oct 28, 2026
For in-code cfgs, it seems like we should be able to use the same principle we use for gating unstable syntax to gate cfgs. Is there a reason we can't / don't do that, to allow
#![feature(...)]to gate these cfgs "as expected"? Lang generally felt that this would be desirable to do if possible. (This would feature-gate regardless of whether the feature is actually set).For the command-line enabling, we felt that is possibly more of a compiler question but requiring some -Z flag felt not-unreasonable.
RalfJung commentedon Oct 28, 2026
We currently gate some cfg variables but not some cfg values. Values are an open-ended set. So today you can write
cfg(target_feature = "foobar")(it always evaluates to false) but if tomorrow an architecture adds a "foobar" target feature then we break such code by requiring a feature gate? What if "foobar" is stable on x86-64 but unstable on aarch64, do we have target-dependent stability checking?Mark-Simulacrum commentedon Nov 4, 2026
Lang discussed again. We generally felt that it may not be a bad idea to have both target-dependent feature checking and to ban writing target_feature = "foobar" (as unstable / not permitted syntax). That has some tradeoffs (as you note) around cross-target testing and the extent to which users need extra annotation / cfg_attr etc.
That said, we'd like to see a Crater run to see what the maximally-restrictive rule here is going to mean for the ecosystem, to see how much flexibility we have in defining the rules. Ideally, that rule would feature-gate cfgs even in short-circuit position (e.g.,
any(true, target_feature = "not yet stable")should get a feature gate error).RalfJung commentedon Nov 4, 2026
And what about
target_feature = "does not exist today but may exist in the future"?That would also have to error, otherwise adding an unstable feature becomes a breaking change. Basically, you seem to be saying that
unknown_cfgsshould become a hard error fortarget_feature?RalfJung commentedon Nov 4, 2026
Also, what about
target_feature = "stable on one architecture but unstable on another"?I don't really see how stability checks are supposed to work here. Note that stability is a function of target features and architectures, but when we see a
target_feature = "foo"in a program, we don't have a good way to find out which architecture the programmer means. Often this will be in a context likeall(target_arch = "...", target_feature = "..."), but those two checks can be arbitrarily far away from each other, and sometimes programmers forget them and assume that the target feature only exists for a single architecture.