@@ -10,7 +10,7 @@ use rustc_middle::query::Providers;
1010use rustc_middle::ty::TyCtxt;
1111use rustc_session::parse::feature_err;
1212use rustc_span::{Span, Symbol, sym};
13- use rustc_target::target_features;
13+ use rustc_target::target_features::{self, Stability} ;
1414
1515use 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
0 commit comments