use rustc_middle::ty::TyCtxt;
use rustc_session::parse::feature_err;
use rustc_span::{Span, Symbol, sym};
-use rustc_target::target_features;
+use rustc_target::target_features::{self, Stability};
use crate::errors;
let target = &tcx.sess.target;
if tcx.sess.opts.actually_rustdoc {
// HACK: rustdoc would like to pretend that we have all the target features, so we
- // have to merge all the lists into one. The result has a "random" stability
- // (depending on the order in which we consider features); all places that check
- // target stability are expected to check `actually_rustdoc` and do nothing when
- // that is set.
- rustc_target::target_features::all_rust_features()
- .map(|(a, b)| (a.to_string(), b.compute_toggleability(target)))
- .collect()
+ // have to merge all the lists into one. To ensure an unstable target never prevents
+ // a stable one from working, we merge the stability info of all instances of the
+ // same target feature name, with the "most stable" taking precedence. And then we
+ // hope that this doesn't cause issues anywhere else in the compiler...
+ let mut result: UnordMap<String, Stability<_>> = Default::default();
+ for (name, stability) in rustc_target::target_features::all_rust_features() {
+ use std::collections::hash_map::Entry;
+ match result.entry(name.to_owned()) {
+ Entry::Vacant(vacant_entry) => {
+ vacant_entry.insert(stability.compute_toggleability(target));
+ }
+ Entry::Occupied(mut occupied_entry) => {
+ // Merge the two stabilities, "more stable" taking precedence.
+ match (occupied_entry.get(), &stability) {
+ (Stability::Stable { .. }, _)
+ | (
+ Stability::Unstable { .. },
+ Stability::Unstable { .. } | Stability::Forbidden { .. },
+ )
+ | (Stability::Forbidden { .. }, Stability::Forbidden { .. }) => {
+ // The stability in the entry is at least as good as the new one, just keep it.
+ }
+ _ => {
+ // Overwrite stabilite.
+ occupied_entry.insert(stability.compute_toggleability(target));
+ }
+ }
+ }
+ }
+ }
+ result
} else {
tcx.sess
.target