about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/librustdoc/json/mod.rs60
-rw-r--r--src/rustdoc-json-types/lib.rs58
-rw-r--r--src/tools/compiletest/src/directive-list.rs3
-rw-r--r--src/tools/jsondocck/src/main.rs2
-rw-r--r--src/tools/jsondoclint/src/validator/tests.rs4
-rw-r--r--tests/rustdoc-json/targets/aarch64_apple_darwin.rs14
-rw-r--r--tests/rustdoc-json/targets/aarch64_reflects_compiler_options.rs10
-rw-r--r--tests/rustdoc-json/targets/aarch64_unknown_linux_gnu.rs14
-rw-r--r--tests/rustdoc-json/targets/i686_pc_windows_msvc.rs14
-rw-r--r--tests/rustdoc-json/targets/i686_unknown_linux_gnu.rs14
-rw-r--r--tests/rustdoc-json/targets/x86_64_apple_darwin.rs14
-rw-r--r--tests/rustdoc-json/targets/x86_64_pc_windows_gnu.rs14
-rw-r--r--tests/rustdoc-json/targets/x86_64_pc_windows_msvc.rs14
-rw-r--r--tests/rustdoc-json/targets/x86_64_reflects_compiler_options.rs10
-rw-r--r--tests/rustdoc-json/targets/x86_64_unknown_linux_gnu.rs14
15 files changed, 257 insertions, 2 deletions
diff --git a/src/librustdoc/json/mod.rs b/src/librustdoc/json/mod.rs
index ba27eed7c11..131a12ce228 100644
--- a/src/librustdoc/json/mod.rs
+++ b/src/librustdoc/json/mod.rs
@@ -14,6 +14,7 @@ use std::io::{BufWriter, Write, stdout};
 use std::path::PathBuf;
 use std::rc::Rc;
 
+use rustc_data_structures::fx::FxHashSet;
 use rustc_hir::def_id::{DefId, DefIdSet};
 use rustc_middle::ty::TyCtxt;
 use rustc_session::Session;
@@ -123,6 +124,58 @@ impl<'tcx> JsonRenderer<'tcx> {
     }
 }
 
+fn target(sess: &rustc_session::Session) -> types::Target {
+    // Build a set of which features are enabled on this target
+    let globally_enabled_features: FxHashSet<&str> =
+        sess.unstable_target_features.iter().map(|name| name.as_str()).collect();
+
+    // Build a map of target feature stability by feature name
+    use rustc_target::target_features::Stability;
+    let feature_stability: FxHashMap<&str, Stability> = sess
+        .target
+        .rust_target_features()
+        .into_iter()
+        .copied()
+        .map(|(name, stability, _)| (name, stability))
+        .collect();
+
+    types::Target {
+        triple: sess.opts.target_triple.tuple().into(),
+        target_features: sess
+            .target
+            .rust_target_features()
+            .into_iter()
+            .copied()
+            .filter(|(_, stability, _)| {
+                // Describe only target features which the user can toggle
+                stability.toggle_allowed().is_ok()
+            })
+            .map(|(name, stability, implied_features)| {
+                types::TargetFeature {
+                    name: name.into(),
+                    unstable_feature_gate: match stability {
+                        Stability::Unstable(feature_gate) => Some(feature_gate.as_str().into()),
+                        _ => None,
+                    },
+                    implies_features: implied_features
+                        .into_iter()
+                        .copied()
+                        .filter(|name| {
+                            // Imply only target features which the user can toggle
+                            feature_stability
+                                .get(name)
+                                .map(|stability| stability.toggle_allowed().is_ok())
+                                .unwrap_or(false)
+                        })
+                        .map(String::from)
+                        .collect(),
+                    globally_enabled: globally_enabled_features.contains(name),
+                }
+            })
+            .collect(),
+    }
+}
+
 impl<'tcx> FormatRenderer<'tcx> for JsonRenderer<'tcx> {
     fn descr() -> &'static str {
         "json"
@@ -248,6 +301,12 @@ impl<'tcx> FormatRenderer<'tcx> for JsonRenderer<'tcx> {
         let e = ExternalCrate { crate_num: LOCAL_CRATE };
         let index = (*self.index).clone().into_inner();
 
