From 50835bf1943193f7a78cc39b17f6e90b72bff5a4 Mon Sep 17 00:00:00 2001 From: ltdk Date: Sat, 27 Jul 2024 01:51:04 -0400 Subject: [PATCH 01/31] impl Default for collection iterators that don't already have it --- .../alloc/src/collections/binary_heap/mod.rs | 14 ++++++++++ library/alloc/src/collections/btree/map.rs | 28 +++++++++++++++++++ .../alloc/src/collections/vec_deque/iter.rs | 14 ++++++++++ .../src/collections/vec_deque/iter_mut.rs | 14 ++++++++++ 4 files changed, 70 insertions(+) diff --git a/library/alloc/src/collections/binary_heap/mod.rs b/library/alloc/src/collections/binary_heap/mod.rs index fe1ff24139554..a4a172b86e54a 100644 --- a/library/alloc/src/collections/binary_heap/mod.rs +++ b/library/alloc/src/collections/binary_heap/mod.rs @@ -1433,6 +1433,20 @@ pub struct Iter<'a, T: 'a> { iter: slice::Iter<'a, T>, } +#[stable(feature = "default_iters", since = "CURRENT_RUSTC_VERSION")] +impl Default for Iter<'_, T> { + /// Creates an empty `binary_heap::Iter`. + /// + /// ``` + /// # use std::collections::binary_heap; + /// let iter: binary_heap::Iter<'_, u8> = Default::default(); + /// assert_eq!(iter.len(), 0); + /// ``` + fn default() -> Self { + Iter { iter: Default::default() } + } +} + #[stable(feature = "collection_debug", since = "1.17.0")] impl fmt::Debug for Iter<'_, T> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { diff --git a/library/alloc/src/collections/btree/map.rs b/library/alloc/src/collections/btree/map.rs index 3875f61efafdf..a2e97bcee829e 100644 --- a/library/alloc/src/collections/btree/map.rs +++ b/library/alloc/src/collections/btree/map.rs @@ -2016,6 +2016,20 @@ impl Default for Range<'_, K, V> { } } +#[stable(feature = "default_iters", since = "CURRENT_RUSTC_VERSION")] +impl Default for RangeMut<'_, K, V> { + /// Creates an empty `btree_map::RangeMut`. + /// + /// ``` + /// # use std::collections::btree_map; + /// let iter: btree_map::RangeMut<'_, u8, u8> = Default::default(); + /// assert_eq!(iter.count(), 0); + /// ``` + fn default() -> Self { + RangeMut { inner: Default::default(), _marker: PhantomData } + } +} + #[stable(feature = "map_values_mut", since = "1.10.0")] impl<'a, K, V> Iterator for ValuesMut<'a, K, V> { type Item = &'a mut V; @@ -2050,6 +2064,20 @@ impl ExactSizeIterator for ValuesMut<'_, K, V> { #[stable(feature = "fused", since = "1.26.0")] impl FusedIterator for ValuesMut<'_, K, V> {} +#[stable(feature = "default_iters", since = "CURRENT_RUSTC_VERSION")] +impl Default for ValuesMut<'_, K, V> { + /// Creates an empty `btree_map::ValuesMut`. + /// + /// ``` + /// # use std::collections::btree_map; + /// let iter: btree_map::ValuesMut<'_, u8, u8> = Default::default(); + /// assert_eq!(iter.count(), 0); + /// ``` + fn default() -> Self { + ValuesMut { inner: Default::default() } + } +} + #[stable(feature = "map_into_keys_values", since = "1.54.0")] impl Iterator for IntoKeys { type Item = K; diff --git a/library/alloc/src/collections/vec_deque/iter.rs b/library/alloc/src/collections/vec_deque/iter.rs index 5a5e7f70854d8..10adc547da666 100644 --- a/library/alloc/src/collections/vec_deque/iter.rs +++ b/library/alloc/src/collections/vec_deque/iter.rs @@ -28,6 +28,20 @@ impl fmt::Debug for Iter<'_, T> { } } +#[stable(feature = "default_iters", since = "CURRENT_RUSTC_VERSION")] +impl Default for Iter<'_, T> { + /// Creates an empty `vec_deque::Iter`. + /// + /// ``` + /// # use std::collections::vec_deque; + /// let iter: vec_deque::Iter<'_, u8> = Default::default(); + /// assert_eq!(iter.len(), 0); + /// ``` + fn default() -> Self { + Iter { i1: Default::default(), i2: Default::default() } + } +} + // FIXME(#26925) Remove in favor of `#[derive(Clone)]` #[stable(feature = "rust1", since = "1.0.0")] impl Clone for Iter<'_, T> { diff --git a/library/alloc/src/collections/vec_deque/iter_mut.rs b/library/alloc/src/collections/vec_deque/iter_mut.rs index 5061931afb7b7..18f11096eeb93 100644 --- a/library/alloc/src/collections/vec_deque/iter_mut.rs +++ b/library/alloc/src/collections/vec_deque/iter_mut.rs @@ -28,6 +28,20 @@ impl fmt::Debug for IterMut<'_, T> { } } +#[stable(feature = "default_iters", since = "CURRENT_RUSTC_VERSION")] +impl Default for IterMut<'_, T> { + /// Creates an empty `vec_deque::IterMut`. + /// + /// ``` + /// # use std::collections::vec_deque; + /// let iter: vec_deque::IterMut<'_, u8> = Default::default(); + /// assert_eq!(iter.len(), 0); + /// ``` + fn default() -> Self { + IterMut { i1: Default::default(), i2: Default::default() } + } +} + #[stable(feature = "rust1", since = "1.0.0")] impl<'a, T> Iterator for IterMut<'a, T> { type Item = &'a mut T; From 0b9972049727f08610c038bce7177571b7111df5 Mon Sep 17 00:00:00 2001 From: ltdk Date: Sat, 27 Jul 2024 02:48:55 -0400 Subject: [PATCH 02/31] Okay, I guess I have to give these a different feature name --- library/alloc/src/collections/binary_heap/mod.rs | 2 +- library/alloc/src/collections/btree/map.rs | 4 ++-- library/alloc/src/collections/vec_deque/iter.rs | 2 +- library/alloc/src/collections/vec_deque/iter_mut.rs | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/library/alloc/src/collections/binary_heap/mod.rs b/library/alloc/src/collections/binary_heap/mod.rs index a4a172b86e54a..72e466f4dcdc4 100644 --- a/library/alloc/src/collections/binary_heap/mod.rs +++ b/library/alloc/src/collections/binary_heap/mod.rs @@ -1433,7 +1433,7 @@ pub struct Iter<'a, T: 'a> { iter: slice::Iter<'a, T>, } -#[stable(feature = "default_iters", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "default_iters_sequel", since = "CURRENT_RUSTC_VERSION")] impl Default for Iter<'_, T> { /// Creates an empty `binary_heap::Iter`. /// diff --git a/library/alloc/src/collections/btree/map.rs b/library/alloc/src/collections/btree/map.rs index a2e97bcee829e..2c28e893f3696 100644 --- a/library/alloc/src/collections/btree/map.rs +++ b/library/alloc/src/collections/btree/map.rs @@ -2016,7 +2016,7 @@ impl Default for Range<'_, K, V> { } } -#[stable(feature = "default_iters", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "default_iters_sequel", since = "CURRENT_RUSTC_VERSION")] impl Default for RangeMut<'_, K, V> { /// Creates an empty `btree_map::RangeMut`. /// @@ -2064,7 +2064,7 @@ impl ExactSizeIterator for ValuesMut<'_, K, V> { #[stable(feature = "fused", since = "1.26.0")] impl FusedIterator for ValuesMut<'_, K, V> {} -#[stable(feature = "default_iters", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "default_iters_sequel", since = "CURRENT_RUSTC_VERSION")] impl Default for ValuesMut<'_, K, V> { /// Creates an empty `btree_map::ValuesMut`. /// diff --git a/library/alloc/src/collections/vec_deque/iter.rs b/library/alloc/src/collections/vec_deque/iter.rs index 10adc547da666..67b5b91c4d4b0 100644 --- a/library/alloc/src/collections/vec_deque/iter.rs +++ b/library/alloc/src/collections/vec_deque/iter.rs @@ -28,7 +28,7 @@ impl fmt::Debug for Iter<'_, T> { } } -#[stable(feature = "default_iters", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "default_iters_sequel", since = "CURRENT_RUSTC_VERSION")] impl Default for Iter<'_, T> { /// Creates an empty `vec_deque::Iter`. /// diff --git a/library/alloc/src/collections/vec_deque/iter_mut.rs b/library/alloc/src/collections/vec_deque/iter_mut.rs index 18f11096eeb93..2726e3e425290 100644 --- a/library/alloc/src/collections/vec_deque/iter_mut.rs +++ b/library/alloc/src/collections/vec_deque/iter_mut.rs @@ -28,7 +28,7 @@ impl fmt::Debug for IterMut<'_, T> { } } -#[stable(feature = "default_iters", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "default_iters_sequel", since = "CURRENT_RUSTC_VERSION")] impl Default for IterMut<'_, T> { /// Creates an empty `vec_deque::IterMut`. /// From ba0d6c973994915faef00e0e15e2957ff2b286b7 Mon Sep 17 00:00:00 2001 From: Jonathan Pallant Date: Thu, 4 Jul 2024 18:34:35 +0100 Subject: [PATCH 03/31] Update generate-copyright This tool now scans for cargo dependencies and includes any important looking license files. We do this because cargo package metadata is not sufficient - the Apache-2.0 license says you have to include any NOTICE file, for example. And authors != copyright holders (cargo has the former, we must include the latter). --- Cargo.lock | 2 + src/bootstrap/src/core/build_steps/run.rs | 2 + src/tools/collect-license-metadata/Cargo.toml | 2 + .../collect-license-metadata/src/main.rs | 5 + src/tools/generate-copyright/Cargo.toml | 3 + .../generate-copyright/src/cargo_metadata.rs | 196 ++++++++++++++++++ src/tools/generate-copyright/src/main.rs | 122 +++++++++-- 7 files changed, 320 insertions(+), 12 deletions(-) create mode 100644 src/tools/generate-copyright/src/cargo_metadata.rs diff --git a/Cargo.lock b/Cargo.lock index a4b4e49f82c2e..0f3a106512d20 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1408,6 +1408,8 @@ dependencies = [ "anyhow", "serde", "serde_json", + "tempfile", + "thiserror", ] [[package]] diff --git a/src/bootstrap/src/core/build_steps/run.rs b/src/bootstrap/src/core/build_steps/run.rs index fde1693646a8b..29d7bcc425bea 100644 --- a/src/bootstrap/src/core/build_steps/run.rs +++ b/src/bootstrap/src/core/build_steps/run.rs @@ -217,6 +217,8 @@ impl Step for GenerateCopyright { let mut cmd = builder.tool_cmd(Tool::GenerateCopyright); cmd.env("LICENSE_METADATA", &license_metadata); cmd.env("DEST", &dest); + cmd.env("OUT_DIR", &builder.out); + cmd.env("CARGO", &builder.initial_cargo); cmd.run(builder); dest diff --git a/src/tools/collect-license-metadata/Cargo.toml b/src/tools/collect-license-metadata/Cargo.toml index d0820cfc2a0e4..edf9e5c5393ea 100644 --- a/src/tools/collect-license-metadata/Cargo.toml +++ b/src/tools/collect-license-metadata/Cargo.toml @@ -2,6 +2,8 @@ name = "collect-license-metadata" version = "0.1.0" edition = "2021" +description = "Runs the reuse tool and caches the output, so rust toolchain devs don't need to have reuse installed" +license = "MIT OR Apache-2.0" [dependencies] anyhow = "1.0.65" diff --git a/src/tools/collect-license-metadata/src/main.rs b/src/tools/collect-license-metadata/src/main.rs index ca6aa01d78c04..dce36bb17b600 100644 --- a/src/tools/collect-license-metadata/src/main.rs +++ b/src/tools/collect-license-metadata/src/main.rs @@ -8,6 +8,11 @@ use anyhow::Error; use crate::licenses::LicensesInterner; +/// The entry point to the binary. +/// +/// You should probably let `bootstrap` execute this program instead of running it directly. +/// +/// Run `x.py run collect-license-metadata` fn main() -> Result<(), Error> { let reuse_exe: PathBuf = std::env::var_os("REUSE_EXE").expect("Missing REUSE_EXE").into(); let dest: PathBuf = std::env::var_os("DEST").expect("Missing DEST").into(); diff --git a/src/tools/generate-copyright/Cargo.toml b/src/tools/generate-copyright/Cargo.toml index 899ef0f8a6c26..bf643876a042b 100644 --- a/src/tools/generate-copyright/Cargo.toml +++ b/src/tools/generate-copyright/Cargo.toml @@ -2,6 +2,7 @@ name = "generate-copyright" version = "0.1.0" edition = "2021" +description = "Produces a manifest of all the copyrighted materials in the Rust Toolchain" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -9,3 +10,5 @@ edition = "2021" anyhow = "1.0.65" serde = { version = "1.0.147", features = ["derive"] } serde_json = "1.0.85" +thiserror = "1" +tempfile = "3" diff --git a/src/tools/generate-copyright/src/cargo_metadata.rs b/src/tools/generate-copyright/src/cargo_metadata.rs new file mode 100644 index 0000000000000..721a6b1c6e627 --- /dev/null +++ b/src/tools/generate-copyright/src/cargo_metadata.rs @@ -0,0 +1,196 @@ +//! Gets metadata about a workspace from Cargo + +use std::collections::{BTreeMap, BTreeSet}; +use std::ffi::{OsStr, OsString}; +use std::path::Path; + +/// Describes how this module can fail +#[derive(Debug, thiserror::Error)] +pub enum Error { + #[error("Failed to run cargo metadata: {0:?}")] + LaunchingMetadata(#[from] std::io::Error), + #[error("Failed get output from cargo metadata: {0:?}")] + GettingMetadata(String), + #[error("Failed parse JSON output from cargo metadata: {0:?}")] + ParsingJson(#[from] serde_json::Error), + #[error("Failed find expected JSON element {0} in output from cargo metadata")] + MissingJsonElement(&'static str), + #[error("Failed find expected JSON element {0} in output from cargo metadata for package {1}")] + MissingJsonElementForPackage(String, String), + #[error("Failed to run cargo vendor: {0:?}")] + LaunchingVendor(std::io::Error), + #[error("Failed to complete cargo vendor")] + RunningVendor, +} + +/// Describes one of our dependencies +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] +pub struct Dependency { + /// The name of the package + pub name: String, + /// The version number + pub version: String, + /// The license it is under + pub license: String, + /// The list of authors from the package metadata + pub authors: Vec, + /// A list of important files from the package, with their contents. + /// + /// This includes *COPYRIGHT*, *NOTICE*, *AUTHOR*, *LICENSE*, and *LICENCE* files, case-insensitive. + pub notices: BTreeMap, +} + +/// Use `cargo` to get a list of dependencies and their license data. +/// +/// This will involve running `cargo vendor` into `${BUILD}/vendor` so we can +/// grab the license files. +/// +/// Any dependency with a path beginning with `root_path` is ignored, as we +/// assume `reuse` has covered it already. +pub fn get( + cargo: &Path, + dest: &Path, + root_path: &Path, + manifest_paths: &[&Path], +) -> Result, Error> { + let mut temp_set = BTreeSet::new(); + // Look at the metadata for each manifest + for manifest_path in manifest_paths { + if manifest_path.file_name() != Some(OsStr::new("Cargo.toml")) { + panic!("cargo_manifest::get requires a path to a Cargo.toml file"); + } + let metadata_json = get_metadata_json(cargo, manifest_path)?; + let packages = metadata_json["packages"] + .as_array() + .ok_or_else(|| Error::MissingJsonElement("packages array"))?; + for package in packages { + let package = + package.as_object().ok_or_else(|| Error::MissingJsonElement("package object"))?; + let manifest_path = package + .get("manifest_path") + .and_then(|v| v.as_str()) + .map(Path::new) + .ok_or_else(|| Error::MissingJsonElement("package.manifest_path"))?; + if manifest_path.starts_with(&root_path) { + // it's an in-tree dependency and reuse covers it + continue; + } + // otherwise it's an out-of-tree dependency + let get_string = |field_name: &str, package_name: &str| { + package.get(field_name).and_then(|v| v.as_str()).ok_or_else(|| { + Error::MissingJsonElementForPackage( + format!("package.{field_name}"), + package_name.to_owned(), + ) + }) + }; + let name = get_string("name", "unknown")?; + let license = get_string("license", name)?; + let version = get_string("version", name)?; + let authors_list = package + .get("authors") + .and_then(|v| v.as_array()) + .ok_or_else(|| Error::MissingJsonElement("package.authors"))?; + let authors: Vec = + authors_list.iter().filter_map(|v| v.as_str()).map(|s| s.to_owned()).collect(); + temp_set.insert(Dependency { + name: name.to_owned(), + version: version.to_owned(), + license: license.to_owned(), + authors, + notices: BTreeMap::new(), + }); + } + } + + // Now do a cargo-vendor and grab everything + let vendor_path = dest.join("vendor"); + println!("Vendoring deps into {}...", vendor_path.display()); + run_cargo_vendor(cargo, &vendor_path, manifest_paths)?; + + // Now for each dependency we found, go and grab any important looking files + let mut output = BTreeSet::new(); + for mut dep in temp_set { + load_important_files(&mut dep, &vendor_path)?; + output.insert(dep); + } + + Ok(output) +} + +/// Get cargo-metdata for a package, as JSON +fn get_metadata_json(cargo: &Path, manifest_path: &Path) -> Result { + let metadata_output = std::process::Command::new(cargo) + .arg("metadata") + .arg("--format-version=1") + .arg("--all-features") + .arg("--manifest-path") + .arg(manifest_path) + .env("RUSTC_BOOTSTRAP", "1") + .output() + .map_err(|e| Error::LaunchingMetadata(e))?; + if !metadata_output.status.success() { + return Err(Error::GettingMetadata( + String::from_utf8(metadata_output.stderr).expect("UTF-8 output from cargo"), + )); + } + let json = serde_json::from_slice(&metadata_output.stdout)?; + Ok(json) +} + +/// Run cargo-vendor, fetching into the given dir +fn run_cargo_vendor(cargo: &Path, dest: &Path, manifest_paths: &[&Path]) -> Result<(), Error> { + let mut vendor_command = std::process::Command::new(cargo); + vendor_command.env("RUSTC_BOOTSTRAP", "1"); + vendor_command.arg("vendor"); + vendor_command.arg("--quiet"); + vendor_command.arg("--versioned-dirs"); + for manifest_path in manifest_paths { + vendor_command.arg("-s"); + vendor_command.arg(manifest_path); + } + vendor_command.arg(dest); + + let vendor_status = vendor_command.status().map_err(|e| Error::LaunchingVendor(e))?; + + if !vendor_status.success() { + return Err(Error::RunningVendor); + } + + Ok(()) +} + +/// Add important files off disk into this dependency. +/// +/// Maybe one-day Cargo.toml will contain enough information that we don't need +/// to do this manual scraping. +fn load_important_files(dep: &mut Dependency, vendor_root: &Path) -> Result<(), Error> { + let name_version = format!("{}-{}", dep.name, dep.version); + println!("Scraping notices for {}...", name_version); + let dep_vendor_path = vendor_root.join(name_version); + for entry in std::fs::read_dir(dep_vendor_path)? { + let entry = entry?; + let metadata = entry.metadata()?; + let path = entry.path(); + if let Some(filename) = path.file_name() { + let lc_filename = filename.to_ascii_lowercase(); + let lc_filename_str = lc_filename.to_string_lossy(); + let mut keep = false; + for m in ["copyright", "licence", "license", "author", "notice"] { + if lc_filename_str.contains(m) { + keep = true; + break; + } + } + if keep { + if metadata.is_dir() { + // scoop up whole directory + } else if metadata.is_file() { + println!("Scraping {}", filename.to_string_lossy()); + dep.notices.insert(filename.to_owned(), std::fs::read_to_string(path)?); + } + } + } + } + Ok(()) +} diff --git a/src/tools/generate-copyright/src/main.rs b/src/tools/generate-copyright/src/main.rs index dce1a558697e6..6191cd158bc9b 100644 --- a/src/tools/generate-copyright/src/main.rs +++ b/src/tools/generate-copyright/src/main.rs @@ -1,54 +1,114 @@ use std::io::Write; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; use anyhow::Error; +mod cargo_metadata; + +/// The entry point to the binary. +/// +/// You should probably let `bootstrap` execute this program instead of running it directly. +/// +/// Run `x.py run generate-metadata` fn main() -> Result<(), Error> { - let dest = env_path("DEST")?; + let dest_file = env_path("DEST")?; + let out_dir = env_path("OUT_DIR")?; + let cargo = env_path("CARGO")?; let license_metadata = env_path("LICENSE_METADATA")?; - let metadata: Metadata = serde_json::from_slice(&std::fs::read(&license_metadata)?)?; + let collected_tree_metadata: Metadata = + serde_json::from_slice(&std::fs::read(&license_metadata)?)?; + + let root_path = std::path::absolute(".")?; + let workspace_paths = [ + Path::new("./Cargo.toml"), + Path::new("./src/tools/cargo/Cargo.toml"), + Path::new("./library/std/Cargo.toml"), + ]; + let collected_cargo_metadata = + cargo_metadata::get(&cargo, &out_dir, &root_path, &workspace_paths)?; let mut buffer = Vec::new(); - render_recursive(&metadata.files, &mut buffer, 0)?; - std::fs::write(&dest, &buffer)?; + writeln!(buffer, "# COPYRIGHT for Rust")?; + writeln!(buffer)?; + writeln!( + buffer, + "This file describes the copyright and licensing information for the source code within The Rust Project git tree, and the third-party dependencies used when building the Rust toolchain (including the Rust Standard Library)" + )?; + writeln!(buffer)?; + writeln!(buffer, "## Table of Contents")?; + writeln!(buffer)?; + writeln!(buffer, "* [In-tree files](#in-tree-files)")?; + writeln!(buffer, "* [Out-of-tree files](#out-of-tree-files)")?; + // writeln!(buffer, "* [License Texts](#license-texts)")?; + writeln!(buffer)?; + + writeln!(buffer, "## In-tree files")?; + writeln!(buffer)?; + writeln!( + buffer, + "The following licenses cover the in-tree source files that were used in this release:" + )?; + writeln!(buffer)?; + render_tree_recursive(&collected_tree_metadata.files, &mut buffer, 0)?; + + writeln!(buffer)?; + + writeln!(buffer, "## Out-of-tree files")?; + writeln!(buffer)?; + writeln!( + buffer, + "The following licenses cover the out-of-tree crates that were used in this release:" + )?; + writeln!(buffer)?; + render_deps(collected_cargo_metadata.iter(), &mut buffer)?; + + std::fs::write(&dest_file, &buffer)?; Ok(()) } -fn render_recursive(node: &Node, buffer: &mut Vec, depth: usize) -> Result<(), Error> { +/// Recursively draw the tree of files/folders we found on disk and their licenses, as +/// markdown, into the given Vec. +fn render_tree_recursive(node: &Node, buffer: &mut Vec, depth: usize) -> Result<(), Error> { let prefix = std::iter::repeat("> ").take(depth + 1).collect::(); match node { Node::Root { children } => { for child in children { - render_recursive(child, buffer, depth)?; + render_tree_recursive(child, buffer, depth)?; } } Node::Directory { name, children, license } => { - render_license(&prefix, std::iter::once(name), license.as_ref(), buffer)?; + render_tree_license(&prefix, std::iter::once(name), license.as_ref(), buffer)?; if !children.is_empty() { writeln!(buffer, "{prefix}")?; writeln!(buffer, "{prefix}*Exceptions:*")?; for child in children { writeln!(buffer, "{prefix}")?; - render_recursive(child, buffer, depth + 1)?; + render_tree_recursive(child, buffer, depth + 1)?; } } } Node::Group { files, directories, license } => { - render_license(&prefix, directories.iter().chain(files.iter()), Some(license), buffer)?; + render_tree_license( + &prefix, + directories.iter().chain(files.iter()), + Some(license), + buffer, + )?; } Node::File { name, license } => { - render_license(&prefix, std::iter::once(name), Some(license), buffer)?; + render_tree_license(&prefix, std::iter::once(name), Some(license), buffer)?; } } Ok(()) } -fn render_license<'a>( +/// Draw a series of sibling files/folders, as markdown, into the given Vec. +fn render_tree_license<'a>( prefix: &str, names: impl Iterator, license: Option<&License>, @@ -67,11 +127,47 @@ fn render_license<'a>( Ok(()) } +/// Render a list of out-of-tree dependencies as markdown into the given Vec. +fn render_deps<'a, 'b>( + deps: impl Iterator, + buffer: &'b mut Vec, +) -> Result<(), Error> { + for dep in deps { + let authors_list = dep.authors.join(", ").replace("<", "\\<").replace(">", "\\>"); + let url = format!("https://crates.io/crates/{}/{}", dep.name, dep.version); + writeln!(buffer)?; + writeln!( + buffer, + "### [{name} {version}]({url})", + name = dep.name, + version = dep.version, + url = url, + )?; + writeln!(buffer)?; + writeln!(buffer, "* Authors: {}", authors_list)?; + writeln!(buffer, "* License: {}", dep.license)?; + for (name, contents) in &dep.notices { + writeln!(buffer)?; + writeln!(buffer, "#### {}", name.to_string_lossy())?; + writeln!(buffer)?; + writeln!(buffer, "
Click to expand")?; + writeln!(buffer)?; + writeln!(buffer, "```")?; + writeln!(buffer, "{}", contents)?; + writeln!(buffer, "```")?; + writeln!(buffer)?; + writeln!(buffer, "
")?; + } + } + Ok(()) +} +/// Describes a tree of metadata for our filesystem tree #[derive(serde::Deserialize)] struct Metadata { files: Node, } +/// Describes one node in our metadata tree #[derive(serde::Deserialize)] #[serde(rename_all = "kebab-case", tag = "type")] pub(crate) enum Node { @@ -81,12 +177,14 @@ pub(crate) enum Node { Group { files: Vec, directories: Vec, license: License }, } +/// A License has an SPDX license name and a list of copyright holders. #[derive(serde::Deserialize)] struct License { spdx: String, copyright: Vec, } +/// Grab an environment variable as a PathBuf, or fail nicely. fn env_path(var: &str) -> Result { if let Some(var) = std::env::var_os(var) { Ok(var.into()) From 204e3eadf1323ebd886ee159b193e231ec4906c9 Mon Sep 17 00:00:00 2001 From: Jonathan Pallant Date: Mon, 29 Jul 2024 11:41:02 +0100 Subject: [PATCH 04/31] generate-copyright: Produce HTML, not Markdown This format works better with large amounts of structured data. We also mark which deps are in the stdlib --- src/bootstrap/src/core/build_steps/run.rs | 2 +- .../generate-copyright/src/cargo_metadata.rs | 81 +++++---- src/tools/generate-copyright/src/main.rs | 172 ++++++++++-------- 3 files changed, 152 insertions(+), 103 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/run.rs b/src/bootstrap/src/core/build_steps/run.rs index 29d7bcc425bea..65d635c0bd69f 100644 --- a/src/bootstrap/src/core/build_steps/run.rs +++ b/src/bootstrap/src/core/build_steps/run.rs @@ -212,7 +212,7 @@ impl Step for GenerateCopyright { let license_metadata = builder.ensure(CollectLicenseMetadata); // Temporary location, it will be moved to the proper one once it's accurate. - let dest = builder.out.join("COPYRIGHT.md"); + let dest = builder.out.join("COPYRIGHT.html"); let mut cmd = builder.tool_cmd(Tool::GenerateCopyright); cmd.env("LICENSE_METADATA", &license_metadata); diff --git a/src/tools/generate-copyright/src/cargo_metadata.rs b/src/tools/generate-copyright/src/cargo_metadata.rs index 721a6b1c6e627..eda53c73c0af6 100644 --- a/src/tools/generate-copyright/src/cargo_metadata.rs +++ b/src/tools/generate-copyright/src/cargo_metadata.rs @@ -1,6 +1,6 @@ //! Gets metadata about a workspace from Cargo -use std::collections::{BTreeMap, BTreeSet}; +use std::collections::BTreeMap; use std::ffi::{OsStr, OsString}; use std::path::Path; @@ -23,13 +23,18 @@ pub enum Error { RunningVendor, } -/// Describes one of our dependencies +/// Uniquely describes a package on crates.io #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] -pub struct Dependency { +pub struct Package { /// The name of the package pub name: String, /// The version number pub version: String, +} + +/// Extra data about a package +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] +pub struct PackageMetadata { /// The license it is under pub license: String, /// The list of authors from the package metadata @@ -40,20 +45,44 @@ pub struct Dependency { pub notices: BTreeMap, } -/// Use `cargo` to get a list of dependencies and their license data. +/// Use `cargo metadata` and `cargo vendor` to get a list of dependencies and their license data. /// /// This will involve running `cargo vendor` into `${BUILD}/vendor` so we can /// grab the license files. /// /// Any dependency with a path beginning with `root_path` is ignored, as we /// assume `reuse` has covered it already. -pub fn get( +pub fn get_metadata_and_notices( cargo: &Path, dest: &Path, root_path: &Path, manifest_paths: &[&Path], -) -> Result, Error> { - let mut temp_set = BTreeSet::new(); +) -> Result, Error> { + let mut output = get_metadata(cargo, root_path, manifest_paths)?; + + // Now do a cargo-vendor and grab everything + let vendor_path = dest.join("vendor"); + println!("Vendoring deps into {}...", vendor_path.display()); + run_cargo_vendor(cargo, &vendor_path, manifest_paths)?; + + // Now for each dependency we found, go and grab any important looking files + for (package, metadata) in output.iter_mut() { + load_important_files(package, metadata, &vendor_path)?; + } + + Ok(output) +} + +/// Use `cargo metadata` to get a list of dependencies and their license data. +/// +/// Any dependency with a path beginning with `root_path` is ignored, as we +/// assume `reuse` has covered it already. +pub fn get_metadata( + cargo: &Path, + root_path: &Path, + manifest_paths: &[&Path], +) -> Result, Error> { + let mut output = BTreeMap::new(); // Look at the metadata for each manifest for manifest_path in manifest_paths { if manifest_path.file_name() != Some(OsStr::new("Cargo.toml")) { @@ -71,7 +100,7 @@ pub fn get( .and_then(|v| v.as_str()) .map(Path::new) .ok_or_else(|| Error::MissingJsonElement("package.manifest_path"))?; - if manifest_path.starts_with(&root_path) { + if manifest_path.starts_with(root_path) { // it's an in-tree dependency and reuse covers it continue; } @@ -93,28 +122,14 @@ pub fn get( .ok_or_else(|| Error::MissingJsonElement("package.authors"))?; let authors: Vec = authors_list.iter().filter_map(|v| v.as_str()).map(|s| s.to_owned()).collect(); - temp_set.insert(Dependency { - name: name.to_owned(), - version: version.to_owned(), - license: license.to_owned(), - authors, - notices: BTreeMap::new(), - }); + let package = Package { name: name.to_owned(), version: version.to_owned() }; + output.insert( + package.clone(), + PackageMetadata { license: license.to_owned(), authors, notices: BTreeMap::new() }, + ); } } - // Now do a cargo-vendor and grab everything - let vendor_path = dest.join("vendor"); - println!("Vendoring deps into {}...", vendor_path.display()); - run_cargo_vendor(cargo, &vendor_path, manifest_paths)?; - - // Now for each dependency we found, go and grab any important looking files - let mut output = BTreeSet::new(); - for mut dep in temp_set { - load_important_files(&mut dep, &vendor_path)?; - output.insert(dep); - } - Ok(output) } @@ -128,7 +143,7 @@ fn get_metadata_json(cargo: &Path, manifest_path: &Path) -> Result Resu } vendor_command.arg(dest); - let vendor_status = vendor_command.status().map_err(|e| Error::LaunchingVendor(e))?; + let vendor_status = vendor_command.status().map_err(Error::LaunchingVendor)?; if !vendor_status.success() { return Err(Error::RunningVendor); @@ -164,8 +179,12 @@ fn run_cargo_vendor(cargo: &Path, dest: &Path, manifest_paths: &[&Path]) -> Resu /// /// Maybe one-day Cargo.toml will contain enough information that we don't need /// to do this manual scraping. -fn load_important_files(dep: &mut Dependency, vendor_root: &Path) -> Result<(), Error> { - let name_version = format!("{}-{}", dep.name, dep.version); +fn load_important_files( + package: &Package, + dep: &mut PackageMetadata, + vendor_root: &Path, +) -> Result<(), Error> { + let name_version = format!("{}-{}", package.name, package.version); println!("Scraping notices for {}...", name_version); let dep_vendor_path = vendor_root.join(name_version); for entry in std::fs::read_dir(dep_vendor_path)? { diff --git a/src/tools/generate-copyright/src/main.rs b/src/tools/generate-copyright/src/main.rs index 6191cd158bc9b..efccba0651e4d 100644 --- a/src/tools/generate-copyright/src/main.rs +++ b/src/tools/generate-copyright/src/main.rs @@ -1,3 +1,4 @@ +use std::collections::BTreeMap; use std::io::Write; use std::path::{Path, PathBuf}; @@ -5,6 +6,33 @@ use anyhow::Error; mod cargo_metadata; +static TOP_BOILERPLATE: &str = r##" + + + + + Copyright notices for The Rust Toolchain + + + +

