Skip to content

Commit c1c8f98

Browse files
authored
Rollup merge of rust-lang#137632 - RalfJung:rustdoc-target-features, r=workingjubilee
rustdoc: when merging target features, keep the highest stability This addresses rust-lang#137366. (Not closing since we might consider a backport.) rustdoc wants to pretend that it runs for all targets at once and has all target features, so `tcx.rust_target_features()` will actually be all the target features. For target features that exist on multiple targets, the stability info for one of the targets will be picked (first or last in the list, I guess). All the code consuming that query has to be aware that the data is basically nonsense when running in rustdoc, but the logic checking for unstable or forbidden `#[target_feature]` attributes was not aware of that. This PR makes the `tcx.rust_target_features()` info in rustdoc slightly less nonsensical (and decidedly less random) by having the "most stable" target feature take precedent. That deals with rust-lang#137366 (a conflict between a stable and a "forbidden" target feature of the same name for different targets), and also deals with the situation (that we did not seem to have yet) of a conflict between a stable and an unstable target feature of the same name. Note that if there are two unstable target features of the same name, rustdoc might still require the "wrong" nightly feature to be enabled -- but this can only possibly affect unstable code so I guess we can wait until that actually happens, and then someone will have to rewrite this entire thing to be less hacky.
2 parents cdc47e7 + 4c939db commit c1c8f98

File tree

2 files changed

+72
-12
lines changed

2 files changed

+72
-12
lines changed

compiler/rustc_codegen_ssa/src/target_features.rs

+44-12
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use rustc_middle::query::Providers;
1010
use rustc_middle::ty::TyCtxt;
1111
use rustc_session::parse::feature_err;
1212
use rustc_span::{Span, Symbol, sym};
13-
use rustc_target::target_features;
13+
use rustc_target::target_features::{self, Stability};
1414

1515
use crate::errors;
1616

@@ -87,12 +87,17 @@ pub(crate) fn from_target_feature_attr(
8787
// But ensure the ABI does not forbid enabling this.
8888
// Here we do assume that LLVM doesn't add even more implied features
8989
// we don't know about, at least no features that would have ABI effects!
90-
if abi_feature_constraints.incompatible.contains(&name.as_str()) {
91-
tcx.dcx().emit_err(errors::ForbiddenTargetFeatureAttr {
92-
span: item.span(),
93-
feature: name.as_str(),
94-
reason: "this feature is incompatible with the target ABI",
95-
});
90+
// We skip this logic in rustdoc, where we want to allow all target features of
91+
// all targets, so we can't check their ABI compatibility and anyway we are not
92+
// generating code so "it's fine".
93+
if !tcx.sess.opts.actually_rustdoc {
94+
if abi_feature_constraints.incompatible.contains(&name.as_str()) {
95+
tcx.dcx().emit_err(errors::ForbiddenTargetFeatureAttr {
96+
span: item.span(),
97+
feature: name.as_str(),
98+
reason: "this feature is incompatible with the target ABI",
99+
});
100+
}
96101
}
97102
target_features.push(TargetFeature { name, implied: name != feature_sym })
98103
}
@@ -142,11 +147,38 @@ pub(crate) fn provide(providers: &mut Providers) {
142147
rust_target_features: |tcx, cnum| {
143148
assert_eq!(cnum, LOCAL_CRATE);
144149
if tcx.sess.opts.actually_rustdoc {
145-
// rustdoc needs to be able to document functions that use all the features, so
146-
// whitelist them all
147-
rustc_target::target_features::all_rust_features()
148-
.map(|(a, b)| (a.to_string(), b))
149-
.collect()
150+
// HACK: rustdoc would like to pretend that we have all the target features, so we
151+
// have to merge all the lists into one. To ensure an unstable target never prevents
152+
// a stable one from working, we merge the stability info of all instances of the
153+
// same target feature name, with the "most stable" taking precedence. And then we
154+
// hope that this doesn't cause issues anywhere else in the compiler...
155+
let mut result: UnordMap<String, Stability> = Default::default();
156+
for (name, stability) in rustc_target::target_features::all_rust_features() {
157+
use std::collections::hash_map::Entry;
158+
match result.entry(name.to_owned()) {
159+
Entry::Vacant(vacant_entry) => {
160+
vacant_entry.insert(stability);
161+
}
162+
Entry::Occupied(mut occupied_entry) => {
163+
// Merge the two stabilities, "more stable" taking precedence.
164+
match (occupied_entry.get(), stability) {
165+
(Stability::Stable, _)
166+
| (
167+
Stability::Unstable { .. },
168+
Stability::Unstable { .. } | Stability::Forbidden { .. },
169+
)
170+
| (Stability::Forbidden { .. }, Stability::Forbidden { .. }) => {
171+
// The stability in the entry is at least as good as the new one, just keep it.
172+
}
173+
_ => {
174+
// Overwrite stabilite.
175+
occupied_entry.insert(stability);
176+
}
177+
}
178+
}
179+
}
180+
}
181+
result
150182
} else {
151183
tcx.sess
152184
.target
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
//! This is a regression test for <https://github.com/rust-lang/rust/issues/137366>, ensuring
2+
//! that we can use the `neon` target feature on ARM32 targets in rustdoc despite there
3+
//! being a "forbidden" feature of the same name for aarch64, and rustdoc merging the
4+
//! target features of all targets.
5+
//@ check-pass
6+
//@ revisions: arm aarch64
7+
//@[arm] compile-flags: --target armv7-unknown-linux-gnueabihf
8+
//@[arm] needs-llvm-components: arm
9+
//@[aarch64] compile-flags: --target aarch64-unknown-none-softfloat
10+
//@[aarch64] needs-llvm-components: aarch64
11+
12+
#![crate_type = "lib"]
13+
#![feature(no_core, lang_items)]
14+
#![feature(arm_target_feature)]
15+
#![no_core]
16+
17+
#[lang = "sized"]
18+
pub trait Sized {}
19+
20+
// `fp-armv8` is "forbidden" on aarch64 as we tie it to `neon`.
21+
#[target_feature(enable = "fp-armv8")]
22+
pub fn fun1() {}
23+
24+
// This would usually be rejected as it changes the ABI.
25+
// But we disable that check in rustdoc since we are building "for all targets" and the
26+
// check can't really handle that.
27+
#[target_feature(enable = "soft-float")]
28+
pub fn fun2() {}

0 commit comments

Comments
 (0)