+        // Note that tcx.rust_target_features is inappropriate here because rustdoc tries to run for
+        // multiple targets: https://github.com/rust-lang/rust/pull/137632
+        //
+        // We want to describe a single target, so pass tcx.sess rather than tcx.
+        let target = target(self.tcx.sess);
+
         debug!("Constructing Output");
         let output_crate = types::Crate {
             root: self.id_from_item_default(e.def_id().into()),
@@ -288,6 +347,7 @@ impl<'tcx> FormatRenderer<'tcx> for JsonRenderer<'tcx> {
                     )
                 })
                 .collect(),
+            target,
             format_version: types::FORMAT_VERSION,
         };
         if let Some(ref out_dir) = self.out_dir {
diff --git a/src/rustdoc-json-types/lib.rs b/src/rustdoc-json-types/lib.rs
index 137fe4c4c35..7247950545a 100644
--- a/src/rustdoc-json-types/lib.rs
+++ b/src/rustdoc-json-types/lib.rs
@@ -30,7 +30,7 @@ pub type FxHashMap<K, V> = HashMap<K, V>; // re-export for use in src/librustdoc
 /// This integer is incremented with every breaking change to the API,
 /// and is returned along with the JSON blob as [`Crate::format_version`].
 /// Consuming code should assert that this value matches the format version(s) that it supports.
-pub const FORMAT_VERSION: u32 = 43;
+pub const FORMAT_VERSION: u32 = 44;
 
 /// The root of the emitted JSON blob.
 ///
@@ -52,11 +52,67 @@ pub struct Crate {
     pub paths: HashMap<Id, ItemSummary>,
     /// Maps `crate_id` of items to a crate name and html_root_url if it exists.
     pub external_crates: HashMap<u32, ExternalCrate>,
+    /// Information about the target for which this documentation was generated
+    pub target: Target,
     /// A single version number to be used in the future when making backwards incompatible changes
     /// to the JSON output.
     pub format_version: u32,
 }
 
+/// Information about a target
+#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
+pub struct Target {
+    /// The target triple for which this documentation was generated
+    pub triple: String,
+    /// A list of features valid for use in `#[target_feature]` attributes
+    /// for the target where this rustdoc JSON was generated.
+    pub target_features: Vec<TargetFeature>,
+}
+
+/// Information about a target feature.
+///
+/// Rust target features are used to influence code generation, especially around selecting
+/// instructions which are not universally supported by the target architecture.
+///
+/// Target features are commonly enabled by the [`#[target_feature]` attribute][1] to influence code
+/// generation for a particular function, and less commonly enabled by compiler options like
+/// `-Ctarget-feature` or `-Ctarget-cpu`. Targets themselves automatically enable certain target
+/// features by default, for example because the target's ABI specification requires saving specific
+/// registers which only exist in an architectural extension.
+///
+/// Target features can imply other target features: for example, x86-64 `avx2` implies `avx`, and
+/// aarch64 `sve2` implies `sve`, since both of these architectural extensions depend on their
+/// predecessors.
+///
+/// Target features can be probed at compile time by [`#[cfg(target_feature)]`][2] or `cfg!(…)`
+/// conditional compilation to determine whether a target feature is enabled in a particular
+/// context.
+///
+/// [1]: https://doc.rust-lang.org/stable/reference/attributes/codegen.html#the-target_feature-attribute
+/// [2]: https://doc.rust-lang.org/reference/conditional-compilation.html#target_feature
+#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
+pub struct TargetFeature {
+    /// The name of this target feature.
+    pub name: String,
+    /// Other target features which are implied by this target feature, if any.
+    pub implies_features: Vec<String>,
+    /// If this target feature is unstable, the name of the associated language feature gate.
+    pub unstable_feature_gate: Option<String>,
+    /// Whether this feature is globally enabled for this compilation session.
+    ///
+    /// Target features can be globally enabled implicitly as a result of the target's definition.
+    /// For example, x86-64 hardware floating point ABIs require saving x87 and SSE2 registers,
+    /// which in turn requires globally enabling the `x87` and `sse2` target features so that the
+    /// generated machine code conforms to the target's ABI.
+    ///
+    /// Target features can also be globally enabled explicitly as a result of compiler flags like
+    /// [`-Ctarget-feature`][1] or [`-Ctarget-cpu`][2].
+    ///
+    /// [1]: https://doc.rust-lang.org/beta/rustc/codegen-options/index.html#target-feature
+    /// [2]: https://doc.rust-lang.org/beta/rustc/codegen-options/index.html#target-cpu
+    pub globally_enabled: bool,
+}
+
 /// Metadata of a crate, either the same crate on which `rustdoc` was invoked, or its dependency.
 #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
 pub struct ExternalCrate {
diff --git a/src/tools/compiletest/src/directive-list.rs b/src/tools/compiletest/src/directive-list.rs
index 44d9c0330f7..f8b92f11bde 100644
--- a/src/tools/compiletest/src/directive-list.rs
+++ b/src/tools/compiletest/src/directive-list.rs
@@ -176,6 +176,7 @@ const KNOWN_DIRECTIVE_NAMES: &[&str] = &[
     "only-32bit",
     "only-64bit",
     "only-aarch64",
+    "only-aarch64-apple-darwin",
     "only-aarch64-unknown-linux-gnu",
     "only-apple",
     "only-arm",
@@ -189,6 +190,7 @@ const KNOWN_DIRECTIVE_NAMES: &[&str] = &[
     "only-gnu",
     "only-i686-pc-windows-gnu",
     "only-i686-pc-windows-msvc",
+    "only-i686-unknown-linux-gnu",
     "only-ios",
     "only-linux",
     "only-loongarch64",
@@ -220,6 +222,7 @@ const KNOWN_DIRECTIVE_NAMES: &[&str] = &[
     "only-windows-msvc",
     "only-x86",
     "only-x86_64",
+    "only-x86_64-apple-darwin",
     "only-x86_64-fortanix-unknown-sgx",
     "only-x86_64-pc-windows-gnu",
     "only-x86_64-pc-windows-msvc",
diff --git a/src/tools/jsondocck/src/main.rs b/src/tools/jsondocck/src/main.rs
index 54249fbd9ae..79e419884c6 100644
--- a/src/tools/jsondocck/src/main.rs
+++ b/src/tools/jsondocck/src/main.rs
@@ -156,7 +156,7 @@ static LINE_PATTERN: LazyLock<Regex> = LazyLock::new(|| {
         r#"
         //@\s+
         (?P<negated>!?)
-        (?P<cmd>[A-Za-z]+(?:-[A-Za-z]+)*)
+        (?P<cmd>[A-Za-z0-9]+(?:-[A-Za-z0-9]+)*)
         (?P<args>.*)$
     "#,
     )
diff --git a/src/tools/jsondoclint/src/validator/tests.rs b/src/tools/jsondoclint/src/validator/tests.rs
index 28deb7e7cee..dd0b4ac5601 100644
--- a/src/tools/jsondoclint/src/validator/tests.rs
+++ b/src/tools/jsondoclint/src/validator/tests.rs
@@ -42,6 +42,7 @@ fn errors_on_missing_links() {
         )]),
         paths: FxHashMap::default(),
         external_crates: FxHashMap::default(),
+        target: rustdoc_json_types::Target { triple: "".to_string(), target_features: vec![] },
         format_version: rustdoc_json_types::FORMAT_VERSION,
     };
 
@@ -112,6 +113,7 @@ fn errors_on_local_in_paths_and_not_index() {
             },
         )]),
         external_crates: FxHashMap::default(),