Copyright notices for The Rust Toolchain

+ +

This file describes the copyright and licensing information for the source +code within The Rust Project git tree, and the third-party dependencies used +when building the Rust toolchain (including the Rust Standard Library).

+ +

Table of Contents

+ +"##; + +static BOTTOM_BOILERPLATE: &str = r#" + + +"#; + /// The entry point to the binary. /// /// You should probably let `bootstrap` execute this program instead of running it directly. @@ -26,43 +54,28 @@ fn main() -> Result<(), Error> { Path::new("./library/std/Cargo.toml"), ]; let collected_cargo_metadata = - cargo_metadata::get(&cargo, &out_dir, &root_path, &workspace_paths)?; + cargo_metadata::get_metadata_and_notices(&cargo, &out_dir, &root_path, &workspace_paths)?; + + let stdlib_set = + cargo_metadata::get_metadata(&cargo, &root_path, &[Path::new("./library/std/Cargo.toml")])?; let mut buffer = Vec::new(); - writeln!(buffer, "# COPYRIGHT for Rust")?; - writeln!(buffer)?; - writeln!( - buffer, - "This file describes the copyright and licensing information for the source code within The Rust Project git tree, and the third-party dependencies used when building the Rust toolchain (including the Rust Standard Library)" - )?; - writeln!(buffer)?; - writeln!(buffer, "## Table of Contents")?; - writeln!(buffer)?; - writeln!(buffer, "* [In-tree files](#in-tree-files)")?; - writeln!(buffer, "* [Out-of-tree files](#out-of-tree-files)")?; - // writeln!(buffer, "* [License Texts](#license-texts)")?; - writeln!(buffer)?; - - writeln!(buffer, "## In-tree files")?; - writeln!(buffer)?; + writeln!(buffer, "{}", TOP_BOILERPLATE)?; + writeln!( buffer, - "The following licenses cover the in-tree source files that were used in this release:" + r#"

In-tree files

The following licenses cover the in-tree source files that were used in this release:

"# )?; - writeln!(buffer)?; - render_tree_recursive(&collected_tree_metadata.files, &mut buffer, 0)?; - - writeln!(buffer)?; + render_tree_recursive(&collected_tree_metadata.files, &mut buffer)?; - writeln!(buffer, "## Out-of-tree files")?; - writeln!(buffer)?; writeln!( buffer, - "The following licenses cover the out-of-tree crates that were used in this release:" + r#"

Out-of-tree dependencies

The following licenses cover the out-of-tree crates that were used in this release:

"# )?; - writeln!(buffer)?; - render_deps(collected_cargo_metadata.iter(), &mut buffer)?; + render_deps(&collected_cargo_metadata, &stdlib_set, &mut buffer)?; + + writeln!(buffer, "{}", BOTTOM_BOILERPLATE)?; std::fs::write(&dest_file, &buffer)?; @@ -71,56 +84,51 @@ fn main() -> Result<(), Error> { /// Recursively draw the tree of files/folders we found on disk and their licenses, as /// markdown, into the given Vec. -fn render_tree_recursive(node: &Node, buffer: &mut Vec, depth: usize) -> Result<(), Error> { - let prefix = std::iter::repeat("> ").take(depth + 1).collect::(); - +fn render_tree_recursive(node: &Node, buffer: &mut Vec) -> Result<(), Error> { + writeln!(buffer, r#"
"#)?; match node { Node::Root { children } => { for child in children { - render_tree_recursive(child, buffer, depth)?; + render_tree_recursive(child, buffer)?; } } Node::Directory { name, children, license } => { - render_tree_license(&prefix, std::iter::once(name), license.as_ref(), buffer)?; + render_tree_license(std::iter::once(name), license.as_ref(), buffer)?; if !children.is_empty() { - writeln!(buffer, "{prefix}")?; - writeln!(buffer, "{prefix}*Exceptions:*")?; + writeln!(buffer, "

Exceptions:

")?; for child in children { - writeln!(buffer, "{prefix}")?; - render_tree_recursive(child, buffer, depth + 1)?; + render_tree_recursive(child, buffer)?; } } } Node::Group { files, directories, license } => { - render_tree_license( - &prefix, - directories.iter().chain(files.iter()), - Some(license), - buffer, - )?; + render_tree_license(directories.iter().chain(files.iter()), Some(license), buffer)?; } Node::File { name, license } => { - render_tree_license(&prefix, std::iter::once(name), Some(license), buffer)?; + render_tree_license(std::iter::once(name), Some(license), buffer)?; } } + writeln!(buffer, "
")?; Ok(()) } /// Draw a series of sibling files/folders, as markdown, into the given Vec. fn render_tree_license<'a>( - prefix: &str, names: impl Iterator, license: Option<&License>, buffer: &mut Vec, ) -> Result<(), Error> { + writeln!(buffer, "

File/Directory: ")?; for name in names { - writeln!(buffer, "{prefix}**`{name}`** ")?; + writeln!(buffer, "{name}")?; } + writeln!(buffer, "

")?; + if let Some(license) = license { - writeln!(buffer, "{prefix}License: `{}`", license.spdx)?; + writeln!(buffer, "

License: {}

", license.spdx)?; for copyright in license.copyright.iter() { - writeln!(buffer, "{prefix}Copyright: {copyright}")?; + writeln!(buffer, "

Copyright: {copyright}

")?; } } @@ -128,36 +136,48 @@ fn render_tree_license<'a>( } /// Render a list of out-of-tree dependencies as markdown into the given Vec. -fn render_deps<'a, 'b>( - deps: impl Iterator, - buffer: &'b mut Vec, +fn render_deps( + all_deps: &BTreeMap, + stdlib_set: &BTreeMap, + buffer: &mut Vec, ) -> Result<(), Error> { - for dep in deps { - let authors_list = dep.authors.join(", ").replace("<", "\\<").replace(">", "\\>"); - let url = format!("https://crates.io/crates/{}/{}", dep.name, dep.version); + for (package, metadata) in all_deps { + let authors_list = if metadata.authors.is_empty() { + "None Specified".to_owned() + } else { + metadata.authors.join(", ") + }; + let url = format!("https://crates.io/crates/{}/{}", package.name, package.version); writeln!(buffer)?; writeln!( buffer, - "### [{name} {version}]({url})", - name = dep.name, - version = dep.version, - url = url, + r#"

📦 {name}-{version}

"#, + name = package.name, + version = package.version, )?; - writeln!(buffer)?; - writeln!(buffer, "* Authors: {}", authors_list)?; - writeln!(buffer, "* License: {}", dep.license)?; - for (name, contents) in &dep.notices { - writeln!(buffer)?; - writeln!(buffer, "#### {}", name.to_string_lossy())?; - writeln!(buffer)?; - writeln!(buffer, "
Click to expand")?; - writeln!(buffer)?; - writeln!(buffer, "```")?; - writeln!(buffer, "{}", contents)?; - writeln!(buffer, "```")?; - writeln!(buffer)?; - writeln!(buffer, "
")?; + writeln!(buffer, r#"

URL: {url}

"#,)?; + writeln!( + buffer, + "

In libstd: {}

", + if stdlib_set.contains_key(package) { "Yes" } else { "No" } + )?; + writeln!(buffer, "

Authors: {}

", escape_html(&authors_list))?; + writeln!(buffer, "

License: {}

", escape_html(&metadata.license))?; + writeln!(buffer, "

Notices: ")?; + if metadata.notices.is_empty() { + writeln!(buffer, "None")?; + } else { + for (name, contents) in &metadata.notices { + writeln!( + buffer, + "

{}", + name.to_string_lossy() + )?; + writeln!(buffer, "
\n{}\n
", contents)?; + writeln!(buffer, "
")?; + } } + writeln!(buffer, "

")?; } Ok(()) } @@ -192,3 +212,13 @@ fn env_path(var: &str) -> Result { anyhow::bail!("missing environment variable {var}") } } + +/// Escapes any invalid HTML characters +fn escape_html(input: &str) -> String { + static MAPPING: [(char, &str); 3] = [('&', "&"), ('<', "<"), ('>', ">")]; + let mut output = input.to_owned(); + for (ch, s) in &MAPPING { + output = output.replace(*ch, s); + } + output +} From 56f84796a4e819be108721f723d8c1b229e5dbdd Mon Sep 17 00:00:00 2001 From: Jonathan Pallant Date: Tue, 30 Jul 2024 13:54:48 +0100 Subject: [PATCH 05/31] generate-copyright: Fix typo --- src/tools/generate-copyright/src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/generate-copyright/src/main.rs b/src/tools/generate-copyright/src/main.rs index efccba0651e4d..af69ab8c8bf36 100644 --- a/src/tools/generate-copyright/src/main.rs +++ b/src/tools/generate-copyright/src/main.rs @@ -37,7 +37,7 @@ static BOTTOM_BOILERPLATE: &str = r#" /// /// You should probably let `bootstrap` execute this program instead of running it directly. /// -/// Run `x.py run generate-metadata` +/// Run `x.py run generate-copyright` fn main() -> Result<(), Error> { let dest_file = env_path("DEST")?; let out_dir = env_path("OUT_DIR")?; From dbab595d78f12c0514cfe2ac4c7c9d083445c14f Mon Sep 17 00:00:00 2001 From: Jonathan Pallant Date: Tue, 30 Jul 2024 13:56:17 +0100 Subject: [PATCH 06/31] generate-copyright: use cargo-metadata --- Cargo.lock | 1 + src/tools/generate-copyright/Cargo.toml | 1 + .../generate-copyright/src/cargo_metadata.rs | 80 +++++-------------- 3 files changed, 20 insertions(+), 62 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0f3a106512d20..eeaeff79ebb25 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1406,6 +1406,7 @@ name = "generate-copyright" version = "0.1.0" dependencies = [ "anyhow", + "cargo_metadata 0.18.1", "serde", "serde_json", "tempfile", diff --git a/src/tools/generate-copyright/Cargo.toml b/src/tools/generate-copyright/Cargo.toml index bf643876a042b..c94cc35fb5036 100644 --- a/src/tools/generate-copyright/Cargo.toml +++ b/src/tools/generate-copyright/Cargo.toml @@ -12,3 +12,4 @@ serde = { version = "1.0.147", features = ["derive"] } serde_json = "1.0.85" thiserror = "1" tempfile = "3" +cargo_metadata = "0.18.1" diff --git a/src/tools/generate-copyright/src/cargo_metadata.rs b/src/tools/generate-copyright/src/cargo_metadata.rs index eda53c73c0af6..655d73715e036 100644 --- a/src/tools/generate-copyright/src/cargo_metadata.rs +++ b/src/tools/generate-copyright/src/cargo_metadata.rs @@ -7,16 +7,10 @@ use std::path::Path; /// Describes how this module can fail #[derive(Debug, thiserror::Error)] pub enum Error { - #[error("Failed to run cargo metadata: {0:?}")] - LaunchingMetadata(#[from] std::io::Error), - #[error("Failed get output from cargo metadata: {0:?}")] - GettingMetadata(String), - #[error("Failed parse JSON output from cargo metadata: {0:?}")] - ParsingJson(#[from] serde_json::Error), - #[error("Failed find expected JSON element {0} in output from cargo metadata")] - MissingJsonElement(&'static str), - #[error("Failed find expected JSON element {0} in output from cargo metadata for package {1}")] - MissingJsonElementForPackage(String, String), + #[error("I/O Error: {0:?}")] + Io(#[from] std::io::Error), + #[error("Failed get output from cargo-metadata: {0:?}")] + GettingMetadata(#[from] cargo_metadata::Error), #[error("Failed to run cargo vendor: {0:?}")] LaunchingVendor(std::io::Error), #[error("Failed to complete cargo vendor")] @@ -88,44 +82,26 @@ pub fn get_metadata( if manifest_path.file_name() != Some(OsStr::new("Cargo.toml")) { panic!("cargo_manifest::get requires a path to a Cargo.toml file"); } - let metadata_json = get_metadata_json(cargo, manifest_path)?; - let packages = metadata_json["packages"] - .as_array() - .ok_or_else(|| Error::MissingJsonElement("packages array"))?; - for package in packages { - let package = - package.as_object().ok_or_else(|| Error::MissingJsonElement("package object"))?; - let manifest_path = package - .get("manifest_path") - .and_then(|v| v.as_str()) - .map(Path::new) - .ok_or_else(|| Error::MissingJsonElement("package.manifest_path"))?; + let metadata = cargo_metadata::MetadataCommand::new() + .cargo_path(cargo) + .env("RUSTC_BOOTSTRAP", "1") + .manifest_path(manifest_path) + .exec()?; + for package in metadata.packages { + let manifest_path = package.manifest_path.as_path(); if manifest_path.starts_with(root_path) { // it's an in-tree dependency and reuse covers it continue; } // otherwise it's an out-of-tree dependency - let get_string = |field_name: &str, package_name: &str| { - package.get(field_name).and_then(|v| v.as_str()).ok_or_else(|| { - Error::MissingJsonElementForPackage( - format!("package.{field_name}"), - package_name.to_owned(), - ) - }) - }; - let name = get_string("name", "unknown")?; - let license = get_string("license", name)?; - let version = get_string("version", name)?; - let authors_list = package - .get("authors") - .and_then(|v| v.as_array()) - .ok_or_else(|| Error::MissingJsonElement("package.authors"))?; - let authors: Vec = - authors_list.iter().filter_map(|v| v.as_str()).map(|s| s.to_owned()).collect(); - let package = Package { name: name.to_owned(), version: version.to_owned() }; + let package_id = Package { name: package.name, version: package.version.to_string() }; output.insert( - package.clone(), - PackageMetadata { license: license.to_owned(), authors, notices: BTreeMap::new() }, + package_id, + PackageMetadata { + license: package.license.unwrap_or_else(|| String::from("Unspecified")), + authors: package.authors, + notices: BTreeMap::new(), + }, ); } } @@ -133,26 +109,6 @@ pub fn get_metadata( Ok(output) } -/// Get cargo-metdata for a package, as JSON -fn get_metadata_json(cargo: &Path, manifest_path: &Path) -> Result { - let metadata_output = std::process::Command::new(cargo) - .arg("metadata") - .arg("--format-version=1") - .arg("--all-features") - .arg("--manifest-path") - .arg(manifest_path) - .env("RUSTC_BOOTSTRAP", "1") - .output() - .map_err(Error::LaunchingMetadata)?; - if !metadata_output.status.success() { - return Err(Error::GettingMetadata( - String::from_utf8(metadata_output.stderr).expect("UTF-8 output from cargo"), - )); - } - let json = serde_json::from_slice(&metadata_output.stdout)?; - Ok(json) -} - /// Run cargo-vendor, fetching into the given dir fn run_cargo_vendor(cargo: &Path, dest: &Path, manifest_paths: &[&Path]) -> Result<(), Error> { let mut vendor_command = std::process::Command::new(cargo); From f7e6bf61a9c3492115d19dc112071adea90a7038 Mon Sep 17 00:00:00 2001 From: Jonathan Pallant Date: Tue, 30 Jul 2024 19:39:06 +0100 Subject: [PATCH 07/31] generate-copyright: use rinja to format the output I can't find a way to derive rinja::Template for Node - I think because it is a recursive type. So I rendered it manually using html_escape. --- src/tools/generate-copyright/Cargo.toml | 6 +- .../generate-copyright/src/cargo_metadata.rs | 12 +- src/tools/generate-copyright/src/main.rs | 226 +++++++----------- .../templates/COPYRIGHT.html | 54 +++++ 4 files changed, 146 insertions(+), 152 deletions(-) create mode 100644 src/tools/generate-copyright/templates/COPYRIGHT.html diff --git a/src/tools/generate-copyright/Cargo.toml b/src/tools/generate-copyright/Cargo.toml index c94cc35fb5036..c00292cf33108 100644 --- a/src/tools/generate-copyright/Cargo.toml +++ b/src/tools/generate-copyright/Cargo.toml @@ -8,8 +8,10 @@ description = "Produces a manifest of all the copyrighted materials in the Rust [dependencies] anyhow = "1.0.65" +cargo_metadata = "0.18.1" +html-escape = "0.2.13" +rinja = "0.2.0" serde = { version = "1.0.147", features = ["derive"] } serde_json = "1.0.85" -thiserror = "1" tempfile = "3" -cargo_metadata = "0.18.1" +thiserror = "1" diff --git a/src/tools/generate-copyright/src/cargo_metadata.rs b/src/tools/generate-copyright/src/cargo_metadata.rs index 655d73715e036..d02b9eeb6f922 100644 --- a/src/tools/generate-copyright/src/cargo_metadata.rs +++ b/src/tools/generate-copyright/src/cargo_metadata.rs @@ -1,7 +1,7 @@ //! Gets metadata about a workspace from Cargo use std::collections::BTreeMap; -use std::ffi::{OsStr, OsString}; +use std::ffi::OsStr; use std::path::Path; /// Describes how this module can fail @@ -36,7 +36,9 @@ pub struct PackageMetadata { /// A list of important files from the package, with their contents. /// /// This includes *COPYRIGHT*, *NOTICE*, *AUTHOR*, *LICENSE*, and *LICENCE* files, case-insensitive. - pub notices: BTreeMap, + pub notices: BTreeMap, + /// If this is true, this dep is in the Rust Standard Library + pub is_in_libstd: Option, } /// Use `cargo metadata` and `cargo vendor` to get a list of dependencies and their license data. @@ -101,6 +103,7 @@ pub fn get_metadata( license: package.license.unwrap_or_else(|| String::from("Unspecified")), authors: package.authors, notices: BTreeMap::new(), + is_in_libstd: None, }, ); } @@ -161,8 +164,9 @@ fn load_important_files( if metadata.is_dir() { // scoop up whole directory } else if metadata.is_file() { - println!("Scraping {}", filename.to_string_lossy()); - dep.notices.insert(filename.to_owned(), std::fs::read_to_string(path)?); + let filename = filename.to_string_lossy(); + println!("Scraping {}", filename); + dep.notices.insert(filename.to_string(), std::fs::read_to_string(path)?); } } } diff --git a/src/tools/generate-copyright/src/main.rs b/src/tools/generate-copyright/src/main.rs index af69ab8c8bf36..03b789b739298 100644 --- a/src/tools/generate-copyright/src/main.rs +++ b/src/tools/generate-copyright/src/main.rs @@ -1,37 +1,17 @@ use std::collections::BTreeMap; -use std::io::Write; use std::path::{Path, PathBuf}; use anyhow::Error; +use rinja::Template; mod cargo_metadata; -static TOP_BOILERPLATE: &str = r##" - - - - - Copyright notices for The Rust Toolchain - - - -

Copyright notices for The Rust Toolchain

- -

This file describes the copyright and licensing information for the source -code within The Rust Project git tree, and the third-party dependencies used -when building the Rust toolchain (including the Rust Standard Library).

- -

Table of Contents

- -"##; - -static BOTTOM_BOILERPLATE: &str = r#" - - -"#; +#[derive(Template)] +#[template(path = "COPYRIGHT.html")] +struct CopyrightTemplate { + in_tree: Node, + dependencies: BTreeMap, +} /// The entry point to the binary. /// @@ -53,150 +33,114 @@ fn main() -> Result<(), Error> { Path::new("./src/tools/cargo/Cargo.toml"), Path::new("./library/std/Cargo.toml"), ]; - let collected_cargo_metadata = + let mut collected_cargo_metadata = cargo_metadata::get_metadata_and_notices(&cargo, &out_dir, &root_path, &workspace_paths)?; let stdlib_set = cargo_metadata::get_metadata(&cargo, &root_path, &[Path::new("./library/std/Cargo.toml")])?; - let mut buffer = Vec::new(); + for (key, value) in collected_cargo_metadata.iter_mut() { + value.is_in_libstd = Some(stdlib_set.contains_key(key)); + } - writeln!(buffer, "{}", TOP_BOILERPLATE)?; + let template = CopyrightTemplate { + in_tree: collected_tree_metadata.files, + dependencies: collected_cargo_metadata, + }; - writeln!( - buffer, - r#"

In-tree files

The following licenses cover the in-tree source files that were used in this release:

"# - )?; - render_tree_recursive(&collected_tree_metadata.files, &mut buffer)?; + let output = template.render()?; - writeln!( - buffer, - r#"

Out-of-tree dependencies

The following licenses cover the out-of-tree crates that were used in this release:

"# - )?; - render_deps(&collected_cargo_metadata, &stdlib_set, &mut buffer)?; + std::fs::write(&dest_file, output)?; - writeln!(buffer, "{}", BOTTOM_BOILERPLATE)?; + Ok(()) +} - std::fs::write(&dest_file, &buffer)?; +/// Describes a tree of metadata for our filesystem tree +#[derive(serde::Deserialize)] +struct Metadata { + files: Node, +} +/// Describes one node in our metadata tree +#[derive(serde::Deserialize)] +#[serde(rename_all = "kebab-case", tag = "type")] +pub(crate) enum Node { + Root { children: Vec }, + Directory { name: String, children: Vec, license: Option }, + File { name: String, license: License }, + Group { files: Vec, directories: Vec, license: License }, +} + +fn with_box(fmt: &mut std::fmt::Formatter<'_>, inner: F) -> std::fmt::Result +where + F: FnOnce(&mut std::fmt::Formatter<'_>) -> std::fmt::Result, +{ + writeln!(fmt, r#"
"#)?; + inner(fmt)?; + writeln!(fmt, "
")?; Ok(()) } -/// Recursively draw the tree of files/folders we found on disk and their licenses, as -/// markdown, into the given Vec. -fn render_tree_recursive(node: &Node, buffer: &mut Vec) -> Result<(), Error> { - writeln!(buffer, r#"
"#)?; - match node { - Node::Root { children } => { - for child in children { - render_tree_recursive(child, buffer)?; +impl std::fmt::Display for Node { + fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Node::Root { children } => { + if children.len() > 1 { + with_box(fmt, |f| { + for child in children { + writeln!(f, "{child}")?; + } + Ok(()) + }) + } else { + for child in children { + writeln!(fmt, "{child}")?; + } + Ok(()) + } } - } - Node::Directory { name, children, license } => { - render_tree_license(std::iter::once(name), license.as_ref(), buffer)?; - if !children.is_empty() { - writeln!(buffer, "

Exceptions:

")?; - for child in children { - render_tree_recursive(child, buffer)?; + Node::Directory { name, children, license } => with_box(fmt, |f| { + render_tree_license(std::iter::once(name), license.as_ref(), f)?; + if !children.is_empty() { + writeln!(f, "

Exceptions:

")?; + for child in children { + writeln!(f, "{child}")?; + } } + Ok(()) + }), + Node::Group { files, directories, license } => with_box(fmt, |f| { + render_tree_license(directories.iter().chain(files.iter()), Some(license), f) + }), + Node::File { name, license } => { + with_box(fmt, |f| render_tree_license(std::iter::once(name), Some(license), f)) } } - Node::Group { files, directories, license } => { - render_tree_license(directories.iter().chain(files.iter()), Some(license), buffer)?; - } - Node::File { name, license } => { - render_tree_license(std::iter::once(name), Some(license), buffer)?; - } } - writeln!(buffer, "
")?; - - Ok(()) } -/// Draw a series of sibling files/folders, as markdown, into the given Vec. +/// Draw a series of sibling files/folders, as HTML, into the given formatter. fn render_tree_license<'a>( names: impl Iterator, license: Option<&License>, - buffer: &mut Vec, -) -> Result<(), Error> { - writeln!(buffer, "

File/Directory: ")?; + f: &mut std::fmt::Formatter<'_>, +) -> std::fmt::Result { + writeln!(f, "

File/Directory: ")?; for name in names { - writeln!(buffer, "{name}")?; + writeln!(f, "{}", html_escape::encode_text(&name))?; } - writeln!(buffer, "

")?; + writeln!(f, "

")?; if let Some(license) = license { - writeln!(buffer, "

License: {}

", license.spdx)?; + writeln!(f, "

License: {}

", html_escape::encode_text(&license.spdx))?; for copyright in license.copyright.iter() { - writeln!(buffer, "

Copyright: {copyright}

")?; + writeln!(f, "

Copyright: {}

", html_escape::encode_text(©right))?; } } Ok(()) } -/// Render a list of out-of-tree dependencies as markdown into the given Vec. -fn render_deps( - all_deps: &BTreeMap, - stdlib_set: &BTreeMap, - buffer: &mut Vec, -) -> Result<(), Error> { - for (package, metadata) in all_deps { - let authors_list = if metadata.authors.is_empty() { - "None Specified".to_owned() - } else { - metadata.authors.join(", ") - }; - let url = format!("https://crates.io/crates/{}/{}", package.name, package.version); - writeln!(buffer)?; - writeln!( - buffer, - r#"

📦 {name}-{version}

"#, - name = package.name, - version = package.version, - )?; - writeln!(buffer, r#"

URL: {url}

"#,)?; - writeln!( - buffer, - "

In libstd: {}

", - if stdlib_set.contains_key(package) { "Yes" } else { "No" } - )?; - writeln!(buffer, "

Authors: {}

", escape_html(&authors_list))?; - writeln!(buffer, "

License: {}

", escape_html(&metadata.license))?; - writeln!(buffer, "

Notices: ")?; - if metadata.notices.is_empty() { - writeln!(buffer, "None")?; - } else { - for (name, contents) in &metadata.notices { - writeln!( - buffer, - "

{}", - name.to_string_lossy() - )?; - writeln!(buffer, "
\n{}\n
", contents)?; - writeln!(buffer, "
")?; - } - } - writeln!(buffer, "

")?; - } - Ok(()) -} -/// Describes a tree of metadata for our filesystem tree -#[derive(serde::Deserialize)] -struct Metadata { - files: Node, -} - -/// Describes one node in our metadata tree -#[derive(serde::Deserialize)] -#[serde(rename_all = "kebab-case", tag = "type")] -pub(crate) enum Node { - Root { children: Vec }, - Directory { name: String, children: Vec, license: Option }, - File { name: String, license: License }, - Group { files: Vec, directories: Vec, license: License }, -} - /// A License has an SPDX license name and a list of copyright holders. #[derive(serde::Deserialize)] struct License { @@ -212,13 +156,3 @@ fn env_path(var: &str) -> Result { anyhow::bail!("missing environment variable {var}") } } - -/// Escapes any invalid HTML characters -fn escape_html(input: &str) -> String { - static MAPPING: [(char, &str); 3] = [('&', "&"), ('<', "<"), ('>', ">")]; - let mut output = input.to_owned(); - for (ch, s) in &MAPPING { - output = output.replace(*ch, s); - } - output -} diff --git a/src/tools/generate-copyright/templates/COPYRIGHT.html b/src/tools/generate-copyright/templates/COPYRIGHT.html new file mode 100644 index 0000000000000..ccb177a54d419 --- /dev/null +++ b/src/tools/generate-copyright/templates/COPYRIGHT.html @@ -0,0 +1,54 @@ + + + + + Copyright notices for The Rust Toolchain + + + +

Copyright notices for The Rust Toolchain

+ +

This file describes the copyright and licensing information for the source +code within The Rust Project git tree, and the third-party dependencies used +when building the Rust toolchain (including the Rust Standard Library).

+ +

Table of Contents

+ + +

In-tree files

+ +

The following licenses cover the in-tree source files that were used in this +release:

+ +{{ in_tree|safe }} + +

Out-of-tree dependencies

+ +

The following licenses cover the out-of-tree crates that were used in this +release:

+ +{% for (key, value) in dependencies %} +

📦 {{key.name}}-{{key.version}}

+

URL: https://crates.io/crates/{{ key.name }}/{{ key.version }}

+

In libstd: {% if value.is_in_libstd.unwrap() %} Yes {% else %} No {% endif %}

+

Authors: {{ value.authors|join(", ") }}

+

License: {{ value.license }}

+ {% let len = value.notices.len() %} + {% if len > 0 %} +

Notices: + {% for (notice_name, notice_text) in value.notices %} +

+ {{ notice_name }} +
+{{ notice_text }}
+                
+
+ {% endfor %} +

