From 039af88e09f4f4beb47406f4771bffc2e61d800a Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 25 Feb 2025 20:38:13 +0100 Subject: [PATCH] also fix potential issues with mixed stable/unstable target features in rustdoc --- .../rustc_codegen_ssa/src/target_features.rs | 45 ++++++++++++++----- 1 file changed, 33 insertions(+), 12 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/target_features.rs b/compiler/rustc_codegen_ssa/src/target_features.rs index 28c6932dd5b..a63e1877e45 100644 --- a/compiler/rustc_codegen_ssa/src/target_features.rs +++ b/compiler/rustc_codegen_ssa/src/target_features.rs @@ -10,7 +10,7 @@ use rustc_middle::query::Providers; 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; @@ -87,10 +87,7 @@ pub(crate) fn from_target_feature_attr( // But ensure the ABI does not forbid enabling this. // Here we do assume that LLVM doesn't add even more implied features // we don't know about, at least no features that would have ABI effects! - // We skip this check in rustdoc, like we skip all target feature related checks. - if !tcx.sess.opts.actually_rustdoc - && abi_feature_constraints.incompatible.contains(&name.as_str()) - { + if abi_feature_constraints.incompatible.contains(&name.as_str()) { tcx.dcx().emit_err(errors::ForbiddenTargetFeatureAttr { span: item.span(), feature: name.as_str(), @@ -146,13 +143,37 @@ pub(crate) fn provide(providers: &mut Providers) { assert_eq!(cnum, LOCAL_CRATE); 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)) - .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 = 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); + } + 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); + } + } + } + } + } + result } else { tcx.sess .target