+        target: rustdoc_json_types::Target { triple: "".to_string(), target_features: vec![] },
         format_version: rustdoc_json_types::FORMAT_VERSION,
     };
 
@@ -216,6 +218,7 @@ fn errors_on_missing_path() {
             ItemSummary { crate_id: 0, path: vec!["foo".to_owned()], kind: ItemKind::Module },
         )]),
         external_crates: FxHashMap::default(),
+        target: rustdoc_json_types::Target { triple: "".to_string(), target_features: vec![] },
         format_version: rustdoc_json_types::FORMAT_VERSION,
     };
 
@@ -259,6 +262,7 @@ fn checks_local_crate_id_is_correct() {
         )]),
         paths: FxHashMap::default(),
         external_crates: FxHashMap::default(),
+        target: rustdoc_json_types::Target { triple: "".to_string(), target_features: vec![] },
         format_version: FORMAT_VERSION,
     };
     check(&krate, &[]);
diff --git a/tests/rustdoc-json/targets/aarch64_apple_darwin.rs b/tests/rustdoc-json/targets/aarch64_apple_darwin.rs
new file mode 100644
index 00000000000..c6ae5517d47
--- /dev/null
+++ b/tests/rustdoc-json/targets/aarch64_apple_darwin.rs
@@ -0,0 +1,14 @@
+//@ only-aarch64-apple-darwin
+
+//@ is "$.target.triple" \"aarch64-apple-darwin\"
+//@ is "$.target.target_features[?(@.name=='vh')].globally_enabled" true
+//@ is "$.target.target_features[?(@.name=='sve')].globally_enabled" false
+//@ has "$.target.target_features[?(@.name=='sve2')].implies_features" '["sve"]'
+//@ is "$.target.target_features[?(@.name=='sve2')].unstable_feature_gate" null
+
+// If this breaks due to stabilization, check rustc_target::target_features for a replacement
+//@ is "$.target.target_features[?(@.name=='cssc')].unstable_feature_gate" '"aarch64_unstable_target_feature"'
+//@ is "$.target.target_features[?(@.name=='v9a')].unstable_feature_gate" '"aarch64_ver_target_feature"'
+
+// Ensure we don't look like x86-64
+//@ !has "$.target.target_features[?(@.name=='avx2')]"
diff --git a/tests/rustdoc-json/targets/aarch64_reflects_compiler_options.rs b/tests/rustdoc-json/targets/aarch64_reflects_compiler_options.rs
new file mode 100644
index 00000000000..f91221eb23c
--- /dev/null
+++ b/tests/rustdoc-json/targets/aarch64_reflects_compiler_options.rs
@@ -0,0 +1,10 @@
+//@ only-aarch64
+
+// If we enable SVE Bit Permute, we should see that it is enabled
+//@ compile-flags: -Ctarget-feature=+sve2-bitperm
+//@ is "$.target.target_features[?(@.name=='sve2-bitperm')].globally_enabled" true
+
+// As well as its dependency chain
+//@ is "$.target.target_features[?(@.name=='sve2')].globally_enabled" true
+//@ is "$.target.target_features[?(@.name=='sve')].globally_enabled" true
+//@ is "$.target.target_features[?(@.name=='neon')].globally_enabled" true
diff --git a/tests/rustdoc-json/targets/aarch64_unknown_linux_gnu.rs b/tests/rustdoc-json/targets/aarch64_unknown_linux_gnu.rs
new file mode 100644
index 00000000000..9139b00a128
--- /dev/null
+++ b/tests/rustdoc-json/targets/aarch64_unknown_linux_gnu.rs
@@ -0,0 +1,14 @@
+//@ only-aarch64-unknown-linux-gnu
+
+//@ is "$.target.triple" \"aarch64-unknown-linux-gnu\"
+//@ is "$.target.target_features[?(@.name=='neon')].globally_enabled" true
+//@ is "$.target.target_features[?(@.name=='sve')].globally_enabled" false
+//@ has "$.target.target_features[?(@.name=='sve2')].implies_features" '["sve"]'
+//@ is "$.target.target_features[?(@.name=='sve2')].unstable_feature_gate" null
+
+// If this breaks due to stabilization, check rustc_target::target_features for a replacement
+//@ is "$.target.target_features[?(@.name=='cssc')].unstable_feature_gate" '"aarch64_unstable_target_feature"'
+//@ is "$.target.target_features[?(@.name=='v9a')].unstable_feature_gate" '"aarch64_ver_target_feature"'
+
+// Ensure we don't look like x86-64
+//@ !has "$.target.target_features[?(@.name=='avx2')]"
diff --git a/tests/rustdoc-json/targets/i686_pc_windows_msvc.rs b/tests/rustdoc-json/targets/i686_pc_windows_msvc.rs
new file mode 100644
index 00000000000..088c741d113
--- /dev/null
+++ b/tests/rustdoc-json/targets/i686_pc_windows_msvc.rs
@@ -0,0 +1,14 @@
+//@ only-i686-pc-windows-msvc
+
+//@ is "$.target.triple" \"i686-pc-windows-msvc\"
+//@ is "$.target.target_features[?(@.name=='sse2')].globally_enabled" true
+//@ is "$.target.target_features[?(@.name=='avx2')].globally_enabled" false
+//@ has "$.target.target_features[?(@.name=='avx2')].implies_features" '["avx"]'
+//@ is "$.target.target_features[?(@.name=='avx2')].unstable_feature_gate" null
+
+// If this breaks due to stabilization, check rustc_target::target_features for a replacement
+//@ is "$.target.target_features[?(@.name=='amx-tile')].unstable_feature_gate" '"x86_amx_intrinsics"'
+//@ is "$.target.target_features[?(@.name=='x87')].unstable_feature_gate" '"x87_target_feature"'
+
+// Ensure we don't look like aarch64
+//@ !has "$.target.target_features[?(@.name=='sve2')]"
diff --git a/tests/rustdoc-json/targets/i686_unknown_linux_gnu.rs b/tests/rustdoc-json/targets/i686_unknown_linux_gnu.rs
new file mode 100644
index 00000000000..03788b000f1
--- /dev/null
+++ b/tests/rustdoc-json/targets/i686_unknown_linux_gnu.rs
@@ -0,0 +1,14 @@
+//@ only-i686-unknown-linux-gnu
+
+//@ is "$.target.triple" \"i686-unknown-linux-gnu\"
+//@ is "$.target.target_features[?(@.name=='sse2')].globally_enabled" true
+//@ is "$.target.target_features[?(@.name=='avx2')].globally_enabled" false
+//@ has "$.target.target_features[?(@.name=='avx2')].implies_features" '["avx"]'
+//@ is "$.target.target_features[?(@.name=='avx2')].unstable_feature_gate" null
+
+// If this breaks due to stabilization, check rustc_target::target_features for a replacement
+//@ is "$.target.target_features[?(@.name=='amx-tile')].unstable_feature_gate" '"x86_amx_intrinsics"'
+//@ is "$.target.target_features[?(@.name=='x87')].unstable_feature_gate" '"x87_target_feature"'
+
+// Ensure we don't look like aarch64
+//@ !has "$.target.target_features[?(@.name=='sve2')]"
diff --git a/tests/rustdoc-json/targets/x86_64_apple_darwin.rs b/tests/rustdoc-json/targets/x86_64_apple_darwin.rs
new file mode 100644
index 00000000000..a46f9138e86
--- /dev/null
+++ b/tests/rustdoc-json/targets/x86_64_apple_darwin.rs
@@ -0,0 +1,14 @@
+//@ only-x86_64-apple-darwin
+
+//@ is "$.target.triple" \"x86_64-apple-darwin\"
+//@ is "$.target.target_features[?(@.name=='sse2')].globally_enabled" true
+//@ is "$.target.target_features[?(@.name=='avx2')].globally_enabled" false
+//@ has "$.target.target_features[?(@.name=='avx2')].implies_features" '["avx"]'
+//@ is "$.target.target_features[?(@.name=='avx2')].unstable_feature_gate" null
+
+// If this breaks due to stabilization, check rustc_target::target_features for a replacement
+//@ is "$.target.target_features[?(@.name=='amx-tile')].unstable_feature_gate" '"x86_amx_intrinsics"'
+//@ is "$.target.target_features[?(@.name=='x87')].unstable_feature_gate" '"x87_target_feature"'
+
+// Ensure we don't look like aarch64
+//@ !has "$.target.target_features[?(@.name=='sve2')]"
diff --git a/tests/rustdoc-json/targets/x86_64_pc_windows_gnu.rs b/tests/rustdoc-json/targets/x86_64_pc_windows_gnu.rs
new file mode 100644
index 00000000000..7da12eb4d58
--- /dev/null
+++ b/tests/rustdoc-json/targets/x86_64_pc_windows_gnu.rs
@@ -0,0 +1,14 @@
+//@ only-x86_64-pc-windows-gnu
+
+//@ is "$.target.triple" \"x86_64-pc-windows-gnu\"
+//@ is "$.target.target_features[?(@.name=='sse2')].globally_enabled" true
+//@ is "$.target.target_features[?(@.name=='avx2')].globally_enabled" false
+//@ has "$.target.target_features[?(@.name=='avx2')].implies_features" '["avx"]'
+//@ is "$.target.target_features[?(@.name=='avx2')].unstable_feature_gate" null
+
+// If this breaks due to stabilization, check rustc_target::target_features for a replacement
+//@ is "$.target.target_features[?(@.name=='amx-tile')].unstable_feature_gate" '"x86_amx_intrinsics"'
+//@ is "$.target.target_features[?(@.name=='x87')].unstable_feature_gate" '"x87_target_feature"'
+
+// Ensure we don't look like aarch64
+//@ !has "$.target.target_features[?(@.name=='sve2')]"
diff --git a/tests/rustdoc-json/targets/x86_64_pc_windows_msvc.rs b/tests/rustdoc-json/targets/x86_64_pc_windows_msvc.rs
new file mode 100644
index 00000000000..d55f5776e85
--- /dev/null
+++ b/tests/rustdoc-json/targets/x86_64_pc_windows_msvc.rs
@@ -0,0 +1,14 @@
+//@ only-x86_64-pc-windows-msvc
+
+//@ is "$.target.triple" \"x86_64-pc-windows-msvc\"
+//@ is "$.target.target_features[?(@.name=='sse2')].globally_enabled" true
+//@ is "$.target.target_features[?(@.name=='avx2')].globally_enabled" false
+//@ has "$.target.target_features[?(@.name=='avx2')].implies_features" '["avx"]'
+//@ is "$.target.target_features[?(@.name=='avx2')].unstable_feature_gate" null
+
+// If this breaks due to stabilization, check rustc_target::target_features for a replacement
+//@ is "$.target.target_features[?(@.name=='amx-tile')].unstable_feature_gate" '"x86_amx_intrinsics"'
+//@ is "$.target.target_features[?(@.name=='x87')].unstable_feature_gate" '"x87_target_feature"'
+
+// Ensure we don't look like aarch64
+//@ !has "$.target.target_features[?(@.name=='sve2')]"
diff --git a/tests/rustdoc-json/targets/x86_64_reflects_compiler_options.rs b/tests/rustdoc-json/targets/x86_64_reflects_compiler_options.rs
new file mode 100644
index 00000000000..ba029b09996
--- /dev/null
+++ b/tests/rustdoc-json/targets/x86_64_reflects_compiler_options.rs
@@ -0,0 +1,10 @@
+//@ only-x86_64
+
+// If we enable AVX2, we should see that it is enabled
+//@ compile-flags: -Ctarget-feature=+avx2
+//@ is "$.target.target_features[?(@.name=='avx2')].globally_enabled" true
+
+// As well as its dependency chain
+//@ is "$.target.target_features[?(@.name=='avx')].globally_enabled" true
+//@ is "$.target.target_features[?(@.name=='sse4.2')].globally_enabled" true
+//@ is "$.target.target_features[?(@.name=='sse4.1')].globally_enabled" true
diff --git a/tests/rustdoc-json/targets/x86_64_unknown_linux_gnu.rs b/tests/rustdoc-json/targets/x86_64_unknown_linux_gnu.rs
new file mode 100644
index 00000000000..3372fe7eb9d
--- /dev/null
+++ b/tests/rustdoc-json/targets/x86_64_unknown_linux_gnu.rs
@@ -0,0 +1,14 @@
+//@ only-x86_64-unknown-linux-gnu
+
+//@ is "$.target.triple" \"x86_64-unknown-linux-gnu\"
+//@ is "$.target.target_features[?(@.name=='sse2')].globally_enabled" true
+//@ is "$.target.target_features[?(@.name=='avx2')].globally_enabled" false
+//@ has "$.target.target_features[?(@.name=='avx2')].implies_features" '["avx"]'
+//@ is "$.target.target_features[?(@.name=='avx2')].unstable_feature_gate" null
+
+// If this breaks due to stabilization, check rustc_target::target_features for a replacement
+//@ is "$.target.target_features[?(@.name=='amx-tile')].unstable_feature_gate" '"x86_amx_intrinsics"'
+//@ is "$.target.target_features[?(@.name=='x87')].unstable_feature_gate" '"x87_target_feature"'
+
+// Ensure we don't look like aarch64
+//@ !has "$.target.target_features[?(@.name=='sve2')]"