+ {% endif %} +{% endfor %} + + \ No newline at end of file From 37ab09010cb627df6b3ffb2d4e95c2cacaf93efb Mon Sep 17 00:00:00 2001 From: Jonathan Pallant Date: Tue, 30 Jul 2024 19:39:38 +0100 Subject: [PATCH 08/31] REUSE.toml: Copyright text isn't parsed as Markdown. --- REUSE.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/REUSE.toml b/REUSE.toml index 1a30d8016c9ea..efd705552478d 100644 --- a/REUSE.toml +++ b/REUSE.toml @@ -163,7 +163,7 @@ SPDX-License-Identifier = "MIT OR Apache-2.0" path = "src/llvm-project/**" precedence = "override" SPDX-FileCopyrightText = [ - "2003-2019 by the contributors listed in [CREDITS.TXT](https://github.com/rust-lang/llvm-project/blob/7738295178045041669876bf32b0543ec8319a5c/llvm/CREDITS.TXT)", + "2003-2019 by the contributors listed in CREDITS.TXT (https://github.com/rust-lang/llvm-project/blob/7738295178045041669876bf32b0543ec8319a5c/llvm/CREDITS.TXT)", "2010 Apple Inc", "2003-2019 University of Illinois at Urbana-Champaign.", ] From 30ac7c9a817793a86d870eeab5a8238acb29b186 Mon Sep 17 00:00:00 2001 From: Jonathan Pallant Date: Wed, 31 Jul 2024 19:26:44 +0100 Subject: [PATCH 09/31] generate-copyright: Render Node with rinja too. --- Cargo.lock | 46 +++++++++++- src/tools/generate-copyright/Cargo.toml | 2 - src/tools/generate-copyright/src/main.rs | 73 +------------------ .../generate-copyright/templates/Node.html | 71 ++++++++++++++++++ 4 files changed, 115 insertions(+), 77 deletions(-) create mode 100644 src/tools/generate-copyright/templates/Node.html diff --git a/Cargo.lock b/Cargo.lock index eeaeff79ebb25..54454e84b5a97 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1407,9 +1407,9 @@ version = "0.1.0" dependencies = [ "anyhow", "cargo_metadata 0.18.1", + "rinja 0.2.0", "serde", "serde_json", - "tempfile", "thiserror", ] @@ -3100,6 +3100,18 @@ dependencies = [ "walkdir", ] +[[package]] +name = "rinja" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2d47a46d7729e891c8accf260e9daa02ae6d570aa2a94fb1fb27eb5364a2323" +dependencies = [ + "humansize", + "num-traits", + "percent-encoding", + "rinja_derive 0.2.0", +] + [[package]] name = "rinja" version = "0.3.0" @@ -3107,7 +3119,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d3762e3740cdbf2fd2be465cc2c26d643ad17353cc2e0223d211c1b096118bd" dependencies = [ "itoa", - "rinja_derive", + "rinja_derive 0.3.0", +] + +[[package]] +name = "rinja_derive" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44dae9afe59d58ed8d988d67d1945f3638125d2fd2104058399382e11bd3ea2a" +dependencies = [ + "basic-toml", + "mime", + "mime_guess", + "once_map", + "proc-macro2", + "quote", + "rinja_parser 0.2.0", + "serde", + "syn 2.0.67", ] [[package]] @@ -3123,11 +3152,20 @@ dependencies = [ "once_map", "proc-macro2", "quote", - "rinja_parser", + "rinja_parser 0.3.0", "serde", "syn 2.0.67", ] +[[package]] +name = "rinja_parser" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b1771c78cd5d3b1646ef8d8f2ed100db936e8b291d3cc06e92a339ff346858c" +dependencies = [ + "nom", +] + [[package]] name = "rinja_parser" version = "0.3.0" @@ -4606,7 +4644,7 @@ dependencies = [ "minifier", "pulldown-cmark 0.9.6", "regex", - "rinja", + "rinja 0.3.0", "rustdoc-json-types", "serde", "serde_json", diff --git a/src/tools/generate-copyright/Cargo.toml b/src/tools/generate-copyright/Cargo.toml index c00292cf33108..d200e2ec9f1e1 100644 --- a/src/tools/generate-copyright/Cargo.toml +++ b/src/tools/generate-copyright/Cargo.toml @@ -9,9 +9,7 @@ description = "Produces a manifest of all the copyrighted materials in the Rust [dependencies] anyhow = "1.0.65" cargo_metadata = "0.18.1" -html-escape = "0.2.13" rinja = "0.2.0" serde = { version = "1.0.147", features = ["derive"] } serde_json = "1.0.85" -tempfile = "3" thiserror = "1" diff --git a/src/tools/generate-copyright/src/main.rs b/src/tools/generate-copyright/src/main.rs index 03b789b739298..37de24648d56d 100644 --- a/src/tools/generate-copyright/src/main.rs +++ b/src/tools/generate-copyright/src/main.rs @@ -62,8 +62,9 @@ struct Metadata { } /// Describes one node in our metadata tree -#[derive(serde::Deserialize)] +#[derive(serde::Deserialize, rinja::Template)] #[serde(rename_all = "kebab-case", tag = "type")] +#[template(path = "Node.html")] pub(crate) enum Node { Root { children: Vec }, Directory { name: String, children: Vec, license: Option }, @@ -71,76 +72,6 @@ pub(crate) enum Node { Group { files: Vec, directories: Vec, license: License }, } -fn with_box(fmt: &mut std::fmt::Formatter<'_>, inner: F) -> std::fmt::Result -where - F: FnOnce(&mut std::fmt::Formatter<'_>) -> std::fmt::Result, -{ - writeln!(fmt, r#"
"#)?; - inner(fmt)?; - writeln!(fmt, "
")?; - Ok(()) -} - -impl std::fmt::Display for Node { - fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Node::Root { children } => { - if children.len() > 1 { - with_box(fmt, |f| { - for child in children { - writeln!(f, "{child}")?; - } - Ok(()) - }) - } else { - for child in children { - writeln!(fmt, "{child}")?; - } - Ok(()) - } - } - Node::Directory { name, children, license } => with_box(fmt, |f| { - render_tree_license(std::iter::once(name), license.as_ref(), f)?; - if !children.is_empty() { - writeln!(f, "

Exceptions:

")?; - for child in children { - writeln!(f, "{child}")?; - } - } - Ok(()) - }), - Node::Group { files, directories, license } => with_box(fmt, |f| { - render_tree_license(directories.iter().chain(files.iter()), Some(license), f) - }), - Node::File { name, license } => { - with_box(fmt, |f| render_tree_license(std::iter::once(name), Some(license), f)) - } - } - } -} - -/// Draw a series of sibling files/folders, as HTML, into the given formatter. -fn render_tree_license<'a>( - names: impl Iterator, - license: Option<&License>, - f: &mut std::fmt::Formatter<'_>, -) -> std::fmt::Result { - writeln!(f, "

File/Directory: ")?; - for name in names { - writeln!(f, "{}", html_escape::encode_text(&name))?; - } - writeln!(f, "

")?; - - if let Some(license) = license { - writeln!(f, "

License: {}

", html_escape::encode_text(&license.spdx))?; - for copyright in license.copyright.iter() { - writeln!(f, "

Copyright: {}

", html_escape::encode_text(©right))?; - } - } - - Ok(()) -} - /// A License has an SPDX license name and a list of copyright holders. #[derive(serde::Deserialize)] struct License { diff --git a/src/tools/generate-copyright/templates/Node.html b/src/tools/generate-copyright/templates/Node.html new file mode 100644 index 0000000000000..a71a1bf3b73d7 --- /dev/null +++ b/src/tools/generate-copyright/templates/Node.html @@ -0,0 +1,71 @@ +{% match self %} + +{% when Node::Root { children } %} + +{% for child in children %} +{{ child|safe }} +{% endfor %} + +{% when Node::Directory { name, children, license } %} + +
+ +

+ File/Directory: {{ name }} +

+ + {% if let Some(license) = license %} + +

License: {{ license.spdx }}

+ {% for copyright in license.copyright.iter() %} +

Copyright: {{ copyright }}

+ {% endfor %} + + {% endif %} + + {% if !children.is_empty() %} + +

Exceptions:

+ {% for child in children %} + {{ child|safe }} + {% endfor %} + + {% endif %} + +
+ +{% when Node::File { name, license } %} + +
+

+ File/Directory: {{ name }} +

+ +

License: {{ license.spdx }}

+ {% for copyright in license.copyright.iter() %} +

Copyright: {{ copyright }}

+ {% endfor %} +
+ +{% when Node::Group { files, directories, license } %} + +
+ +

+ File/Directory: + {% for name in files %} + {{ name }} + {% endfor %} + {% for name in directories %} + {{ name }} + {% endfor %} +

+ +

License: {{ license.spdx }}

+ {% for copyright in license.copyright.iter() %} +

Copyright: {{ copyright }}

+ {% endfor %} + +
+ +{% endmatch %} From 5277b67b6977e3fcef64b0ce21cecd3a5dc9c22a Mon Sep 17 00:00:00 2001 From: Jonathan Pallant Date: Tue, 6 Aug 2024 11:02:11 +0100 Subject: [PATCH 10/31] generate-copyright: gather files inside interesting folders --- .../generate-copyright/src/cargo_metadata.rs | 50 ++++++++++++------- 1 file changed, 33 insertions(+), 17 deletions(-) diff --git a/src/tools/generate-copyright/src/cargo_metadata.rs b/src/tools/generate-copyright/src/cargo_metadata.rs index d02b9eeb6f922..c85e4aa371a2a 100644 --- a/src/tools/generate-copyright/src/cargo_metadata.rs +++ b/src/tools/generate-copyright/src/cargo_metadata.rs @@ -2,7 +2,7 @@ use std::collections::BTreeMap; use std::ffi::OsStr; -use std::path::Path; +use std::path::{Path, PathBuf}; /// Describes how this module can fail #[derive(Debug, thiserror::Error)] @@ -15,6 +15,8 @@ pub enum Error { LaunchingVendor(std::io::Error), #[error("Failed to complete cargo vendor")] RunningVendor, + #[error("Bad path {0:?} whilst scraping files")] + Scraping(PathBuf), } /// Uniquely describes a package on crates.io @@ -150,24 +152,38 @@ fn load_important_files( let entry = entry?; let metadata = entry.metadata()?; let path = entry.path(); - if let Some(filename) = path.file_name() { - let lc_filename = filename.to_ascii_lowercase(); - let lc_filename_str = lc_filename.to_string_lossy(); - let mut keep = false; - for m in ["copyright", "licence", "license", "author", "notice"] { - if lc_filename_str.contains(m) { - keep = true; - break; - } + let Some(filename) = path.file_name() else { + return Err(Error::Scraping(path)); + }; + let lc_filename = filename.to_ascii_lowercase(); + let lc_filename_str = lc_filename.to_string_lossy(); + let mut keep = false; + for m in ["copyright", "licence", "license", "author", "notice"] { + if lc_filename_str.contains(m) { + keep = true; + break; } - if keep { - if metadata.is_dir() { - // scoop up whole directory - } else if metadata.is_file() { - let filename = filename.to_string_lossy(); - println!("Scraping {}", filename); - dep.notices.insert(filename.to_string(), std::fs::read_to_string(path)?); + } + if keep { + if metadata.is_dir() { + for inner_entry in std::fs::read_dir(entry.path())? { + let inner_entry = inner_entry?; + if inner_entry.metadata()?.is_file() { + let inner_filename = inner_entry.file_name(); + let inner_filename_str = inner_filename.to_string_lossy(); + let qualified_filename = + format!("{}/{}", lc_filename_str, inner_filename_str); + println!("Scraping {}", qualified_filename); + dep.notices.insert( + qualified_filename.to_string(), + std::fs::read_to_string(inner_entry.path())?, + ); + } } + } else if metadata.is_file() { + let filename = filename.to_string_lossy(); + println!("Scraping {}", filename); + dep.notices.insert(filename.to_string(), std::fs::read_to_string(path)?); } } } From 4e24e9b1adb8ffd57d7315028407922c44069f26 Mon Sep 17 00:00:00 2001 From: Jonathan Pallant Date: Tue, 6 Aug 2024 12:03:37 +0100 Subject: [PATCH 11/31] Update to rinja 0.3 --- Cargo.lock | 49 ++++--------------------- src/tools/generate-copyright/Cargo.toml | 2 +- 2 files changed, 8 insertions(+), 43 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 54454e84b5a97..67fee5b3f059f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1407,7 +1407,7 @@ version = "0.1.0" dependencies = [ "anyhow", "cargo_metadata 0.18.1", - "rinja 0.2.0", + "rinja", "serde", "serde_json", "thiserror", @@ -3100,43 +3100,17 @@ dependencies = [ "walkdir", ] -[[package]] -name = "rinja" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2d47a46d7729e891c8accf260e9daa02ae6d570aa2a94fb1fb27eb5364a2323" -dependencies = [ - "humansize", - "num-traits", - "percent-encoding", - "rinja_derive 0.2.0", -] - [[package]] name = "rinja" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d3762e3740cdbf2fd2be465cc2c26d643ad17353cc2e0223d211c1b096118bd" dependencies = [ + "humansize", "itoa", - "rinja_derive 0.3.0", -] - -[[package]] -name = "rinja_derive" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44dae9afe59d58ed8d988d67d1945f3638125d2fd2104058399382e11bd3ea2a" -dependencies = [ - "basic-toml", - "mime", - "mime_guess", - "once_map", - "proc-macro2", - "quote", - "rinja_parser 0.2.0", - "serde", - "syn 2.0.67", + "num-traits", + "percent-encoding", + "rinja_derive", ] [[package]] @@ -3152,20 +3126,11 @@ dependencies = [ "once_map", "proc-macro2", "quote", - "rinja_parser 0.3.0", + "rinja_parser", "serde", "syn 2.0.67", ] -[[package]] -name = "rinja_parser" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b1771c78cd5d3b1646ef8d8f2ed100db936e8b291d3cc06e92a339ff346858c" -dependencies = [ - "nom", -] - [[package]] name = "rinja_parser" version = "0.3.0" @@ -4644,7 +4609,7 @@ dependencies = [ "minifier", "pulldown-cmark 0.9.6", "regex", - "rinja 0.3.0", + "rinja", "rustdoc-json-types", "serde", "serde_json", diff --git a/src/tools/generate-copyright/Cargo.toml b/src/tools/generate-copyright/Cargo.toml index d200e2ec9f1e1..404101abd41bf 100644 --- a/src/tools/generate-copyright/Cargo.toml +++ b/src/tools/generate-copyright/Cargo.toml @@ -9,7 +9,7 @@ description = "Produces a manifest of all the copyrighted materials in the Rust [dependencies] anyhow = "1.0.65" cargo_metadata = "0.18.1" -rinja = "0.2.0" +rinja = "0.3.0" serde = { version = "1.0.147", features = ["derive"] } serde_json = "1.0.85" thiserror = "1" From 99579f3ec1ee070d3d8203df1168f948b05b48dc Mon Sep 17 00:00:00 2001 From: Jonathan Pallant Date: Tue, 6 Aug 2024 12:12:57 +0100 Subject: [PATCH 12/31] Apparently library/std is now part of a workspace at library/ --- src/tools/generate-copyright/src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/generate-copyright/src/main.rs b/src/tools/generate-copyright/src/main.rs index 37de24648d56d..afa75d0d67140 100644 --- a/src/tools/generate-copyright/src/main.rs +++ b/src/tools/generate-copyright/src/main.rs @@ -31,7 +31,7 @@ fn main() -> Result<(), Error> { let workspace_paths = [ Path::new("./Cargo.toml"), Path::new("./src/tools/cargo/Cargo.toml"), - Path::new("./library/std/Cargo.toml"), + Path::new("./library/Cargo.toml"), ]; let mut collected_cargo_metadata = cargo_metadata::get_metadata_and_notices(&cargo, &out_dir, &root_path, &workspace_paths)?; From b174cf827bc020f45ca603723eb8d3c4c5ae339e Mon Sep 17 00:00:00 2001 From: Jane Losare-Lusby Date: Tue, 6 Aug 2024 13:18:46 -0700 Subject: [PATCH 13/31] Integrate crlf directly into related test file instead via of .gitattributes --- tests/rustdoc-ui/intra-doc/.gitattributes | 2 +- tests/rustdoc-ui/intra-doc/warning-crlf.rs | 52 +++++++++++----------- 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/tests/rustdoc-ui/intra-doc/.gitattributes b/tests/rustdoc-ui/intra-doc/.gitattributes index 6c125fac52f5e..0ac2e3c27f431 100644 --- a/tests/rustdoc-ui/intra-doc/.gitattributes +++ b/tests/rustdoc-ui/intra-doc/.gitattributes @@ -1 +1 @@ -warning-crlf.rs eol=crlf +warning-crlf.rs -text diff --git a/tests/rustdoc-ui/intra-doc/warning-crlf.rs b/tests/rustdoc-ui/intra-doc/warning-crlf.rs index ab096b860f819..9d3a4673c9d01 100644 --- a/tests/rustdoc-ui/intra-doc/warning-crlf.rs +++ b/tests/rustdoc-ui/intra-doc/warning-crlf.rs @@ -1,26 +1,26 @@ -// ignore-tidy-cr -//@ check-pass - -// This file checks the spans of intra-link warnings in a file with CRLF line endings. The -// .gitattributes file in this directory should enforce it. - -/// [error] -pub struct A; -//~^^ WARNING `error` - -/// -/// docs [error1] -//~^ WARNING `error1` - -/// docs [error2] -/// -pub struct B; -//~^^^ WARNING `error2` - -/** - * This is a multi-line comment. - * - * It also has an [error]. - */ -pub struct C; -//~^^^ WARNING `error` +// ignore-tidy-cr +//@ check-pass + +// This file checks the spans of intra-link warnings in a file with CRLF line endings. The +// .gitattributes file in this directory should enforce it. + +/// [error] +pub struct A; +//~^^ WARNING `error` + +/// +/// docs [error1] +//~^ WARNING `error1` + +/// docs [error2] +/// +pub struct B; +//~^^^ WARNING `error2` + +/** + * This is a multi-line comment. + * + * It also has an [error]. + */ +pub struct C; +//~^^^ WARNING `error` From 74653b61a67ae7db9f77ea1e09e65e40686c9058 Mon Sep 17 00:00:00 2001 From: Caleb Zulawski Date: Fri, 26 Jul 2024 00:05:20 -0400 Subject: [PATCH 14/31] Add implied target features to target_feature attribute --- compiler/rustc_codegen_llvm/src/llvm_util.rs | 16 ----- .../rustc_codegen_ssa/src/target_features.rs | 40 ++++++++++--- compiler/rustc_middle/src/query/mod.rs | 6 ++ compiler/rustc_target/src/target_features.rs | 58 +++++++++++++++++-- tests/ui/target-feature/implied-features.rs | 24 ++++++++ 5 files changed, 113 insertions(+), 31 deletions(-) create mode 100644 tests/ui/target-feature/implied-features.rs diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs index af8a9be1ccbfd..dc21b92a95f76 100644 --- a/compiler/rustc_codegen_llvm/src/llvm_util.rs +++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs @@ -646,22 +646,6 @@ pub(crate) fn global_llvm_features(sess: &Session, diagnostics: bool) -> Vec, which didn't make - // it into a released version of LLVM yet. - // - // This doesn't use the "implicit target feature" system because it is only - // used for function attributes in other targets, which fixes this bug as - // well on the function attribute level. - if sess.target.families.contains(&"wasm".into()) { - if features.iter().any(|f| f == "+relaxed-simd") - && !features.iter().any(|f| f == "+simd128") - { - features.push("+simd128".into()); - } - } - if diagnostics && let Some(f) = check_tied_features(sess, &featsmap) { sess.dcx().emit_err(TargetFeatureDisableOrEnable { features: f, diff --git a/compiler/rustc_codegen_ssa/src/target_features.rs b/compiler/rustc_codegen_ssa/src/target_features.rs index 127244a34f8f0..1bf842b53a3c5 100644 --- a/compiler/rustc_codegen_ssa/src/target_features.rs +++ b/compiler/rustc_codegen_ssa/src/target_features.rs @@ -1,7 +1,7 @@ use rustc_ast::ast; use rustc_attr::InstructionSetAttr; -use rustc_data_structures::fx::FxIndexSet; -use rustc_data_structures::unord::UnordMap; +use rustc_data_structures::fx::{FxHashMap, FxIndexSet}; +use rustc_data_structures::unord::{UnordMap, UnordSet}; use rustc_errors::Applicability; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LocalDefId, LOCAL_CRATE}; @@ -30,6 +30,7 @@ pub fn from_target_feature( .emit(); }; let rust_features = tcx.features(); + let mut added_target_features = Vec::new(); for item in list { // Only `enable = ...` is accepted in the meta-item list. if !item.has_name(sym::enable) { @@ -44,7 +45,7 @@ pub fn from_target_feature( }; // We allow comma separation to enable multiple features. - target_features.extend(value.as_str().split(',').filter_map(|feature| { + added_target_features.extend(value.as_str().split(',').filter_map(|feature| { let Some(feature_gate) = supported_target_features.get(feature) else { let msg = format!("the feature named `{feature}` is not valid for this target"); let mut err = tcx.dcx().struct_span_err(item.span(), msg); @@ -98,13 +99,12 @@ pub fn from_target_feature( })); } - for (feature, requires) in tcx.sess.target.implicit_target_features() { - if target_features.iter().any(|f| f.as_str() == *feature) - && !target_features.iter().any(|f| f.as_str() == *requires) - { - target_features.push(Symbol::intern(requires)); - } + // Add implied features + for feature in added_target_features.iter() { + target_features + .extend(tcx.implied_target_features(*feature).clone().into_sorted_stable_ord()); } + target_features.extend(added_target_features) } /// Computes the set of target features used in a function for the purposes of @@ -162,6 +162,28 @@ pub(crate) fn provide(providers: &mut Providers) { .collect() } }, + implied_target_features: |tcx, feature| { + let implied_features = tcx + .sess + .target + .implied_target_features() + .iter() + .map(|(f, i)| (Symbol::intern(f), i)) + .collect::>(); + + // implied target features have their own implied target features, so we traverse the + // map until there are no more features to add + let mut features = UnordSet::new(); + let mut new_features = vec![feature]; + while let Some(new_feature) = new_features.pop() { + if features.insert(new_feature) { + if let Some(implied_features) = implied_features.get(&new_feature) { + new_features.extend(implied_features.iter().copied().map(Symbol::intern)) + } + } + } + features + }, asm_target_features, ..*providers } diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index c22c2e985abba..b6a2943265034 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -2183,6 +2183,12 @@ rustc_queries! { desc { "looking up supported target features" } } + query implied_target_features(feature: Symbol) -> &'tcx UnordSet { + arena_cache + eval_always + desc { "looking up implied target features" } + } + query features_query(_: ()) -> &'tcx rustc_feature::Features { feedable desc { "looking up enabled feature gates" } diff --git a/compiler/rustc_target/src/target_features.rs b/compiler/rustc_target/src/target_features.rs index 4e2617c467949..5b79495831a34 100644 --- a/compiler/rustc_target/src/target_features.rs +++ b/compiler/rustc_target/src/target_features.rs @@ -339,8 +339,6 @@ const WASM_ALLOWED_FEATURES: &[(&str, Stability)] = &[ // tidy-alphabetical-end ]; -const WASM_IMPLICIT_FEATURES: &[(&str, &str)] = &[("relaxed-simd", "simd128")]; - const BPF_ALLOWED_FEATURES: &[(&str, Stability)] = &[("alu32", Unstable(sym::bpf_target_feature))]; const CSKY_ALLOWED_FEATURES: &[(&str, Stability)] = &[ @@ -411,6 +409,54 @@ const IBMZ_ALLOWED_FEATURES: &[(&str, Stability)] = &[ // tidy-alphabetical-end ]; +const X86_IMPLIED_FEATURES: &[(&str, &[&str])] = &[ + // tidy-alphabetical-start + ("aes", &["sse2"]), + ("avx", &["sse4.2"]), + ("avx2", &["avx"]), + ("f16c", &["avx"]), + ("fma", &["avx"]), + ("pclmulqdq", &["sse2"]), + ("sha", &["sse2"]), + ("sse2", &["sse"]), + ("sse3", &["sse2"]), + ("sse4.1", &["ssse3"]), + ("sse4.2", &["sse4.1"]), + ("ssse3", &["sse3"]), + // tidy-alphabetical-end +]; + +const AARCH64_IMPLIED_FEATURES: &[(&str, &[&str])] = &[ + // tidy-alphabetical-start + ("aes", &["neon"]), + ("f32mm", &["sve"]), + ("f64mm", &["sve"]), + ("fcma", &["neon"]), + ("fhm", &["fp16"]), + ("fp16", &["neon"]), + ("jsconv", &["neon"]), + ("rcpc2", &["rcpc"]), + ("sha2", &["neon"]), + ("sha3", &["sha2"]), + ("sm4", &["neon"]), + ("sve", &["fp16"]), + ("sve2", &["sve"]), + ("sve2-aes", &["sve2", "aes"]), + ("sve2-bitperm", &["sve2"]), + ("sve2-sha3", &["sve2", "sha3"]), + ("sve2-sm4", &["sve2", "sm4"]), + // tidy-alphabetical-end +]; + +const RISCV_IMPLIED_FEATURES: &[(&str, &[&str])] = &[ + // tidy-alphabetical-start + ("zb", &["zba", "zbc", "zbs"]), + ("zk", &["zkn", "zkr", "zks", "zkt", "zbkb", "zbkc", "zkbx"]), + ("zkn", &["zknd", "zkne", "zknh", "zbkb", "zbkc", "zkbx"]), + ("zks", &["zksed", "zksh", "zbkb", "zbkc", "zkbx"]), + // tidy-alphabetical-end +]; + /// When rustdoc is running, provide a list of all known features so that all their respective /// primitives may be documented. /// @@ -458,11 +504,11 @@ impl super::spec::Target { } } - /// Returns a list of target features. Each items first target feature - /// implicitly enables the second one. - pub fn implicit_target_features(&self) -> &'static [(&'static str, &'static str)] { + pub fn implied_target_features(&self) -> &'static [(&'static str, &'static [&'static str])] { match &*self.arch { - "wasm32" | "wasm64" => WASM_IMPLICIT_FEATURES, + "aarch4" => AARCH64_IMPLIED_FEATURES, + "riscv32" | "riscv64" => RISCV_IMPLIED_FEATURES, + "x86" | "x86_64" => X86_IMPLIED_FEATURES, _ => &[], } } diff --git a/tests/ui/target-feature/implied-features.rs b/tests/ui/target-feature/implied-features.rs new file mode 100644 index 0000000000000..c6d9ba78c21e6 --- /dev/null +++ b/tests/ui/target-feature/implied-features.rs @@ -0,0 +1,24 @@ +//@ only-x86_64 +//@ run-pass +#![feature(target_feature_11)] +#![allow(dead_code)] + +#[target_feature(enable = "ssse3")] +fn call_ssse3() {} + +#[target_feature(enable = "avx")] +fn call_avx() {} + +#[target_feature(enable = "avx2")] +fn test_avx2() { + call_ssse3(); + call_avx(); +} + +#[target_feature(enable = "fma")] +fn test_fma() { + call_ssse3(); + call_avx(); +} + +fn main() {} From 22c59529441da1cca8eb2cc50d4162e3adf95355 Mon Sep 17 00:00:00 2001 From: Caleb Zulawski Date: Fri, 26 Jul 2024 10:04:27 -0400 Subject: [PATCH 15/31] Add test to ensure implied target features work with asm, and fix failing tests --- .../rustc_codegen_ssa/src/target_features.rs | 12 ++-- .../const-eval/const_fn_target_feature.stderr | 2 +- .../rfc-2396-target_feature-11/safe-calls.rs | 7 +-- .../safe-calls.stderr | 60 +++++++------------ .../asm-implied-features-issue-128125.rs | 10 ++++ tests/ui/target-feature/implied-features.rs | 2 +- 6 files changed, 44 insertions(+), 49 deletions(-) create mode 100644 tests/ui/target-feature/asm-implied-features-issue-128125.rs diff --git a/compiler/rustc_codegen_ssa/src/target_features.rs b/compiler/rustc_codegen_ssa/src/target_features.rs index 1bf842b53a3c5..24b2c9c51c6e1 100644 --- a/compiler/rustc_codegen_ssa/src/target_features.rs +++ b/compiler/rustc_codegen_ssa/src/target_features.rs @@ -1,7 +1,7 @@ use rustc_ast::ast; use rustc_attr::InstructionSetAttr; use rustc_data_structures::fx::{FxHashMap, FxIndexSet}; -use rustc_data_structures::unord::{UnordMap, UnordSet}; +use rustc_data_structures::unord::{ExtendUnord, UnordMap, UnordSet}; use rustc_errors::Applicability; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LocalDefId, LOCAL_CRATE}; @@ -99,12 +99,14 @@ pub fn from_target_feature( })); } - // Add implied features + // Add both explicit and implied target features, using a set to deduplicate + let mut target_features_set = UnordSet::new(); for feature in added_target_features.iter() { - target_features - .extend(tcx.implied_target_features(*feature).clone().into_sorted_stable_ord()); + target_features_set + .extend_unord(tcx.implied_target_features(*feature).clone().into_items()); } - target_features.extend(added_target_features) + target_features_set.extend(added_target_features); + target_features.extend(target_features_set.into_sorted_stable_ord()) } /// Computes the set of target features used in a function for the purposes of diff --git a/tests/ui/consts/const-eval/const_fn_target_feature.stderr b/tests/ui/consts/const-eval/const_fn_target_feature.stderr index d3a00b57ebb50..ad40d733546af 100644 --- a/tests/ui/consts/const-eval/const_fn_target_feature.stderr +++ b/tests/ui/consts/const-eval/const_fn_target_feature.stderr @@ -2,7 +2,7 @@ error[E0080]: evaluation of constant value failed --> $DIR/const_fn_target_feature.rs:11:24 | LL | const B: () = unsafe { avx2_fn() }; - | ^^^^^^^^^ calling a function that requires unavailable target features: avx2 + | ^^^^^^^^^ calling a function that requires unavailable target features: avx, avx2, sse4.1, sse4.2 error: aborting due to 1 previous error diff --git a/tests/ui/rfcs/rfc-2396-target_feature-11/safe-calls.rs b/tests/ui/rfcs/rfc-2396-target_feature-11/safe-calls.rs index de002ef71d7d3..fec4e75290fc8 100644 --- a/tests/ui/rfcs/rfc-2396-target_feature-11/safe-calls.rs +++ b/tests/ui/rfcs/rfc-2396-target_feature-11/safe-calls.rs @@ -34,6 +34,7 @@ fn foo() { #[target_feature(enable = "sse2")] fn bar() { + sse2(); avx_bmi2(); //~^ ERROR call to function `avx_bmi2` with `#[target_feature]` is unsafe Quux.avx_bmi2(); @@ -43,7 +44,6 @@ fn bar() { #[target_feature(enable = "avx")] fn baz() { sse2(); - //~^ ERROR call to function `sse2` with `#[target_feature]` is unsafe avx_bmi2(); //~^ ERROR call to function `avx_bmi2` with `#[target_feature]` is unsafe Quux.avx_bmi2(); @@ -54,7 +54,8 @@ fn baz() { #[target_feature(enable = "bmi2")] fn qux() { sse2(); - //~^ ERROR call to function `sse2` with `#[target_feature]` is unsafe + avx_bmi2(); + Quux.avx_bmi2(); } const _: () = sse2(); @@ -64,8 +65,6 @@ const _: () = sse2_and_fxsr(); //~^ ERROR call to function `sse2_and_fxsr` with `#[target_feature]` is unsafe #[deny(unsafe_op_in_unsafe_fn)] -#[target_feature(enable = "avx")] -#[target_feature(enable = "bmi2")] unsafe fn needs_unsafe_block() { sse2(); //~^ ERROR call to function `sse2` with `#[target_feature]` is unsafe diff --git a/tests/ui/rfcs/rfc-2396-target_feature-11/safe-calls.stderr b/tests/ui/rfcs/rfc-2396-target_feature-11/safe-calls.stderr index 537819ab8595c..c2227f8e84783 100644 --- a/tests/ui/rfcs/rfc-2396-target_feature-11/safe-calls.stderr +++ b/tests/ui/rfcs/rfc-2396-target_feature-11/safe-calls.stderr @@ -4,8 +4,8 @@ error[E0133]: call to function `sse2` with `#[target_feature]` is unsafe and req LL | sse2(); | ^^^^^^ call to function with `#[target_feature]` | - = help: in order for the call to be safe, the context requires the following additional target feature: sse2 - = note: the sse2 target feature being enabled in the build configuration does not remove the requirement to list it in `#[target_feature]` + = help: in order for the call to be safe, the context requires the following additional target features: sse and sse2 + = note: the sse and sse2 target features being enabled in the build configuration does not remove the requirement to list them in `#[target_feature]` error[E0133]: call to function `avx_bmi2` with `#[target_feature]` is unsafe and requires unsafe function or block --> $DIR/safe-calls.rs:29:5 @@ -13,7 +13,8 @@ error[E0133]: call to function `avx_bmi2` with `#[target_feature]` is unsafe and LL | avx_bmi2(); | ^^^^^^^^^^ call to function with `#[target_feature]` | - = help: in order for the call to be safe, the context requires the following additional target features: avx and bmi2 + = help: in order for the call to be safe, the context requires the following additional target features: avx, sse, sse2, sse3, sse4.1, sse4.2, ssse3, and bmi2 + = note: the sse and sse2 target features being enabled in the build configuration does not remove the requirement to list them in `#[target_feature]` error[E0133]: call to function `Quux::avx_bmi2` with `#[target_feature]` is unsafe and requires unsafe function or block --> $DIR/safe-calls.rs:31:5 @@ -21,32 +22,24 @@ error[E0133]: call to function `Quux::avx_bmi2` with `#[target_feature]` is unsa LL | Quux.avx_bmi2(); | ^^^^^^^^^^^^^^^ call to function with `#[target_feature]` | - = help: in order for the call to be safe, the context requires the following additional target features: avx and bmi2 + = help: in order for the call to be safe, the context requires the following additional target features: avx, sse, sse2, sse3, sse4.1, sse4.2, ssse3, and bmi2 + = note: the sse and sse2 target features being enabled in the build configuration does not remove the requirement to list them in `#[target_feature]` error[E0133]: call to function `avx_bmi2` with `#[target_feature]` is unsafe and requires unsafe function or block - --> $DIR/safe-calls.rs:37:5 + --> $DIR/safe-calls.rs:38:5 | LL | avx_bmi2(); | ^^^^^^^^^^ call to function with `#[target_feature]` | - = help: in order for the call to be safe, the context requires the following additional target features: avx and bmi2 + = help: in order for the call to be safe, the context requires the following additional target features: avx, sse3, sse4.1, sse4.2, ssse3, and bmi2 error[E0133]: call to function `Quux::avx_bmi2` with `#[target_feature]` is unsafe and requires unsafe function or block - --> $DIR/safe-calls.rs:39:5 + --> $DIR/safe-calls.rs:40:5 | LL | Quux.avx_bmi2(); | ^^^^^^^^^^^^^^^ call to function with `#[target_feature]` | - = help: in order for the call to be safe, the context requires the following additional target features: avx and bmi2 - -error[E0133]: call to function `sse2` with `#[target_feature]` is unsafe and requires unsafe function or block - --> $DIR/safe-calls.rs:45:5 - | -LL | sse2(); - | ^^^^^^ call to function with `#[target_feature]` - | - = help: in order for the call to be safe, the context requires the following additional target feature: sse2 - = note: the sse2 target feature being enabled in the build configuration does not remove the requirement to list it in `#[target_feature]` + = help: in order for the call to be safe, the context requires the following additional target features: avx, sse3, sse4.1, sse4.2, ssse3, and bmi2 error[E0133]: call to function `avx_bmi2` with `#[target_feature]` is unsafe and requires unsafe function or block --> $DIR/safe-calls.rs:47:5 @@ -65,52 +58,43 @@ LL | Quux.avx_bmi2(); = help: in order for the call to be safe, the context requires the following additional target feature: bmi2 error[E0133]: call to function `sse2` with `#[target_feature]` is unsafe and requires unsafe function or block - --> $DIR/safe-calls.rs:56:5 - | -LL | sse2(); - | ^^^^^^ call to function with `#[target_feature]` - | - = help: in order for the call to be safe, the context requires the following additional target feature: sse2 - = note: the sse2 target feature being enabled in the build configuration does not remove the requirement to list it in `#[target_feature]` - -error[E0133]: call to function `sse2` with `#[target_feature]` is unsafe and requires unsafe function or block - --> $DIR/safe-calls.rs:60:15 + --> $DIR/safe-calls.rs:61:15 | LL | const _: () = sse2(); | ^^^^^^ call to function with `#[target_feature]` | - = help: in order for the call to be safe, the context requires the following additional target feature: sse2 - = note: the sse2 target feature being enabled in the build configuration does not remove the requirement to list it in `#[target_feature]` + = help: in order for the call to be safe, the context requires the following additional target features: sse and sse2 + = note: the sse and sse2 target features being enabled in the build configuration does not remove the requirement to list them in `#[target_feature]` error[E0133]: call to function `sse2_and_fxsr` with `#[target_feature]` is unsafe and requires unsafe function or block - --> $DIR/safe-calls.rs:63:15 + --> $DIR/safe-calls.rs:64:15 | LL | const _: () = sse2_and_fxsr(); | ^^^^^^^^^^^^^^^ call to function with `#[target_feature]` | - = help: in order for the call to be safe, the context requires the following additional target features: sse2 and fxsr - = note: the fxsr and sse2 target features being enabled in the build configuration does not remove the requirement to list them in `#[target_feature]` + = help: in order for the call to be safe, the context requires the following additional target features: sse, sse2, and fxsr + = note: the fxsr, sse, and sse2 target features being enabled in the build configuration does not remove the requirement to list them in `#[target_feature]` error[E0133]: call to function `sse2` with `#[target_feature]` is unsafe and requires unsafe block - --> $DIR/safe-calls.rs:70:5 + --> $DIR/safe-calls.rs:69:5 | LL | sse2(); | ^^^^^^ call to function with `#[target_feature]` | = note: for more information, see issue #71668 - = help: in order for the call to be safe, the context requires the following additional target feature: sse2 - = note: the sse2 target feature being enabled in the build configuration does not remove the requirement to list it in `#[target_feature]` + = help: in order for the call to be safe, the context requires the following additional target features: sse and sse2 + = note: the sse and sse2 target features being enabled in the build configuration does not remove the requirement to list them in `#[target_feature]` note: an unsafe function restricts its caller, but its body is safe by default - --> $DIR/safe-calls.rs:69:1 + --> $DIR/safe-calls.rs:68:1 | LL | unsafe fn needs_unsafe_block() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: the lint level is defined here - --> $DIR/safe-calls.rs:66:8 + --> $DIR/safe-calls.rs:67:8 | LL | #[deny(unsafe_op_in_unsafe_fn)] | ^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 12 previous errors +error: aborting due to 10 previous errors For more information about this error, try `rustc --explain E0133`. diff --git a/tests/ui/target-feature/asm-implied-features-issue-128125.rs b/tests/ui/target-feature/asm-implied-features-issue-128125.rs new file mode 100644 index 0000000000000..2b4f1d7df8563 --- /dev/null +++ b/tests/ui/target-feature/asm-implied-features-issue-128125.rs @@ -0,0 +1,10 @@ +//@ only-x86_64 +//@ build-pass +#![allow(dead_code)] + +#[target_feature(enable = "avx2")] +unsafe fn demo(v: std::arch::x86_64::__m256i) { + std::arch::asm!("/* {v} */", v = in(ymm_reg) v); +} + +fn main() {} diff --git a/tests/ui/target-feature/implied-features.rs b/tests/ui/target-feature/implied-features.rs index c6d9ba78c21e6..4fdd843e6c289 100644 --- a/tests/ui/target-feature/implied-features.rs +++ b/tests/ui/target-feature/implied-features.rs @@ -1,5 +1,5 @@ //@ only-x86_64 -//@ run-pass +//@ build-pass #![feature(target_feature_11)] #![allow(dead_code)] From 520a5a535f4a1432d7f9742171a11d21f518f652 Mon Sep 17 00:00:00 2001 From: Caleb Zulawski Date: Fri, 26 Jul 2024 11:27:21 -0400 Subject: [PATCH 16/31] Fix codegen tests --- tests/codegen/sse42-implies-crc32.rs | 2 +- tests/codegen/target-feature-overrides.rs | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/codegen/sse42-implies-crc32.rs b/tests/codegen/sse42-implies-crc32.rs index 94fcd77bc8842..8a9c496a3a541 100644 --- a/tests/codegen/sse42-implies-crc32.rs +++ b/tests/codegen/sse42-implies-crc32.rs @@ -12,4 +12,4 @@ pub unsafe fn crc32sse(v: u8) -> u32 { _mm_crc32_u8(out, v) } -// CHECK: attributes #0 {{.*"target-features"=".*\+sse4.2,\+crc32"}} +// CHECK: attributes #0 {{.*"target-features"=".*\+sse4.2,\+crc32.*"}} diff --git a/tests/codegen/target-feature-overrides.rs b/tests/codegen/target-feature-overrides.rs index 1e2c364dbbc9a..87783706d9535 100644 --- a/tests/codegen/target-feature-overrides.rs +++ b/tests/codegen/target-feature-overrides.rs @@ -1,7 +1,7 @@ //@ revisions: COMPAT INCOMPAT //@ needs-llvm-components: x86 //@ compile-flags: --target=x86_64-unknown-linux-gnu -Copt-level=3 -//@ [COMPAT] compile-flags: -Ctarget-feature=+avx2,+avx +//@ [COMPAT] compile-flags: -Ctarget-feature=+avx2,+avx,+sse4.2,+sse4.1,+ssse3,+sse3 //@ [INCOMPAT] compile-flags: -Ctarget-feature=-avx2,-avx // See also tests/assembly/target-feature-multiple.rs @@ -39,8 +39,8 @@ pub unsafe fn banana() -> u32 { } // CHECK: attributes [[APPLEATTRS]] -// COMPAT-SAME: "target-features"="+avx2,+avx,+avx" -// INCOMPAT-SAME: "target-features"="-avx2,-avx,+avx" +// COMPAT-SAME: "target-features"="+avx2,+avx,{{.*}}" +// INCOMPAT-SAME: "target-features"="-avx2,-avx,+avx,{{.*}}" // CHECK: attributes [[BANANAATTRS]] -// COMPAT-SAME: "target-features"="+avx2,+avx" +// COMPAT-SAME: "target-features"="+avx2,+avx,{{.*}}" // INCOMPAT-SAME: "target-features"="-avx2,-avx" From c866e1f812b7436abce2b03ec56d62c77d6684a2 Mon Sep 17 00:00:00 2001 From: Caleb Zulawski Date: Mon, 29 Jul 2024 00:09:47 -0400 Subject: [PATCH 17/31] Add missing features --- compiler/rustc_target/src/target_features.rs | 26 ++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/compiler/rustc_target/src/target_features.rs b/compiler/rustc_target/src/target_features.rs index 5b79495831a34..64f966d7a305e 100644 --- a/compiler/rustc_target/src/target_features.rs +++ b/compiler/rustc_target/src/target_features.rs @@ -414,8 +414,22 @@ const X86_IMPLIED_FEATURES: &[(&str, &[&str])] = &[ ("aes", &["sse2"]), ("avx", &["sse4.2"]), ("avx2", &["avx"]), + ("avx512bf16", &["avx512bw"]), + ("avx512bitalg", &["avx512bw"]), + ("avx512bw", &["avx512f"]), + ("avx512cd", &["avx512f"]), + ("avx512dq", &["avx512f"]), + ("avx512f", &["avx2"]), + ("avx512fp16", &["avx512bw", "avx512vl", "avx512dq"]), + ("avx512vbmi", &["avx512bw"]), + ("avx512vbmi2", &["avx512bw"]), + ("avx512vl", &["avx512f"]), + ("avx512vnni", &["avx512f"]), + ("avx512vp2intersect", &["avx512f"]), + ("avx512vpopcntdq", &["avx512f"]), ("f16c", &["avx"]), ("fma", &["avx"]), + ("gfni", &["sse2"]), ("pclmulqdq", &["sse2"]), ("sha", &["sse2"]), ("sse2", &["sse"]), @@ -423,6 +437,11 @@ const X86_IMPLIED_FEATURES: &[(&str, &[&str])] = &[ ("sse4.1", &["ssse3"]), ("sse4.2", &["sse4.1"]), ("ssse3", &["sse3"]), + ("vaes", &["avx", "aes"]), + ("vpclmulqdq", &["avx", "pclmulqdq"]), + ("xsavec", &["xsave"]), + ("xsaveopt", &["xsave"]), + ("xsaves", &["xsave"]), // tidy-alphabetical-end ]; @@ -457,6 +476,12 @@ const RISCV_IMPLIED_FEATURES: &[(&str, &[&str])] = &[ // tidy-alphabetical-end ]; +const WASM_IMPLIED_FEATURES: &[(&str, &[&str])] = &[ + // tidy-alphabetical-start + ("relaxed-simd", &["simd128"]), + // tidy-alphabetical-end +]; + /// When rustdoc is running, provide a list of all known features so that all their respective /// primitives may be documented. /// @@ -509,6 +534,7 @@ impl super::spec::Target { "aarch4" => AARCH64_IMPLIED_FEATURES, "riscv32" | "riscv64" => RISCV_IMPLIED_FEATURES, "x86" | "x86_64" => X86_IMPLIED_FEATURES, + "wasm32" | "wasm64" => WASM_IMPLIED_FEATURES, _ => &[], } } From 34f29a24bbd471cf60f0517457ae024f90ec2263 Mon Sep 17 00:00:00 2001 From: Caleb Zulawski Date: Tue, 30 Jul 2024 23:58:51 -0400 Subject: [PATCH 18/31] Add +sse4.2 due to #128426 --- src/tools/miri/tests/pass/shims/x86/intrinsics-x86-aes-vaes.rs | 2 +- src/tools/miri/tests/pass/shims/x86/intrinsics-x86-avx.rs | 2 +- src/tools/miri/tests/pass/shims/x86/intrinsics-x86-avx2.rs | 2 +- src/tools/miri/tests/pass/shims/x86/intrinsics-x86-avx512.rs | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-aes-vaes.rs b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-aes-vaes.rs index 7363c75361779..13ebe56146381 100644 --- a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-aes-vaes.rs +++ b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-aes-vaes.rs @@ -7,7 +7,7 @@ //@ignore-target-s390x //@ignore-target-thumbv7em //@ignore-target-wasm32 -//@compile-flags: -C target-feature=+aes,+vaes,+avx512f +//@compile-flags: -C target-feature=+aes,+vaes,+avx512f,+sse4.2 #![feature(avx512_target_feature, stdarch_x86_avx512)] diff --git a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-avx.rs b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-avx.rs index 728f57d48f17e..18693a2a97d11 100644 --- a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-avx.rs +++ b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-avx.rs @@ -7,7 +7,7 @@ //@ignore-target-s390x //@ignore-target-thumbv7em //@ignore-target-wasm32 -//@compile-flags: -C target-feature=+avx +//@compile-flags: -C target-feature=+avx,+sse4.2 #[cfg(target_arch = "x86")] use std::arch::x86::*; diff --git a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-avx2.rs b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-avx2.rs index 80d125bb85650..f42ff6dfb51b2 100644 --- a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-avx2.rs +++ b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-avx2.rs @@ -7,7 +7,7 @@ //@ignore-target-s390x //@ignore-target-thumbv7em //@ignore-target-wasm32 -//@compile-flags: -C target-feature=+avx2 +//@compile-flags: -C target-feature=+avx2,+sse4.2 #[cfg(target_arch = "x86")] use std::arch::x86::*; diff --git a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-avx512.rs b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-avx512.rs index 66bfcb20f1c99..7d25854ccc075 100644 --- a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-avx512.rs +++ b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-avx512.rs @@ -7,7 +7,7 @@ //@ignore-target-s390x //@ignore-target-thumbv7em //@ignore-target-wasm32 -//@compile-flags: -C target-feature=+avx512f,+avx512vl,+avx512bitalg,+avx512vpopcntdq +//@compile-flags: -C target-feature=+avx512f,+avx512vl,+avx512bitalg,+avx512vpopcntdq,+sse4.2 #![feature(avx512_target_feature)] #![feature(stdarch_x86_avx512)] From 3c48f6548bce89b459952f8d7ca077cfa99159aa Mon Sep 17 00:00:00 2001 From: Caleb Zulawski Date: Wed, 31 Jul 2024 01:50:02 -0400 Subject: [PATCH 19/31] Bless tests --- .../fail/function_calls/simd_feature_flag_difference.stderr | 4 ++-- src/tools/miri/tests/fail/function_calls/target_feature.rs | 2 +- .../miri/tests/fail/function_calls/target_feature.stderr | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/tools/miri/tests/fail/function_calls/simd_feature_flag_difference.stderr b/src/tools/miri/tests/fail/function_calls/simd_feature_flag_difference.stderr index 2544421c7e8c7..8ebd5d2726ffd 100644 --- a/src/tools/miri/tests/fail/function_calls/simd_feature_flag_difference.stderr +++ b/src/tools/miri/tests/fail/function_calls/simd_feature_flag_difference.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: calling a function that requires unavailable target features: avx +error: Undefined Behavior: calling a function that requires unavailable target features: avx, sse3, sse4.1, sse4.2, ssse3 --> $DIR/simd_feature_flag_difference.rs:LL:CC | LL | unsafe { foo(0.0, x) } - | ^^^^^^^^^^^ calling a function that requires unavailable target features: avx + | ^^^^^^^^^^^ calling a function that requires unavailable target features: avx, sse3, sse4.1, sse4.2, ssse3 | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information diff --git a/src/tools/miri/tests/fail/function_calls/target_feature.rs b/src/tools/miri/tests/fail/function_calls/target_feature.rs index 84e01eb4803fe..713d7c16f4c25 100644 --- a/src/tools/miri/tests/fail/function_calls/target_feature.rs +++ b/src/tools/miri/tests/fail/function_calls/target_feature.rs @@ -4,7 +4,7 @@ fn main() { assert!(!is_x86_feature_detected!("ssse3")); unsafe { - ssse3_fn(); //~ ERROR: calling a function that requires unavailable target features: ssse3 + ssse3_fn(); //~ ERROR: calling a function that requires unavailable target features: sse3, ssse3 } } diff --git a/src/tools/miri/tests/fail/function_calls/target_feature.stderr b/src/tools/miri/tests/fail/function_calls/target_feature.stderr index 4d3cf6e9d3bc0..74a862c7b681c 100644 --- a/src/tools/miri/tests/fail/function_calls/target_feature.stderr +++ b/src/tools/miri/tests/fail/function_calls/target_feature.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: calling a function that requires unavailable target features: ssse3 +error: Undefined Behavior: calling a function that requires unavailable target features: sse3, ssse3 --> $DIR/target_feature.rs:LL:CC | LL | ssse3_fn(); - | ^^^^^^^^^^ calling a function that requires unavailable target features: ssse3 + | ^^^^^^^^^^ calling a function that requires unavailable target features: sse3, ssse3 | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information From fbd618d4aa8f3a6c998b81aa83a543d4c09d9bb3 Mon Sep 17 00:00:00 2001 From: Caleb Zulawski Date: Fri, 2 Aug 2024 00:20:49 -0400 Subject: [PATCH 20/31] Refactor and fill out target feature lists --- compiler/rustc_codegen_gcc/src/gcc_util.rs | 4 +- compiler/rustc_codegen_gcc/src/lib.rs | 2 +- compiler/rustc_codegen_llvm/src/llvm_util.rs | 28 +- .../rustc_codegen_ssa/src/target_features.rs | 6 +- compiler/rustc_target/src/target_features.rs | 613 ++++++++---------- 5 files changed, 293 insertions(+), 360 deletions(-) diff --git a/compiler/rustc_codegen_gcc/src/gcc_util.rs b/compiler/rustc_codegen_gcc/src/gcc_util.rs index 8bb90efe6fb7c..5308ccdb61469 100644 --- a/compiler/rustc_codegen_gcc/src/gcc_util.rs +++ b/compiler/rustc_codegen_gcc/src/gcc_util.rs @@ -65,8 +65,8 @@ pub(crate) fn global_gcc_features(sess: &Session, diagnostics: bool) -> Vec Vec { sess.target .supported_target_features() .iter() - .filter_map(|&(feature, gate)| { + .filter_map(|&(feature, gate, _)| { if sess.is_nightly_build() || allow_unstable || gate.is_stable() { Some(feature) } else { @@ -386,7 +386,7 @@ fn print_target_features(out: &mut String, sess: &Session, tm: &llvm::TargetMach .target .supported_target_features() .iter() - .map(|(feature, _gate)| { + .map(|(feature, _gate, _implied)| { // LLVM asserts that these are sorted. LLVM and Rust both use byte comparison for these strings. let llvm_feature = to_llvm_features(sess, *feature).llvm_feature_name; let desc = @@ -571,17 +571,19 @@ pub(crate) fn global_llvm_features(sess: &Session, diagnostics: bool) -> Vec Vec>(); // implied target features have their own implied target features, so we traverse the diff --git a/compiler/rustc_target/src/target_features.rs b/compiler/rustc_target/src/target_features.rs index 64f966d7a305e..f408db1dcbad2 100644 --- a/compiler/rustc_target/src/target_features.rs +++ b/compiler/rustc_target/src/target_features.rs @@ -53,136 +53,146 @@ impl Stability { // // Stabilizing a target feature requires t-lang approval. -const ARM_ALLOWED_FEATURES: &[(&str, Stability)] = &[ +type ImpliedFeatures = &'static [&'static str]; + +const ARM_ALLOWED_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ // tidy-alphabetical-start - ("aclass", Unstable(sym::arm_target_feature)), - ("aes", Unstable(sym::arm_target_feature)), - ("crc", Unstable(sym::arm_target_feature)), - ("d32", Unstable(sym::arm_target_feature)), - ("dotprod", Unstable(sym::arm_target_feature)), - ("dsp", Unstable(sym::arm_target_feature)), - ("fp-armv8", Unstable(sym::arm_target_feature)), - ("i8mm", Unstable(sym::arm_target_feature)), - ("mclass", Unstable(sym::arm_target_feature)), - ("neon", Unstable(sym::arm_target_feature)), - ("rclass", Unstable(sym::arm_target_feature)), - ("sha2", Unstable(sym::arm_target_feature)), + ("aclass", Unstable(sym::arm_target_feature), &[]), + ("aes", Unstable(sym::arm_target_feature), &["neon"]), + ("crc", Unstable(sym::arm_target_feature), &[]), + ("d32", Unstable(sym::arm_target_feature), &[]), + ("dotprod", Unstable(sym::arm_target_feature), &["neon"]), + ("dsp", Unstable(sym::arm_target_feature), &[]), + ("fp-armv8", Unstable(sym::arm_target_feature), &["vfp4"]), + ("i8mm", Unstable(sym::arm_target_feature), &["neon"]), + ("mclass", Unstable(sym::arm_target_feature), &[]), + ("neon", Unstable(sym::arm_target_feature), &["vfp3"]), + ("rclass", Unstable(sym::arm_target_feature), &[]), + ("sha2", Unstable(sym::arm_target_feature), &["neon"]), // This is needed for inline assembly, but shouldn't be stabilized as-is // since it should be enabled per-function using #[instruction_set], not // #[target_feature]. - ("thumb-mode", Unstable(sym::arm_target_feature)), - ("thumb2", Unstable(sym::arm_target_feature)), - ("trustzone", Unstable(sym::arm_target_feature)), - ("v5te", Unstable(sym::arm_target_feature)), - ("v6", Unstable(sym::arm_target_feature)), - ("v6k", Unstable(sym::arm_target_feature)), - ("v6t2", Unstable(sym::arm_target_feature)), - ("v7", Unstable(sym::arm_target_feature)), - ("v8", Unstable(sym::arm_target_feature)), - ("vfp2", Unstable(sym::arm_target_feature)), - ("vfp3", Unstable(sym::arm_target_feature)), - ("vfp4", Unstable(sym::arm_target_feature)), - ("virtualization", Unstable(sym::arm_target_feature)), + ("thumb-mode", Unstable(sym::arm_target_feature), &[]), + ("thumb2", Unstable(sym::arm_target_feature), &[]), + ("trustzone", Unstable(sym::arm_target_feature), &[]), + ("v5te", Unstable(sym::arm_target_feature), &[]), + ("v6", Unstable(sym::arm_target_feature), &["v5te"]), + ("v6k", Unstable(sym::arm_target_feature), &["v6"]), + ("v6t2", Unstable(sym::arm_target_feature), &["v6k", "thumb2"]), + ("v7", Unstable(sym::arm_target_feature), &["v6t2"]), + ("v8", Unstable(sym::arm_target_feature), &["v7"]), + ("vfp2", Unstable(sym::arm_target_feature), &[]), + ("vfp3", Unstable(sym::arm_target_feature), &["vfp2", "d32"]), + ("vfp4", Unstable(sym::arm_target_feature), &["vfp3"]), + ("virtualization", Unstable(sym::arm_target_feature), &[]), // tidy-alphabetical-end ]; -const AARCH64_ALLOWED_FEATURES: &[(&str, Stability)] = &[ +const AARCH64_ALLOWED_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ // tidy-alphabetical-start // FEAT_AES & FEAT_PMULL - ("aes", Stable), + ("aes", Stable, &["neon"]), // FEAT_BF16 - ("bf16", Stable), + ("bf16", Stable, &[]), // FEAT_BTI - ("bti", Stable), + ("bti", Stable, &[]), // FEAT_CRC - ("crc", Stable), + ("crc", Stable, &[]), // FEAT_DIT - ("dit", Stable), + ("dit", Stable, &[]), // FEAT_DotProd - ("dotprod", Stable), + ("dotprod", Stable, &["neon"]), // FEAT_DPB - ("dpb", Stable), + ("dpb", Stable, &[]), // FEAT_DPB2 - ("dpb2", Stable), + ("dpb2", Stable, &["dpb"]), // FEAT_F32MM - ("f32mm", Stable), + ("f32mm", Stable, &["sve"]), // FEAT_F64MM - ("f64mm", Stable), + ("f64mm", Stable, &["sve"]), // FEAT_FCMA - ("fcma", Stable), + ("fcma", Stable, &["neon"]), // FEAT_FHM - ("fhm", Stable), + ("fhm", Stable, &["fp16"]), // FEAT_FLAGM - ("flagm", Stable), + ("flagm", Stable, &[]), // FEAT_FP16 - ("fp16", Stable), + ("fp16", Stable, &[]), // FEAT_FRINTTS - ("frintts", Stable), + ("frintts", Stable, &[]), // FEAT_I8MM - ("i8mm", Stable), + ("i8mm", Stable, &[]), // FEAT_JSCVT - ("jsconv", Stable), + ("jsconv", Stable, &[]), // FEAT_LOR - ("lor", Stable), + ("lor", Stable, &[]), // FEAT_LSE - ("lse", Stable), + ("lse", Stable, &[]), // FEAT_MTE & FEAT_MTE2 - ("mte", Stable), + ("mte", Stable, &[]), // FEAT_AdvSimd & FEAT_FP - ("neon", Stable), + ("neon", Stable, &[]), // FEAT_PAUTH (address authentication) - ("paca", Stable), + ("paca", Stable, &[]), // FEAT_PAUTH (generic authentication) - ("pacg", Stable), + ("pacg", Stable, &[]), // FEAT_PAN - ("pan", Stable), + ("pan", Stable, &[]), // FEAT_PMUv3 - ("pmuv3", Stable), + ("pmuv3", Stable, &[]), // FEAT_RAND - ("rand", Stable), + ("rand", Stable, &[]), // FEAT_RAS & FEAT_RASv1p1 - ("ras", Stable), + ("ras", Stable, &[]), // FEAT_RCPC - ("rcpc", Stable), + ("rcpc", Stable, &[]), // FEAT_RCPC2 - ("rcpc2", Stable), + ("rcpc2", Stable, &["rcpc"]), // FEAT_RDM - ("rdm", Stable), + ("rdm", Stable, &["neon"]), // FEAT_SB - ("sb", Stable), + ("sb", Stable, &[]), // FEAT_SHA1 & FEAT_SHA256 - ("sha2", Stable), + ("sha2", Stable, &["neon"]), // FEAT_SHA512 & FEAT_SHA3 - ("sha3", Stable), + ("sha3", Stable, &["sha2"]), // FEAT_SM3 & FEAT_SM4 - ("sm4", Stable), + ("sm4", Stable, &["neon"]), // FEAT_SPE - ("spe", Stable), + ("spe", Stable, &[]), // FEAT_SSBS & FEAT_SSBS2 - ("ssbs", Stable), + ("ssbs", Stable, &[]), // FEAT_SVE - ("sve", Stable), + ("sve", Stable, &[]), // FEAT_SVE2 - ("sve2", Stable), + ("sve2", Stable, &["sve"]), // FEAT_SVE2_AES - ("sve2-aes", Stable), + ("sve2-aes", Stable, &["sve2", "aes"]), // FEAT_SVE2_BitPerm - ("sve2-bitperm", Stable), + ("sve2-bitperm", Stable, &["sve2"]), // FEAT_SVE2_SHA3 - ("sve2-sha3", Stable), + ("sve2-sha3", Stable, &["sve2", "sha3"]), // FEAT_SVE2_SM4 - ("sve2-sm4", Stable), + ("sve2-sm4", Stable, &["sve2", "sm4"]), // FEAT_TME - ("tme", Stable), - ("v8.1a", Unstable(sym::aarch64_ver_target_feature)), - ("v8.2a", Unstable(sym::aarch64_ver_target_feature)), - ("v8.3a", Unstable(sym::aarch64_ver_target_feature)), - ("v8.4a", Unstable(sym::aarch64_ver_target_feature)), - ("v8.5a", Unstable(sym::aarch64_ver_target_feature)), - ("v8.6a", Unstable(sym::aarch64_ver_target_feature)), - ("v8.7a", Unstable(sym::aarch64_ver_target_feature)), + ("tme", Stable, &[]), + ( + "v8.1a", + Unstable(sym::aarch64_ver_target_feature), + &["crc", "lse", "rdm", "pan", "lor", "vh"], + ), + ("v8.2a", Unstable(sym::aarch64_ver_target_feature), &["v8.1a", "ras", "dpb"]), + ( + "v8.3a", + Unstable(sym::aarch64_ver_target_feature), + &["v8.2a", "rcpc", "paca", "pacg", "jsconv"], + ), + ("v8.4a", Unstable(sym::aarch64_ver_target_feature), &["v8.3a", "dotprod", "dit", "flagm"]), + ("v8.5a", Unstable(sym::aarch64_ver_target_feature), &["v8.4a", "ssbs", "sb", "dpb2", "bti"]), + ("v8.6a", Unstable(sym::aarch64_ver_target_feature), &["v8.5a", "bf16", "i8mm"]), + ("v8.7a", Unstable(sym::aarch64_ver_target_feature), &[]), // FEAT_VHE - ("vh", Stable), + ("vh", Stable, &[]), // tidy-alphabetical-end ]; @@ -190,295 +200,223 @@ const AARCH64_TIED_FEATURES: &[&[&str]] = &[ &["paca", "pacg"], // Together these represent `pauth` in LLVM ]; -const X86_ALLOWED_FEATURES: &[(&str, Stability)] = &[ +const X86_ALLOWED_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ // tidy-alphabetical-start - ("adx", Stable), - ("aes", Stable), - ("amx-bf16", Unstable(sym::x86_amx_intrinsics)), - ("amx-complex", Unstable(sym::x86_amx_intrinsics)), - ("amx-fp16", Unstable(sym::x86_amx_intrinsics)), - ("amx-int8", Unstable(sym::x86_amx_intrinsics)), - ("amx-tile", Unstable(sym::x86_amx_intrinsics)), - ("avx", Stable), - ("avx2", Stable), - ("avx512bf16", Unstable(sym::avx512_target_feature)), - ("avx512bitalg", Unstable(sym::avx512_target_feature)), - ("avx512bw", Unstable(sym::avx512_target_feature)), - ("avx512cd", Unstable(sym::avx512_target_feature)), - ("avx512dq", Unstable(sym::avx512_target_feature)), - ("avx512f", Unstable(sym::avx512_target_feature)), - ("avx512fp16", Unstable(sym::avx512_target_feature)), - ("avx512ifma", Unstable(sym::avx512_target_feature)), - ("avx512vbmi", Unstable(sym::avx512_target_feature)), - ("avx512vbmi2", Unstable(sym::avx512_target_feature)), - ("avx512vl", Unstable(sym::avx512_target_feature)), - ("avx512vnni", Unstable(sym::avx512_target_feature)), - ("avx512vp2intersect", Unstable(sym::avx512_target_feature)), - ("avx512vpopcntdq", Unstable(sym::avx512_target_feature)), - ("avxifma", Unstable(sym::avx512_target_feature)), - ("avxneconvert", Unstable(sym::avx512_target_feature)), - ("avxvnni", Unstable(sym::avx512_target_feature)), - ("avxvnniint16", Unstable(sym::avx512_target_feature)), - ("avxvnniint8", Unstable(sym::avx512_target_feature)), - ("bmi1", Stable), - ("bmi2", Stable), - ("cmpxchg16b", Stable), - ("ermsb", Unstable(sym::ermsb_target_feature)), - ("f16c", Stable), - ("fma", Stable), - ("fxsr", Stable), - ("gfni", Unstable(sym::avx512_target_feature)), - ("lahfsahf", Unstable(sym::lahfsahf_target_feature)), - ("lzcnt", Stable), - ("movbe", Stable), - ("pclmulqdq", Stable), - ("popcnt", Stable), - ("prfchw", Unstable(sym::prfchw_target_feature)), - ("rdrand", Stable), - ("rdseed", Stable), - ("rtm", Unstable(sym::rtm_target_feature)), - ("sha", Stable), - ("sha512", Unstable(sym::sha512_sm_x86)), - ("sm3", Unstable(sym::sha512_sm_x86)), - ("sm4", Unstable(sym::sha512_sm_x86)), - ("sse", Stable), - ("sse2", Stable), - ("sse3", Stable), - ("sse4.1", Stable), - ("sse4.2", Stable), - ("sse4a", Unstable(sym::sse4a_target_feature)), - ("ssse3", Stable), - ("tbm", Unstable(sym::tbm_target_feature)), - ("vaes", Unstable(sym::avx512_target_feature)), - ("vpclmulqdq", Unstable(sym::avx512_target_feature)), - ("xop", Unstable(sym::xop_target_feature)), - ("xsave", Stable), - ("xsavec", Stable), - ("xsaveopt", Stable), - ("xsaves", Stable), + ("adx", Stable, &[]), + ("aes", Stable, &["sse2"]), + ("amx-bf16", Unstable(sym::x86_amx_intrinsics), &["amx-tile"]), + ("amx-complex", Unstable(sym::x86_amx_intrinsics), &["amx-tile"]), + ("amx-fp16", Unstable(sym::x86_amx_intrinsics), &["amx-tile"]), + ("amx-int8", Unstable(sym::x86_amx_intrinsics), &["amx-tile"]), + ("amx-tile", Unstable(sym::x86_amx_intrinsics), &[]), + ("avx", Stable, &["sse4.2"]), + ("avx2", Stable, &["avx"]), + ("avx512bf16", Unstable(sym::avx512_target_feature), &["avx512bw"]), + ("avx512bitalg", Unstable(sym::avx512_target_feature), &["avx512bw"]), + ("avx512bw", Unstable(sym::avx512_target_feature), &["avx512f"]), + ("avx512cd", Unstable(sym::avx512_target_feature), &["avx512f"]), + ("avx512dq", Unstable(sym::avx512_target_feature), &["avx512f"]), + ("avx512f", Unstable(sym::avx512_target_feature), &["avx2", "fma", "f16c"]), + ("avx512fp16", Unstable(sym::avx512_target_feature), &["avx512bw", "avx512vl", "avx512dq"]), + ("avx512ifma", Unstable(sym::avx512_target_feature), &["avx512f"]), + ("avx512vbmi", Unstable(sym::avx512_target_feature), &["avx512bw"]), + ("avx512vbmi2", Unstable(sym::avx512_target_feature), &["avx512bw"]), + ("avx512vl", Unstable(sym::avx512_target_feature), &["avx512f"]), + ("avx512vnni", Unstable(sym::avx512_target_feature), &["avx512f"]), + ("avx512vp2intersect", Unstable(sym::avx512_target_feature), &["avx512f"]), + ("avx512vpopcntdq", Unstable(sym::avx512_target_feature), &["avx512f"]), + ("avxifma", Unstable(sym::avx512_target_feature), &["avx2"]), + ("avxneconvert", Unstable(sym::avx512_target_feature), &["avx2"]), + ("avxvnni", Unstable(sym::avx512_target_feature), &["avx2"]), + ("avxvnniint16", Unstable(sym::avx512_target_feature), &["avx2"]), + ("avxvnniint8", Unstable(sym::avx512_target_feature), &["avx2"]), + ("bmi1", Stable, &[]), + ("bmi2", Stable, &[]), + ("cmpxchg16b", Stable, &[]), + ("ermsb", Unstable(sym::ermsb_target_feature), &[]), + ("f16c", Stable, &["avx"]), + ("fma", Stable, &["avx"]), + ("fxsr", Stable, &[]), + ("gfni", Unstable(sym::avx512_target_feature), &["sse2"]), + ("lahfsahf", Unstable(sym::lahfsahf_target_feature), &[]), + ("lzcnt", Stable, &[]), + ("movbe", Stable, &[]), + ("pclmulqdq", Stable, &[]), + ("popcnt", Stable, &[]), + ("prfchw", Unstable(sym::prfchw_target_feature), &[]), + ("rdrand", Stable, &[]), + ("rdseed", Stable, &[]), + ("rtm", Unstable(sym::rtm_target_feature), &[]), + ("sha", Stable, &["sse2"]), + ("sha512", Unstable(sym::sha512_sm_x86), &["avx2"]), + ("sm3", Unstable(sym::sha512_sm_x86), &["avx"]), + ("sm4", Unstable(sym::sha512_sm_x86), &["avx2"]), + ("sse", Stable, &[]), + ("sse2", Stable, &["sse"]), + ("sse3", Stable, &["sse2"]), + ("sse4.1", Stable, &["ssse3"]), + ("sse4.2", Stable, &["sse4.1"]), + ("sse4a", Unstable(sym::sse4a_target_feature), &["sse3"]), + ("ssse3", Stable, &["sse3"]), + ("tbm", Unstable(sym::tbm_target_feature), &[]), + ("vaes", Unstable(sym::avx512_target_feature), &["avx2", "aes"]), + ("vpclmulqdq", Unstable(sym::avx512_target_feature), &["avx", "pclmulqdq"]), + ("xop", Unstable(sym::xop_target_feature), &[/*"fma4", */ "avx", "sse4a"]), + ("xsave", Stable, &[]), + ("xsavec", Stable, &["xsave"]), + ("xsaveopt", Stable, &["xsave"]), + ("xsaves", Stable, &["xsave"]), // tidy-alphabetical-end ]; -const HEXAGON_ALLOWED_FEATURES: &[(&str, Stability)] = &[ +const HEXAGON_ALLOWED_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ // tidy-alphabetical-start - ("hvx", Unstable(sym::hexagon_target_feature)), - ("hvx-length128b", Unstable(sym::hexagon_target_feature)), + ("hvx", Unstable(sym::hexagon_target_feature), &[]), + ("hvx-length128b", Unstable(sym::hexagon_target_feature), &["hvx"]), // tidy-alphabetical-end ]; -const POWERPC_ALLOWED_FEATURES: &[(&str, Stability)] = &[ +const POWERPC_ALLOWED_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ // tidy-alphabetical-start - ("altivec", Unstable(sym::powerpc_target_feature)), - ("power10-vector", Unstable(sym::powerpc_target_feature)), - ("power8-altivec", Unstable(sym::powerpc_target_feature)), - ("power8-vector", Unstable(sym::powerpc_target_feature)), - ("power9-altivec", Unstable(sym::powerpc_target_feature)), - ("power9-vector", Unstable(sym::powerpc_target_feature)), - ("vsx", Unstable(sym::powerpc_target_feature)), + ("altivec", Unstable(sym::powerpc_target_feature), &[]), + ("power10-vector", Unstable(sym::powerpc_target_feature), &["power9-vector"]), + ("power8-altivec", Unstable(sym::powerpc_target_feature), &["altivec"]), + ("power8-vector", Unstable(sym::powerpc_target_feature), &["vsx", "power8-altivec"]), + ("power9-altivec", Unstable(sym::powerpc_target_feature), &["power8-altivec"]), + ("power9-vector", Unstable(sym::powerpc_target_feature), &["power8-vector", "power9-altivec"]), + ("vsx", Unstable(sym::powerpc_target_feature), &["altivec"]), // tidy-alphabetical-end ]; -const MIPS_ALLOWED_FEATURES: &[(&str, Stability)] = &[ +const MIPS_ALLOWED_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ // tidy-alphabetical-start - ("fp64", Unstable(sym::mips_target_feature)), - ("msa", Unstable(sym::mips_target_feature)), - ("virt", Unstable(sym::mips_target_feature)), + ("fp64", Unstable(sym::mips_target_feature), &[]), + ("msa", Unstable(sym::mips_target_feature), &[]), + ("virt", Unstable(sym::mips_target_feature), &[]), // tidy-alphabetical-end ]; -const RISCV_ALLOWED_FEATURES: &[(&str, Stability)] = &[ +const RISCV_ALLOWED_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ // tidy-alphabetical-start - ("a", Stable), - ("c", Stable), - ("d", Unstable(sym::riscv_target_feature)), - ("e", Unstable(sym::riscv_target_feature)), - ("f", Unstable(sym::riscv_target_feature)), - ("m", Stable), - ("relax", Unstable(sym::riscv_target_feature)), - ("unaligned-scalar-mem", Unstable(sym::riscv_target_feature)), - ("v", Unstable(sym::riscv_target_feature)), - ("zba", Stable), - ("zbb", Stable), - ("zbc", Stable), - ("zbkb", Stable), - ("zbkc", Stable), - ("zbkx", Stable), - ("zbs", Stable), - ("zdinx", Unstable(sym::riscv_target_feature)), - ("zfh", Unstable(sym::riscv_target_feature)), - ("zfhmin", Unstable(sym::riscv_target_feature)), - ("zfinx", Unstable(sym::riscv_target_feature)), - ("zhinx", Unstable(sym::riscv_target_feature)), - ("zhinxmin", Unstable(sym::riscv_target_feature)), - ("zk", Stable), - ("zkn", Stable), - ("zknd", Stable), - ("zkne", Stable), - ("zknh", Stable), - ("zkr", Stable), - ("zks", Stable), - ("zksed", Stable), - ("zksh", Stable), - ("zkt", Stable), + ("a", Stable, &[]), + ("c", Stable, &[]), + ("d", Unstable(sym::riscv_target_feature), &["f"]), + ("e", Unstable(sym::riscv_target_feature), &[]), + ("f", Unstable(sym::riscv_target_feature), &[]), + ("m", Stable, &[]), + ("relax", Unstable(sym::riscv_target_feature), &[]), + ("unaligned-scalar-mem", Unstable(sym::riscv_target_feature), &[]), + ("v", Unstable(sym::riscv_target_feature), &[]), + ("zba", Stable, &[]), + ("zbb", Stable, &[]), + ("zbc", Stable, &[]), + ("zbkb", Stable, &[]), + ("zbkc", Stable, &[]), + ("zbkx", Stable, &[]), + ("zbs", Stable, &[]), + ("zdinx", Unstable(sym::riscv_target_feature), &["zfinx"]), + ("zfh", Unstable(sym::riscv_target_feature), &["zfhmin"]), + ("zfhmin", Unstable(sym::riscv_target_feature), &["f"]), + ("zfinx", Unstable(sym::riscv_target_feature), &[]), + ("zhinx", Unstable(sym::riscv_target_feature), &["zhinxmin"]), + ("zhinxmin", Unstable(sym::riscv_target_feature), &["zfinx"]), + ("zk", Stable, &["zkn", "zkr", "zkt"]), + ("zkn", Stable, &["zbkb", "zbkc", "zbkx", "zkne", "zknd", "zknh"]), + ("zknd", Stable, &[]), + ("zkne", Stable, &[]), + ("zknh", Stable, &[]), + ("zkr", Stable, &[]), + ("zks", Stable, &["zbkb", "bzkc", "zbkx", "zksed", "zksh"]), + ("zksed", Stable, &[]), + ("zksh", Stable, &[]), + ("zkt", Stable, &[]), // tidy-alphabetical-end ]; -const WASM_ALLOWED_FEATURES: &[(&str, Stability)] = &[ +const WASM_ALLOWED_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ // tidy-alphabetical-start - ("atomics", Unstable(sym::wasm_target_feature)), - ("bulk-memory", Stable), - ("exception-handling", Unstable(sym::wasm_target_feature)), - ("extended-const", Stable), - ("multivalue", Unstable(sym::wasm_target_feature)), - ("mutable-globals", Stable), - ("nontrapping-fptoint", Stable), - ("reference-types", Unstable(sym::wasm_target_feature)), - ("relaxed-simd", Stable), - ("sign-ext", Stable), - ("simd128", Stable), + ("atomics", Unstable(sym::wasm_target_feature), &[]), + ("bulk-memory", Stable, &[]), + ("exception-handling", Unstable(sym::wasm_target_feature), &[]), + ("extended-const", Stable, &[]), + ("multivalue", Unstable(sym::wasm_target_feature), &[]), + ("mutable-globals", Stable, &[]), + ("nontrapping-fptoint", Stable, &[]), + ("reference-types", Unstable(sym::wasm_target_feature), &[]), + ("relaxed-simd", Stable, &["simd128"]), + ("sign-ext", Stable, &[]), + ("simd128", Stable, &[]), // tidy-alphabetical-end ]; -const BPF_ALLOWED_FEATURES: &[(&str, Stability)] = &[("alu32", Unstable(sym::bpf_target_feature))]; +const BPF_ALLOWED_FEATURES: &[(&str, Stability, ImpliedFeatures)] = + &[("alu32", Unstable(sym::bpf_target_feature), &[])]; -const CSKY_ALLOWED_FEATURES: &[(&str, Stability)] = &[ +const CSKY_ALLOWED_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ // tidy-alphabetical-start - ("10e60", Unstable(sym::csky_target_feature)), - ("2e3", Unstable(sym::csky_target_feature)), - ("3e3r1", Unstable(sym::csky_target_feature)), - ("3e3r2", Unstable(sym::csky_target_feature)), - ("3e3r3", Unstable(sym::csky_target_feature)), - ("3e7", Unstable(sym::csky_target_feature)), - ("7e10", Unstable(sym::csky_target_feature)), - ("cache", Unstable(sym::csky_target_feature)), - ("doloop", Unstable(sym::csky_target_feature)), - ("dsp1e2", Unstable(sym::csky_target_feature)), - ("dspe60", Unstable(sym::csky_target_feature)), - ("e1", Unstable(sym::csky_target_feature)), - ("e2", Unstable(sym::csky_target_feature)), - ("edsp", Unstable(sym::csky_target_feature)), - ("elrw", Unstable(sym::csky_target_feature)), - ("float1e2", Unstable(sym::csky_target_feature)), - ("float1e3", Unstable(sym::csky_target_feature)), - ("float3e4", Unstable(sym::csky_target_feature)), - ("float7e60", Unstable(sym::csky_target_feature)), - ("floate1", Unstable(sym::csky_target_feature)), - ("hard-tp", Unstable(sym::csky_target_feature)), - ("high-registers", Unstable(sym::csky_target_feature)), - ("hwdiv", Unstable(sym::csky_target_feature)), - ("mp", Unstable(sym::csky_target_feature)), - ("mp1e2", Unstable(sym::csky_target_feature)), - ("nvic", Unstable(sym::csky_target_feature)), - ("trust", Unstable(sym::csky_target_feature)), - ("vdsp2e60f", Unstable(sym::csky_target_feature)), - ("vdspv1", Unstable(sym::csky_target_feature)), - ("vdspv2", Unstable(sym::csky_target_feature)), + ("10e60", Unstable(sym::csky_target_feature), &["7e10"]), + ("2e3", Unstable(sym::csky_target_feature), &["e2"]), + ("3e3r1", Unstable(sym::csky_target_feature), &[]), + ("3e3r2", Unstable(sym::csky_target_feature), &["3e3r1", "doloop"]), + ("3e3r3", Unstable(sym::csky_target_feature), &["doloop"]), + ("3e7", Unstable(sym::csky_target_feature), &["2e3"]), + ("7e10", Unstable(sym::csky_target_feature), &["3e7"]), + ("cache", Unstable(sym::csky_target_feature), &[]), + ("doloop", Unstable(sym::csky_target_feature), &[]), + ("dsp1e2", Unstable(sym::csky_target_feature), &[]), + ("dspe60", Unstable(sym::csky_target_feature), &[]), + ("e1", Unstable(sym::csky_target_feature), &["elrw"]), + ("e2", Unstable(sym::csky_target_feature), &["e2"]), + ("edsp", Unstable(sym::csky_target_feature), &[]), + ("elrw", Unstable(sym::csky_target_feature), &[]), + ("float1e2", Unstable(sym::csky_target_feature), &[]), + ("float1e3", Unstable(sym::csky_target_feature), &[]), + ("float3e4", Unstable(sym::csky_target_feature), &[]), + ("float7e60", Unstable(sym::csky_target_feature), &[]), + ("floate1", Unstable(sym::csky_target_feature), &[]), + ("hard-tp", Unstable(sym::csky_target_feature), &[]), + ("high-registers", Unstable(sym::csky_target_feature), &[]), + ("hwdiv", Unstable(sym::csky_target_feature), &[]), + ("mp", Unstable(sym::csky_target_feature), &["2e3"]), + ("mp1e2", Unstable(sym::csky_target_feature), &["3e7"]), + ("nvic", Unstable(sym::csky_target_feature), &[]), + ("trust", Unstable(sym::csky_target_feature), &[]), + ("vdsp2e60f", Unstable(sym::csky_target_feature), &[]), + ("vdspv1", Unstable(sym::csky_target_feature), &[]), + ("vdspv2", Unstable(sym::csky_target_feature), &[]), // tidy-alphabetical-end //fpu // tidy-alphabetical-start - ("fdivdu", Unstable(sym::csky_target_feature)), - ("fpuv2_df", Unstable(sym::csky_target_feature)), - ("fpuv2_sf", Unstable(sym::csky_target_feature)), - ("fpuv3_df", Unstable(sym::csky_target_feature)), - ("fpuv3_hf", Unstable(sym::csky_target_feature)), - ("fpuv3_hi", Unstable(sym::csky_target_feature)), - ("fpuv3_sf", Unstable(sym::csky_target_feature)), - ("hard-float", Unstable(sym::csky_target_feature)), - ("hard-float-abi", Unstable(sym::csky_target_feature)), - // tidy-alphabetical-end -]; - -const LOONGARCH_ALLOWED_FEATURES: &[(&str, Stability)] = &[ - // tidy-alphabetical-start - ("d", Unstable(sym::loongarch_target_feature)), - ("f", Unstable(sym::loongarch_target_feature)), - ("frecipe", Unstable(sym::loongarch_target_feature)), - ("lasx", Unstable(sym::loongarch_target_feature)), - ("lbt", Unstable(sym::loongarch_target_feature)), - ("lsx", Unstable(sym::loongarch_target_feature)), - ("lvz", Unstable(sym::loongarch_target_feature)), - ("relax", Unstable(sym::loongarch_target_feature)), - ("ual", Unstable(sym::loongarch_target_feature)), - // tidy-alphabetical-end -]; - -const IBMZ_ALLOWED_FEATURES: &[(&str, Stability)] = &[ - // tidy-alphabetical-start - ("backchain", Unstable(sym::s390x_target_feature)), - ("vector", Unstable(sym::s390x_target_feature)), - // tidy-alphabetical-end -]; - -const X86_IMPLIED_FEATURES: &[(&str, &[&str])] = &[ - // tidy-alphabetical-start - ("aes", &["sse2"]), - ("avx", &["sse4.2"]), - ("avx2", &["avx"]), - ("avx512bf16", &["avx512bw"]), - ("avx512bitalg", &["avx512bw"]), - ("avx512bw", &["avx512f"]), - ("avx512cd", &["avx512f"]), - ("avx512dq", &["avx512f"]), - ("avx512f", &["avx2"]), - ("avx512fp16", &["avx512bw", "avx512vl", "avx512dq"]), - ("avx512vbmi", &["avx512bw"]), - ("avx512vbmi2", &["avx512bw"]), - ("avx512vl", &["avx512f"]), - ("avx512vnni", &["avx512f"]), - ("avx512vp2intersect", &["avx512f"]), - ("avx512vpopcntdq", &["avx512f"]), - ("f16c", &["avx"]), - ("fma", &["avx"]), - ("gfni", &["sse2"]), - ("pclmulqdq", &["sse2"]), - ("sha", &["sse2"]), - ("sse2", &["sse"]), - ("sse3", &["sse2"]), - ("sse4.1", &["ssse3"]), - ("sse4.2", &["sse4.1"]), - ("ssse3", &["sse3"]), - ("vaes", &["avx", "aes"]), - ("vpclmulqdq", &["avx", "pclmulqdq"]), - ("xsavec", &["xsave"]), - ("xsaveopt", &["xsave"]), - ("xsaves", &["xsave"]), - // tidy-alphabetical-end -]; - -const AARCH64_IMPLIED_FEATURES: &[(&str, &[&str])] = &[ - // tidy-alphabetical-start - ("aes", &["neon"]), - ("f32mm", &["sve"]), - ("f64mm", &["sve"]), - ("fcma", &["neon"]), - ("fhm", &["fp16"]), - ("fp16", &["neon"]), - ("jsconv", &["neon"]), - ("rcpc2", &["rcpc"]), - ("sha2", &["neon"]), - ("sha3", &["sha2"]), - ("sm4", &["neon"]), - ("sve", &["fp16"]), - ("sve2", &["sve"]), - ("sve2-aes", &["sve2", "aes"]), - ("sve2-bitperm", &["sve2"]), - ("sve2-sha3", &["sve2", "sha3"]), - ("sve2-sm4", &["sve2", "sm4"]), + ("fdivdu", Unstable(sym::csky_target_feature), &[]), + ("fpuv2_df", Unstable(sym::csky_target_feature), &[]), + ("fpuv2_sf", Unstable(sym::csky_target_feature), &[]), + ("fpuv3_df", Unstable(sym::csky_target_feature), &[]), + ("fpuv3_hf", Unstable(sym::csky_target_feature), &[]), + ("fpuv3_hi", Unstable(sym::csky_target_feature), &[]), + ("fpuv3_sf", Unstable(sym::csky_target_feature), &[]), + ("hard-float", Unstable(sym::csky_target_feature), &[]), + ("hard-float-abi", Unstable(sym::csky_target_feature), &[]), // tidy-alphabetical-end ]; -const RISCV_IMPLIED_FEATURES: &[(&str, &[&str])] = &[ +const LOONGARCH_ALLOWED_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ // tidy-alphabetical-start - ("zb", &["zba", "zbc", "zbs"]), - ("zk", &["zkn", "zkr", "zks", "zkt", "zbkb", "zbkc", "zkbx"]), - ("zkn", &["zknd", "zkne", "zknh", "zbkb", "zbkc", "zkbx"]), - ("zks", &["zksed", "zksh", "zbkb", "zbkc", "zkbx"]), + ("d", Unstable(sym::loongarch_target_feature), &["f"]), + ("f", Unstable(sym::loongarch_target_feature), &[]), + ("frecipe", Unstable(sym::loongarch_target_feature), &[]), + ("lasx", Unstable(sym::loongarch_target_feature), &["lsx"]), + ("lbt", Unstable(sym::loongarch_target_feature), &[]), + ("lsx", Unstable(sym::loongarch_target_feature), &["d"]), + ("lvz", Unstable(sym::loongarch_target_feature), &[]), + ("relax", Unstable(sym::loongarch_target_feature), &[]), + ("ual", Unstable(sym::loongarch_target_feature), &[]), // tidy-alphabetical-end ]; -const WASM_IMPLIED_FEATURES: &[(&str, &[&str])] = &[ +const IBMZ_ALLOWED_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ // tidy-alphabetical-start - ("relaxed-simd", &["simd128"]), + ("backchain", Unstable(sym::s390x_target_feature), &[]), + ("vector", Unstable(sym::s390x_target_feature), &[]), // tidy-alphabetical-end ]; @@ -501,10 +439,13 @@ pub fn all_known_features() -> impl Iterator { .chain(LOONGARCH_ALLOWED_FEATURES) .chain(IBMZ_ALLOWED_FEATURES) .cloned() + .map(|(f, s, _)| (f, s)) } impl super::spec::Target { - pub fn supported_target_features(&self) -> &'static [(&'static str, Stability)] { + pub fn supported_target_features( + &self, + ) -> &'static [(&'static str, Stability, ImpliedFeatures)] { match &*self.arch { "arm" => ARM_ALLOWED_FEATURES, "aarch64" | "arm64ec" => AARCH64_ALLOWED_FEATURES, @@ -528,14 +469,4 @@ impl super::spec::Target { _ => &[], } } - - pub fn implied_target_features(&self) -> &'static [(&'static str, &'static [&'static str])] { - match &*self.arch { - "aarch4" => AARCH64_IMPLIED_FEATURES, - "riscv32" | "riscv64" => RISCV_IMPLIED_FEATURES, - "x86" | "x86_64" => X86_IMPLIED_FEATURES, - "wasm32" | "wasm64" => WASM_IMPLIED_FEATURES, - _ => &[], - } - } } From 484aca885765739d5fa1f3d77e082552d8c4bc58 Mon Sep 17 00:00:00 2001 From: Caleb Zulawski Date: Sat, 3 Aug 2024 04:45:48 -0400 Subject: [PATCH 21/31] Don't use LLVM's target features --- compiler/rustc_codegen_llvm/src/back/write.rs | 8 ++- compiler/rustc_codegen_llvm/src/context.rs | 2 +- compiler/rustc_codegen_llvm/src/lib.rs | 2 +- compiler/rustc_codegen_llvm/src/llvm_util.rs | 63 ++++++++++++++----- .../rustc_codegen_ssa/src/target_features.rs | 23 +------ compiler/rustc_target/src/target_features.rs | 25 ++++++++ 6 files changed, 84 insertions(+), 39 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index 5a7909d151139..890fcf508a8bd 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -95,11 +95,15 @@ pub fn write_output_file<'ll>( } } -pub fn create_informational_target_machine(sess: &Session) -> OwnedTargetMachine { +pub fn create_informational_target_machine( + sess: &Session, + extra_features: bool, +) -> OwnedTargetMachine { let config = TargetMachineFactoryConfig { split_dwarf_file: None, output_obj_file: None }; // Can't use query system here quite yet because this function is invoked before the query // system/tcx is set up. - let features = llvm_util::global_llvm_features(sess, false); + let features = + if extra_features { llvm_util::global_llvm_features(sess, false) } else { Vec::new() }; target_machine_factory(sess, config::OptLevel::No, &features)(config) .unwrap_or_else(|err| llvm_err(sess.dcx(), err).raise()) } diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index ea930421b5869..1dc3fbfc7b303 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -149,7 +149,7 @@ pub unsafe fn create_module<'ll>( // Ensure the data-layout values hardcoded remain the defaults. { - let tm = crate::back::write::create_informational_target_machine(tcx.sess); + let tm = crate::back::write::create_informational_target_machine(tcx.sess, true); unsafe { llvm::LLVMRustSetDataLayoutFromTargetMachine(llmod, &tm); } diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index 41e9cfd1066b9..333f1fdf6e03a 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -434,7 +434,7 @@ impl ModuleLlvm { ModuleLlvm { llmod_raw, llcx, - tm: ManuallyDrop::new(create_informational_target_machine(tcx.sess)), + tm: ManuallyDrop::new(create_informational_target_machine(tcx.sess, true)), } } } diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs index c70f6dd8180bf..e85974b7cc1ec 100644 --- a/compiler/rustc_codegen_llvm/src/llvm_util.rs +++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs @@ -308,7 +308,53 @@ pub fn check_tied_features( /// Used to generate cfg variables and apply features /// Must express features in the way Rust understands them pub fn target_features(sess: &Session, allow_unstable: bool) -> Vec { - let target_machine = create_informational_target_machine(sess); + let rust_features = sess + .target + .supported_target_features() + .iter() + .map(|(feature, _, _)| { + (to_llvm_features(sess, feature).llvm_feature_name, Symbol::intern(feature)) + }) + .collect::>(); + + let mut features = FxHashSet::default(); + + // Add base features for the target + let target_machine = create_informational_target_machine(sess, false); + features.extend( + sess.target + .supported_target_features() + .iter() + .filter(|(feature, _, _)| { + // skip checking special features, as LLVM may not understands them + if RUSTC_SPECIAL_FEATURES.contains(feature) { + return true; + } + // check that all features in a given smallvec are enabled + for llvm_feature in to_llvm_features(sess, feature) { + let cstr = SmallCStr::new(llvm_feature); + if !unsafe { llvm::LLVMRustHasFeature(&target_machine, cstr.as_ptr()) } { + return false; + } + } + true + }) + .map(|(feature, _, _)| Symbol::intern(feature)), + ); + + // Add enabled features + for llvm_feature in global_llvm_features(sess, false) { + let (add, llvm_feature) = llvm_feature.split_at(1); + let feature = + rust_features.get(llvm_feature).cloned().unwrap_or(Symbol::intern(llvm_feature)); + if add == "+" { + features.extend(sess.target.implied_target_features(std::iter::once(feature))); + } else if add == "-" { + features.remove(&feature); + } + } + + // Filter enabled features based on feature gates sess.target .supported_target_features() .iter() @@ -320,18 +366,7 @@ pub fn target_features(sess: &Session, allow_unstable: bool) -> Vec { } }) .filter(|feature| { - // skip checking special features, as LLVM may not understands them - if RUSTC_SPECIAL_FEATURES.contains(feature) { - return true; - } - // check that all features in a given smallvec are enabled - for llvm_feature in to_llvm_features(sess, feature) { - let cstr = SmallCStr::new(llvm_feature); - if !unsafe { llvm::LLVMRustHasFeature(&target_machine, cstr.as_ptr()) } { - return false; - } - } - true + RUSTC_SPECIAL_FEATURES.contains(feature) || features.contains(&Symbol::intern(feature)) }) .map(|feature| Symbol::intern(feature)) .collect() @@ -440,7 +475,7 @@ fn print_target_features(out: &mut String, sess: &Session, tm: &llvm::TargetMach pub(crate) fn print(req: &PrintRequest, mut out: &mut String, sess: &Session) { require_inited(); - let tm = create_informational_target_machine(sess); + let tm = create_informational_target_machine(sess, true); match req.kind { PrintKind::TargetCPUs => { // SAFETY generate a C compatible string from a byte slice to pass diff --git a/compiler/rustc_codegen_ssa/src/target_features.rs b/compiler/rustc_codegen_ssa/src/target_features.rs index 536f39375bb90..c84b844cd0401 100644 --- a/compiler/rustc_codegen_ssa/src/target_features.rs +++ b/compiler/rustc_codegen_ssa/src/target_features.rs @@ -1,6 +1,6 @@ use rustc_ast::ast; use rustc_attr::InstructionSetAttr; -use rustc_data_structures::fx::{FxHashMap, FxIndexSet}; +use rustc_data_structures::fx::FxIndexSet; use rustc_data_structures::unord::{ExtendUnord, UnordMap, UnordSet}; use rustc_errors::Applicability; use rustc_hir::def::DefKind; @@ -165,26 +165,7 @@ pub(crate) fn provide(providers: &mut Providers) { } }, implied_target_features: |tcx, feature| { - let implied_features = tcx - .sess - .target - .supported_target_features() - .iter() - .map(|(f, _, i)| (Symbol::intern(f), i)) - .collect::>(); - - // implied target features have their own implied target features, so we traverse the - // map until there are no more features to add - let mut features = UnordSet::new(); - let mut new_features = vec![feature]; - while let Some(new_feature) = new_features.pop() { - if features.insert(new_feature) { - if let Some(implied_features) = implied_features.get(&new_feature) { - new_features.extend(implied_features.iter().copied().map(Symbol::intern)) - } - } - } - features + tcx.sess.target.implied_target_features(std::iter::once(feature)).into() }, asm_target_features, ..*providers diff --git a/compiler/rustc_target/src/target_features.rs b/compiler/rustc_target/src/target_features.rs index f408db1dcbad2..0e9979e1bb7f6 100644 --- a/compiler/rustc_target/src/target_features.rs +++ b/compiler/rustc_target/src/target_features.rs @@ -1,3 +1,4 @@ +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_span::symbol::{sym, Symbol}; /// Features that control behaviour of rustc, rather than the codegen. @@ -469,4 +470,28 @@ impl super::spec::Target { _ => &[], } } + + pub fn implied_target_features( + &self, + base_features: impl Iterator, + ) -> FxHashSet { + let implied_features = self + .supported_target_features() + .iter() + .map(|(f, _, i)| (Symbol::intern(f), i)) + .collect::>(); + + // implied target features have their own implied target features, so we traverse the + // map until there are no more features to add + let mut features = FxHashSet::default(); + let mut new_features = base_features.collect::>(); + while let Some(new_feature) = new_features.pop() { + if features.insert(new_feature) { + if let Some(implied_features) = implied_features.get(&new_feature) { + new_features.extend(implied_features.iter().copied().map(Symbol::intern)) + } + } + } + features + } } From a25da077cf7606f42ffad17ee1562f932aa19b12 Mon Sep 17 00:00:00 2001 From: Caleb Zulawski Date: Sat, 3 Aug 2024 23:51:37 -0400 Subject: [PATCH 22/31] Don't use LLVM to compute -Ctarget-feature --- compiler/rustc_codegen_llvm/src/back/write.rs | 5 +- compiler/rustc_codegen_llvm/src/context.rs | 2 +- compiler/rustc_codegen_llvm/src/lib.rs | 4 +- compiler/rustc_codegen_llvm/src/llvm_util.rs | 211 +++++++++--------- compiler/rustc_target/src/target_features.rs | 8 +- tests/codegen/target-feature-overrides.rs | 2 +- 6 files changed, 120 insertions(+), 112 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index 890fcf508a8bd..a1f2433ab6f3b 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -97,13 +97,12 @@ pub fn write_output_file<'ll>( pub fn create_informational_target_machine( sess: &Session, - extra_features: bool, + only_base_features: bool, ) -> OwnedTargetMachine { let config = TargetMachineFactoryConfig { split_dwarf_file: None, output_obj_file: None }; // Can't use query system here quite yet because this function is invoked before the query // system/tcx is set up. - let features = - if extra_features { llvm_util::global_llvm_features(sess, false) } else { Vec::new() }; + let features = llvm_util::global_llvm_features(sess, false, only_base_features); target_machine_factory(sess, config::OptLevel::No, &features)(config) .unwrap_or_else(|err| llvm_err(sess.dcx(), err).raise()) } diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index 1dc3fbfc7b303..173b8a479efc5 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -149,7 +149,7 @@ pub unsafe fn create_module<'ll>( // Ensure the data-layout values hardcoded remain the defaults. { - let tm = crate::back::write::create_informational_target_machine(tcx.sess, true); + let tm = crate::back::write::create_informational_target_machine(tcx.sess, false); unsafe { llvm::LLVMRustSetDataLayoutFromTargetMachine(llmod, &tm); } diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index 333f1fdf6e03a..518a86e0cb06d 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -269,7 +269,7 @@ impl CodegenBackend for LlvmCodegenBackend { fn provide(&self, providers: &mut Providers) { providers.global_backend_features = - |tcx, ()| llvm_util::global_llvm_features(tcx.sess, true) + |tcx, ()| llvm_util::global_llvm_features(tcx.sess, true, false) } fn print(&self, req: &PrintRequest, out: &mut String, sess: &Session) { @@ -434,7 +434,7 @@ impl ModuleLlvm { ModuleLlvm { llmod_raw, llcx, - tm: ManuallyDrop::new(create_informational_target_machine(tcx.sess, true)), + tm: ManuallyDrop::new(create_informational_target_machine(tcx.sess, false)), } } } diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs index e85974b7cc1ec..3190853a84c90 100644 --- a/compiler/rustc_codegen_llvm/src/llvm_util.rs +++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs @@ -308,19 +308,10 @@ pub fn check_tied_features( /// Used to generate cfg variables and apply features /// Must express features in the way Rust understands them pub fn target_features(sess: &Session, allow_unstable: bool) -> Vec { - let rust_features = sess - .target - .supported_target_features() - .iter() - .map(|(feature, _, _)| { - (to_llvm_features(sess, feature).llvm_feature_name, Symbol::intern(feature)) - }) - .collect::>(); - let mut features = FxHashSet::default(); // Add base features for the target - let target_machine = create_informational_target_machine(sess, false); + let target_machine = create_informational_target_machine(sess, true); features.extend( sess.target .supported_target_features() @@ -343,13 +334,16 @@ pub fn target_features(sess: &Session, allow_unstable: bool) -> Vec { ); // Add enabled features - for llvm_feature in global_llvm_features(sess, false) { - let (add, llvm_feature) = llvm_feature.split_at(1); - let feature = - rust_features.get(llvm_feature).cloned().unwrap_or(Symbol::intern(llvm_feature)); - if add == "+" { + for (enabled, feature) in + sess.opts.cg.target_feature.split(',').filter_map(|s| match s.chars().next() { + Some('+') => Some((true, Symbol::intern(&s[1..]))), + Some('-') => Some((false, Symbol::intern(&s[1..]))), + _ => None, + }) + { + if enabled { features.extend(sess.target.implied_target_features(std::iter::once(feature))); - } else if add == "-" { + } else { features.remove(&feature); } } @@ -475,7 +469,7 @@ fn print_target_features(out: &mut String, sess: &Session, tm: &llvm::TargetMach pub(crate) fn print(req: &PrintRequest, mut out: &mut String, sess: &Session) { require_inited(); - let tm = create_informational_target_machine(sess, true); + let tm = create_informational_target_machine(sess, false); match req.kind { PrintKind::TargetCPUs => { // SAFETY generate a C compatible string from a byte slice to pass @@ -523,7 +517,11 @@ pub fn target_cpu(sess: &Session) -> &str { /// The list of LLVM features computed from CLI flags (`-Ctarget-cpu`, `-Ctarget-feature`, /// `--target` and similar). -pub(crate) fn global_llvm_features(sess: &Session, diagnostics: bool) -> Vec { +pub(crate) fn global_llvm_features( + sess: &Session, + diagnostics: bool, + only_base_features: bool, +) -> Vec { // Features that come earlier are overridden by conflicting features later in the string. // Typically we'll want more explicit settings to override the implicit ones, so: // @@ -583,96 +581,109 @@ pub(crate) fn global_llvm_features(sess: &Session, diagnostics: bool) -> Vec return None, - Some(c @ ('+' | '-')) => c, - Some(_) => { - if diagnostics { - sess.dcx().emit_warn(UnknownCTargetFeaturePrefix { feature: s }); + if !only_base_features { + let supported_features = sess.target.supported_target_features(); + let (llvm_major, _, _) = get_version(); + let mut featsmap = FxHashMap::default(); + let feats = sess + .opts + .cg + .target_feature + .split(',') + .filter_map(|s| { + let enable_disable = match s.chars().next() { + None => return None, + Some(c @ ('+' | '-')) => c, + Some(_) => { + if diagnostics { + sess.dcx().emit_warn(UnknownCTargetFeaturePrefix { feature: s }); + } + return None; } - return None; - } - }; + }; - let feature = backend_feature_name(sess, s)?; - // Warn against use of LLVM specific feature names and unstable features on the CLI. - if diagnostics { - let feature_state = supported_features.iter().find(|&&(v, _, _)| v == feature); - if feature_state.is_none() { - let rust_feature = - supported_features.iter().find_map(|&(rust_feature, _, _)| { - let llvm_features = to_llvm_features(sess, rust_feature); - if llvm_features.contains(feature) - && !llvm_features.contains(rust_feature) - { - Some(rust_feature) - } else { - None + let feature = backend_feature_name(sess, s)?; + // Warn against use of LLVM specific feature names and unstable features on the CLI. + if diagnostics { + let feature_state = supported_features.iter().find(|&&(v, _, _)| v == feature); + if feature_state.is_none() { + let rust_feature = + supported_features.iter().find_map(|&(rust_feature, _, _)| { + let llvm_features = to_llvm_features(sess, rust_feature); + if llvm_features.contains(feature) + && !llvm_features.contains(rust_feature) + { + Some(rust_feature) + } else { + None + } + }); + let unknown_feature = if let Some(rust_feature) = rust_feature { + UnknownCTargetFeature { + feature, + rust_feature: PossibleFeature::Some { rust_feature }, } - }); - let unknown_feature = if let Some(rust_feature) = rust_feature { - UnknownCTargetFeature { - feature, - rust_feature: PossibleFeature::Some { rust_feature }, - } - } else { - UnknownCTargetFeature { feature, rust_feature: PossibleFeature::None } - }; - sess.dcx().emit_warn(unknown_feature); - } else if feature_state - .is_some_and(|(_name, feature_gate, _implied)| !feature_gate.is_stable()) - { - // An unstable feature. Warn about using it. - sess.dcx().emit_warn(UnstableCTargetFeature { feature }); + } else { + UnknownCTargetFeature { feature, rust_feature: PossibleFeature::None } + }; + sess.dcx().emit_warn(unknown_feature); + } else if feature_state + .is_some_and(|(_name, feature_gate, _implied)| !feature_gate.is_stable()) + { + // An unstable feature. Warn about using it. + sess.dcx().emit_warn(UnstableCTargetFeature { feature }); + } } - } - if diagnostics { - // FIXME(nagisa): figure out how to not allocate a full hashset here. - featsmap.insert(feature, enable_disable == '+'); - } + if diagnostics { + // FIXME(nagisa): figure out how to not allocate a full hashset here. + featsmap.insert(feature, enable_disable == '+'); + } - // rustc-specific features do not get passed down to LLVM… - if RUSTC_SPECIFIC_FEATURES.contains(&feature) { - return None; - } + // rustc-specific features do not get passed down to LLVM… + if RUSTC_SPECIFIC_FEATURES.contains(&feature) { + return None; + } - // if the target-feature is "backchain" and LLVM version is greater than 18 - // then we also need to add "+backchain" to the target-features attribute. - // otherwise, we will only add the naked `backchain` attribute to the attribute-group. - if feature == "backchain" && llvm_major < 18 { - return None; - } - // ... otherwise though we run through `to_llvm_features` when - // passing requests down to LLVM. This means that all in-language - // features also work on the command line instead of having two - // different names when the LLVM name and the Rust name differ. - let llvm_feature = to_llvm_features(sess, feature); - - Some( - std::iter::once(format!("{}{}", enable_disable, llvm_feature.llvm_feature_name)) - .chain(llvm_feature.dependency.into_iter().filter_map(move |feat| { - match (enable_disable, feat) { + // if the target-feature is "backchain" and LLVM version is greater than 18 + // then we also need to add "+backchain" to the target-features attribute. + // otherwise, we will only add the naked `backchain` attribute to the attribute-group. + if feature == "backchain" && llvm_major < 18 { + return None; + } + // ... otherwise though we run through `to_llvm_features` when + // passing requests down to LLVM. This means that all in-language + // features also work on the command line instead of having two + // different names when the LLVM name and the Rust name differ. + let llvm_feature = to_llvm_features(sess, feature); + + Some( + std::iter::once(format!( + "{}{}", + enable_disable, llvm_feature.llvm_feature_name + )) + .chain(llvm_feature.dependency.into_iter().filter_map( + move |feat| match (enable_disable, feat) { ('-' | '+', TargetFeatureFoldStrength::Both(f)) | ('+', TargetFeatureFoldStrength::EnableOnly(f)) => { Some(format!("{enable_disable}{f}")) } _ => None, - } - })), - ) - }) - .flatten(); - features.extend(feats); + }, + )), + ) + }) + .flatten(); + features.extend(feats); + + if diagnostics && let Some(f) = check_tied_features(sess, &featsmap) { + sess.dcx().emit_err(TargetFeatureDisableOrEnable { + features: f, + span: None, + missing_features: None, + }); + } + } // -Zfixed-x18 if sess.opts.unstable_opts.fixed_x18 { @@ -683,14 +694,6 @@ pub(crate) fn global_llvm_features(sess: &Session, diagnostics: bool) -> Vec Date: Sun, 4 Aug 2024 01:02:53 -0400 Subject: [PATCH 23/31] Remove redundant implied features --- compiler/rustc_codegen_llvm/src/llvm_util.rs | 36 ++------------------ compiler/rustc_target/src/target_features.rs | 6 ++-- 2 files changed, 6 insertions(+), 36 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs index 3190853a84c90..10cba179c7564 100644 --- a/compiler/rustc_codegen_llvm/src/llvm_util.rs +++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs @@ -239,40 +239,8 @@ pub fn to_llvm_features<'a>(sess: &Session, s: &'a str) -> LLVMFeature<'a> { } // In LLVM neon implicitly enables fp, but we manually enable // neon when a feature only implicitly enables fp - ("aarch64", "f32mm") => { - LLVMFeature::with_dependency("f32mm", TargetFeatureFoldStrength::EnableOnly("neon")) - } - ("aarch64", "f64mm") => { - LLVMFeature::with_dependency("f64mm", TargetFeatureFoldStrength::EnableOnly("neon")) - } - ("aarch64", "fhm") => { - LLVMFeature::with_dependency("fp16fml", TargetFeatureFoldStrength::EnableOnly("neon")) - } - ("aarch64", "fp16") => { - LLVMFeature::with_dependency("fullfp16", TargetFeatureFoldStrength::EnableOnly("neon")) - } - ("aarch64", "jsconv") => { - LLVMFeature::with_dependency("jsconv", TargetFeatureFoldStrength::EnableOnly("neon")) - } - ("aarch64", "sve") => { - LLVMFeature::with_dependency("sve", TargetFeatureFoldStrength::EnableOnly("neon")) - } - ("aarch64", "sve2") => { - LLVMFeature::with_dependency("sve2", TargetFeatureFoldStrength::EnableOnly("neon")) - } - ("aarch64", "sve2-aes") => { - LLVMFeature::with_dependency("sve2-aes", TargetFeatureFoldStrength::EnableOnly("neon")) - } - ("aarch64", "sve2-sm4") => { - LLVMFeature::with_dependency("sve2-sm4", TargetFeatureFoldStrength::EnableOnly("neon")) - } - ("aarch64", "sve2-sha3") => { - LLVMFeature::with_dependency("sve2-sha3", TargetFeatureFoldStrength::EnableOnly("neon")) - } - ("aarch64", "sve2-bitperm") => LLVMFeature::with_dependency( - "sve2-bitperm", - TargetFeatureFoldStrength::EnableOnly("neon"), - ), + ("aarch64", "fhm") => LLVMFeature::new("fp16fml"), + ("aarch64", "fp16") => LLVMFeature::new("fullfp16"), // In LLVM 18, `unaligned-scalar-mem` was merged with `unaligned-vector-mem` into a single feature called // `fast-unaligned-access`. In LLVM 19, it was split back out. ("riscv32" | "riscv64", "unaligned-scalar-mem") if get_version().0 == 18 => { diff --git a/compiler/rustc_target/src/target_features.rs b/compiler/rustc_target/src/target_features.rs index 63bdcfddfc5f7..746926308b6b4 100644 --- a/compiler/rustc_target/src/target_features.rs +++ b/compiler/rustc_target/src/target_features.rs @@ -118,13 +118,15 @@ const AARCH64_ALLOWED_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ // FEAT_FLAGM ("flagm", Stable, &[]), // FEAT_FP16 - ("fp16", Stable, &[]), + // Rust ties FP and Neon: https://github.com/rust-lang/rust/pull/91608 + ("fp16", Stable, &["neon"]), // FEAT_FRINTTS ("frintts", Stable, &[]), // FEAT_I8MM ("i8mm", Stable, &[]), // FEAT_JSCVT - ("jsconv", Stable, &[]), + // Rust ties FP and Neon: https://github.com/rust-lang/rust/pull/91608 + ("jsconv", Stable, &["neon"]), // FEAT_LOR ("lor", Stable, &[]), // FEAT_LSE From 6b96a60611c5edaa107b109b5a50e58a64a33fc2 Mon Sep 17 00:00:00 2001 From: Caleb Zulawski Date: Sun, 4 Aug 2024 15:32:17 -0400 Subject: [PATCH 24/31] Add implied features to non-target-feature functions --- compiler/rustc_codegen_llvm/src/llvm_util.rs | 26 ++++++++++++++++---- tests/codegen/target-feature-overrides.rs | 6 ++--- tests/codegen/tied-features-strength.rs | 2 +- 3 files changed, 25 insertions(+), 9 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs index 10cba179c7564..1a80824a3b70d 100644 --- a/compiler/rustc_codegen_llvm/src/llvm_util.rs +++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs @@ -8,6 +8,7 @@ use libc::c_int; use rustc_codegen_ssa::base::wants_wasm_eh; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::small_c_str::SmallCStr; +use rustc_data_structures::unord::UnordSet; use rustc_fs_util::path_to_c_string; use rustc_middle::bug; use rustc_session::config::{PrintKind, PrintRequest}; @@ -553,11 +554,26 @@ pub(crate) fn global_llvm_features( let supported_features = sess.target.supported_target_features(); let (llvm_major, _, _) = get_version(); let mut featsmap = FxHashMap::default(); - let feats = sess - .opts - .cg - .target_feature - .split(',') + + // insert implied features + let mut all_rust_features = vec![]; + for feature in sess.opts.cg.target_feature.split(',') { + match feature.strip_prefix('+') { + Some(feature) => all_rust_features.extend( + UnordSet::from( + sess.target + .implied_target_features(std::iter::once(Symbol::intern(feature))), + ) + .to_sorted_stable_ord() + .iter() + .map(|s| format!("+{}", s.as_str())), + ), + _ => all_rust_features.push(feature.to_string()), + } + } + + let feats = all_rust_features + .iter() .filter_map(|s| { let enable_disable = match s.chars().next() { None => return None, diff --git a/tests/codegen/target-feature-overrides.rs b/tests/codegen/target-feature-overrides.rs index 5b7489c96cf97..f38a1ae72de5d 100644 --- a/tests/codegen/target-feature-overrides.rs +++ b/tests/codegen/target-feature-overrides.rs @@ -1,7 +1,7 @@ //@ revisions: COMPAT INCOMPAT //@ needs-llvm-components: x86 //@ compile-flags: --target=x86_64-unknown-linux-gnu -Copt-level=3 -//@ [COMPAT] compile-flags: -Ctarget-feature=+avx2,+avx +//@ [COMPAT] compile-flags: -Ctarget-feature=+avx2 //@ [INCOMPAT] compile-flags: -Ctarget-feature=-avx2,-avx // See also tests/assembly/target-feature-multiple.rs @@ -39,8 +39,8 @@ pub unsafe fn banana() -> u32 { } // CHECK: attributes [[APPLEATTRS]] -// COMPAT-SAME: "target-features"="+avx2,+avx,{{.*}}" +// COMPAT-SAME: "target-features"="+avx,+avx2,{{.*}}" // INCOMPAT-SAME: "target-features"="-avx2,-avx,+avx,{{.*}}" // CHECK: attributes [[BANANAATTRS]] -// COMPAT-SAME: "target-features"="+avx2,+avx,{{.*}}" +// COMPAT-SAME: "target-features"="+avx,+avx2,{{.*}}" // INCOMPAT-SAME: "target-features"="-avx2,-avx" diff --git a/tests/codegen/tied-features-strength.rs b/tests/codegen/tied-features-strength.rs index 7f0805bc1b435..1b4596ae2cb57 100644 --- a/tests/codegen/tied-features-strength.rs +++ b/tests/codegen/tied-features-strength.rs @@ -8,7 +8,7 @@ // is LLVM-14 we can remove the optional regex matching for this feature. //@ [ENABLE_SVE] compile-flags: -C target-feature=+sve -Copt-level=0 -// ENABLE_SVE: attributes #0 = { {{.*}} "target-features"="{{((\+outline-atomics,?)|(\+v8a,?)?|(\+sve,?)|(\+neon,?))*}}" } +// ENABLE_SVE: attributes #0 = { {{.*}} "target-features"="{{((\+outline-atomics,?)|(\+v8a,?)?|(\+sve,?)|(\+neon,?)|(\+fp-armv8,?))*}}" } //@ [DISABLE_SVE] compile-flags: -C target-feature=-sve -Copt-level=0 // DISABLE_SVE: attributes #0 = { {{.*}} "target-features"="{{((\+outline-atomics,?)|(\+v8a,?)?|(-sve,?)|(\+neon,?))*}}" } From 83276f568032f14b1af7e5cd9f7d928734af8d09 Mon Sep 17 00:00:00 2001 From: Caleb Zulawski Date: Sun, 4 Aug 2024 23:51:59 -0400 Subject: [PATCH 25/31] Hide implicit target features from diagnostics when possible --- compiler/rustc_codegen_gcc/src/attributes.rs | 2 +- compiler/rustc_codegen_llvm/src/attributes.rs | 2 +- .../rustc_codegen_ssa/src/target_features.rs | 28 ++++++++++++++----- .../rustc_const_eval/src/interpret/call.rs | 13 +++++++-- .../src/middle/codegen_fn_attrs.rs | 11 +++++++- .../rustc_mir_build/src/check_unsafety.rs | 26 +++++++++++++---- compiler/rustc_mir_transform/src/inline.rs | 4 ++- .../simd_feature_flag_difference.stderr | 4 +-- .../fail/function_calls/target_feature.rs | 2 +- .../fail/function_calls/target_feature.stderr | 4 +-- .../pass/shims/x86/intrinsics-x86-aes-vaes.rs | 2 +- .../pass/shims/x86/intrinsics-x86-avx.rs | 2 +- .../pass/shims/x86/intrinsics-x86-avx2.rs | 2 +- .../pass/shims/x86/intrinsics-x86-avx512.rs | 2 +- .../const-eval/const_fn_target_feature.stderr | 2 +- .../safe-calls.stderr | 26 ++++++++--------- 16 files changed, 89 insertions(+), 43 deletions(-) diff --git a/compiler/rustc_codegen_gcc/src/attributes.rs b/compiler/rustc_codegen_gcc/src/attributes.rs index e521551304ef8..5fdf2680aac88 100644 --- a/compiler/rustc_codegen_gcc/src/attributes.rs +++ b/compiler/rustc_codegen_gcc/src/attributes.rs @@ -75,7 +75,7 @@ pub fn from_fn_attrs<'gcc, 'tcx>( let function_features = codegen_fn_attrs .target_features .iter() - .map(|features| features.as_str()) + .map(|features| features.name.as_str()) .collect::>(); if let Some(features) = check_tied_features( diff --git a/compiler/rustc_codegen_llvm/src/attributes.rs b/compiler/rustc_codegen_llvm/src/attributes.rs index ad38814a68b6d..876f05c6277c1 100644 --- a/compiler/rustc_codegen_llvm/src/attributes.rs +++ b/compiler/rustc_codegen_llvm/src/attributes.rs @@ -496,7 +496,7 @@ pub fn from_fn_attrs<'ll, 'tcx>( to_add.extend(tune_cpu_attr(cx)); let function_features = - codegen_fn_attrs.target_features.iter().map(|f| f.as_str()).collect::>(); + codegen_fn_attrs.target_features.iter().map(|f| f.name.as_str()).collect::>(); if let Some(f) = llvm_util::check_tied_features( cx.tcx.sess, diff --git a/compiler/rustc_codegen_ssa/src/target_features.rs b/compiler/rustc_codegen_ssa/src/target_features.rs index c84b844cd0401..145b1ece23007 100644 --- a/compiler/rustc_codegen_ssa/src/target_features.rs +++ b/compiler/rustc_codegen_ssa/src/target_features.rs @@ -6,6 +6,7 @@ use rustc_errors::Applicability; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LocalDefId, LOCAL_CRATE}; use rustc_middle::bug; +use rustc_middle::middle::codegen_fn_attrs::TargetFeature; use rustc_middle::query::Providers; use rustc_middle::ty::TyCtxt; use rustc_session::parse::feature_err; @@ -18,7 +19,7 @@ pub fn from_target_feature( tcx: TyCtxt<'_>, attr: &ast::Attribute, supported_target_features: &UnordMap>, - target_features: &mut Vec, + target_features: &mut Vec, ) { let Some(list) = attr.meta_item_list() else { return }; let bad_item = |span| { @@ -99,14 +100,27 @@ pub fn from_target_feature( })); } - // Add both explicit and implied target features, using a set to deduplicate - let mut target_features_set = UnordSet::new(); + // Add explicit features + target_features.extend( + added_target_features.iter().copied().map(|name| TargetFeature { name, implied: false }), + ); + + // Add implied features + let mut implied_target_features = UnordSet::new(); for feature in added_target_features.iter() { - target_features_set + implied_target_features .extend_unord(tcx.implied_target_features(*feature).clone().into_items()); } - target_features_set.extend(added_target_features); - target_features.extend(target_features_set.into_sorted_stable_ord()) + for feature in added_target_features.iter() { + implied_target_features.remove(feature); + } + target_features.extend( + implied_target_features + .into_sorted_stable_ord() + .iter() + .copied() + .map(|name| TargetFeature { name, implied: true }), + ) } /// Computes the set of target features used in a function for the purposes of @@ -115,7 +129,7 @@ fn asm_target_features(tcx: TyCtxt<'_>, did: DefId) -> &FxIndexSet { let mut target_features = tcx.sess.unstable_target_features.clone(); if tcx.def_kind(did).has_codegen_attrs() { let attrs = tcx.codegen_fn_attrs(did); - target_features.extend(&attrs.target_features); + target_features.extend(attrs.target_features.iter().map(|feature| feature.name)); match attrs.instruction_set { None => {} Some(InstructionSetAttr::ArmA32) => { diff --git a/compiler/rustc_const_eval/src/interpret/call.rs b/compiler/rustc_const_eval/src/interpret/call.rs index 2c5147678e8cb..e5c195f08d71b 100644 --- a/compiler/rustc_const_eval/src/interpret/call.rs +++ b/compiler/rustc_const_eval/src/interpret/call.rs @@ -317,19 +317,26 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { && attrs .target_features .iter() - .any(|feature| !self.tcx.sess.target_features.contains(feature)) + .any(|feature| !self.tcx.sess.target_features.contains(&feature.name)) { + // Don't include implicit features in the error, unless only implicit features are + // missing. This should be rare, because it can only happen when an implicit feature + // is disabled, e.g. `+avx2,-avx` + let missing_explicit_features = attrs.target_features.iter().any(|feature| { + !feature.implied && !self.tcx.sess.target_features.contains(&feature.name) + }); throw_ub_custom!( fluent::const_eval_unavailable_target_features_for_fn, unavailable_feats = attrs .target_features .iter() - .filter(|&feature| !self.tcx.sess.target_features.contains(feature)) + .filter(|&feature| !(missing_explicit_features && feature.implied) + && !self.tcx.sess.target_features.contains(&feature.name)) .fold(String::new(), |mut s, feature| { if !s.is_empty() { s.push_str(", "); } - s.push_str(feature.as_str()); + s.push_str(feature.name.as_str()); s }), ); diff --git a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs index ff6a3a9c12d36..b7d290e58d22b 100644 --- a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs +++ b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs @@ -28,7 +28,7 @@ pub struct CodegenFnAttrs { pub link_ordinal: Option, /// The `#[target_feature(enable = "...")]` attribute and the enabled /// features (only enabled features are supported right now). - pub target_features: Vec, + pub target_features: Vec, /// The `#[linkage = "..."]` attribute on Rust-defined items and the value we found. pub linkage: Option, /// The `#[linkage = "..."]` attribute on foreign items and the value we found. @@ -51,6 +51,15 @@ pub struct CodegenFnAttrs { pub patchable_function_entry: Option, } +#[derive(Copy, Clone, Debug, TyEncodable, TyDecodable, HashStable)] +pub struct TargetFeature { + /// The name of the target feature (e.g. "avx") + pub name: Symbol, + /// The feature is implied by another feature, rather than explicitly added by the + /// `#[target_feature]` attribute + pub implied: bool, +} + #[derive(Copy, Clone, Debug, TyEncodable, TyDecodable, HashStable)] pub struct PatchableFunctionEntry { /// Nops to prepend to the function diff --git a/compiler/rustc_mir_build/src/check_unsafety.rs b/compiler/rustc_mir_build/src/check_unsafety.rs index 48018fcaa36df..0b4f0632f2b36 100644 --- a/compiler/rustc_mir_build/src/check_unsafety.rs +++ b/compiler/rustc_mir_build/src/check_unsafety.rs @@ -5,6 +5,7 @@ use std::ops::Bound; use rustc_errors::DiagArgValue; use rustc_hir::def::DefKind; use rustc_hir::{self as hir, BindingMode, ByRef, HirId, Mutability}; +use rustc_middle::middle::codegen_fn_attrs::TargetFeature; use rustc_middle::mir::BorrowKind; use rustc_middle::span_bug; use rustc_middle::thir::visit::Visitor; @@ -31,7 +32,7 @@ struct UnsafetyVisitor<'a, 'tcx> { safety_context: SafetyContext, /// The `#[target_feature]` attributes of the body. Used for checking /// calls to functions with `#[target_feature]` (RFC 2396). - body_target_features: &'tcx [Symbol], + body_target_features: &'tcx [TargetFeature], /// When inside the LHS of an assignment to a field, this is the type /// of the LHS and the span of the assignment expression. assignment_info: Option>, @@ -442,14 +443,29 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> { // is_like_wasm check in hir_analysis/src/collect.rs let callee_features = &self.tcx.codegen_fn_attrs(func_did).target_features; if !self.tcx.sess.target.options.is_like_wasm - && !callee_features - .iter() - .all(|feature| self.body_target_features.contains(feature)) + && !callee_features.iter().all(|feature| { + self.body_target_features.iter().any(|f| f.name == feature.name) + }) { + // Don't include implicit features in the error, unless only implicit + // features are missing. + let missing_explicit_features = callee_features.iter().any(|feature| { + !feature.implied + && !self.body_target_features.iter().any(|body_feature| { + !feature.implied && body_feature.name == feature.name + }) + }); let missing: Vec<_> = callee_features .iter() .copied() - .filter(|feature| !self.body_target_features.contains(feature)) + .filter(|feature| { + !(missing_explicit_features && feature.implied) + && !self + .body_target_features + .iter() + .any(|body_feature| body_feature.name == feature.name) + }) + .map(|feature| feature.name) .collect(); let build_enabled = self .tcx diff --git a/compiler/rustc_mir_transform/src/inline.rs b/compiler/rustc_mir_transform/src/inline.rs index f30732e6aaf3b..0f012242c3738 100644 --- a/compiler/rustc_mir_transform/src/inline.rs +++ b/compiler/rustc_mir_transform/src/inline.rs @@ -479,7 +479,9 @@ impl<'tcx> Inliner<'tcx> { return Err("incompatible instruction set"); } - if callee_attrs.target_features != self.codegen_fn_attrs.target_features { + let callee_feature_names = callee_attrs.target_features.iter().map(|f| f.name); + let this_feature_names = self.codegen_fn_attrs.target_features.iter().map(|f| f.name); + if callee_feature_names.ne(this_feature_names) { // In general it is not correct to inline a callee with target features that are a // subset of the caller. This is because the callee might contain calls, and the ABI of // those calls depends on the target features of the surrounding function. By moving a diff --git a/src/tools/miri/tests/fail/function_calls/simd_feature_flag_difference.stderr b/src/tools/miri/tests/fail/function_calls/simd_feature_flag_difference.stderr index 8ebd5d2726ffd..2544421c7e8c7 100644 --- a/src/tools/miri/tests/fail/function_calls/simd_feature_flag_difference.stderr +++ b/src/tools/miri/tests/fail/function_calls/simd_feature_flag_difference.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: calling a function that requires unavailable target features: avx, sse3, sse4.1, sse4.2, ssse3 +error: Undefined Behavior: calling a function that requires unavailable target features: avx --> $DIR/simd_feature_flag_difference.rs:LL:CC | LL | unsafe { foo(0.0, x) } - | ^^^^^^^^^^^ calling a function that requires unavailable target features: avx, sse3, sse4.1, sse4.2, ssse3 + | ^^^^^^^^^^^ calling a function that requires unavailable target features: avx | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information diff --git a/src/tools/miri/tests/fail/function_calls/target_feature.rs b/src/tools/miri/tests/fail/function_calls/target_feature.rs index 713d7c16f4c25..84e01eb4803fe 100644 --- a/src/tools/miri/tests/fail/function_calls/target_feature.rs +++ b/src/tools/miri/tests/fail/function_calls/target_feature.rs @@ -4,7 +4,7 @@ fn main() { assert!(!is_x86_feature_detected!("ssse3")); unsafe { - ssse3_fn(); //~ ERROR: calling a function that requires unavailable target features: sse3, ssse3 + ssse3_fn(); //~ ERROR: calling a function that requires unavailable target features: ssse3 } } diff --git a/src/tools/miri/tests/fail/function_calls/target_feature.stderr b/src/tools/miri/tests/fail/function_calls/target_feature.stderr index 74a862c7b681c..4d3cf6e9d3bc0 100644 --- a/src/tools/miri/tests/fail/function_calls/target_feature.stderr +++ b/src/tools/miri/tests/fail/function_calls/target_feature.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: calling a function that requires unavailable target features: sse3, ssse3 +error: Undefined Behavior: calling a function that requires unavailable target features: ssse3 --> $DIR/target_feature.rs:LL:CC | LL | ssse3_fn(); - | ^^^^^^^^^^ calling a function that requires unavailable target features: sse3, ssse3 + | ^^^^^^^^^^ calling a function that requires unavailable target features: ssse3 | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information diff --git a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-aes-vaes.rs b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-aes-vaes.rs index 13ebe56146381..7363c75361779 100644 --- a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-aes-vaes.rs +++ b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-aes-vaes.rs @@ -7,7 +7,7 @@ //@ignore-target-s390x //@ignore-target-thumbv7em //@ignore-target-wasm32 -//@compile-flags: -C target-feature=+aes,+vaes,+avx512f,+sse4.2 +//@compile-flags: -C target-feature=+aes,+vaes,+avx512f #![feature(avx512_target_feature, stdarch_x86_avx512)] diff --git a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-avx.rs b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-avx.rs index 18693a2a97d11..728f57d48f17e 100644 --- a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-avx.rs +++ b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-avx.rs @@ -7,7 +7,7 @@ //@ignore-target-s390x //@ignore-target-thumbv7em //@ignore-target-wasm32 -//@compile-flags: -C target-feature=+avx,+sse4.2 +//@compile-flags: -C target-feature=+avx #[cfg(target_arch = "x86")] use std::arch::x86::*; diff --git a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-avx2.rs b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-avx2.rs index f42ff6dfb51b2..80d125bb85650 100644 --- a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-avx2.rs +++ b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-avx2.rs @@ -7,7 +7,7 @@ //@ignore-target-s390x //@ignore-target-thumbv7em //@ignore-target-wasm32 -//@compile-flags: -C target-feature=+avx2,+sse4.2 +//@compile-flags: -C target-feature=+avx2 #[cfg(target_arch = "x86")] use std::arch::x86::*; diff --git a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-avx512.rs b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-avx512.rs index 7d25854ccc075..66bfcb20f1c99 100644 --- a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-avx512.rs +++ b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-avx512.rs @@ -7,7 +7,7 @@ //@ignore-target-s390x //@ignore-target-thumbv7em //@ignore-target-wasm32 -//@compile-flags: -C target-feature=+avx512f,+avx512vl,+avx512bitalg,+avx512vpopcntdq,+sse4.2 +//@compile-flags: -C target-feature=+avx512f,+avx512vl,+avx512bitalg,+avx512vpopcntdq #![feature(avx512_target_feature)] #![feature(stdarch_x86_avx512)] diff --git a/tests/ui/consts/const-eval/const_fn_target_feature.stderr b/tests/ui/consts/const-eval/const_fn_target_feature.stderr index ad40d733546af..d3a00b57ebb50 100644 --- a/tests/ui/consts/const-eval/const_fn_target_feature.stderr +++ b/tests/ui/consts/const-eval/const_fn_target_feature.stderr @@ -2,7 +2,7 @@ error[E0080]: evaluation of constant value failed --> $DIR/const_fn_target_feature.rs:11:24 | LL | const B: () = unsafe { avx2_fn() }; - | ^^^^^^^^^ calling a function that requires unavailable target features: avx, avx2, sse4.1, sse4.2 + | ^^^^^^^^^ calling a function that requires unavailable target features: avx2 error: aborting due to 1 previous error diff --git a/tests/ui/rfcs/rfc-2396-target_feature-11/safe-calls.stderr b/tests/ui/rfcs/rfc-2396-target_feature-11/safe-calls.stderr index c2227f8e84783..1ddf05b40a606 100644 --- a/tests/ui/rfcs/rfc-2396-target_feature-11/safe-calls.stderr +++ b/tests/ui/rfcs/rfc-2396-target_feature-11/safe-calls.stderr @@ -4,8 +4,8 @@ error[E0133]: call to function `sse2` with `#[target_feature]` is unsafe and req LL | sse2(); | ^^^^^^ call to function with `#[target_feature]` | - = help: in order for the call to be safe, the context requires the following additional target features: sse and sse2 - = note: the sse and sse2 target features being enabled in the build configuration does not remove the requirement to list them in `#[target_feature]` + = help: in order for the call to be safe, the context requires the following additional target feature: sse2 + = note: the sse2 target feature being enabled in the build configuration does not remove the requirement to list it in `#[target_feature]` error[E0133]: call to function `avx_bmi2` with `#[target_feature]` is unsafe and requires unsafe function or block --> $DIR/safe-calls.rs:29:5 @@ -13,8 +13,7 @@ error[E0133]: call to function `avx_bmi2` with `#[target_feature]` is unsafe and LL | avx_bmi2(); | ^^^^^^^^^^ call to function with `#[target_feature]` | - = help: in order for the call to be safe, the context requires the following additional target features: avx, sse, sse2, sse3, sse4.1, sse4.2, ssse3, and bmi2 - = note: the sse and sse2 target features being enabled in the build configuration does not remove the requirement to list them in `#[target_feature]` + = help: in order for the call to be safe, the context requires the following additional target features: avx and bmi2 error[E0133]: call to function `Quux::avx_bmi2` with `#[target_feature]` is unsafe and requires unsafe function or block --> $DIR/safe-calls.rs:31:5 @@ -22,8 +21,7 @@ error[E0133]: call to function `Quux::avx_bmi2` with `#[target_feature]` is unsa LL | Quux.avx_bmi2(); | ^^^^^^^^^^^^^^^ call to function with `#[target_feature]` | - = help: in order for the call to be safe, the context requires the following additional target features: avx, sse, sse2, sse3, sse4.1, sse4.2, ssse3, and bmi2 - = note: the sse and sse2 target features being enabled in the build configuration does not remove the requirement to list them in `#[target_feature]` + = help: in order for the call to be safe, the context requires the following additional target features: avx and bmi2 error[E0133]: call to function `avx_bmi2` with `#[target_feature]` is unsafe and requires unsafe function or block --> $DIR/safe-calls.rs:38:5 @@ -31,7 +29,7 @@ error[E0133]: call to function `avx_bmi2` with `#[target_feature]` is unsafe and LL | avx_bmi2(); | ^^^^^^^^^^ call to function with `#[target_feature]` | - = help: in order for the call to be safe, the context requires the following additional target features: avx, sse3, sse4.1, sse4.2, ssse3, and bmi2 + = help: in order for the call to be safe, the context requires the following additional target features: avx and bmi2 error[E0133]: call to function `Quux::avx_bmi2` with `#[target_feature]` is unsafe and requires unsafe function or block --> $DIR/safe-calls.rs:40:5 @@ -39,7 +37,7 @@ error[E0133]: call to function `Quux::avx_bmi2` with `#[target_feature]` is unsa LL | Quux.avx_bmi2(); | ^^^^^^^^^^^^^^^ call to function with `#[target_feature]` | - = help: in order for the call to be safe, the context requires the following additional target features: avx, sse3, sse4.1, sse4.2, ssse3, and bmi2 + = help: in order for the call to be safe, the context requires the following additional target features: avx and bmi2 error[E0133]: call to function `avx_bmi2` with `#[target_feature]` is unsafe and requires unsafe function or block --> $DIR/safe-calls.rs:47:5 @@ -63,8 +61,8 @@ error[E0133]: call to function `sse2` with `#[target_feature]` is unsafe and req LL | const _: () = sse2(); | ^^^^^^ call to function with `#[target_feature]` | - = help: in order for the call to be safe, the context requires the following additional target features: sse and sse2 - = note: the sse and sse2 target features being enabled in the build configuration does not remove the requirement to list them in `#[target_feature]` + = help: in order for the call to be safe, the context requires the following additional target feature: sse2 + = note: the sse2 target feature being enabled in the build configuration does not remove the requirement to list it in `#[target_feature]` error[E0133]: call to function `sse2_and_fxsr` with `#[target_feature]` is unsafe and requires unsafe function or block --> $DIR/safe-calls.rs:64:15 @@ -72,8 +70,8 @@ error[E0133]: call to function `sse2_and_fxsr` with `#[target_feature]` is unsaf LL | const _: () = sse2_and_fxsr(); | ^^^^^^^^^^^^^^^ call to function with `#[target_feature]` | - = help: in order for the call to be safe, the context requires the following additional target features: sse, sse2, and fxsr - = note: the fxsr, sse, and sse2 target features being enabled in the build configuration does not remove the requirement to list them in `#[target_feature]` + = help: in order for the call to be safe, the context requires the following additional target features: sse2 and fxsr + = note: the fxsr and sse2 target features being enabled in the build configuration does not remove the requirement to list them in `#[target_feature]` error[E0133]: call to function `sse2` with `#[target_feature]` is unsafe and requires unsafe block --> $DIR/safe-calls.rs:69:5 @@ -82,8 +80,8 @@ LL | sse2(); | ^^^^^^ call to function with `#[target_feature]` | = note: for more information, see issue #71668 - = help: in order for the call to be safe, the context requires the following additional target features: sse and sse2 - = note: the sse and sse2 target features being enabled in the build configuration does not remove the requirement to list them in `#[target_feature]` + = help: in order for the call to be safe, the context requires the following additional target feature: sse2 + = note: the sse2 target feature being enabled in the build configuration does not remove the requirement to list it in `#[target_feature]` note: an unsafe function restricts its caller, but its body is safe by default --> $DIR/safe-calls.rs:68:1 | From 0b98a0c72769c2549827ec2320beb5478ca3c335 Mon Sep 17 00:00:00 2001 From: Caleb Zulawski Date: Mon, 5 Aug 2024 10:31:03 -0400 Subject: [PATCH 26/31] Fix typo Co-authored-by: Amanieu d'Antras --- compiler/rustc_target/src/target_features.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_target/src/target_features.rs b/compiler/rustc_target/src/target_features.rs index 746926308b6b4..da66ba270b33c 100644 --- a/compiler/rustc_target/src/target_features.rs +++ b/compiler/rustc_target/src/target_features.rs @@ -335,7 +335,7 @@ const RISCV_ALLOWED_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ ("zkne", Stable, &[]), ("zknh", Stable, &[]), ("zkr", Stable, &[]), - ("zks", Stable, &["zbkb", "bzkc", "zbkx", "zksed", "zksh"]), + ("zks", Stable, &["zbkb", "zbkc", "zbkx", "zksed", "zksh"]), ("zksed", Stable, &[]), ("zksh", Stable, &[]), ("zkt", Stable, &[]), From 8818c9552821721e4be5c19832b4e3ac64090feb Mon Sep 17 00:00:00 2001 From: Caleb Zulawski Date: Tue, 6 Aug 2024 00:35:32 -0400 Subject: [PATCH 27/31] Disallow enabling features without their implied features --- compiler/rustc_codegen_llvm/src/llvm_util.rs | 6 ++++-- compiler/rustc_codegen_ssa/src/target_features.rs | 8 ++++---- compiler/rustc_const_eval/src/interpret/call.rs | 8 +------- compiler/rustc_middle/src/query/mod.rs | 2 +- compiler/rustc_mir_build/src/check_unsafety.rs | 10 +--------- 5 files changed, 11 insertions(+), 23 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs index 1a80824a3b70d..9fd8ca43789dd 100644 --- a/compiler/rustc_codegen_llvm/src/llvm_util.rs +++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs @@ -277,7 +277,7 @@ pub fn check_tied_features( /// Used to generate cfg variables and apply features /// Must express features in the way Rust understands them pub fn target_features(sess: &Session, allow_unstable: bool) -> Vec { - let mut features = FxHashSet::default(); + let mut features = vec![]; // Add base features for the target let target_machine = create_informational_target_machine(sess, true); @@ -313,7 +313,9 @@ pub fn target_features(sess: &Session, allow_unstable: bool) -> Vec { if enabled { features.extend(sess.target.implied_target_features(std::iter::once(feature))); } else { - features.remove(&feature); + features.retain(|f| { + !sess.target.implied_target_features(std::iter::once(*f)).contains(&feature) + }); } } diff --git a/compiler/rustc_codegen_ssa/src/target_features.rs b/compiler/rustc_codegen_ssa/src/target_features.rs index 145b1ece23007..cf8f7fa25d856 100644 --- a/compiler/rustc_codegen_ssa/src/target_features.rs +++ b/compiler/rustc_codegen_ssa/src/target_features.rs @@ -1,7 +1,7 @@ use rustc_ast::ast; use rustc_attr::InstructionSetAttr; use rustc_data_structures::fx::FxIndexSet; -use rustc_data_structures::unord::{ExtendUnord, UnordMap, UnordSet}; +use rustc_data_structures::unord::{UnordMap, UnordSet}; use rustc_errors::Applicability; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LocalDefId, LOCAL_CRATE}; @@ -108,8 +108,7 @@ pub fn from_target_feature( // Add implied features let mut implied_target_features = UnordSet::new(); for feature in added_target_features.iter() { - implied_target_features - .extend_unord(tcx.implied_target_features(*feature).clone().into_items()); + implied_target_features.extend(tcx.implied_target_features(*feature).clone()); } for feature in added_target_features.iter() { implied_target_features.remove(feature); @@ -179,7 +178,8 @@ pub(crate) fn provide(providers: &mut Providers) { } }, implied_target_features: |tcx, feature| { - tcx.sess.target.implied_target_features(std::iter::once(feature)).into() + UnordSet::from(tcx.sess.target.implied_target_features(std::iter::once(feature))) + .into_sorted_stable_ord() }, asm_target_features, ..*providers diff --git a/compiler/rustc_const_eval/src/interpret/call.rs b/compiler/rustc_const_eval/src/interpret/call.rs index e5c195f08d71b..b5f3d07d90b35 100644 --- a/compiler/rustc_const_eval/src/interpret/call.rs +++ b/compiler/rustc_const_eval/src/interpret/call.rs @@ -319,18 +319,12 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { .iter() .any(|feature| !self.tcx.sess.target_features.contains(&feature.name)) { - // Don't include implicit features in the error, unless only implicit features are - // missing. This should be rare, because it can only happen when an implicit feature - // is disabled, e.g. `+avx2,-avx` - let missing_explicit_features = attrs.target_features.iter().any(|feature| { - !feature.implied && !self.tcx.sess.target_features.contains(&feature.name) - }); throw_ub_custom!( fluent::const_eval_unavailable_target_features_for_fn, unavailable_feats = attrs .target_features .iter() - .filter(|&feature| !(missing_explicit_features && feature.implied) + .filter(|&feature| !feature.implied && !self.tcx.sess.target_features.contains(&feature.name)) .fold(String::new(), |mut s, feature| { if !s.is_empty() { diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index b6a2943265034..5b114c9515c19 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -2183,7 +2183,7 @@ rustc_queries! { desc { "looking up supported target features" } } - query implied_target_features(feature: Symbol) -> &'tcx UnordSet { + query implied_target_features(feature: Symbol) -> &'tcx Vec { arena_cache eval_always desc { "looking up implied target features" } diff --git a/compiler/rustc_mir_build/src/check_unsafety.rs b/compiler/rustc_mir_build/src/check_unsafety.rs index 0b4f0632f2b36..54a4204da71e8 100644 --- a/compiler/rustc_mir_build/src/check_unsafety.rs +++ b/compiler/rustc_mir_build/src/check_unsafety.rs @@ -447,19 +447,11 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> { self.body_target_features.iter().any(|f| f.name == feature.name) }) { - // Don't include implicit features in the error, unless only implicit - // features are missing. - let missing_explicit_features = callee_features.iter().any(|feature| { - !feature.implied - && !self.body_target_features.iter().any(|body_feature| { - !feature.implied && body_feature.name == feature.name - }) - }); let missing: Vec<_> = callee_features .iter() .copied() .filter(|feature| { - !(missing_explicit_features && feature.implied) + !feature.implied && !self .body_target_features .iter() From d1d21ede82614f2b575cb16bcbabe75183721740 Mon Sep 17 00:00:00 2001 From: John Paul Adrian Glaubitz Date: Wed, 7 Aug 2024 09:56:28 +0200 Subject: [PATCH 28/31] rustc_codegen_ssa: Set architecture for object crate for 32-bit SPARC The object crate was recently updated to recognize the 32-bit SPARC ELF targets EM_SPARC and EM_SPARC32PLUS, so the proper architecture for 32-bit SPARC can now be set in rustc_codegen_ssa. --- compiler/rustc_codegen_ssa/src/back/metadata.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/compiler/rustc_codegen_ssa/src/back/metadata.rs b/compiler/rustc_codegen_ssa/src/back/metadata.rs index 19394029c949a..9b5a797ad5106 100644 --- a/compiler/rustc_codegen_ssa/src/back/metadata.rs +++ b/compiler/rustc_codegen_ssa/src/back/metadata.rs @@ -208,6 +208,7 @@ pub(crate) fn create_object_file(sess: &Session) -> Option (Architecture::PowerPc64, None), "riscv32" => (Architecture::Riscv32, None), "riscv64" => (Architecture::Riscv64, None), + "sparc" => (Architecture::Sparc32Plus, None), "sparc64" => (Architecture::Sparc64, None), "avr" => (Architecture::Avr, None), "msp430" => (Architecture::Msp430, None), From c1897960c016d7296b945175b0fef9546f6326a6 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 7 Aug 2024 15:29:00 +0200 Subject: [PATCH 29/31] unused_parens: do not lint against parens around &raw --- compiler/rustc_lint/src/unused.rs | 7 ++ tests/ui/lint/lint-unnecessary-parens.fixed | 8 ++ tests/ui/lint/lint-unnecessary-parens.rs | 8 ++ tests/ui/lint/lint-unnecessary-parens.stderr | 88 +++++++++++++------- 4 files changed, 79 insertions(+), 32 deletions(-) diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs index 795333224ba00..e2c05129ee238 100644 --- a/compiler/rustc_lint/src/unused.rs +++ b/compiler/rustc_lint/src/unused.rs @@ -675,6 +675,13 @@ trait UnusedDelimLint { return true; } + // Do not lint against parentheses around `&raw [const|mut] expr`. + // These parentheses will have to be added e.g. when calling a method on the result of this + // expression, and we want to avoid churn wrt adding and removing parentheses. + if matches!(inner.kind, ast::ExprKind::AddrOf(ast::BorrowKind::Raw, ..)) { + return true; + } + // Check if LHS needs parens to prevent false-positives in cases like // `fn x() -> u8 { ({ 0 } + 1) }`. // diff --git a/tests/ui/lint/lint-unnecessary-parens.fixed b/tests/ui/lint/lint-unnecessary-parens.fixed index 760897c5143f1..089aa1b7ab7de 100644 --- a/tests/ui/lint/lint-unnecessary-parens.fixed +++ b/tests/ui/lint/lint-unnecessary-parens.fixed @@ -1,6 +1,7 @@ //@ run-rustfix #![deny(unused_parens)] +#![feature(raw_ref_op)] #![allow(while_true)] // for rustfix #[derive(Eq, PartialEq)] @@ -125,4 +126,11 @@ fn main() { // FIXME: false positive. This parenthesis is required. unit! {} - One //~ ERROR unnecessary parentheses around block return value }; + + // Do *not* lint around `&raw` (but do lint when `&` creates a reference). + let mut x = 0; + let _r = &x; //~ ERROR unnecessary parentheses + let _r = &mut x; //~ ERROR unnecessary parentheses + let _r = (&raw const x); + let _r = (&raw mut x); } diff --git a/tests/ui/lint/lint-unnecessary-parens.rs b/tests/ui/lint/lint-unnecessary-parens.rs index 7cbaac8ae5403..dc77ee003523e 100644 --- a/tests/ui/lint/lint-unnecessary-parens.rs +++ b/tests/ui/lint/lint-unnecessary-parens.rs @@ -1,6 +1,7 @@ //@ run-rustfix #![deny(unused_parens)] +#![feature(raw_ref_op)] #![allow(while_true)] // for rustfix #[derive(Eq, PartialEq)] @@ -125,4 +126,11 @@ fn main() { // FIXME: false positive. This parenthesis is required. (unit! {} - One) //~ ERROR unnecessary parentheses around block return value }; + + // Do *not* lint around `&raw` (but do lint when `&` creates a reference). + let mut x = 0; + let _r = (&x); //~ ERROR unnecessary parentheses + let _r = (&mut x); //~ ERROR unnecessary parentheses + let _r = (&raw const x); + let _r = (&raw mut x); } diff --git a/tests/ui/lint/lint-unnecessary-parens.stderr b/tests/ui/lint/lint-unnecessary-parens.stderr index 755dd5fc3094b..c9422437a9fcd 100644 --- a/tests/ui/lint/lint-unnecessary-parens.stderr +++ b/tests/ui/lint/lint-unnecessary-parens.stderr @@ -1,5 +1,5 @@ error: unnecessary parentheses around `return` value - --> $DIR/lint-unnecessary-parens.rs:13:12 + --> $DIR/lint-unnecessary-parens.rs:14:12 | LL | return (1); | ^ ^ @@ -16,7 +16,7 @@ LL + return 1; | error: unnecessary parentheses around `return` value - --> $DIR/lint-unnecessary-parens.rs:16:12 + --> $DIR/lint-unnecessary-parens.rs:17:12 | LL | return (X { y }); | ^ ^ @@ -28,7 +28,7 @@ LL + return X { y }; | error: unnecessary parentheses around type - --> $DIR/lint-unnecessary-parens.rs:19:46 + --> $DIR/lint-unnecessary-parens.rs:20:46 | LL | pub fn unused_parens_around_return_type() -> (u32) { | ^ ^ @@ -40,7 +40,7 @@ LL + pub fn unused_parens_around_return_type() -> u32 { | error: unnecessary parentheses around block return value - --> $DIR/lint-unnecessary-parens.rs:25:9 + --> $DIR/lint-unnecessary-parens.rs:26:9 | LL | (5) | ^ ^ @@ -52,7 +52,7 @@ LL + 5 | error: unnecessary parentheses around block return value - --> $DIR/lint-unnecessary-parens.rs:27:5 + --> $DIR/lint-unnecessary-parens.rs:28:5 | LL | (5) | ^ ^ @@ -64,7 +64,7 @@ LL + 5 | error: unnecessary parentheses around `if` condition - --> $DIR/lint-unnecessary-parens.rs:39:7 + --> $DIR/lint-unnecessary-parens.rs:40:7 | LL | if(true) {} | ^ ^ @@ -76,7 +76,7 @@ LL + if true {} | error: unnecessary parentheses around `while` condition - --> $DIR/lint-unnecessary-parens.rs:40:10 + --> $DIR/lint-unnecessary-parens.rs:41:10 | LL | while(true) {} | ^ ^ @@ -88,7 +88,7 @@ LL + while true {} | error: unnecessary parentheses around `for` iterator expression - --> $DIR/lint-unnecessary-parens.rs:41:13 + --> $DIR/lint-unnecessary-parens.rs:42:13 | LL | for _ in(e) {} | ^ ^ @@ -100,7 +100,7 @@ LL + for _ in e {} | error: unnecessary parentheses around `match` scrutinee expression - --> $DIR/lint-unnecessary-parens.rs:42:10 + --> $DIR/lint-unnecessary-parens.rs:43:10 | LL | match(1) { _ => ()} | ^ ^ @@ -112,7 +112,7 @@ LL + match 1 { _ => ()} | error: unnecessary parentheses around `return` value - --> $DIR/lint-unnecessary-parens.rs:43:11 + --> $DIR/lint-unnecessary-parens.rs:44:11 | LL | return(1); | ^ ^ @@ -124,7 +124,7 @@ LL + return 1; | error: unnecessary parentheses around assigned value - --> $DIR/lint-unnecessary-parens.rs:74:31 + --> $DIR/lint-unnecessary-parens.rs:75:31 | LL | pub const CONST_ITEM: usize = (10); | ^ ^ @@ -136,7 +136,7 @@ LL + pub const CONST_ITEM: usize = 10; | error: unnecessary parentheses around assigned value - --> $DIR/lint-unnecessary-parens.rs:75:33 + --> $DIR/lint-unnecessary-parens.rs:76:33 | LL | pub static STATIC_ITEM: usize = (10); | ^ ^ @@ -148,7 +148,7 @@ LL + pub static STATIC_ITEM: usize = 10; | error: unnecessary parentheses around function argument - --> $DIR/lint-unnecessary-parens.rs:79:9 + --> $DIR/lint-unnecessary-parens.rs:80:9 | LL | bar((true)); | ^ ^ @@ -160,7 +160,7 @@ LL + bar(true); | error: unnecessary parentheses around `if` condition - --> $DIR/lint-unnecessary-parens.rs:81:8 + --> $DIR/lint-unnecessary-parens.rs:82:8 | LL | if (true) {} | ^ ^ @@ -172,7 +172,7 @@ LL + if true {} | error: unnecessary parentheses around `while` condition - --> $DIR/lint-unnecessary-parens.rs:82:11 + --> $DIR/lint-unnecessary-parens.rs:83:11 | LL | while (true) {} | ^ ^ @@ -184,7 +184,7 @@ LL + while true {} | error: unnecessary parentheses around `match` scrutinee expression - --> $DIR/lint-unnecessary-parens.rs:83:11 + --> $DIR/lint-unnecessary-parens.rs:84:11 | LL | match (true) { | ^ ^ @@ -196,7 +196,7 @@ LL + match true { | error: unnecessary parentheses around `let` scrutinee expression - --> $DIR/lint-unnecessary-parens.rs:86:16 + --> $DIR/lint-unnecessary-parens.rs:87:16 | LL | if let 1 = (1) {} | ^ ^ @@ -208,7 +208,7 @@ LL + if let 1 = 1 {} | error: unnecessary parentheses around `let` scrutinee expression - --> $DIR/lint-unnecessary-parens.rs:87:19 + --> $DIR/lint-unnecessary-parens.rs:88:19 | LL | while let 1 = (2) {} | ^ ^ @@ -220,7 +220,7 @@ LL + while let 1 = 2 {} | error: unnecessary parentheses around method argument - --> $DIR/lint-unnecessary-parens.rs:103:24 + --> $DIR/lint-unnecessary-parens.rs:104:24 | LL | X { y: false }.foo((true)); | ^ ^ @@ -232,7 +232,7 @@ LL + X { y: false }.foo(true); | error: unnecessary parentheses around assigned value - --> $DIR/lint-unnecessary-parens.rs:105:18 + --> $DIR/lint-unnecessary-parens.rs:106:18 | LL | let mut _a = (0); | ^ ^ @@ -244,7 +244,7 @@ LL + let mut _a = 0; | error: unnecessary parentheses around assigned value - --> $DIR/lint-unnecessary-parens.rs:106:10 + --> $DIR/lint-unnecessary-parens.rs:107:10 | LL | _a = (0); | ^ ^ @@ -256,7 +256,7 @@ LL + _a = 0; | error: unnecessary parentheses around assigned value - --> $DIR/lint-unnecessary-parens.rs:107:11 + --> $DIR/lint-unnecessary-parens.rs:108:11 | LL | _a += (1); | ^ ^ @@ -268,7 +268,7 @@ LL + _a += 1; | error: unnecessary parentheses around pattern - --> $DIR/lint-unnecessary-parens.rs:109:8 + --> $DIR/lint-unnecessary-parens.rs:110:8 | LL | let(mut _a) = 3; | ^ ^ @@ -280,7 +280,7 @@ LL + let mut _a = 3; | error: unnecessary parentheses around pattern - --> $DIR/lint-unnecessary-parens.rs:110:9 + --> $DIR/lint-unnecessary-parens.rs:111:9 | LL | let (mut _a) = 3; | ^ ^ @@ -292,7 +292,7 @@ LL + let mut _a = 3; | error: unnecessary parentheses around pattern - --> $DIR/lint-unnecessary-parens.rs:111:8 + --> $DIR/lint-unnecessary-parens.rs:112:8 | LL | let( mut _a) = 3; | ^^ ^ @@ -304,7 +304,7 @@ LL + let mut _a = 3; | error: unnecessary parentheses around pattern - --> $DIR/lint-unnecessary-parens.rs:113:8 + --> $DIR/lint-unnecessary-parens.rs:114:8 | LL | let(_a) = 3; | ^ ^ @@ -316,7 +316,7 @@ LL + let _a = 3; | error: unnecessary parentheses around pattern - --> $DIR/lint-unnecessary-parens.rs:114:9 + --> $DIR/lint-unnecessary-parens.rs:115:9 | LL | let (_a) = 3; | ^ ^ @@ -328,7 +328,7 @@ LL + let _a = 3; | error: unnecessary parentheses around pattern - --> $DIR/lint-unnecessary-parens.rs:115:8 + --> $DIR/lint-unnecessary-parens.rs:116:8 | LL | let( _a) = 3; | ^^ ^ @@ -340,7 +340,7 @@ LL + let _a = 3; | error: unnecessary parentheses around block return value - --> $DIR/lint-unnecessary-parens.rs:121:9 + --> $DIR/lint-unnecessary-parens.rs:122:9 | LL | (unit!() - One) | ^ ^ @@ -352,7 +352,7 @@ LL + unit!() - One | error: unnecessary parentheses around block return value - --> $DIR/lint-unnecessary-parens.rs:123:9 + --> $DIR/lint-unnecessary-parens.rs:124:9 | LL | (unit![] - One) | ^ ^ @@ -364,7 +364,7 @@ LL + unit![] - One | error: unnecessary parentheses around block return value - --> $DIR/lint-unnecessary-parens.rs:126:9 + --> $DIR/lint-unnecessary-parens.rs:127:9 | LL | (unit! {} - One) | ^ ^ @@ -375,5 +375,29 @@ LL - (unit! {} - One) LL + unit! {} - One | -error: aborting due to 31 previous errors +error: unnecessary parentheses around assigned value + --> $DIR/lint-unnecessary-parens.rs:132:14 + | +LL | let _r = (&x); + | ^ ^ + | +help: remove these parentheses + | +LL - let _r = (&x); +LL + let _r = &x; + | + +error: unnecessary parentheses around assigned value + --> $DIR/lint-unnecessary-parens.rs:133:14 + | +LL | let _r = (&mut x); + | ^ ^ + | +help: remove these parentheses + | +LL - let _r = (&mut x); +LL + let _r = &mut x; + | + +error: aborting due to 33 previous errors From 8c06dc4dda857ce988c35f6207e53edd1502f74b Mon Sep 17 00:00:00 2001 From: bohan Date: Tue, 6 Aug 2024 19:27:15 +0800 Subject: [PATCH 30/31] make `import.vis` is not mutable --- .../rustc_resolve/src/build_reduced_graph.rs | 10 +-- compiler/rustc_resolve/src/check_unused.rs | 2 +- compiler/rustc_resolve/src/diagnostics.rs | 23 +++--- compiler/rustc_resolve/src/ident.rs | 70 +++++++++++++++---- compiler/rustc_resolve/src/imports.rs | 53 +++++++------- compiler/rustc_resolve/src/late.rs | 3 +- .../rustc_resolve/src/late/diagnostics.rs | 1 + compiler/rustc_resolve/src/lib.rs | 3 +- compiler/rustc_resolve/src/macros.rs | 27 +++++-- 9 files changed, 129 insertions(+), 63 deletions(-) diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs index 2fa3692bb289f..307625bcfb17c 100644 --- a/compiler/rustc_resolve/src/build_reduced_graph.rs +++ b/compiler/rustc_resolve/src/build_reduced_graph.rs @@ -283,6 +283,7 @@ impl<'a, 'b, 'tcx> BuildReducedGraphVisitor<'a, 'b, 'tcx> { parent_scope, finalize.then(|| Finalize::new(id, path.span)), None, + None, ) { PathResult::Module(ModuleOrUniformRoot::Module(module)) => { let res = module.res().expect("visibility resolved to unnamed block"); @@ -372,7 +373,7 @@ impl<'a, 'b, 'tcx> BuildReducedGraphVisitor<'a, 'b, 'tcx> { has_attributes: !item.attrs.is_empty(), root_span, root_id, - vis: Cell::new(Some(vis)), + vis, used: Default::default(), }); @@ -888,7 +889,7 @@ impl<'a, 'b, 'tcx> BuildReducedGraphVisitor<'a, 'b, 'tcx> { root_span: item.span, span: item.span, module_path: Vec::new(), - vis: Cell::new(Some(vis)), + vis, used: Cell::new(used.then_some(Used::Other)), }); self.r.potentially_unused_imports.push(import); @@ -1089,7 +1090,7 @@ impl<'a, 'b, 'tcx> BuildReducedGraphVisitor<'a, 'b, 'tcx> { root_span: span, span, module_path: Vec::new(), - vis: Cell::new(Some(ty::Visibility::Restricted(CRATE_DEF_ID))), + vis: ty::Visibility::Restricted(CRATE_DEF_ID), used: Default::default(), }) }; @@ -1125,6 +1126,7 @@ impl<'a, 'b, 'tcx> BuildReducedGraphVisitor<'a, 'b, 'tcx> { ident, MacroNS, &self.parent_scope, + None, ); if let Ok(binding) = result { let import = macro_use_import(self, ident.span, false); @@ -1253,7 +1255,7 @@ impl<'a, 'b, 'tcx> BuildReducedGraphVisitor<'a, 'b, 'tcx> { root_span: span, span, module_path: Vec::new(), - vis: Cell::new(Some(vis)), + vis, used: Cell::new(Some(Used::Other)), }); let import_binding = self.r.import(binding, import); diff --git a/compiler/rustc_resolve/src/check_unused.rs b/compiler/rustc_resolve/src/check_unused.rs index a819997156105..75b8ecebdd914 100644 --- a/compiler/rustc_resolve/src/check_unused.rs +++ b/compiler/rustc_resolve/src/check_unused.rs @@ -382,7 +382,7 @@ impl Resolver<'_, '_> { for import in self.potentially_unused_imports.iter() { match import.kind { _ if import.used.get().is_some() - || import.expect_vis().is_public() + || import.vis.is_public() || import.span.is_dummy() => { if let ImportKind::MacroUse { .. } = import.kind { diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index 8080bb60e415e..942026ef01223 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -1052,6 +1052,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { parent_scope, false, false, + None, ) { suggestions.extend( ext.helper_attrs @@ -1506,6 +1507,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { None, false, None, + None, ) { let desc = match binding.res() { Res::Def(DefKind::Macro(MacroKind::Bang), _) => { @@ -1983,6 +1985,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { parent_scope: &ParentScope<'a>, ribs: Option<&PerNS>>>, ignore_binding: Option>, + ignore_import: Option>, module: Option>, failed_segment_idx: usize, ident: Ident, @@ -2066,11 +2069,13 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { parent_scope, None, ignore_binding, + ignore_import, ) .ok() } else if let Some(ribs) = ribs && let Some(TypeNS | ValueNS) = opt_ns { + assert!(ignore_import.is_none()); match self.resolve_ident_in_lexical_scope( ident, ns_to_try, @@ -2091,6 +2096,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { None, false, ignore_binding, + ignore_import, ) .ok() }; @@ -2132,6 +2138,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { } else if ident.name.as_str().chars().next().is_some_and(|c| c.is_ascii_uppercase()) { // Check whether the name refers to an item in the value namespace. let binding = if let Some(ribs) = ribs { + assert!(ignore_import.is_none()); self.resolve_ident_in_lexical_scope( ident, ValueNS, @@ -2206,6 +2213,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { None, false, ignore_binding, + ignore_import, ) { let descr = binding.res().descr(); (format!("{descr} `{ident}` is not a crate or module"), suggestion) @@ -2259,7 +2267,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { ) -> Option<(Vec, Option)> { // Replace first ident with `self` and check if that is valid. path[0].ident.name = kw::SelfLower; - let result = self.maybe_resolve_path(&path, None, parent_scope); + let result = self.maybe_resolve_path(&path, None, parent_scope, None); debug!("make_missing_self_suggestion: path={:?} result={:?}", path, result); if let PathResult::Module(..) = result { Some((path, None)) } else { None } } @@ -2278,7 +2286,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { ) -> Option<(Vec, Option)> { // Replace first ident with `crate` and check if that is valid. path[0].ident.name = kw::Crate; - let result = self.maybe_resolve_path(&path, None, parent_scope); + let result = self.maybe_resolve_path(&path, None, parent_scope, None); debug!("make_missing_crate_suggestion: path={:?} result={:?}", path, result); if let PathResult::Module(..) = result { Some(( @@ -2309,7 +2317,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { ) -> Option<(Vec, Option)> { // Replace first ident with `crate` and check if that is valid. path[0].ident.name = kw::Super; - let result = self.maybe_resolve_path(&path, None, parent_scope); + let result = self.maybe_resolve_path(&path, None, parent_scope, None); debug!("make_missing_super_suggestion: path={:?} result={:?}", path, result); if let PathResult::Module(..) = result { Some((path, None)) } else { None } } @@ -2343,7 +2351,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { for name in extern_crate_names.into_iter() { // Replace first ident with a crate name and check if that is valid. path[0].ident.name = name; - let result = self.maybe_resolve_path(&path, None, parent_scope); + let result = self.maybe_resolve_path(&path, None, parent_scope, None); debug!( "make_external_crate_suggestion: name={:?} path={:?} result={:?}", name, path, result @@ -2509,12 +2517,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { } /// Finds a cfg-ed out item inside `module` with the matching name. - pub(crate) fn find_cfg_stripped( - &mut self, - err: &mut Diag<'_>, - segment: &Symbol, - module: DefId, - ) { + pub(crate) fn find_cfg_stripped(&self, err: &mut Diag<'_>, segment: &Symbol, module: DefId) { let local_items; let symbols = if module.is_local() { local_items = self diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs index 947ba569ab0e2..149c639efab86 100644 --- a/compiler/rustc_resolve/src/ident.rs +++ b/compiler/rustc_resolve/src/ident.rs @@ -14,6 +14,7 @@ use Determinacy::*; use Namespace::*; use crate::errors::{ParamKindInEnumDiscriminant, ParamKindInNonTrivialAnonConst}; +use crate::imports::Import; use crate::late::{ConstantHasGenerics, NoConstantGenericsReason, PathSource, Rib, RibKind}; use crate::macros::{sub_namespace_match, MacroRulesScope}; use crate::{ @@ -351,6 +352,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { parent_scope, finalize.map(|finalize| Finalize { used: Used::Scope, ..finalize }), ignore_binding, + None, ); if let Ok(binding) = item { // The ident resolves to an item. @@ -364,6 +366,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { finalize, finalize.is_some(), ignore_binding, + None, ) .ok() .map(LexicalScopeBinding::Item) @@ -383,6 +386,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { finalize: Option, force: bool, ignore_binding: Option>, + ignore_import: Option>, ) -> Result, Determinacy> { bitflags::bitflags! { #[derive(Clone, Copy)] @@ -455,6 +459,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { parent_scope, true, force, + ignore_import, ) { Ok((Some(ext), _)) => { if ext.helper_attrs.contains(&ident.name) { @@ -496,6 +501,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { parent_scope, finalize, ignore_binding, + ignore_import, ); match binding { Ok(binding) => Ok((binding, Flags::MODULE | Flags::MISC_SUGGEST_CRATE)), @@ -518,6 +524,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { !matches!(scope_set, ScopeSet::Late(..)), finalize.map(|finalize| Finalize { used: Used::Scope, ..finalize }), ignore_binding, + ignore_import, ); match binding { Ok(binding) => { @@ -585,6 +592,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { parent_scope, None, ignore_binding, + ignore_import, ) { if matches!(use_prelude, UsePrelude::Yes) || this.is_builtin_macro(binding.res()) @@ -738,8 +746,9 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { ident: Ident, ns: Namespace, parent_scope: &ParentScope<'a>, + ignore_import: Option>, ) -> Result, Determinacy> { - self.resolve_ident_in_module_ext(module, ident, ns, parent_scope, None, None) + self.resolve_ident_in_module_ext(module, ident, ns, parent_scope, None, None, ignore_import) .map_err(|(determinacy, _)| determinacy) } @@ -752,9 +761,18 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { parent_scope: &ParentScope<'a>, finalize: Option, ignore_binding: Option>, + ignore_import: Option>, ) -> Result, Determinacy> { - self.resolve_ident_in_module_ext(module, ident, ns, parent_scope, finalize, ignore_binding) - .map_err(|(determinacy, _)| determinacy) + self.resolve_ident_in_module_ext( + module, + ident, + ns, + parent_scope, + finalize, + ignore_binding, + ignore_import, + ) + .map_err(|(determinacy, _)| determinacy) } #[instrument(level = "debug", skip(self))] @@ -766,6 +784,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { parent_scope: &ParentScope<'a>, finalize: Option, ignore_binding: Option>, + ignore_import: Option>, ) -> Result, (Determinacy, Weak)> { let tmp_parent_scope; let mut adjusted_parent_scope = parent_scope; @@ -792,6 +811,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { false, finalize, ignore_binding, + ignore_import, ) } @@ -804,6 +824,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { parent_scope: &ParentScope<'a>, finalize: Option, ignore_binding: Option>, + ignore_import: Option>, ) -> Result, Determinacy> { self.resolve_ident_in_module_unadjusted_ext( module, @@ -813,6 +834,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { false, finalize, ignore_binding, + ignore_import, ) .map_err(|(determinacy, _)| determinacy) } @@ -831,6 +853,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { // This binding should be ignored during in-module resolution, so that we don't get // "self-confirming" import resolutions during import validation and checking. ignore_binding: Option>, + ignore_import: Option>, ) -> Result, (Determinacy, Weak)> { let module = match module { ModuleOrUniformRoot::Module(module) => module, @@ -843,6 +866,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { finalize, finalize.is_some(), ignore_binding, + ignore_import, ); return binding.map_err(|determinacy| (determinacy, Weak::No)); } @@ -879,6 +903,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { finalize, finalize.is_some(), ignore_binding, + ignore_import, ); return binding.map_err(|determinacy| (determinacy, Weak::No)); } @@ -962,25 +987,23 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { // Check if one of single imports can still define the name, // if it can then our result is not determined and can be invalidated. for single_import in &resolution.single_imports { - let Some(import_vis) = single_import.vis.get() else { - // This branch handles a cycle in single imports, which occurs - // when we've previously **steal** the `vis` value during an import - // process. + if ignore_import == Some(*single_import) { + // This branch handles a cycle in single imports. // // For example: // ``` // use a::b; // use b as a; // ``` - // 1. Steal the `vis` in `use a::b` and attempt to locate `a` in the + // 1. Record `use a::b` as the `ignore_import` and attempt to locate `a` in the // current module. // 2. Encounter the import `use b as a`, which is a `single_import` for `a`, // and try to find `b` in the current module. // 3. Re-encounter the `use a::b` import since it's a `single_import` of `b`. // This leads to entering this branch. continue; - }; - if !self.is_accessible_from(import_vis, parent_scope.module) { + } + if !self.is_accessible_from(single_import.vis, parent_scope.module) { continue; } if let Some(ignored) = ignore_binding @@ -1022,6 +1045,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { &single_import.parent_scope, None, ignore_binding, + ignore_import, ) { Err(Determined) => continue, Ok(binding) @@ -1070,10 +1094,10 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { // Check if one of glob imports can still define the name, // if it can then our "no resolution" result is not determined and can be invalidated. for glob_import in module.globs.borrow().iter() { - let Some(import_vis) = glob_import.vis.get() else { + if ignore_import == Some(*glob_import) { continue; - }; - if !self.is_accessible_from(import_vis, parent_scope.module) { + } + if !self.is_accessible_from(glob_import.vis, parent_scope.module) { continue; } let module = match glob_import.imported_module.get() { @@ -1100,6 +1124,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { adjusted_parent_scope, None, ignore_binding, + ignore_import, ); match result { @@ -1412,8 +1437,9 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { path: &[Segment], opt_ns: Option, // `None` indicates a module path in import parent_scope: &ParentScope<'a>, + ignore_import: Option>, ) -> PathResult<'a> { - self.resolve_path_with_ribs(path, opt_ns, parent_scope, None, None, None) + self.resolve_path_with_ribs(path, opt_ns, parent_scope, None, None, None, ignore_import) } #[instrument(level = "debug", skip(self))] @@ -1424,8 +1450,17 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { parent_scope: &ParentScope<'a>, finalize: Option, ignore_binding: Option>, + ignore_import: Option>, ) -> PathResult<'a> { - self.resolve_path_with_ribs(path, opt_ns, parent_scope, finalize, None, ignore_binding) + self.resolve_path_with_ribs( + path, + opt_ns, + parent_scope, + finalize, + None, + ignore_binding, + ignore_import, + ) } pub(crate) fn resolve_path_with_ribs( @@ -1436,6 +1471,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { finalize: Option, ribs: Option<&PerNS>>>, ignore_binding: Option>, + ignore_import: Option>, ) -> PathResult<'a> { let mut module = None; let mut allow_super = true; @@ -1538,10 +1574,12 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { parent_scope, finalize, ignore_binding, + ignore_import, ) } else if let Some(ribs) = ribs && let Some(TypeNS | ValueNS) = opt_ns { + assert!(ignore_import.is_none()); match self.resolve_ident_in_lexical_scope( ident, ns, @@ -1570,6 +1608,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { finalize, finalize.is_some(), ignore_binding, + ignore_import, ) }; @@ -1644,6 +1683,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { parent_scope, ribs, ignore_binding, + ignore_import, module, segment_idx, ident, diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs index c7af21027b8d3..4a891d12281c4 100644 --- a/compiler/rustc_resolve/src/imports.rs +++ b/compiler/rustc_resolve/src/imports.rs @@ -175,7 +175,7 @@ pub(crate) struct ImportData<'a> { pub module_path: Vec, /// The resolution of `module_path`. pub imported_module: Cell>>, - pub vis: Cell>, + pub vis: ty::Visibility, pub used: Cell>, } @@ -195,10 +195,6 @@ impl<'a> ImportData<'a> { } } - pub(crate) fn expect_vis(&self) -> ty::Visibility { - self.vis.get().expect("encountered cleared import visibility") - } - pub(crate) fn id(&self) -> Option { match self.kind { ImportKind::Single { id, .. } @@ -267,7 +263,7 @@ fn pub_use_of_private_extern_crate_hack( match (&import.kind, &binding.kind) { (ImportKind::Single { .. }, NameBindingKind::Import { import: binding_import, .. }) if let ImportKind::ExternCrate { id, .. } = binding_import.kind - && import.expect_vis().is_public() => + && import.vis.is_public() => { Some(id) } @@ -279,7 +275,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { /// Given a binding and an import that resolves to it, /// return the corresponding binding defined by the import. pub(crate) fn import(&self, binding: NameBinding<'a>, import: Import<'a>) -> NameBinding<'a> { - let import_vis = import.expect_vis().to_def_id(); + let import_vis = import.vis.to_def_id(); let vis = if binding.vis.is_at_least(import_vis, self.tcx) || pub_use_of_private_extern_crate_hack(import, binding).is_some() { @@ -773,11 +769,12 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { let module = if let Some(module) = import.imported_module.get() { module } else { - // For better failure detection, pretend that the import will - // not define any names while resolving its module path. - let orig_vis = import.vis.take(); - let path_res = self.maybe_resolve_path(&import.module_path, None, &import.parent_scope); - import.vis.set(orig_vis); + let path_res = self.maybe_resolve_path( + &import.module_path, + None, + &import.parent_scope, + Some(import), + ); match path_res { PathResult::Module(module) => module, @@ -807,16 +804,13 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { self.per_ns(|this, ns| { if !type_ns_only || ns == TypeNS { if let Err(Undetermined) = source_bindings[ns].get() { - // For better failure detection, pretend that the import will - // not define any names while resolving its module path. - let orig_vis = import.vis.take(); let binding = this.maybe_resolve_ident_in_module( module, source, ns, &import.parent_scope, + Some(import), ); - import.vis.set(orig_vis); source_bindings[ns].set(binding); } else { return; @@ -855,7 +849,6 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { /// Optionally returns an unresolved import error. This error is buffered and used to /// consolidate multiple unresolved import errors into a single diagnostic. fn finalize_import(&mut self, import: Import<'a>) -> Option { - let orig_vis = import.vis.take(); let ignore_binding = match &import.kind { ImportKind::Single { target_bindings, .. } => target_bindings[TypeNS].get(), _ => None, @@ -874,11 +867,12 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { &import.parent_scope, Some(finalize), ignore_binding, + Some(import), ); let no_ambiguity = ambiguity_errors_len(&self.ambiguity_errors) == prev_ambiguity_errors_len; - import.vis.set(orig_vis); + let module = match path_res { PathResult::Module(module) => { // Consistency checks, analogous to `finalize_macro_resolutions`. @@ -1013,8 +1007,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { } if !is_prelude && let Some(max_vis) = max_vis.get() - && let import_vis = import.expect_vis() - && !max_vis.is_at_least(import_vis, self.tcx) + && !max_vis.is_at_least(import.vis, self.tcx) { let def_id = self.local_def_id(id); self.lint_buffer.buffer_lint( @@ -1023,7 +1016,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { import.span, BuiltinLintDiag::RedundantImportVisibility { max_vis: max_vis.to_string(def_id, self.tcx), - import_vis: import_vis.to_string(def_id, self.tcx), + import_vis: import.vis.to_string(def_id, self.tcx), span: import.span, }, ); @@ -1038,9 +1031,14 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { // importing it if available. let mut path = import.module_path.clone(); path.push(Segment::from_ident(ident)); - if let PathResult::Module(ModuleOrUniformRoot::Module(module)) = - self.resolve_path(&path, None, &import.parent_scope, Some(finalize), ignore_binding) - { + if let PathResult::Module(ModuleOrUniformRoot::Module(module)) = self.resolve_path( + &path, + None, + &import.parent_scope, + Some(finalize), + ignore_binding, + None, + ) { let res = module.res().map(|r| (r, ident)); for error in &mut self.privacy_errors[privacy_errors_len..] { error.outermost_res = res; @@ -1051,7 +1049,6 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { let mut all_ns_err = true; self.per_ns(|this, ns| { if !type_ns_only || ns == TypeNS { - let orig_vis = import.vis.take(); let binding = this.resolve_ident_in_module( module, ident, @@ -1059,8 +1056,8 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { &import.parent_scope, Some(Finalize { report_private: false, ..finalize }), target_bindings[ns].get(), + Some(import), ); - import.vis.set(orig_vis); match binding { Ok(binding) => { @@ -1123,6 +1120,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { &import.parent_scope, Some(finalize), None, + None, ); if binding.is_ok() { all_ns_failed = false; @@ -1233,7 +1231,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { let mut crate_private_reexport = false; self.per_ns(|this, ns| { if let Ok(binding) = source_bindings[ns].get() { - if !binding.vis.is_at_least(import.expect_vis(), this.tcx) { + if !binding.vis.is_at_least(import.vis, this.tcx) { reexport_error = Some((ns, binding)); if let ty::Visibility::Restricted(binding_def_id) = binding.vis { if binding_def_id.is_top_level_module() { @@ -1370,6 +1368,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { None, false, target_bindings[ns].get(), + None, ) { Ok(other_binding) => { is_redundant = binding.res() == other_binding.res() diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index f844930ad265e..4d28c0a3ef1bb 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -1388,6 +1388,7 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { finalize, Some(&self.ribs), None, + None, ) } @@ -4186,7 +4187,7 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { let path_seg = |seg: &Segment| PathSegment::from_ident(seg.ident); let path = Path { segments: path.iter().map(path_seg).collect(), span, tokens: None }; if let Ok((_, res)) = - self.r.resolve_macro_path(&path, None, &self.parent_scope, false, false) + self.r.resolve_macro_path(&path, None, &self.parent_scope, false, false, None) { return Ok(Some(PartialRes::new(res))); } diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index f126563fe58f0..f9896fb21964d 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -2058,6 +2058,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { ident, ns, &self.parent_scope, + None, ) { let res = binding.res(); if filter_fn(res) { diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 79d3bb685b384..023e428dc1ba7 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -2120,7 +2120,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { } } - match self.maybe_resolve_path(&segments, Some(ns), &parent_scope) { + match self.maybe_resolve_path(&segments, Some(ns), &parent_scope, None) { PathResult::Module(ModuleOrUniformRoot::Module(module)) => Some(module.res().unwrap()), PathResult::NonModule(path_res) => path_res.full_res(), PathResult::Module(ModuleOrUniformRoot::ExternPrelude) | PathResult::Failed { .. } => { @@ -2204,6 +2204,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { ident, ValueNS, parent_scope, + None, ) else { return; }; diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index 64ae0d82952d5..a6301a367a4b3 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -39,6 +39,7 @@ use crate::errors::{ self, AddAsNonDerive, CannotDetermineMacroResolution, CannotFindIdentInThisScope, MacroExpectedFound, RemoveSurroundingDerive, }; +use crate::imports::Import; use crate::Namespace::*; use crate::{ BindingKey, BuiltinMacroState, DeriveData, Determinacy, Finalize, MacroData, ModuleKind, @@ -399,6 +400,7 @@ impl<'a, 'tcx> ResolverExpand for Resolver<'a, 'tcx> { &parent_scope, true, force, + None, ) { Ok((Some(ext), _)) => { if !ext.helper_attrs.is_empty() { @@ -551,6 +553,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { force, deleg_impl, invoc_in_mod_inert_attr.map(|def_id| (def_id, node_id)), + None, ) { Ok((Some(ext), res)) => (ext, res), Ok((None, res)) => (self.dummy_ext(kind), res), @@ -704,8 +707,18 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { parent_scope: &ParentScope<'a>, trace: bool, force: bool, + ignore_import: Option>, ) -> Result<(Option>, Res), Determinacy> { - self.resolve_macro_or_delegation_path(path, kind, parent_scope, trace, force, None, None) + self.resolve_macro_or_delegation_path( + path, + kind, + parent_scope, + trace, + force, + None, + None, + ignore_import, + ) } fn resolve_macro_or_delegation_path( @@ -717,6 +730,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { force: bool, deleg_impl: Option, invoc_in_mod_inert_attr: Option<(LocalDefId, NodeId)>, + ignore_import: Option>, ) -> Result<(Option>, Res), Determinacy> { let path_span = ast_path.span; let mut path = Segment::from_path(ast_path); @@ -733,7 +747,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { let res = if deleg_impl.is_some() || path.len() > 1 { let ns = if deleg_impl.is_some() { TypeNS } else { MacroNS }; - let res = match self.maybe_resolve_path(&path, Some(ns), parent_scope) { + let res = match self.maybe_resolve_path(&path, Some(ns), parent_scope, ignore_import) { PathResult::NonModule(path_res) if let Some(res) = path_res.full_res() => Ok(res), PathResult::Indeterminate if !force => return Err(Determinacy::Undetermined), PathResult::NonModule(..) @@ -768,6 +782,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { None, force, None, + None, ); if let Err(Determinacy::Undetermined) = binding { return Err(Determinacy::Undetermined); @@ -852,6 +867,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { &parent_scope, Some(Finalize::new(ast::CRATE_NODE_ID, path_span)), None, + None, ) { PathResult::NonModule(path_res) if let Some(res) = path_res.full_res() => { check_consistency(self, &path, path_span, kind, initial_res, res) @@ -871,7 +887,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { if let PathResult::Failed { span, label, module, .. } = path_res { // try to suggest if it's not a macro, maybe a function if let PathResult::NonModule(partial_res) = - self.maybe_resolve_path(&path, Some(ValueNS), &parent_scope) + self.maybe_resolve_path(&path, Some(ValueNS), &parent_scope, None) && partial_res.unresolved_segments() == 0 { let sm = self.tcx.sess.source_map(); @@ -921,6 +937,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { Some(Finalize::new(ast::CRATE_NODE_ID, ident.span)), true, None, + None, ) { Ok(binding) => { let initial_res = initial_binding.map(|initial_binding| { @@ -966,6 +983,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { Some(Finalize::new(ast::CRATE_NODE_ID, ident.span)), true, None, + None, ); } } @@ -1070,6 +1088,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { None, false, None, + None, ); if fallback_binding.ok().and_then(|b| b.res().opt_def_id()) != Some(def_id) { self.tcx.sess.psess.buffer_lint( @@ -1143,7 +1162,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { let mut indeterminate = false; for ns in namespaces { - match self.maybe_resolve_path(path, Some(*ns), &parent_scope) { + match self.maybe_resolve_path(path, Some(*ns), &parent_scope, None) { PathResult::Module(ModuleOrUniformRoot::Module(_)) => return Ok(true), PathResult::NonModule(partial_res) if partial_res.unresolved_segments() == 0 => { return Ok(true); From 273c67db83caf0260956b87dd976a7727e4d2cd3 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 5 Aug 2024 12:49:31 +0200 Subject: [PATCH 31/31] codegen: better centralize function attribute computation --- compiler/rustc_codegen_llvm/src/abi.rs | 26 ++++++++++++++++--- compiler/rustc_codegen_llvm/src/attributes.rs | 3 ++- compiler/rustc_codegen_llvm/src/callee.rs | 4 +-- compiler/rustc_codegen_llvm/src/declare.rs | 2 +- compiler/rustc_codegen_llvm/src/mono_item.rs | 4 +-- 5 files changed, 28 insertions(+), 11 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/abi.rs b/compiler/rustc_codegen_llvm/src/abi.rs index b8f4203126381..1277b7898c2d9 100644 --- a/compiler/rustc_codegen_llvm/src/abi.rs +++ b/compiler/rustc_codegen_llvm/src/abi.rs @@ -5,10 +5,10 @@ use rustc_codegen_ssa::mir::operand::{OperandRef, OperandValue}; use rustc_codegen_ssa::mir::place::{PlaceRef, PlaceValue}; use rustc_codegen_ssa::traits::*; use rustc_codegen_ssa::MemFlags; -use rustc_middle::bug; use rustc_middle::ty::layout::LayoutOf; pub use rustc_middle::ty::layout::{FAT_PTR_ADDR, FAT_PTR_EXTRA}; use rustc_middle::ty::Ty; +use rustc_middle::{bug, ty}; use rustc_session::config; pub use rustc_target::abi::call::*; use rustc_target::abi::{self, HasDataLayout, Int, Size}; @@ -16,6 +16,7 @@ pub use rustc_target::spec::abi::Abi; use rustc_target::spec::SanitizerSet; use smallvec::SmallVec; +use crate::attributes::llfn_attrs_from_instance; use crate::builder::Builder; use crate::context::CodegenCx; use crate::llvm::{self, Attribute, AttributePlace}; @@ -310,7 +311,16 @@ pub trait FnAbiLlvmExt<'ll, 'tcx> { fn llvm_type(&self, cx: &CodegenCx<'ll, 'tcx>) -> &'ll Type; fn ptr_to_llvm_type(&self, cx: &CodegenCx<'ll, 'tcx>) -> &'ll Type; fn llvm_cconv(&self) -> llvm::CallConv; - fn apply_attrs_llfn(&self, cx: &CodegenCx<'ll, 'tcx>, llfn: &'ll Value); + + /// Apply attributes to a function declaration/definition. + fn apply_attrs_llfn( + &self, + cx: &CodegenCx<'ll, 'tcx>, + llfn: &'ll Value, + instance: Option>, + ); + + /// Apply attributes to a function call. fn apply_attrs_callsite(&self, bx: &mut Builder<'_, 'll, 'tcx>, callsite: &'ll Value); } @@ -396,7 +406,12 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> { self.conv.into() } - fn apply_attrs_llfn(&self, cx: &CodegenCx<'ll, 'tcx>, llfn: &'ll Value) { + fn apply_attrs_llfn( + &self, + cx: &CodegenCx<'ll, 'tcx>, + llfn: &'ll Value, + instance: Option>, + ) { let mut func_attrs = SmallVec::<[_; 3]>::new(); if self.ret.layout.abi.is_uninhabited() { func_attrs.push(llvm::AttributeKind::NoReturn.create_attr(cx.llcx)); @@ -477,6 +492,11 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> { } } } + + // If the declaration has an associated instance, compute extra attributes based on that. + if let Some(instance) = instance { + llfn_attrs_from_instance(cx, llfn, instance); + } } fn apply_attrs_callsite(&self, bx: &mut Builder<'_, 'll, 'tcx>, callsite: &'ll Value) { diff --git a/compiler/rustc_codegen_llvm/src/attributes.rs b/compiler/rustc_codegen_llvm/src/attributes.rs index ad38814a68b6d..0cd6810249453 100644 --- a/compiler/rustc_codegen_llvm/src/attributes.rs +++ b/compiler/rustc_codegen_llvm/src/attributes.rs @@ -324,9 +324,10 @@ fn create_alloc_family_attr(llcx: &llvm::Context) -> &llvm::Attribute { llvm::CreateAttrStringValue(llcx, "alloc-family", "__rust_alloc") } +/// Helper for `FnAbi::apply_attrs_llfn`: /// Composite function which sets LLVM attributes for function depending on its AST (`#[attribute]`) /// attributes. -pub fn from_fn_attrs<'ll, 'tcx>( +pub fn llfn_attrs_from_instance<'ll, 'tcx>( cx: &CodegenCx<'ll, 'tcx>, llfn: &'ll Value, instance: ty::Instance<'tcx>, diff --git a/compiler/rustc_codegen_llvm/src/callee.rs b/compiler/rustc_codegen_llvm/src/callee.rs index 9030b3d848f9a..663c5be46e5e4 100644 --- a/compiler/rustc_codegen_llvm/src/callee.rs +++ b/compiler/rustc_codegen_llvm/src/callee.rs @@ -10,8 +10,8 @@ use rustc_middle::ty::{self, Instance, TypeVisitableExt}; use tracing::debug; use crate::context::CodegenCx; +use crate::llvm; use crate::value::Value; -use crate::{attributes, llvm}; /// Codegens a reference to a fn/method item, monomorphizing and /// inlining as it goes. @@ -78,8 +78,6 @@ pub fn get_fn<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>, instance: Instance<'tcx>) -> }; debug!("get_fn: not casting pointer!"); - attributes::from_fn_attrs(cx, llfn, instance); - // Apply an appropriate linkage/visibility value to our item that we // just declared. // diff --git a/compiler/rustc_codegen_llvm/src/declare.rs b/compiler/rustc_codegen_llvm/src/declare.rs index c4887464e19c2..2aa349b278234 100644 --- a/compiler/rustc_codegen_llvm/src/declare.rs +++ b/compiler/rustc_codegen_llvm/src/declare.rs @@ -137,7 +137,7 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { llvm::Visibility::Default, fn_abi.llvm_type(self), ); - fn_abi.apply_attrs_llfn(self, llfn); + fn_abi.apply_attrs_llfn(self, llfn, instance); if self.tcx.sess.is_sanitizer_cfi_enabled() { if let Some(instance) = instance { diff --git a/compiler/rustc_codegen_llvm/src/mono_item.rs b/compiler/rustc_codegen_llvm/src/mono_item.rs index d59eaae1ba9b8..f1ef359594b4b 100644 --- a/compiler/rustc_codegen_llvm/src/mono_item.rs +++ b/compiler/rustc_codegen_llvm/src/mono_item.rs @@ -12,7 +12,7 @@ use tracing::debug; use crate::context::CodegenCx; use crate::errors::SymbolAlreadyDefined; use crate::type_of::LayoutLlvmExt; -use crate::{attributes, base, llvm}; +use crate::{base, llvm}; impl<'tcx> PreDefineMethods<'tcx> for CodegenCx<'_, 'tcx> { fn predefine_static( @@ -87,8 +87,6 @@ impl<'tcx> PreDefineMethods<'tcx> for CodegenCx<'_, 'tcx> { debug!("predefine_fn: instance = {:?}", instance); - attributes::from_fn_attrs(self, lldecl, instance); - unsafe { if self.should_assume_dso_local(lldecl, false) { llvm::LLVMRustSetDSOLocal(lldecl, true);