From 30721b0e909d7451a37bbb4ae371e1c909a0cbaf Mon Sep 17 00:00:00 2001 From: jyn Date: Mon, 26 May 2025 20:10:17 -0400 Subject: [PATCH 001/195] Add stubs for environment variables; document some of the important ones This uses a very hacky regex that will probably miss some variables. But having some docs seems better than none at all. This uses a very hacky regex that will probably miss some variables. But having some docs seems better than none at all. In particular, this generates stubs for the following env vars: - COLORTERM - QNX_TARGET - RUST_BACKTRACE - RUSTC_BLESS - RUSTC_BOOTSTRAP - RUSTC_BREAK_ON_ICE - RUSTC_CTFE_BACKTRACE - RUSTC_FORCE_RUSTC_VERSION - RUSTC_GRAPHVIZ_FONT - RUSTC_ICE - RUSTC_LOG - RUSTC_OVERRIDE_VERSION_STRING - RUSTC_RETRY_LINKER_ON_SEGFAULT - RUSTC_TRANSLATION_NO_DEBUG_ASSERT - RUST_DEP_GRAPH_FILTER - RUST_DEP_GRAPH - RUST_FORBID_DEP_GRAPH_EDGE - RUST_MIN_STACK - RUST_TARGET_PATH - SDKROOT - TERM - UNSTABLE_RUSTDOC_TEST_LINE - UNSTABLE_RUSTDOC_TEST_PATH [rendered](![screenshot of unstable-book running locally, with 14 environment variables shown in the sidebar](https://github.com/user-attachments/assets/8238d094-fb7a-456f-ad43-7c07aa2c44dd)) --- .../COLORTERM.md | 5 +++ .../QNX_TARGET.md | 11 ++++++ .../compiler-environment-variables/SDKROOT.md | 6 ++++ .../compiler-environment-variables/TERM.md | 5 +++ .../src/compiler-flags/terminal-urls.md | 13 +++++++ src/tools/tidy/src/features.rs | 35 +++++++++++++++++++ src/tools/tidy/src/unstable_book.rs | 2 ++ src/tools/unstable-book-gen/src/main.rs | 29 +++++++++++---- .../unstable-book-gen/src/stub-env-var.md | 9 +++++ 9 files changed, 109 insertions(+), 6 deletions(-) create mode 100644 src/doc/unstable-book/src/compiler-environment-variables/COLORTERM.md create mode 100644 src/doc/unstable-book/src/compiler-environment-variables/QNX_TARGET.md create mode 100644 src/doc/unstable-book/src/compiler-environment-variables/SDKROOT.md create mode 100644 src/doc/unstable-book/src/compiler-environment-variables/TERM.md create mode 100644 src/doc/unstable-book/src/compiler-flags/terminal-urls.md create mode 100644 src/tools/unstable-book-gen/src/stub-env-var.md diff --git a/src/doc/unstable-book/src/compiler-environment-variables/COLORTERM.md b/src/doc/unstable-book/src/compiler-environment-variables/COLORTERM.md new file mode 100644 index 000000000000..f5be63b796c9 --- /dev/null +++ b/src/doc/unstable-book/src/compiler-environment-variables/COLORTERM.md @@ -0,0 +1,5 @@ +# `COLORTERM` + +This environment variable is used by [`-Zterminal-urls`] to detect if URLs are supported by the terminal emulator. + +[`-Zterminal-urls`]: ../compiler-flags/terminal-urls.html diff --git a/src/doc/unstable-book/src/compiler-environment-variables/QNX_TARGET.md b/src/doc/unstable-book/src/compiler-environment-variables/QNX_TARGET.md new file mode 100644 index 000000000000..cc98fc3b3b28 --- /dev/null +++ b/src/doc/unstable-book/src/compiler-environment-variables/QNX_TARGET.md @@ -0,0 +1,11 @@ +# `QNX_TARGET` + +---- + +This environment variable is mandatory when linking on `nto-qnx*_iosock` platforms. It is used to determine an `-L` path to pass to the QNX linker. + +You should [set this variable] by running `source qnxsdp-env.sh`. +See [the QNX docs] for more background information. + +[set this variable]: https://www.qnx.com/developers/docs/qsc/com.qnx.doc.qsc.inst_larg_org/topic/build_server_developer_steps.html +[the QNX docs]: https://www.qnx.com/developers/docs/7.1/index.html#com.qnx.doc.neutrino.io_sock/topic/migrate_app.html. diff --git a/src/doc/unstable-book/src/compiler-environment-variables/SDKROOT.md b/src/doc/unstable-book/src/compiler-environment-variables/SDKROOT.md new file mode 100644 index 000000000000..b00c2f773ad6 --- /dev/null +++ b/src/doc/unstable-book/src/compiler-environment-variables/SDKROOT.md @@ -0,0 +1,6 @@ +# `SDKROOT` + +This environment variable is used on MacOS targets. +It is passed through to the linker (either as `-isysroot` or `-syslibroot`) without changes. + +Note that this variable is not always respected. When the SDKROOT is clearly wrong (e.g. when the platform of the SDK does not match the `--target` used by rustc), this is ignored and rustc does its own detection. diff --git a/src/doc/unstable-book/src/compiler-environment-variables/TERM.md b/src/doc/unstable-book/src/compiler-environment-variables/TERM.md new file mode 100644 index 000000000000..9208fd378a3a --- /dev/null +++ b/src/doc/unstable-book/src/compiler-environment-variables/TERM.md @@ -0,0 +1,5 @@ +# `TERM` + +This environment variable is used by [`-Zterminal-urls`] to detect if URLs are supported by the terminal emulator. + +[`-Zterminal-urls`]: ../compiler-flags/terminal-urls.html diff --git a/src/doc/unstable-book/src/compiler-flags/terminal-urls.md b/src/doc/unstable-book/src/compiler-flags/terminal-urls.md new file mode 100644 index 000000000000..a5427978f258 --- /dev/null +++ b/src/doc/unstable-book/src/compiler-flags/terminal-urls.md @@ -0,0 +1,13 @@ +# `-Z terminal-urls` + +The tracking feature for this issue is [#125586] + +[#125586]: https://github.com/rust-lang/rust/issues/125586 + +--- + +This flag takes either a boolean or the string "auto". + +When enabled, use the OSC 8 hyperlink terminal specification to print hyperlinks in the compiler output. +Use "auto" to try and autodetect whether the terminal emulator supports hyperlinks. +Currently, "auto" only enables hyperlinks if `COLORTERM=truecolor` and `TERM=xterm-256color`. diff --git a/src/tools/tidy/src/features.rs b/src/tools/tidy/src/features.rs index fcd7943e6e0a..cbb7e18a8db7 100644 --- a/src/tools/tidy/src/features.rs +++ b/src/tools/tidy/src/features.rs @@ -9,6 +9,7 @@ //! * All unstable lang features have tests to ensure they are actually unstable. //! * Language features in a group are sorted by feature name. +use std::collections::BTreeSet; use std::collections::hash_map::{Entry, HashMap}; use std::ffi::OsStr; use std::num::NonZeroU32; @@ -21,6 +22,7 @@ use crate::walk::{filter_dirs, filter_not_rust, walk, walk_many}; mod tests; mod version; +use regex::Regex; use version::Version; const FEATURE_GROUP_START_PREFIX: &str = "// feature-group-start"; @@ -610,3 +612,36 @@ fn map_lib_features( }, ); } + +fn should_document(var: &str) -> bool { + if var.starts_with("RUSTC_") || var.starts_with("RUST_") || var.starts_with("UNSTABLE_RUSTDOC_") + { + return true; + } + ["SDKROOT", "QNX_TARGET", "COLORTERM", "TERM"].contains(&var) +} + +pub fn collect_env_vars(compiler: &Path) -> BTreeSet { + let env_var_regex: Regex = Regex::new(r#"env::var(_os)?\("([^"]+)"#).unwrap(); + + let mut vars = BTreeSet::new(); + walk( + compiler, + // skip build scripts, tests, and non-rust files + |path, _is_dir| { + filter_dirs(path) + || filter_not_rust(path) + || path.ends_with("build.rs") + || path.ends_with("tests.rs") + }, + &mut |_entry, contents| { + for env_var in env_var_regex.captures_iter(contents).map(|c| c.get(2).unwrap().as_str()) + { + if should_document(env_var) { + vars.insert(env_var.to_owned()); + } + } + }, + ); + vars +} diff --git a/src/tools/tidy/src/unstable_book.rs b/src/tools/tidy/src/unstable_book.rs index a2453a6c9605..5b0bf2201c17 100644 --- a/src/tools/tidy/src/unstable_book.rs +++ b/src/tools/tidy/src/unstable_book.rs @@ -6,6 +6,8 @@ use crate::features::{CollectedFeatures, Features, Status}; pub const PATH_STR: &str = "doc/unstable-book"; +pub const ENV_VARS_DIR: &str = "src/compiler-environment-variables"; + pub const COMPILER_FLAGS_DIR: &str = "src/compiler-flags"; pub const LANG_FEATURES_DIR: &str = "src/language-features"; diff --git a/src/tools/unstable-book-gen/src/main.rs b/src/tools/unstable-book-gen/src/main.rs index b9a2d313182f..2a3388590cdf 100644 --- a/src/tools/unstable-book-gen/src/main.rs +++ b/src/tools/unstable-book-gen/src/main.rs @@ -5,11 +5,11 @@ use std::env; use std::fs::{self, write}; use std::path::Path; -use tidy::features::{Features, collect_lang_features, collect_lib_features}; +use tidy::features::{Features, collect_env_vars, collect_lang_features, collect_lib_features}; use tidy::t; use tidy::unstable_book::{ - LANG_FEATURES_DIR, LIB_FEATURES_DIR, PATH_STR, collect_unstable_book_section_file_names, - collect_unstable_feature_names, + ENV_VARS_DIR, LANG_FEATURES_DIR, LIB_FEATURES_DIR, PATH_STR, + collect_unstable_book_section_file_names, collect_unstable_feature_names, }; fn generate_stub_issue(path: &Path, name: &str, issue: u32) { @@ -22,6 +22,11 @@ fn generate_stub_no_issue(path: &Path, name: &str) { t!(write(path, content), path); } +fn generate_stub_env_var(path: &Path, name: &str) { + let content = format!(include_str!("stub-env-var.md"), name = name); + t!(write(path, content), path); +} + fn set_to_summary_str(set: &BTreeSet, dir: &str) -> String { set.iter() .map(|ref n| format!(" - [{}]({}/{}.md)", n.replace('-', "_"), dir, n)) @@ -54,7 +59,7 @@ fn generate_summary(path: &Path, lang_features: &Features, lib_features: &Featur t!(write(&summary_path, content), summary_path); } -fn generate_unstable_book_files(src: &Path, out: &Path, features: &Features) { +fn generate_feature_files(src: &Path, out: &Path, features: &Features) { let unstable_features = collect_unstable_feature_names(features); let unstable_section_file_names = collect_unstable_book_section_file_names(src); t!(fs::create_dir_all(&out)); @@ -72,6 +77,16 @@ fn generate_unstable_book_files(src: &Path, out: &Path, features: &Features) { } } +fn generate_env_files(src: &Path, out: &Path, env_vars: &BTreeSet) { + let env_var_file_names = collect_unstable_book_section_file_names(src); + t!(fs::create_dir_all(&out)); + for env_var in env_vars - &env_var_file_names { + let file_name = format!("{env_var}.md"); + let out_file_path = out.join(&file_name); + generate_stub_env_var(&out_file_path, &env_var); + } +} + fn copy_recursive(from: &Path, to: &Path) { for entry in t!(fs::read_dir(from)) { let e = t!(entry); @@ -101,21 +116,23 @@ fn main() { .into_iter() .filter(|&(ref name, _)| !lang_features.contains_key(name)) .collect(); + let env_vars = collect_env_vars(compiler_path); let doc_src_path = src_path.join(PATH_STR); t!(fs::create_dir_all(&dest_path)); - generate_unstable_book_files( + generate_feature_files( &doc_src_path.join(LANG_FEATURES_DIR), &dest_path.join(LANG_FEATURES_DIR), &lang_features, ); - generate_unstable_book_files( + generate_feature_files( &doc_src_path.join(LIB_FEATURES_DIR), &dest_path.join(LIB_FEATURES_DIR), &lib_features, ); + generate_env_files(&doc_src_path.join(ENV_VARS_DIR), &dest_path.join(ENV_VARS_DIR), &env_vars); copy_recursive(&doc_src_path, &dest_path); diff --git a/src/tools/unstable-book-gen/src/stub-env-var.md b/src/tools/unstable-book-gen/src/stub-env-var.md new file mode 100644 index 000000000000..e8a7ddb855a6 --- /dev/null +++ b/src/tools/unstable-book-gen/src/stub-env-var.md @@ -0,0 +1,9 @@ +# `{name}` + +Environment variables have no tracking issue. This environment variable has no documentation, and therefore is likely internal to the compiler and not meant for general use. + +See [the code][github search] for more information. + +[github search]: https://github.com/search?q=repo%3Arust-lang%2Frust+%22{name}%22+path%3Acompiler&type=code + +------------------------ From 1140e90074b0cbcfdea8535e4b51877e2838227e Mon Sep 17 00:00:00 2001 From: binarycat Date: Tue, 27 May 2025 12:42:25 -0500 Subject: [PATCH 002/195] rustdoc search: prefer stable items in search results fixes https://github.com/rust-lang/rust/issues/138067 --- src/librustdoc/formats/cache.rs | 1 + src/librustdoc/html/render/mod.rs | 9 ++++++++- src/librustdoc/html/render/search_index.rs | 6 ++++++ src/librustdoc/html/static/js/rustdoc.d.ts | 5 ++++- src/librustdoc/html/static/js/search.js | 21 ++++++++++++++++++++- tests/rustdoc-js-std/core-transmute.js | 2 +- tests/rustdoc-js-std/transmute-fail.js | 2 +- tests/rustdoc-js-std/transmute.js | 2 +- tests/rustdoc-js/sort-stability.js | 9 +++++++++ tests/rustdoc-js/sort-stability.rs | 16 ++++++++++++++++ 10 files changed, 67 insertions(+), 6 deletions(-) create mode 100644 tests/rustdoc-js/sort-stability.js create mode 100644 tests/rustdoc-js/sort-stability.rs diff --git a/src/librustdoc/formats/cache.rs b/src/librustdoc/formats/cache.rs index 4989bd718c9f..c3251ca78062 100644 --- a/src/librustdoc/formats/cache.rs +++ b/src/librustdoc/formats/cache.rs @@ -586,6 +586,7 @@ fn add_item_to_search_index(tcx: TyCtxt<'_>, cache: &mut Cache, item: &clean::It search_type, aliases, deprecation, + stability: item.stability(tcx), }; cache.search_index.push(index_item); } diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index 66d5aafa3c1e..29917bb0fca2 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -50,7 +50,8 @@ use std::{fs, str}; use askama::Template; use itertools::Either; use rustc_attr_data_structures::{ - ConstStability, DeprecatedSince, Deprecation, RustcVersion, StabilityLevel, StableSince, + ConstStability, DeprecatedSince, Deprecation, RustcVersion, Stability, StabilityLevel, + StableSince, }; use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; use rustc_hir::Mutability; @@ -140,6 +141,12 @@ pub(crate) struct IndexItem { pub(crate) search_type: Option, pub(crate) aliases: Box<[Symbol]>, pub(crate) deprecation: Option, + pub(crate) stability: Option, +} +impl IndexItem { + fn is_unstable(&self) -> bool { + matches!(&self.stability, Some(Stability { level: StabilityLevel::Unstable { .. }, .. })) + } } /// A type used for the search index. diff --git a/src/librustdoc/html/render/search_index.rs b/src/librustdoc/html/render/search_index.rs index aff8684ee3a0..6635aa02e97d 100644 --- a/src/librustdoc/html/render/search_index.rs +++ b/src/librustdoc/html/render/search_index.rs @@ -93,6 +93,7 @@ pub(crate) fn build_index( ), aliases: item.attrs.get_doc_aliases(), deprecation: item.deprecation(tcx), + stability: item.stability(tcx), }); } } @@ -642,6 +643,7 @@ pub(crate) fn build_index( let mut parents_backref_queue = VecDeque::new(); let mut functions = String::with_capacity(self.items.len()); let mut deprecated = Vec::with_capacity(self.items.len()); + let mut unstable = Vec::with_capacity(self.items.len()); let mut type_backref_queue = VecDeque::new(); @@ -698,6 +700,9 @@ pub(crate) fn build_index( // bitmasks always use 1-indexing for items, with 0 as the crate itself deprecated.push(u32::try_from(index + 1).unwrap()); } + if item.is_unstable() { + unstable.push(u32::try_from(index + 1).unwrap()); + } } for (index, path) in &revert_extra_paths { @@ -736,6 +741,7 @@ pub(crate) fn build_index( crate_data.serialize_field("r", &re_exports)?; crate_data.serialize_field("b", &self.associated_item_disambiguators)?; crate_data.serialize_field("c", &bitmap_to_string(&deprecated))?; + crate_data.serialize_field("u", &bitmap_to_string(&unstable))?; crate_data.serialize_field("e", &bitmap_to_string(&self.empty_desc))?; crate_data.serialize_field("P", ¶m_names)?; if has_aliases { diff --git a/src/librustdoc/html/static/js/rustdoc.d.ts b/src/librustdoc/html/static/js/rustdoc.d.ts index 0d2e19e019f3..8110f1f5a5e5 100644 --- a/src/librustdoc/html/static/js/rustdoc.d.ts +++ b/src/librustdoc/html/static/js/rustdoc.d.ts @@ -129,7 +129,7 @@ declare namespace rustdoc { /** * A single parsed "atom" in a search query. For example, - * + * * std::fmt::Formatter, Write -> Result<()> * ┏━━━━━━━━━━━━━━━━━━ ┌──── ┏━━━━━┅┅┅┅┄┄┄┄┄┄┄┄┄┄┄┄┄┄┐ * ┃ │ ┗ QueryElement { ┊ @@ -442,6 +442,8 @@ declare namespace rustdoc { * of `p`) but is used for modules items like free functions. * * `c` is an array of item indices that are deprecated. + * + * `u` is an array of item indices that are unstable. */ type RawSearchIndexCrate = { doc: string, @@ -456,6 +458,7 @@ declare namespace rustdoc { p: Array<[number, string] | [number, string, number] | [number, string, number, number] | [number, string, number, number, string]>, b: Array<[number, String]>, c: string, + u: string, r: Array<[number, number]>, P: Array<[number, string]>, }; diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js index dce5fddb3177..da15433622af 100644 --- a/src/librustdoc/html/static/js/search.js +++ b/src/librustdoc/html/static/js/search.js @@ -1463,6 +1463,11 @@ class DocSearch { * @type {Map} */ this.searchIndexEmptyDesc = new Map(); + /** + * @type {Map} + */ + this.searchIndexUnstable = new Map(); + /** * @type {Uint32Array} */ @@ -2048,9 +2053,10 @@ class DocSearch { }; const descShardList = [descShard]; - // Deprecated items and items with no description + // Deprecated and unstable items and items with no description this.searchIndexDeprecated.set(crate, new RoaringBitmap(crateCorpus.c)); this.searchIndexEmptyDesc.set(crate, new RoaringBitmap(crateCorpus.e)); + this.searchIndexUnstable.set(crate, new RoaringBitmap(crateCorpus.u)); let descIndex = 0; /** @@ -3284,6 +3290,19 @@ class DocSearch { return a - b; } + // sort unstable items later + a = Number( + // @ts-expect-error + this.searchIndexUnstable.get(aaa.item.crate).contains(aaa.item.bitIndex), + ); + b = Number( + // @ts-expect-error + this.searchIndexUnstable.get(bbb.item.crate).contains(bbb.item.bitIndex), + ); + if (a !== b) { + return a - b; + } + // sort by crate (current crate comes first) a = Number(aaa.item.crate !== preferredCrate); b = Number(bbb.item.crate !== preferredCrate); diff --git a/tests/rustdoc-js-std/core-transmute.js b/tests/rustdoc-js-std/core-transmute.js index 8c9910a32d7f..b15f398902c3 100644 --- a/tests/rustdoc-js-std/core-transmute.js +++ b/tests/rustdoc-js-std/core-transmute.js @@ -3,9 +3,9 @@ const EXPECTED = [ { 'query': 'generic:T -> generic:U', 'others': [ + { 'path': 'core::mem', 'name': 'transmute' }, { 'path': 'core::intrinsics::simd', 'name': 'simd_as' }, { 'path': 'core::intrinsics::simd', 'name': 'simd_cast' }, - { 'path': 'core::mem', 'name': 'transmute' }, ], }, ]; diff --git a/tests/rustdoc-js-std/transmute-fail.js b/tests/rustdoc-js-std/transmute-fail.js index ddfb27619481..459e8dc3f002 100644 --- a/tests/rustdoc-js-std/transmute-fail.js +++ b/tests/rustdoc-js-std/transmute-fail.js @@ -6,9 +6,9 @@ const EXPECTED = [ // should-fail tag and the search query below: 'query': 'generic:T -> generic:T', 'others': [ + { 'path': 'std::mem', 'name': 'transmute' }, { 'path': 'std::intrinsics::simd', 'name': 'simd_as' }, { 'path': 'std::intrinsics::simd', 'name': 'simd_cast' }, - { 'path': 'std::mem', 'name': 'transmute' }, ], }, ]; diff --git a/tests/rustdoc-js-std/transmute.js b/tests/rustdoc-js-std/transmute.js index f52e0ab14362..a85b02e29947 100644 --- a/tests/rustdoc-js-std/transmute.js +++ b/tests/rustdoc-js-std/transmute.js @@ -5,9 +5,9 @@ const EXPECTED = [ // should-fail tag and the search query below: 'query': 'generic:T -> generic:U', 'others': [ + { 'path': 'std::mem', 'name': 'transmute' }, { 'path': 'std::intrinsics::simd', 'name': 'simd_as' }, { 'path': 'std::intrinsics::simd', 'name': 'simd_cast' }, - { 'path': 'std::mem', 'name': 'transmute' }, ], }, ]; diff --git a/tests/rustdoc-js/sort-stability.js b/tests/rustdoc-js/sort-stability.js new file mode 100644 index 000000000000..8c095619a086 --- /dev/null +++ b/tests/rustdoc-js/sort-stability.js @@ -0,0 +1,9 @@ +const EXPECTED = [ + { + 'query': 'foo', + 'others': [ + {"path": "sort_stability::old", "name": "foo"}, + {"path": "sort_stability::new", "name": "foo"}, + ], + }, +]; diff --git a/tests/rustdoc-js/sort-stability.rs b/tests/rustdoc-js/sort-stability.rs new file mode 100644 index 000000000000..68662bb3aab6 --- /dev/null +++ b/tests/rustdoc-js/sort-stability.rs @@ -0,0 +1,16 @@ +#![feature(staged_api)] +#![stable(feature = "foo_lib", since = "1.0.0")] + +#[stable(feature = "old_foo", since = "1.0.1")] +pub mod old { + /// Old, stable foo + #[stable(feature = "old_foo", since = "1.0.1")] + pub fn foo() {} +} + +#[unstable(feature = "new_foo", issue = "none")] +pub mod new { + /// New, unstable foo + #[unstable(feature = "new_foo", issue = "none")] + pub fn foo() {} +} From 788fb08bdb375eb811d682b4f24566d0aad1baca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Fri, 18 Jul 2025 12:51:44 +0200 Subject: [PATCH 003/195] Reject relaxed bounds inside associated type bounds --- compiler/rustc_ast_lowering/src/lib.rs | 12 ++++++++---- .../src/hir_ty_lowering/bounds.rs | 8 +++----- tests/ui/trait-bounds/more_maybe_bounds.rs | 8 ++++++-- tests/ui/trait-bounds/more_maybe_bounds.stderr | 6 +++--- .../unsized/relaxed-bounds-invalid-places.rs | 4 ++++ .../relaxed-bounds-invalid-places.stderr | 18 +++++++++++++----- 6 files changed, 37 insertions(+), 19 deletions(-) diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 533ee9bff54f..d3e8f62618f8 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -296,6 +296,7 @@ enum RelaxedBoundPolicy<'a> { enum RelaxedBoundForbiddenReason { TraitObjectTy, SuperTrait, + AssocTyBounds, LateBoundVarsInScope, } @@ -1102,9 +1103,11 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { &*self.arena.alloc(self.ty(constraint.span, hir::TyKind::Err(guar))); hir::AssocItemConstraintKind::Equality { term: err_ty.into() } } else { - // FIXME(#135229): These should be forbidden! - let bounds = - self.lower_param_bounds(bounds, RelaxedBoundPolicy::Allowed, itctx); + let bounds = self.lower_param_bounds( + bounds, + RelaxedBoundPolicy::Forbidden(RelaxedBoundForbiddenReason::AssocTyBounds), + itctx, + ); hir::AssocItemConstraintKind::Bound { bounds } } } @@ -2117,7 +2120,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { diag.emit(); return; } - RelaxedBoundForbiddenReason::LateBoundVarsInScope => {} + RelaxedBoundForbiddenReason::AssocTyBounds + | RelaxedBoundForbiddenReason::LateBoundVarsInScope => {} }; } } diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs index d7a827c649dd..bd0d0d7c1062 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs @@ -199,12 +199,10 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { // However, this can easily get out of sync! Ideally, we would perform this step // where we are guaranteed to catch *all* bounds like in // `Self::lower_poly_trait_ref`. List of concrete issues: - // FIXME(more_maybe_bounds): We don't call this for e.g., trait object tys or - // supertrait bounds! + // FIXME(more_maybe_bounds): We don't call this for trait object tys, supertrait + // bounds or associated type bounds (ATB)! // FIXME(trait_alias, #143122): We don't call it for the RHS. Arguably however, - // AST lowering should reject them outright. - // FIXME(associated_type_bounds): We don't call this for them. However, AST - // lowering should reject them outright (#135229). + // AST lowering should reject them outright. let bounds = collect_relaxed_bounds(hir_bounds, self_ty_where_predicates); self.check_and_report_invalid_relaxed_bounds(bounds); } diff --git a/tests/ui/trait-bounds/more_maybe_bounds.rs b/tests/ui/trait-bounds/more_maybe_bounds.rs index 47348b0a0dd7..d367dd5b299d 100644 --- a/tests/ui/trait-bounds/more_maybe_bounds.rs +++ b/tests/ui/trait-bounds/more_maybe_bounds.rs @@ -1,7 +1,7 @@ // FIXME(more_maybe_bounds): Even under `more_maybe_bounds` / `-Zexperimental-default-bounds`, // trying to relax non-default bounds should still be an error in all contexts! As you can see -// there are places like supertrait bounds and trait object types where we currently don't perform -// this check. +// there are places like supertrait bounds, trait object types or associated type bounds (ATB) +// where we currently don't perform this check. #![feature(auto_traits, more_maybe_bounds, negative_impls)] trait Trait1 {} @@ -13,11 +13,15 @@ trait Trait4 where Self: Trait1 {} // FIXME: `?Trait2` should be rejected, `Trait2` isn't marked `#[lang = "default_traitN"]`. fn foo(_: Box<(dyn Trait3 + ?Trait2)>) {} + fn bar(_: &T) {} //~^ ERROR bound modifier `?` can only be applied to default traits like `Sized` //~| ERROR bound modifier `?` can only be applied to default traits like `Sized` //~| ERROR bound modifier `?` can only be applied to default traits like `Sized` +// FIXME: `?Trait1` should be rejected, `Trait1` isn't marked `#[lang = "default_traitN"]`. +fn baz() where T: Iterator {} + struct S; impl !Trait2 for S {} impl Trait1 for S {} diff --git a/tests/ui/trait-bounds/more_maybe_bounds.stderr b/tests/ui/trait-bounds/more_maybe_bounds.stderr index 09c9fc311657..8dd83fc7728a 100644 --- a/tests/ui/trait-bounds/more_maybe_bounds.stderr +++ b/tests/ui/trait-bounds/more_maybe_bounds.stderr @@ -1,17 +1,17 @@ error: bound modifier `?` can only be applied to default traits like `Sized` - --> $DIR/more_maybe_bounds.rs:16:20 + --> $DIR/more_maybe_bounds.rs:17:20 | LL | fn bar(_: &T) {} | ^^^^^^^ error: bound modifier `?` can only be applied to default traits like `Sized` - --> $DIR/more_maybe_bounds.rs:16:30 + --> $DIR/more_maybe_bounds.rs:17:30 | LL | fn bar(_: &T) {} | ^^^^^^^ error: bound modifier `?` can only be applied to default traits like `Sized` - --> $DIR/more_maybe_bounds.rs:16:40 + --> $DIR/more_maybe_bounds.rs:17:40 | LL | fn bar(_: &T) {} | ^^^^^^^ diff --git a/tests/ui/unsized/relaxed-bounds-invalid-places.rs b/tests/ui/unsized/relaxed-bounds-invalid-places.rs index b8eda1e7786b..4c1f242a01ce 100644 --- a/tests/ui/unsized/relaxed-bounds-invalid-places.rs +++ b/tests/ui/unsized/relaxed-bounds-invalid-places.rs @@ -22,6 +22,10 @@ impl S1 { fn f() where T: ?Sized {} //~ ERROR this relaxed bound is not permitted here } +// Test associated type bounds (ATB). +// issue: +struct S6(T) where T: Iterator; //~ ERROR this relaxed bound is not permitted here + trait Tr: ?Sized {} //~ ERROR relaxed bounds are not permitted in supertrait bounds // Test that relaxed `Sized` bounds are rejected in trait object types: diff --git a/tests/ui/unsized/relaxed-bounds-invalid-places.stderr b/tests/ui/unsized/relaxed-bounds-invalid-places.stderr index 30285d626936..d3f0535e2f0c 100644 --- a/tests/ui/unsized/relaxed-bounds-invalid-places.stderr +++ b/tests/ui/unsized/relaxed-bounds-invalid-places.stderr @@ -38,8 +38,16 @@ LL | fn f() where T: ?Sized {} | = note: in this context, relaxed bounds are only allowed on type parameters defined by the closest item +error: this relaxed bound is not permitted here + --> $DIR/relaxed-bounds-invalid-places.rs:27:41 + | +LL | struct S6(T) where T: Iterator; + | ^^^^^^ + | + = note: in this context, relaxed bounds are only allowed on type parameters defined by the closest item + error: relaxed bounds are not permitted in supertrait bounds - --> $DIR/relaxed-bounds-invalid-places.rs:25:11 + --> $DIR/relaxed-bounds-invalid-places.rs:29:11 | LL | trait Tr: ?Sized {} | ^^^^^^ @@ -47,19 +55,19 @@ LL | trait Tr: ?Sized {} = note: traits are `?Sized` by default error: relaxed bounds are not permitted in trait object types - --> $DIR/relaxed-bounds-invalid-places.rs:29:20 + --> $DIR/relaxed-bounds-invalid-places.rs:33:20 | LL | type O1 = dyn Tr + ?Sized; | ^^^^^^ error: relaxed bounds are not permitted in trait object types - --> $DIR/relaxed-bounds-invalid-places.rs:30:15 + --> $DIR/relaxed-bounds-invalid-places.rs:34:15 | LL | type O2 = dyn ?Sized + ?Sized + Tr; | ^^^^^^ error: relaxed bounds are not permitted in trait object types - --> $DIR/relaxed-bounds-invalid-places.rs:30:24 + --> $DIR/relaxed-bounds-invalid-places.rs:34:24 | LL | type O2 = dyn ?Sized + ?Sized + Tr; | ^^^^^^ @@ -76,5 +84,5 @@ error: bound modifier `?` can only be applied to `Sized` LL | struct S5(*const T) where T: ?Trait<'static> + ?Sized; | ^^^^^^^^^^^^^^^ -error: aborting due to 11 previous errors +error: aborting due to 12 previous errors From 30033b45bb2fcbc0f267acaf9fab22411422e166 Mon Sep 17 00:00:00 2001 From: gewitternacht <60887951+gewitternacht@users.noreply.github.com> Date: Tue, 22 Jul 2025 23:16:49 +0200 Subject: [PATCH 004/195] document assumptions about `Clone` and `Eq` traits --- library/core/src/clone.rs | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/library/core/src/clone.rs b/library/core/src/clone.rs index a34d1b4a0649..7afdf50ce51d 100644 --- a/library/core/src/clone.rs +++ b/library/core/src/clone.rs @@ -139,6 +139,30 @@ mod uninit; /// // Note: With the manual implementations the above line will compile. /// ``` /// +/// ## `Clone` and `PartialEq`/`Eq` +/// `Clone` is intended for the duplication of objects. Consequently, when implementing +/// both `Clone` and [`PartialEq`], the following property is expected to hold: +/// ```text +/// x == x -> x.clone() == x +/// ``` +/// In other words, if an object compares equal to itself, +/// its clone must also compare equal to the original. +/// +/// For types that also implement [`Eq`] – for which `x == x` always holds – +/// this implies that `x.clone() == x` must always be true. +/// Standard library collections such as +/// [`HashMap`], [`HashSet`], [`BTreeMap`], [`BTreeSet`] and [`BinaryHeap`] +/// rely on their keys respecting this property for correct behavior. +/// +/// This property is automatically satisfied when deriving both `Clone` and [`PartialEq`] +/// using `#[derive(Clone, PartialEq)]` or when additionally deriving [`Eq`] +/// using `#[derive(Clone, PartialEq, Eq)]`. +/// +/// Violating this property is a logic error. The behavior resulting from a logic error is not +/// specified, but users of the trait must ensure that such logic errors do *not* result in +/// undefined behavior. This means that `unsafe` code **must not** rely on the correctness of these +/// methods. +/// /// ## Additional implementors /// /// In addition to the [implementors listed below][impls], From cdbcd72f341c949e4fa11f58d76a094bd435d18f Mon Sep 17 00:00:00 2001 From: gewitternacht <60887951+gewitternacht@users.noreply.github.com> Date: Wed, 23 Jul 2025 00:41:48 +0200 Subject: [PATCH 005/195] remove trailing whitespace --- library/core/src/clone.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/core/src/clone.rs b/library/core/src/clone.rs index 7afdf50ce51d..64916f49f3a5 100644 --- a/library/core/src/clone.rs +++ b/library/core/src/clone.rs @@ -155,7 +155,7 @@ mod uninit; /// rely on their keys respecting this property for correct behavior. /// /// This property is automatically satisfied when deriving both `Clone` and [`PartialEq`] -/// using `#[derive(Clone, PartialEq)]` or when additionally deriving [`Eq`] +/// using `#[derive(Clone, PartialEq)]` or when additionally deriving [`Eq`] /// using `#[derive(Clone, PartialEq, Eq)]`. /// /// Violating this property is a logic error. The behavior resulting from a logic error is not From 623317eb5e2bf73b53a5cd52d693ac032cf5aa32 Mon Sep 17 00:00:00 2001 From: gewitternacht <60887951+gewitternacht@users.noreply.github.com> Date: Wed, 23 Jul 2025 01:44:18 +0200 Subject: [PATCH 006/195] add links to collections --- library/core/src/clone.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/library/core/src/clone.rs b/library/core/src/clone.rs index 64916f49f3a5..c4778edf0fcc 100644 --- a/library/core/src/clone.rs +++ b/library/core/src/clone.rs @@ -176,6 +176,11 @@ mod uninit; /// (even if the referent doesn't), /// while variables captured by mutable reference never implement `Clone`. /// +/// [`HashMap`]: ../../std/collections/struct.HashMap.html +/// [`HashSet`]: ../../std/collections/struct.HashSet.html +/// [`BTreeMap`]: ../../std/collections/struct.BTreeMap.html +/// [`BTreeSet`]: ../../std/collections/struct.BTreeSet.html +/// [`BinaryHeap`]: ../../std/collections/struct.BinaryHeap.html /// [impls]: #implementors #[stable(feature = "rust1", since = "1.0.0")] #[lang = "clone"] From a383fb0c738967ea8c6af200d8f4a37c751427c1 Mon Sep 17 00:00:00 2001 From: WANG Rui Date: Thu, 9 Jan 2025 20:35:49 +0800 Subject: [PATCH 007/195] asm: Stabilize loongarch32 --- compiler/rustc_ast_lowering/src/asm.rs | 1 + .../asm-experimental-arch.md | 5 -- tests/assembly-llvm/asm/loongarch-type.rs | 51 +++++++++------ .../bad-reg.loongarch32_ilp32d.stderr | 38 ++++++++++++ .../bad-reg.loongarch32_ilp32s.stderr | 62 +++++++++++++++++++ .../bad-reg.loongarch64_lp64d.stderr | 12 ++-- .../bad-reg.loongarch64_lp64s.stderr | 20 +++--- tests/ui/asm/loongarch/bad-reg.rs | 15 +++-- 8 files changed, 158 insertions(+), 46 deletions(-) create mode 100644 tests/ui/asm/loongarch/bad-reg.loongarch32_ilp32d.stderr create mode 100644 tests/ui/asm/loongarch/bad-reg.loongarch32_ilp32s.stderr diff --git a/compiler/rustc_ast_lowering/src/asm.rs b/compiler/rustc_ast_lowering/src/asm.rs index af279e07acc6..d44faad017ee 100644 --- a/compiler/rustc_ast_lowering/src/asm.rs +++ b/compiler/rustc_ast_lowering/src/asm.rs @@ -48,6 +48,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { | asm::InlineAsmArch::Arm64EC | asm::InlineAsmArch::RiscV32 | asm::InlineAsmArch::RiscV64 + | asm::InlineAsmArch::LoongArch32 | asm::InlineAsmArch::LoongArch64 | asm::InlineAsmArch::S390x ); diff --git a/src/doc/unstable-book/src/language-features/asm-experimental-arch.md b/src/doc/unstable-book/src/language-features/asm-experimental-arch.md index 121f94934359..d9566c9f55c5 100644 --- a/src/doc/unstable-book/src/language-features/asm-experimental-arch.md +++ b/src/doc/unstable-book/src/language-features/asm-experimental-arch.md @@ -19,7 +19,6 @@ This feature tracks `asm!` and `global_asm!` support for the following architect - M68k - CSKY - SPARC -- LoongArch32 ## Register classes @@ -54,8 +53,6 @@ This feature tracks `asm!` and `global_asm!` support for the following architect | CSKY | `freg` | `f[0-31]` | `f` | | SPARC | `reg` | `r[2-29]` | `r` | | SPARC | `yreg` | `y` | Only clobbers | -| LoongArch32 | `reg` | `$r1`, `$r[4-20]`, `$r[23,30]` | `r` | -| LoongArch32 | `freg` | `$f[0-31]` | `f` | > **Notes**: > - NVPTX doesn't have a fixed register set, so named registers are not supported. @@ -94,8 +91,6 @@ This feature tracks `asm!` and `global_asm!` support for the following architect | CSKY | `freg` | None | `f32`, | | SPARC | `reg` | None | `i8`, `i16`, `i32`, `i64` (SPARC64 only) | | SPARC | `yreg` | N/A | Only clobbers | -| LoongArch32 | `reg` | None | `i8`, `i16`, `i32`, `f32` | -| LoongArch32 | `freg` | None | `f32`, `f64` | ## Register aliases diff --git a/tests/assembly-llvm/asm/loongarch-type.rs b/tests/assembly-llvm/asm/loongarch-type.rs index c782be19f1d2..95d811a6bd0a 100644 --- a/tests/assembly-llvm/asm/loongarch-type.rs +++ b/tests/assembly-llvm/asm/loongarch-type.rs @@ -1,8 +1,15 @@ //@ add-core-stubs +//@ revisions: loongarch32 loongarch64 + //@ assembly-output: emit-asm -//@ compile-flags: --target loongarch64-unknown-linux-gnu + +//@[loongarch32] compile-flags: --target loongarch32-unknown-none +//@[loongarch32] needs-llvm-components: loongarch + +//@[loongarch64] compile-flags: --target loongarch64-unknown-none +//@[loongarch64] needs-llvm-components: loongarch + //@ compile-flags: -Zmerge-functions=disabled -//@ needs-llvm-components: loongarch #![feature(no_core, f16)] #![crate_type = "rlib"] @@ -22,7 +29,7 @@ extern "C" { // CHECK-LABEL: sym_fn: // CHECK: #APP // CHECK: pcalau12i $t0, %got_pc_hi20(extern_func) -// CHECK: ld.d $t0, $t0, %got_pc_lo12(extern_func) +// CHECK: ld.{{[wd]}} $t0, $t0, %got_pc_lo12(extern_func) // CHECK: #NO_APP #[no_mangle] pub unsafe fn sym_fn() { @@ -32,7 +39,7 @@ pub unsafe fn sym_fn() { // CHECK-LABEL: sym_static: // CHECK: #APP // CHECK: pcalau12i $t0, %got_pc_hi20(extern_static) -// CHECK: ld.d $t0, $t0, %got_pc_lo12(extern_static) +// CHECK: ld.{{[wd]}} $t0, $t0, %got_pc_lo12(extern_static) // CHECK: #NO_APP #[no_mangle] pub unsafe fn sym_static() { @@ -87,16 +94,18 @@ check!(reg_i32, i32, reg, "move"); // CHECK: #NO_APP check!(reg_f32, f32, reg, "move"); -// CHECK-LABEL: reg_i64: -// CHECK: #APP -// CHECK: move ${{[a-z0-9]+}}, ${{[a-z0-9]+}} -// CHECK: #NO_APP +// loongarch64-LABEL: reg_i64: +// loongarch64: #APP +// loongarch64: move ${{[a-z0-9]+}}, ${{[a-z0-9]+}} +// loongarch64: #NO_APP +#[cfg(loongarch64)] check!(reg_i64, i64, reg, "move"); -// CHECK-LABEL: reg_f64: -// CHECK: #APP -// CHECK: move ${{[a-z0-9]+}}, ${{[a-z0-9]+}} -// CHECK: #NO_APP +// loongarch64-LABEL: reg_f64: +// loongarch64: #APP +// loongarch64: move ${{[a-z0-9]+}}, ${{[a-z0-9]+}} +// loongarch64: #NO_APP +#[cfg(loongarch64)] check!(reg_f64, f64, reg, "move"); // CHECK-LABEL: reg_ptr: @@ -153,16 +162,18 @@ check_reg!(r4_i32, i32, "$r4", "move"); // CHECK: #NO_APP check_reg!(r4_f32, f32, "$r4", "move"); -// CHECK-LABEL: r4_i64: -// CHECK: #APP -// CHECK: move $a0, $a0 -// CHECK: #NO_APP +// loongarch64-LABEL: r4_i64: +// loongarch64: #APP +// loongarch64: move $a0, $a0 +// loongarch64: #NO_APP +#[cfg(loongarch64)] check_reg!(r4_i64, i64, "$r4", "move"); -// CHECK-LABEL: r4_f64: -// CHECK: #APP -// CHECK: move $a0, $a0 -// CHECK: #NO_APP +// loongarch64-LABEL: r4_f64: +// loongarch64: #APP +// loongarch64: move $a0, $a0 +// loongarch64: #NO_APP +#[cfg(loongarch64)] check_reg!(r4_f64, f64, "$r4", "move"); // CHECK-LABEL: r4_ptr: diff --git a/tests/ui/asm/loongarch/bad-reg.loongarch32_ilp32d.stderr b/tests/ui/asm/loongarch/bad-reg.loongarch32_ilp32d.stderr new file mode 100644 index 000000000000..8742d4bd82cd --- /dev/null +++ b/tests/ui/asm/loongarch/bad-reg.loongarch32_ilp32d.stderr @@ -0,0 +1,38 @@ +error: invalid register `$r0`: constant zero cannot be used as an operand for inline asm + --> $DIR/bad-reg.rs:27:18 + | +LL | asm!("", out("$r0") _); + | ^^^^^^^^^^^^ + +error: invalid register `$tp`: reserved for TLS + --> $DIR/bad-reg.rs:29:18 + | +LL | asm!("", out("$tp") _); + | ^^^^^^^^^^^^ + +error: invalid register `$sp`: the stack pointer cannot be used as an operand for inline asm + --> $DIR/bad-reg.rs:31:18 + | +LL | asm!("", out("$sp") _); + | ^^^^^^^^^^^^ + +error: invalid register `$r21`: reserved by the ABI + --> $DIR/bad-reg.rs:33:18 + | +LL | asm!("", out("$r21") _); + | ^^^^^^^^^^^^^ + +error: invalid register `$fp`: the frame pointer cannot be used as an operand for inline asm + --> $DIR/bad-reg.rs:35:18 + | +LL | asm!("", out("$fp") _); + | ^^^^^^^^^^^^ + +error: invalid register `$r31`: $r31 is used internally by LLVM and cannot be used as an operand for inline asm + --> $DIR/bad-reg.rs:37:18 + | +LL | asm!("", out("$r31") _); + | ^^^^^^^^^^^^^ + +error: aborting due to 6 previous errors + diff --git a/tests/ui/asm/loongarch/bad-reg.loongarch32_ilp32s.stderr b/tests/ui/asm/loongarch/bad-reg.loongarch32_ilp32s.stderr new file mode 100644 index 000000000000..e6cb6e40c701 --- /dev/null +++ b/tests/ui/asm/loongarch/bad-reg.loongarch32_ilp32s.stderr @@ -0,0 +1,62 @@ +error: invalid register `$r0`: constant zero cannot be used as an operand for inline asm + --> $DIR/bad-reg.rs:27:18 + | +LL | asm!("", out("$r0") _); + | ^^^^^^^^^^^^ + +error: invalid register `$tp`: reserved for TLS + --> $DIR/bad-reg.rs:29:18 + | +LL | asm!("", out("$tp") _); + | ^^^^^^^^^^^^ + +error: invalid register `$sp`: the stack pointer cannot be used as an operand for inline asm + --> $DIR/bad-reg.rs:31:18 + | +LL | asm!("", out("$sp") _); + | ^^^^^^^^^^^^ + +error: invalid register `$r21`: reserved by the ABI + --> $DIR/bad-reg.rs:33:18 + | +LL | asm!("", out("$r21") _); + | ^^^^^^^^^^^^^ + +error: invalid register `$fp`: the frame pointer cannot be used as an operand for inline asm + --> $DIR/bad-reg.rs:35:18 + | +LL | asm!("", out("$fp") _); + | ^^^^^^^^^^^^ + +error: invalid register `$r31`: $r31 is used internally by LLVM and cannot be used as an operand for inline asm + --> $DIR/bad-reg.rs:37:18 + | +LL | asm!("", out("$r31") _); + | ^^^^^^^^^^^^^ + +error: register class `freg` requires at least one of the following target features: d, f + --> $DIR/bad-reg.rs:41:26 + | +LL | asm!("/* {} */", in(freg) f); + | ^^^^^^^^^^ + +error: register class `freg` requires at least one of the following target features: d, f + --> $DIR/bad-reg.rs:43:26 + | +LL | asm!("/* {} */", out(freg) _); + | ^^^^^^^^^^^ + +error: register class `freg` requires at least one of the following target features: d, f + --> $DIR/bad-reg.rs:45:26 + | +LL | asm!("/* {} */", in(freg) d); + | ^^^^^^^^^^ + +error: register class `freg` requires at least one of the following target features: d, f + --> $DIR/bad-reg.rs:47:26 + | +LL | asm!("/* {} */", out(freg) d); + | ^^^^^^^^^^^ + +error: aborting due to 10 previous errors + diff --git a/tests/ui/asm/loongarch/bad-reg.loongarch64_lp64d.stderr b/tests/ui/asm/loongarch/bad-reg.loongarch64_lp64d.stderr index 0e5441196501..8742d4bd82cd 100644 --- a/tests/ui/asm/loongarch/bad-reg.loongarch64_lp64d.stderr +++ b/tests/ui/asm/loongarch/bad-reg.loongarch64_lp64d.stderr @@ -1,35 +1,35 @@ error: invalid register `$r0`: constant zero cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:22:18 + --> $DIR/bad-reg.rs:27:18 | LL | asm!("", out("$r0") _); | ^^^^^^^^^^^^ error: invalid register `$tp`: reserved for TLS - --> $DIR/bad-reg.rs:24:18 + --> $DIR/bad-reg.rs:29:18 | LL | asm!("", out("$tp") _); | ^^^^^^^^^^^^ error: invalid register `$sp`: the stack pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:26:18 + --> $DIR/bad-reg.rs:31:18 | LL | asm!("", out("$sp") _); | ^^^^^^^^^^^^ error: invalid register `$r21`: reserved by the ABI - --> $DIR/bad-reg.rs:28:18 + --> $DIR/bad-reg.rs:33:18 | LL | asm!("", out("$r21") _); | ^^^^^^^^^^^^^ error: invalid register `$fp`: the frame pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:30:18 + --> $DIR/bad-reg.rs:35:18 | LL | asm!("", out("$fp") _); | ^^^^^^^^^^^^ error: invalid register `$r31`: $r31 is used internally by LLVM and cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:32:18 + --> $DIR/bad-reg.rs:37:18 | LL | asm!("", out("$r31") _); | ^^^^^^^^^^^^^ diff --git a/tests/ui/asm/loongarch/bad-reg.loongarch64_lp64s.stderr b/tests/ui/asm/loongarch/bad-reg.loongarch64_lp64s.stderr index 6d0410dc6a13..e6cb6e40c701 100644 --- a/tests/ui/asm/loongarch/bad-reg.loongarch64_lp64s.stderr +++ b/tests/ui/asm/loongarch/bad-reg.loongarch64_lp64s.stderr @@ -1,59 +1,59 @@ error: invalid register `$r0`: constant zero cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:22:18 + --> $DIR/bad-reg.rs:27:18 | LL | asm!("", out("$r0") _); | ^^^^^^^^^^^^ error: invalid register `$tp`: reserved for TLS - --> $DIR/bad-reg.rs:24:18 + --> $DIR/bad-reg.rs:29:18 | LL | asm!("", out("$tp") _); | ^^^^^^^^^^^^ error: invalid register `$sp`: the stack pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:26:18 + --> $DIR/bad-reg.rs:31:18 | LL | asm!("", out("$sp") _); | ^^^^^^^^^^^^ error: invalid register `$r21`: reserved by the ABI - --> $DIR/bad-reg.rs:28:18 + --> $DIR/bad-reg.rs:33:18 | LL | asm!("", out("$r21") _); | ^^^^^^^^^^^^^ error: invalid register `$fp`: the frame pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:30:18 + --> $DIR/bad-reg.rs:35:18 | LL | asm!("", out("$fp") _); | ^^^^^^^^^^^^ error: invalid register `$r31`: $r31 is used internally by LLVM and cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:32:18 + --> $DIR/bad-reg.rs:37:18 | LL | asm!("", out("$r31") _); | ^^^^^^^^^^^^^ error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:36:26 + --> $DIR/bad-reg.rs:41:26 | LL | asm!("/* {} */", in(freg) f); | ^^^^^^^^^^ error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:38:26 + --> $DIR/bad-reg.rs:43:26 | LL | asm!("/* {} */", out(freg) _); | ^^^^^^^^^^^ error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:40:26 + --> $DIR/bad-reg.rs:45:26 | LL | asm!("/* {} */", in(freg) d); | ^^^^^^^^^^ error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:42:26 + --> $DIR/bad-reg.rs:47:26 | LL | asm!("/* {} */", out(freg) d); | ^^^^^^^^^^^ diff --git a/tests/ui/asm/loongarch/bad-reg.rs b/tests/ui/asm/loongarch/bad-reg.rs index 685b460bc922..0d3eba07f14d 100644 --- a/tests/ui/asm/loongarch/bad-reg.rs +++ b/tests/ui/asm/loongarch/bad-reg.rs @@ -1,6 +1,11 @@ //@ add-core-stubs //@ needs-asm-support -//@ revisions: loongarch64_lp64d loongarch64_lp64s +//@ revisions: loongarch32_ilp32d loongarch32_ilp32s loongarch64_lp64d loongarch64_lp64s +//@ min-llvm-version: 20 +//@[loongarch32_ilp32d] compile-flags: --target loongarch32-unknown-none +//@[loongarch32_ilp32d] needs-llvm-components: loongarch +//@[loongarch32_ilp32s] compile-flags: --target loongarch32-unknown-none-softfloat +//@[loongarch32_ilp32s] needs-llvm-components: loongarch //@[loongarch64_lp64d] compile-flags: --target loongarch64-unknown-linux-gnu //@[loongarch64_lp64d] needs-llvm-components: loongarch //@[loongarch64_lp64s] compile-flags: --target loongarch64-unknown-none-softfloat @@ -34,12 +39,12 @@ fn f() { asm!("", out("$f0") _); // ok asm!("/* {} */", in(freg) f); - //[loongarch64_lp64s]~^ ERROR register class `freg` requires at least one of the following target features: d, f + //[loongarch32_ilp32s,loongarch64_lp64s]~^ ERROR register class `freg` requires at least one of the following target features: d, f asm!("/* {} */", out(freg) _); - //[loongarch64_lp64s]~^ ERROR register class `freg` requires at least one of the following target features: d, f + //[loongarch32_ilp32s,loongarch64_lp64s]~^ ERROR register class `freg` requires at least one of the following target features: d, f asm!("/* {} */", in(freg) d); - //[loongarch64_lp64s]~^ ERROR register class `freg` requires at least one of the following target features: d, f + //[loongarch32_ilp32s,loongarch64_lp64s]~^ ERROR register class `freg` requires at least one of the following target features: d, f asm!("/* {} */", out(freg) d); - //[loongarch64_lp64s]~^ ERROR register class `freg` requires at least one of the following target features: d, f + //[loongarch32_ilp32s,loongarch64_lp64s]~^ ERROR register class `freg` requires at least one of the following target features: d, f } } From 90bb5cacb5c1a5fe20ba821d28e7eb7a21e35d09 Mon Sep 17 00:00:00 2001 From: Kivooeo Date: Thu, 24 Jul 2025 18:29:09 +0500 Subject: [PATCH 008/195] moved 34 tests to organized locations --- .../issue-16256.rs => closures/unused-closure-warning-16256.rs} | 0 .../array-slice-coercion-mismatch-15783.rs} | 0 .../derive-partial-ord-discriminant-64bit.rs} | 0 .../issue-15523.rs => derives/derive-partial-ord-discriminant.rs} | 0 .../ui/{issues/issue-15763.rs => drop/early-return-drop-order.rs} | 0 tests/ui/{issues/issue-15063.rs => drop/enum-drop-impl-15063.rs} | 0 .../issue-15858.rs => drop/generic-drop-trait-bound-15858.rs} | 0 .../ui/{issues/issue-16492.rs => drop/struct-field-drop-order.rs} | 0 .../issue-16151.rs => drop/vec-replace-skip-while-drop.rs} | 0 .../issue-16441.rs => extern/empty-struct-extern-fn-16441.rs} | 0 .../issue-15094.rs => fn/fn-traits-call-once-signature.rs} | 0 .../issue-15774.rs => imports/enum-variant-import-path-15774.rs} | 0 .../return-block-type-inference-15965.rs} | 0 .../issue-15756.rs => iterators/chunk-iterator-mut-pattern.rs} | 0 .../issue-15673.rs => iterators/iterator-sum-array-15673.rs} | 0 .../explicit-lifetime-required-15034.rs} | 0 .../struct-lifetime-inference-15735.rs} | 0 .../issue-15167.rs => macros/macro-hygiene-scope-15167.rs} | 0 .../issue-15189.rs => macros/macro-variable-capture-15189.rs} | 0 .../issue-15793.rs => match/nested-enum-match-optimization.rs} | 0 .../issue-15571.rs => moves/match-move-same-binding-15571.rs} | 0 .../issue-15207.rs => never/never-type-method-call-15207.rs} | 0 .../issue-15896.rs => pattern/enum-struct-pattern-mismatch.rs} | 0 .../refutable-pattern-for-loop-15381.rs} | 0 .../issue-15104.rs => pattern/slice-pattern-recursion-15104.rs} | 0 .../issue-16149.rs => pattern/static-binding-shadow-16149.rs} | 0 .../issue-15260.rs => pattern/struct-field-duplicate-binding.rs} | 0 .../issue-15129-rpass.rs => pattern/tuple-enum-match-15129.rs} | 0 .../unit-type-struct-pattern-mismatch.rs} | 0 .../issue-16452.rs => statics/conditional-static-declaration.rs} | 0 .../issue-15043.rs => structs/static-struct-init-15043.rs} | 0 .../{issues/issue-15444.rs => traits/fn-type-trait-impl-15444.rs} | 0 .../issue-15734.rs => traits/index-trait-multiple-impls.rs} | 0 .../issue-16048.rs => traits/lifetime-mismatch-trait-impl.rs} | 0 34 files changed, 0 insertions(+), 0 deletions(-) rename tests/ui/{issues/issue-16256.rs => closures/unused-closure-warning-16256.rs} (100%) rename tests/ui/{issues/issue-15783.rs => coercion/array-slice-coercion-mismatch-15783.rs} (100%) rename tests/ui/{issues/issue-15523-big.rs => derives/derive-partial-ord-discriminant-64bit.rs} (100%) rename tests/ui/{issues/issue-15523.rs => derives/derive-partial-ord-discriminant.rs} (100%) rename tests/ui/{issues/issue-15763.rs => drop/early-return-drop-order.rs} (100%) rename tests/ui/{issues/issue-15063.rs => drop/enum-drop-impl-15063.rs} (100%) rename tests/ui/{issues/issue-15858.rs => drop/generic-drop-trait-bound-15858.rs} (100%) rename tests/ui/{issues/issue-16492.rs => drop/struct-field-drop-order.rs} (100%) rename tests/ui/{issues/issue-16151.rs => drop/vec-replace-skip-while-drop.rs} (100%) rename tests/ui/{issues/issue-16441.rs => extern/empty-struct-extern-fn-16441.rs} (100%) rename tests/ui/{issues/issue-15094.rs => fn/fn-traits-call-once-signature.rs} (100%) rename tests/ui/{issues/issue-15774.rs => imports/enum-variant-import-path-15774.rs} (100%) rename tests/ui/{issues/issue-15965.rs => inference/return-block-type-inference-15965.rs} (100%) rename tests/ui/{issues/issue-15756.rs => iterators/chunk-iterator-mut-pattern.rs} (100%) rename tests/ui/{issues/issue-15673.rs => iterators/iterator-sum-array-15673.rs} (100%) rename tests/ui/{issues/issue-15034.rs => lifetimes/explicit-lifetime-required-15034.rs} (100%) rename tests/ui/{issues/issue-15735.rs => lifetimes/struct-lifetime-inference-15735.rs} (100%) rename tests/ui/{issues/issue-15167.rs => macros/macro-hygiene-scope-15167.rs} (100%) rename tests/ui/{issues/issue-15189.rs => macros/macro-variable-capture-15189.rs} (100%) rename tests/ui/{issues/issue-15793.rs => match/nested-enum-match-optimization.rs} (100%) rename tests/ui/{issues/issue-15571.rs => moves/match-move-same-binding-15571.rs} (100%) rename tests/ui/{issues/issue-15207.rs => never/never-type-method-call-15207.rs} (100%) rename tests/ui/{issues/issue-15896.rs => pattern/enum-struct-pattern-mismatch.rs} (100%) rename tests/ui/{issues/issue-15381.rs => pattern/refutable-pattern-for-loop-15381.rs} (100%) rename tests/ui/{issues/issue-15104.rs => pattern/slice-pattern-recursion-15104.rs} (100%) rename tests/ui/{issues/issue-16149.rs => pattern/static-binding-shadow-16149.rs} (100%) rename tests/ui/{issues/issue-15260.rs => pattern/struct-field-duplicate-binding.rs} (100%) rename tests/ui/{issues/issue-15129-rpass.rs => pattern/tuple-enum-match-15129.rs} (100%) rename tests/ui/{issues/issue-16401.rs => pattern/unit-type-struct-pattern-mismatch.rs} (100%) rename tests/ui/{issues/issue-16452.rs => statics/conditional-static-declaration.rs} (100%) rename tests/ui/{issues/issue-15043.rs => structs/static-struct-init-15043.rs} (100%) rename tests/ui/{issues/issue-15444.rs => traits/fn-type-trait-impl-15444.rs} (100%) rename tests/ui/{issues/issue-15734.rs => traits/index-trait-multiple-impls.rs} (100%) rename tests/ui/{issues/issue-16048.rs => traits/lifetime-mismatch-trait-impl.rs} (100%) diff --git a/tests/ui/issues/issue-16256.rs b/tests/ui/closures/unused-closure-warning-16256.rs similarity index 100% rename from tests/ui/issues/issue-16256.rs rename to tests/ui/closures/unused-closure-warning-16256.rs diff --git a/tests/ui/issues/issue-15783.rs b/tests/ui/coercion/array-slice-coercion-mismatch-15783.rs similarity index 100% rename from tests/ui/issues/issue-15783.rs rename to tests/ui/coercion/array-slice-coercion-mismatch-15783.rs diff --git a/tests/ui/issues/issue-15523-big.rs b/tests/ui/derives/derive-partial-ord-discriminant-64bit.rs similarity index 100% rename from tests/ui/issues/issue-15523-big.rs rename to tests/ui/derives/derive-partial-ord-discriminant-64bit.rs diff --git a/tests/ui/issues/issue-15523.rs b/tests/ui/derives/derive-partial-ord-discriminant.rs similarity index 100% rename from tests/ui/issues/issue-15523.rs rename to tests/ui/derives/derive-partial-ord-discriminant.rs diff --git a/tests/ui/issues/issue-15763.rs b/tests/ui/drop/early-return-drop-order.rs similarity index 100% rename from tests/ui/issues/issue-15763.rs rename to tests/ui/drop/early-return-drop-order.rs diff --git a/tests/ui/issues/issue-15063.rs b/tests/ui/drop/enum-drop-impl-15063.rs similarity index 100% rename from tests/ui/issues/issue-15063.rs rename to tests/ui/drop/enum-drop-impl-15063.rs diff --git a/tests/ui/issues/issue-15858.rs b/tests/ui/drop/generic-drop-trait-bound-15858.rs similarity index 100% rename from tests/ui/issues/issue-15858.rs rename to tests/ui/drop/generic-drop-trait-bound-15858.rs diff --git a/tests/ui/issues/issue-16492.rs b/tests/ui/drop/struct-field-drop-order.rs similarity index 100% rename from tests/ui/issues/issue-16492.rs rename to tests/ui/drop/struct-field-drop-order.rs diff --git a/tests/ui/issues/issue-16151.rs b/tests/ui/drop/vec-replace-skip-while-drop.rs similarity index 100% rename from tests/ui/issues/issue-16151.rs rename to tests/ui/drop/vec-replace-skip-while-drop.rs diff --git a/tests/ui/issues/issue-16441.rs b/tests/ui/extern/empty-struct-extern-fn-16441.rs similarity index 100% rename from tests/ui/issues/issue-16441.rs rename to tests/ui/extern/empty-struct-extern-fn-16441.rs diff --git a/tests/ui/issues/issue-15094.rs b/tests/ui/fn/fn-traits-call-once-signature.rs similarity index 100% rename from tests/ui/issues/issue-15094.rs rename to tests/ui/fn/fn-traits-call-once-signature.rs diff --git a/tests/ui/issues/issue-15774.rs b/tests/ui/imports/enum-variant-import-path-15774.rs similarity index 100% rename from tests/ui/issues/issue-15774.rs rename to tests/ui/imports/enum-variant-import-path-15774.rs diff --git a/tests/ui/issues/issue-15965.rs b/tests/ui/inference/return-block-type-inference-15965.rs similarity index 100% rename from tests/ui/issues/issue-15965.rs rename to tests/ui/inference/return-block-type-inference-15965.rs diff --git a/tests/ui/issues/issue-15756.rs b/tests/ui/iterators/chunk-iterator-mut-pattern.rs similarity index 100% rename from tests/ui/issues/issue-15756.rs rename to tests/ui/iterators/chunk-iterator-mut-pattern.rs diff --git a/tests/ui/issues/issue-15673.rs b/tests/ui/iterators/iterator-sum-array-15673.rs similarity index 100% rename from tests/ui/issues/issue-15673.rs rename to tests/ui/iterators/iterator-sum-array-15673.rs diff --git a/tests/ui/issues/issue-15034.rs b/tests/ui/lifetimes/explicit-lifetime-required-15034.rs similarity index 100% rename from tests/ui/issues/issue-15034.rs rename to tests/ui/lifetimes/explicit-lifetime-required-15034.rs diff --git a/tests/ui/issues/issue-15735.rs b/tests/ui/lifetimes/struct-lifetime-inference-15735.rs similarity index 100% rename from tests/ui/issues/issue-15735.rs rename to tests/ui/lifetimes/struct-lifetime-inference-15735.rs diff --git a/tests/ui/issues/issue-15167.rs b/tests/ui/macros/macro-hygiene-scope-15167.rs similarity index 100% rename from tests/ui/issues/issue-15167.rs rename to tests/ui/macros/macro-hygiene-scope-15167.rs diff --git a/tests/ui/issues/issue-15189.rs b/tests/ui/macros/macro-variable-capture-15189.rs similarity index 100% rename from tests/ui/issues/issue-15189.rs rename to tests/ui/macros/macro-variable-capture-15189.rs diff --git a/tests/ui/issues/issue-15793.rs b/tests/ui/match/nested-enum-match-optimization.rs similarity index 100% rename from tests/ui/issues/issue-15793.rs rename to tests/ui/match/nested-enum-match-optimization.rs diff --git a/tests/ui/issues/issue-15571.rs b/tests/ui/moves/match-move-same-binding-15571.rs similarity index 100% rename from tests/ui/issues/issue-15571.rs rename to tests/ui/moves/match-move-same-binding-15571.rs diff --git a/tests/ui/issues/issue-15207.rs b/tests/ui/never/never-type-method-call-15207.rs similarity index 100% rename from tests/ui/issues/issue-15207.rs rename to tests/ui/never/never-type-method-call-15207.rs diff --git a/tests/ui/issues/issue-15896.rs b/tests/ui/pattern/enum-struct-pattern-mismatch.rs similarity index 100% rename from tests/ui/issues/issue-15896.rs rename to tests/ui/pattern/enum-struct-pattern-mismatch.rs diff --git a/tests/ui/issues/issue-15381.rs b/tests/ui/pattern/refutable-pattern-for-loop-15381.rs similarity index 100% rename from tests/ui/issues/issue-15381.rs rename to tests/ui/pattern/refutable-pattern-for-loop-15381.rs diff --git a/tests/ui/issues/issue-15104.rs b/tests/ui/pattern/slice-pattern-recursion-15104.rs similarity index 100% rename from tests/ui/issues/issue-15104.rs rename to tests/ui/pattern/slice-pattern-recursion-15104.rs diff --git a/tests/ui/issues/issue-16149.rs b/tests/ui/pattern/static-binding-shadow-16149.rs similarity index 100% rename from tests/ui/issues/issue-16149.rs rename to tests/ui/pattern/static-binding-shadow-16149.rs diff --git a/tests/ui/issues/issue-15260.rs b/tests/ui/pattern/struct-field-duplicate-binding.rs similarity index 100% rename from tests/ui/issues/issue-15260.rs rename to tests/ui/pattern/struct-field-duplicate-binding.rs diff --git a/tests/ui/issues/issue-15129-rpass.rs b/tests/ui/pattern/tuple-enum-match-15129.rs similarity index 100% rename from tests/ui/issues/issue-15129-rpass.rs rename to tests/ui/pattern/tuple-enum-match-15129.rs diff --git a/tests/ui/issues/issue-16401.rs b/tests/ui/pattern/unit-type-struct-pattern-mismatch.rs similarity index 100% rename from tests/ui/issues/issue-16401.rs rename to tests/ui/pattern/unit-type-struct-pattern-mismatch.rs diff --git a/tests/ui/issues/issue-16452.rs b/tests/ui/statics/conditional-static-declaration.rs similarity index 100% rename from tests/ui/issues/issue-16452.rs rename to tests/ui/statics/conditional-static-declaration.rs diff --git a/tests/ui/issues/issue-15043.rs b/tests/ui/structs/static-struct-init-15043.rs similarity index 100% rename from tests/ui/issues/issue-15043.rs rename to tests/ui/structs/static-struct-init-15043.rs diff --git a/tests/ui/issues/issue-15444.rs b/tests/ui/traits/fn-type-trait-impl-15444.rs similarity index 100% rename from tests/ui/issues/issue-15444.rs rename to tests/ui/traits/fn-type-trait-impl-15444.rs diff --git a/tests/ui/issues/issue-15734.rs b/tests/ui/traits/index-trait-multiple-impls.rs similarity index 100% rename from tests/ui/issues/issue-15734.rs rename to tests/ui/traits/index-trait-multiple-impls.rs diff --git a/tests/ui/issues/issue-16048.rs b/tests/ui/traits/lifetime-mismatch-trait-impl.rs similarity index 100% rename from tests/ui/issues/issue-16048.rs rename to tests/ui/traits/lifetime-mismatch-trait-impl.rs From c72bb70ffd284538519242663fa9443037f4d41e Mon Sep 17 00:00:00 2001 From: gewitternacht <60887951+gewitternacht@users.noreply.github.com> Date: Tue, 29 Jul 2025 08:39:28 +0200 Subject: [PATCH 009/195] clearer wording for `unsafe` code --- library/core/src/clone.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/core/src/clone.rs b/library/core/src/clone.rs index c4778edf0fcc..6fcba298a432 100644 --- a/library/core/src/clone.rs +++ b/library/core/src/clone.rs @@ -160,8 +160,8 @@ mod uninit; /// /// Violating this property is a logic error. The behavior resulting from a logic error is not /// specified, but users of the trait must ensure that such logic errors do *not* result in -/// undefined behavior. This means that `unsafe` code **must not** rely on the correctness of these -/// methods. +/// undefined behavior. This means that `unsafe` code **must not** rely on this property +/// being satisfied. /// /// ## Additional implementors /// From 36f2045a930a3367963881db4f423d7de33b46a5 Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Sat, 12 Jul 2025 13:47:45 +0000 Subject: [PATCH 010/195] Implement `stability_implications` without a visitor. --- compiler/rustc_hir/src/def.rs | 41 +++++++++ compiler/rustc_passes/src/stability.rs | 111 +++++++------------------ 2 files changed, 72 insertions(+), 80 deletions(-) diff --git a/compiler/rustc_hir/src/def.rs b/compiler/rustc_hir/src/def.rs index 3fee9af01b36..8774fb4d5858 100644 --- a/compiler/rustc_hir/src/def.rs +++ b/compiler/rustc_hir/src/def.rs @@ -305,6 +305,11 @@ impl DefKind { matches!(self, DefKind::Mod | DefKind::Enum | DefKind::Trait) } + #[inline] + pub fn is_adt(self) -> bool { + matches!(self, DefKind::Struct | DefKind::Union | DefKind::Enum) + } + #[inline] pub fn is_fn_like(self) -> bool { matches!( @@ -313,6 +318,42 @@ impl DefKind { ) } + pub fn has_generics(self) -> bool { + match self { + DefKind::AnonConst + | DefKind::AssocConst + | DefKind::AssocFn + | DefKind::AssocTy + | DefKind::Closure + | DefKind::Const + | DefKind::Ctor(..) + | DefKind::Enum + | DefKind::Field + | DefKind::Fn + | DefKind::ForeignTy + | DefKind::Impl { .. } + | DefKind::InlineConst + | DefKind::OpaqueTy + | DefKind::Static { .. } + | DefKind::Struct + | DefKind::SyntheticCoroutineBody + | DefKind::Trait + | DefKind::TraitAlias + | DefKind::TyAlias + | DefKind::Union + | DefKind::Variant => true, + DefKind::ConstParam + | DefKind::ExternCrate + | DefKind::ForeignMod + | DefKind::GlobalAsm + | DefKind::LifetimeParam + | DefKind::Macro(_) + | DefKind::Mod + | DefKind::TyParam + | DefKind::Use => false, + } + } + /// Whether `query get_codegen_attrs` should be used with this definition. pub fn has_codegen_attrs(self) -> bool { match self { diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs index 9ed7293873f5..abc1821e76d1 100644 --- a/compiler/rustc_passes/src/stability.rs +++ b/compiler/rustc_passes/src/stability.rs @@ -267,93 +267,51 @@ fn lookup_const_stability(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option { - tcx: TyCtxt<'tcx>, - implications: UnordMap, -} +fn stability_implications(tcx: TyCtxt<'_>, LocalCrate: LocalCrate) -> UnordMap { + let mut implications = UnordMap::default(); -impl<'tcx> Annotator<'tcx> { - /// Determine the stability for a node based on its attributes and inherited stability. The - /// stability is recorded in the index and used as the parent. If the node is a function, - /// `fn_sig` is its signature. - #[instrument(level = "trace", skip(self))] - fn annotate(&mut self, def_id: LocalDefId) { - if !self.tcx.features().staged_api() { - return; - } - - if let Some(stability) = self.tcx.lookup_stability(def_id) + let mut register_implication = |def_id| { + if let Some(stability) = tcx.lookup_stability(def_id) && let StabilityLevel::Unstable { implied_by: Some(implied_by), .. } = stability.level { - self.implications.insert(implied_by, stability.feature); + implications.insert(implied_by, stability.feature); } - if let Some(stability) = self.tcx.lookup_const_stability(def_id) + if let Some(stability) = tcx.lookup_const_stability(def_id) && let StabilityLevel::Unstable { implied_by: Some(implied_by), .. } = stability.level { - self.implications.insert(implied_by, stability.feature); + implications.insert(implied_by, stability.feature); } - } -} + }; -impl<'tcx> Visitor<'tcx> for Annotator<'tcx> { - /// Because stability levels are scoped lexically, we want to walk - /// nested items in the context of the outer item, so enable - /// deep-walking. - type NestedFilter = nested_filter::All; - - fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt { - self.tcx - } - - fn visit_item(&mut self, i: &'tcx Item<'tcx>) { - match i.kind { - hir::ItemKind::Struct(_, _, ref sd) => { - if let Some(ctor_def_id) = sd.ctor_def_id() { - self.annotate(ctor_def_id); + if tcx.features().staged_api() { + register_implication(CRATE_DEF_ID); + for def_id in tcx.hir_crate_items(()).definitions() { + register_implication(def_id); + let def_kind = tcx.def_kind(def_id); + if def_kind.is_adt() { + let adt = tcx.adt_def(def_id); + for variant in adt.variants() { + if variant.def_id != def_id.to_def_id() { + register_implication(variant.def_id.expect_local()); + } + for field in &variant.fields { + register_implication(field.did.expect_local()); + } + if let Some(ctor_def_id) = variant.ctor_def_id() { + register_implication(ctor_def_id.expect_local()) + } + } + } + if def_kind.has_generics() { + for param in tcx.generics_of(def_id).own_params.iter() { + register_implication(param.def_id.expect_local()) } } - _ => {} } - - self.annotate(i.owner_id.def_id); - intravisit::walk_item(self, i) } - fn visit_trait_item(&mut self, ti: &'tcx hir::TraitItem<'tcx>) { - self.annotate(ti.owner_id.def_id); - intravisit::walk_trait_item(self, ti); - } - - fn visit_impl_item(&mut self, ii: &'tcx hir::ImplItem<'tcx>) { - self.annotate(ii.owner_id.def_id); - intravisit::walk_impl_item(self, ii); - } - - fn visit_variant(&mut self, var: &'tcx Variant<'tcx>) { - self.annotate(var.def_id); - if let Some(ctor_def_id) = var.data.ctor_def_id() { - self.annotate(ctor_def_id); - } - - intravisit::walk_variant(self, var) - } - - fn visit_field_def(&mut self, s: &'tcx FieldDef<'tcx>) { - self.annotate(s.def_id); - intravisit::walk_field_def(self, s); - } - - fn visit_foreign_item(&mut self, i: &'tcx hir::ForeignItem<'tcx>) { - self.annotate(i.owner_id.def_id); - intravisit::walk_foreign_item(self, i); - } - - fn visit_generic_param(&mut self, p: &'tcx hir::GenericParam<'tcx>) { - self.annotate(p.def_id); - intravisit::walk_generic_param(self, p); - } + implications } struct MissingStabilityAnnotations<'tcx> { @@ -565,13 +523,6 @@ impl<'tcx> Visitor<'tcx> for MissingStabilityAnnotations<'tcx> { } } -fn stability_implications(tcx: TyCtxt<'_>, LocalCrate: LocalCrate) -> UnordMap { - let mut annotator = Annotator { tcx, implications: Default::default() }; - annotator.annotate(CRATE_DEF_ID); - tcx.hir_walk_toplevel_module(&mut annotator); - annotator.implications -} - /// Cross-references the feature names of unstable APIs with enabled /// features and possibly prints errors. fn check_mod_unstable_api_usage(tcx: TyCtxt<'_>, module_def_id: LocalModDefId) { From 9b107bed9f2d2098dbbaade0ddbc363c651cd38b Mon Sep 17 00:00:00 2001 From: Scott McMurray Date: Sun, 27 Jul 2025 21:37:17 -0700 Subject: [PATCH 011/195] Add a mir-opt test for an unneeded drop_in_place --- .../mir-opt/remove_unneeded_drop_in_place.rs | 13 ++++++++++++ ...ce.slice_in_place.RemoveUnneededDrops.diff | 20 +++++++++++++++++++ 2 files changed, 33 insertions(+) create mode 100644 tests/mir-opt/remove_unneeded_drop_in_place.rs create mode 100644 tests/mir-opt/remove_unneeded_drop_in_place.slice_in_place.RemoveUnneededDrops.diff diff --git a/tests/mir-opt/remove_unneeded_drop_in_place.rs b/tests/mir-opt/remove_unneeded_drop_in_place.rs new file mode 100644 index 000000000000..7fb7b1e78de0 --- /dev/null +++ b/tests/mir-opt/remove_unneeded_drop_in_place.rs @@ -0,0 +1,13 @@ +//@ test-mir-pass: RemoveUnneededDrops +//@ needs-unwind + +// EMIT_MIR remove_unneeded_drop_in_place.slice_in_place.RemoveUnneededDrops.diff +unsafe fn slice_in_place(ptr: *mut [char]) { + std::ptr::drop_in_place(ptr) +} + +fn main() { + // CHECK-LABEL: fn main( + let mut a = ['o', 'k']; + unsafe { slice_in_place(&raw mut a) }; +} diff --git a/tests/mir-opt/remove_unneeded_drop_in_place.slice_in_place.RemoveUnneededDrops.diff b/tests/mir-opt/remove_unneeded_drop_in_place.slice_in_place.RemoveUnneededDrops.diff new file mode 100644 index 000000000000..4068e7940bc6 --- /dev/null +++ b/tests/mir-opt/remove_unneeded_drop_in_place.slice_in_place.RemoveUnneededDrops.diff @@ -0,0 +1,20 @@ +- // MIR for `slice_in_place` before RemoveUnneededDrops ++ // MIR for `slice_in_place` after RemoveUnneededDrops + + fn slice_in_place(_1: *mut [char]) -> () { + debug ptr => _1; + let mut _0: (); + let mut _2: *mut [char]; + + bb0: { + StorageLive(_2); + _2 = copy _1; + _0 = drop_in_place::<[char]>(move _2) -> [return: bb1, unwind continue]; + } + + bb1: { + StorageDead(_2); + return; + } + } + From 6e75abd0aa4f22f6291f1a25ff81e0c6b959ad26 Mon Sep 17 00:00:00 2001 From: Ayush Singh Date: Sun, 20 Jul 2025 22:35:43 +0530 Subject: [PATCH 012/195] std: sys: io: io_slice: Add UEFI types UEFI networking APIs do support vectored read/write. While the types for UDP4, UDP6, TCP4 and TCP6 are defined separately, they are essentially the same C struct. So we can map IoSlice and IoSliceMut to have the same binary representation. Since all UEFI networking types for read/write are DSTs, `IoSlice` and `IoSliceMut` will need to be copied to the end of the transmit/receive structures. So having the same binary representation just allows us to do a single memcpy instead of having to loop and set the DST. Signed-off-by: Ayush Singh --- library/std/src/sys/io/io_slice/uefi.rs | 74 +++++++++++++++ library/std/src/sys/io/mod.rs | 3 + library/std/src/sys/pal/uefi/tests.rs | 119 ++++++++++++++++++++++++ 3 files changed, 196 insertions(+) create mode 100644 library/std/src/sys/io/io_slice/uefi.rs diff --git a/library/std/src/sys/io/io_slice/uefi.rs b/library/std/src/sys/io/io_slice/uefi.rs new file mode 100644 index 000000000000..909cfbea0b7b --- /dev/null +++ b/library/std/src/sys/io/io_slice/uefi.rs @@ -0,0 +1,74 @@ +//! A buffer type used with `Write::write_vectored` for UEFI Networking APIs. Vectored writing to +//! File is not supported as of UEFI Spec 2.11. + +use crate::marker::PhantomData; +use crate::slice; + +#[derive(Copy, Clone)] +#[repr(C)] +pub struct IoSlice<'a> { + len: u32, + data: *const u8, + _p: PhantomData<&'a [u8]>, +} + +impl<'a> IoSlice<'a> { + #[inline] + pub fn new(buf: &'a [u8]) -> IoSlice<'a> { + let len = buf.len().try_into().unwrap(); + Self { len, data: buf.as_ptr(), _p: PhantomData } + } + + #[inline] + pub fn advance(&mut self, n: usize) { + self.len = u32::try_from(n) + .ok() + .and_then(|n| self.len.checked_sub(n)) + .expect("advancing IoSlice beyond its length"); + unsafe { self.data = self.data.add(n) }; + } + + #[inline] + pub const fn as_slice(&self) -> &'a [u8] { + unsafe { slice::from_raw_parts(self.data, self.len as usize) } + } +} + +#[repr(C)] +pub struct IoSliceMut<'a> { + len: u32, + data: *mut u8, + _p: PhantomData<&'a mut [u8]>, +} + +impl<'a> IoSliceMut<'a> { + #[inline] + pub fn new(buf: &'a mut [u8]) -> IoSliceMut<'a> { + let len = buf.len().try_into().unwrap(); + Self { len, data: buf.as_mut_ptr(), _p: PhantomData } + } + + #[inline] + pub fn advance(&mut self, n: usize) { + self.len = u32::try_from(n) + .ok() + .and_then(|n| self.len.checked_sub(n)) + .expect("advancing IoSlice beyond its length"); + unsafe { self.data = self.data.add(n) }; + } + + #[inline] + pub fn as_slice(&self) -> &[u8] { + unsafe { slice::from_raw_parts(self.data, self.len as usize) } + } + + #[inline] + pub const fn into_slice(self) -> &'a mut [u8] { + unsafe { slice::from_raw_parts_mut(self.data, self.len as usize) } + } + + #[inline] + pub fn as_mut_slice(&mut self) -> &mut [u8] { + unsafe { slice::from_raw_parts_mut(self.data, self.len as usize) } + } +} diff --git a/library/std/src/sys/io/mod.rs b/library/std/src/sys/io/mod.rs index 4d0365d42fd9..ae75f4d97b43 100644 --- a/library/std/src/sys/io/mod.rs +++ b/library/std/src/sys/io/mod.rs @@ -11,6 +11,9 @@ mod io_slice { } else if #[cfg(target_os = "wasi")] { mod wasi; pub use wasi::*; + } else if #[cfg(target_os = "uefi")] { + mod uefi; + pub use uefi::*; } else { mod unsupported; pub use unsupported::*; diff --git a/library/std/src/sys/pal/uefi/tests.rs b/library/std/src/sys/pal/uefi/tests.rs index 38658cc4e9ac..49e75a1a70d7 100644 --- a/library/std/src/sys/pal/uefi/tests.rs +++ b/library/std/src/sys/pal/uefi/tests.rs @@ -1,5 +1,6 @@ use super::alloc::*; use super::time::*; +use crate::io::{IoSlice, IoSliceMut}; use crate::time::Duration; #[test] @@ -39,3 +40,121 @@ fn epoch() { }; assert_eq!(system_time_internal::uefi_time_to_duration(t), Duration::new(0, 0)); } + +// UEFI IoSlice and IoSliceMut Tests +// +// Strictly speaking, vectored read/write types for UDP4, UDP6, TCP4, TCP6 are defined +// separately in the UEFI Spec. However, they have the same signature. These tests just ensure +// that `IoSlice` and `IoSliceMut` are compatible with the vectored types for all the +// networking protocols. + +unsafe fn to_slice(val: &T) -> &[u8] { + let len = size_of_val(val); + unsafe { crate::slice::from_raw_parts(crate::ptr::from_ref(val).cast(), len) } +} + +#[test] +fn io_slice_single() { + let mut data = [0, 1, 2, 3, 4]; + + let tcp4_frag = r_efi::protocols::tcp4::FragmentData { + fragment_length: data.len().try_into().unwrap(), + fragment_buffer: data.as_mut_ptr().cast(), + }; + let tcp6_frag = r_efi::protocols::tcp6::FragmentData { + fragment_length: data.len().try_into().unwrap(), + fragment_buffer: data.as_mut_ptr().cast(), + }; + let udp4_frag = r_efi::protocols::udp4::FragmentData { + fragment_length: data.len().try_into().unwrap(), + fragment_buffer: data.as_mut_ptr().cast(), + }; + let udp6_frag = r_efi::protocols::udp6::FragmentData { + fragment_length: data.len().try_into().unwrap(), + fragment_buffer: data.as_mut_ptr().cast(), + }; + let io_slice = IoSlice::new(&data); + + unsafe { + assert_eq!(to_slice(&io_slice), to_slice(&tcp4_frag)); + assert_eq!(to_slice(&io_slice), to_slice(&tcp6_frag)); + assert_eq!(to_slice(&io_slice), to_slice(&udp4_frag)); + assert_eq!(to_slice(&io_slice), to_slice(&udp6_frag)); + } +} + +#[test] +fn io_slice_mut_single() { + let mut data = [0, 1, 2, 3, 4]; + + let tcp4_frag = r_efi::protocols::tcp4::FragmentData { + fragment_length: data.len().try_into().unwrap(), + fragment_buffer: data.as_mut_ptr().cast(), + }; + let tcp6_frag = r_efi::protocols::tcp6::FragmentData { + fragment_length: data.len().try_into().unwrap(), + fragment_buffer: data.as_mut_ptr().cast(), + }; + let udp4_frag = r_efi::protocols::udp4::FragmentData { + fragment_length: data.len().try_into().unwrap(), + fragment_buffer: data.as_mut_ptr().cast(), + }; + let udp6_frag = r_efi::protocols::udp6::FragmentData { + fragment_length: data.len().try_into().unwrap(), + fragment_buffer: data.as_mut_ptr().cast(), + }; + let io_slice_mut = IoSliceMut::new(&mut data); + + unsafe { + assert_eq!(to_slice(&io_slice_mut), to_slice(&tcp4_frag)); + assert_eq!(to_slice(&io_slice_mut), to_slice(&tcp6_frag)); + assert_eq!(to_slice(&io_slice_mut), to_slice(&udp4_frag)); + assert_eq!(to_slice(&io_slice_mut), to_slice(&udp6_frag)); + } +} + +#[test] +fn io_slice_multi() { + let mut data = [0, 1, 2, 3, 4]; + + let tcp4_frag = r_efi::protocols::tcp4::FragmentData { + fragment_length: data.len().try_into().unwrap(), + fragment_buffer: data.as_mut_ptr().cast(), + }; + let rhs = + [tcp4_frag.clone(), tcp4_frag.clone(), tcp4_frag.clone(), tcp4_frag.clone(), tcp4_frag]; + let lhs = [ + IoSlice::new(&data), + IoSlice::new(&data), + IoSlice::new(&data), + IoSlice::new(&data), + IoSlice::new(&data), + ]; + + unsafe { + assert_eq!(to_slice(&lhs), to_slice(&rhs)); + } +} + +#[test] +fn io_slice_basic() { + let data = [0, 1, 2, 3, 4]; + let mut io_slice = IoSlice::new(&data); + + assert_eq!(data, io_slice.as_slice()); + io_slice.advance(2); + assert_eq!(&data[2..], io_slice.as_slice()); +} + +#[test] +fn io_slice_mut_basic() { + let data = [0, 1, 2, 3, 4]; + let mut data_clone = [0, 1, 2, 3, 4]; + let mut io_slice_mut = IoSliceMut::new(&mut data_clone); + + assert_eq!(data, io_slice_mut.as_slice()); + assert_eq!(data, io_slice_mut.as_mut_slice()); + + io_slice_mut.advance(2); + assert_eq!(&data[2..], io_slice_mut.into_slice()); +} From d5f2260bd37966b76ef6ee9c22d0cdebdcbe97a9 Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Mon, 4 Aug 2025 23:07:15 +0000 Subject: [PATCH 013/195] Add doc-comment. --- compiler/rustc_hir/src/def.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/compiler/rustc_hir/src/def.rs b/compiler/rustc_hir/src/def.rs index 8774fb4d5858..f2567f73d999 100644 --- a/compiler/rustc_hir/src/def.rs +++ b/compiler/rustc_hir/src/def.rs @@ -318,6 +318,7 @@ impl DefKind { ) } + /// Whether the corresponding item has generic parameters, ie. the `generics_of` query works. pub fn has_generics(self) -> bool { match self { DefKind::AnonConst From 4e81ecaf3a45a4bb5a8a78ac407d1ef6a7367e72 Mon Sep 17 00:00:00 2001 From: Scott McMurray Date: Sun, 27 Jul 2025 21:39:56 -0700 Subject: [PATCH 014/195] Let `RemoveUnneededDrops` also remove `drop_in_place` --- .../src/remove_unneeded_drops.rs | 31 ++++++++++++++----- .../mir-opt/remove_unneeded_drop_in_place.rs | 6 +++- ...ce.slice_in_place.RemoveUnneededDrops.diff | 8 ++--- 3 files changed, 33 insertions(+), 12 deletions(-) diff --git a/compiler/rustc_mir_transform/src/remove_unneeded_drops.rs b/compiler/rustc_mir_transform/src/remove_unneeded_drops.rs index 43f80508e4a8..6c2dfc59da24 100644 --- a/compiler/rustc_mir_transform/src/remove_unneeded_drops.rs +++ b/compiler/rustc_mir_transform/src/remove_unneeded_drops.rs @@ -4,7 +4,13 @@ //! useful because (unlike MIR building) it runs after type checking, so it can make use of //! `TypingMode::PostAnalysis` to provide more precise type information, especially about opaque //! types. +//! +//! When we're optimizing, we also remove calls to `drop_in_place` when `T` isn't `needs_drop`, +//! as those are essentially equivalent to `Drop` terminators. While the compiler doesn't insert +//! them automatically, preferring the built-in instead, they're common in generic code (such as +//! `Vec::truncate`) so removing them from things like inlined `Vec` is helpful. +use rustc_hir::LangItem; use rustc_middle::mir::*; use rustc_middle::ty::TyCtxt; use tracing::{debug, trace}; @@ -21,15 +27,26 @@ impl<'tcx> crate::MirPass<'tcx> for RemoveUnneededDrops { let mut should_simplify = false; for block in body.basic_blocks.as_mut() { let terminator = block.terminator_mut(); - if let TerminatorKind::Drop { place, target, .. } = terminator.kind { - let ty = place.ty(&body.local_decls, tcx); - if ty.ty.needs_drop(tcx, typing_env) { - continue; + let (ty, target) = match terminator.kind { + TerminatorKind::Drop { place, target, .. } => { + (place.ty(&body.local_decls, tcx).ty, target) } - debug!("SUCCESS: replacing `drop` with goto({:?})", target); - terminator.kind = TerminatorKind::Goto { target }; - should_simplify = true; + TerminatorKind::Call { ref func, target: Some(target), .. } + if tcx.sess.mir_opt_level() > 0 + && let Some((def_id, generics)) = func.const_fn_def() + && tcx.is_lang_item(def_id, LangItem::DropInPlace) => + { + (generics.type_at(0), target) + } + _ => continue, + }; + + if ty.needs_drop(tcx, typing_env) { + continue; } + debug!("SUCCESS: replacing `drop` with goto({:?})", target); + terminator.kind = TerminatorKind::Goto { target }; + should_simplify = true; } // if we applied optimizations, we potentially have some cfg to cleanup to diff --git a/tests/mir-opt/remove_unneeded_drop_in_place.rs b/tests/mir-opt/remove_unneeded_drop_in_place.rs index 7fb7b1e78de0..470c6499d169 100644 --- a/tests/mir-opt/remove_unneeded_drop_in_place.rs +++ b/tests/mir-opt/remove_unneeded_drop_in_place.rs @@ -1,13 +1,17 @@ //@ test-mir-pass: RemoveUnneededDrops //@ needs-unwind +//@ compile-flags: -Z mir-opt-level=1 // EMIT_MIR remove_unneeded_drop_in_place.slice_in_place.RemoveUnneededDrops.diff unsafe fn slice_in_place(ptr: *mut [char]) { + // CHECK-LABEL: fn slice_in_place(_1: *mut [char]) + // CHECK: bb0: { + // CHECK-NEXT: return; + // CHECK-NEXT: } std::ptr::drop_in_place(ptr) } fn main() { - // CHECK-LABEL: fn main( let mut a = ['o', 'k']; unsafe { slice_in_place(&raw mut a) }; } diff --git a/tests/mir-opt/remove_unneeded_drop_in_place.slice_in_place.RemoveUnneededDrops.diff b/tests/mir-opt/remove_unneeded_drop_in_place.slice_in_place.RemoveUnneededDrops.diff index 4068e7940bc6..4d70e7151c39 100644 --- a/tests/mir-opt/remove_unneeded_drop_in_place.slice_in_place.RemoveUnneededDrops.diff +++ b/tests/mir-opt/remove_unneeded_drop_in_place.slice_in_place.RemoveUnneededDrops.diff @@ -9,10 +9,10 @@ bb0: { StorageLive(_2); _2 = copy _1; - _0 = drop_in_place::<[char]>(move _2) -> [return: bb1, unwind continue]; - } - - bb1: { +- _0 = drop_in_place::<[char]>(move _2) -> [return: bb1, unwind continue]; +- } +- +- bb1: { StorageDead(_2); return; } From bdaabc17b664952ce0b9666b9b57188a6126935b Mon Sep 17 00:00:00 2001 From: Sasha Pourcelot Date: Mon, 4 Aug 2025 20:47:43 +0200 Subject: [PATCH 015/195] Add test for "missing function argument" on multiline call --- .../fn/fn-arg-count-mismatch-diagnostics.rs | 12 +++++++ .../fn-arg-count-mismatch-diagnostics.stderr | 32 +++++++++++++++++-- 2 files changed, 41 insertions(+), 3 deletions(-) diff --git a/tests/ui/fn/fn-arg-count-mismatch-diagnostics.rs b/tests/ui/fn/fn-arg-count-mismatch-diagnostics.rs index b2f80ba1bf62..3b12ea1a7368 100644 --- a/tests/ui/fn/fn-arg-count-mismatch-diagnostics.rs +++ b/tests/ui/fn/fn-arg-count-mismatch-diagnostics.rs @@ -46,9 +46,21 @@ impl Bar { } } +fn function_with_lots_of_arguments(a: i32, b: char, c: i32, d: i32, e: i32, f: i32) {} + fn main() { foo(1, 2, 3); //~^ ERROR function takes 4 arguments but 3 bar(1, 2, 3); //~^ ERROR function takes 6 arguments but 3 + + let variable_name = 42; + function_with_lots_of_arguments( + variable_name, + variable_name, + variable_name, + variable_name, + variable_name, + ); + //~^^^^^^^ ERROR this function takes 6 arguments but 5 arguments were supplied [E0061] } diff --git a/tests/ui/fn/fn-arg-count-mismatch-diagnostics.stderr b/tests/ui/fn/fn-arg-count-mismatch-diagnostics.stderr index 6af7671af03b..494d9bf60a65 100644 --- a/tests/ui/fn/fn-arg-count-mismatch-diagnostics.stderr +++ b/tests/ui/fn/fn-arg-count-mismatch-diagnostics.stderr @@ -52,7 +52,7 @@ LL | <$from>::$method(8, /* u8 */) | ++++++++++ error[E0061]: this function takes 4 arguments but 3 arguments were supplied - --> $DIR/fn-arg-count-mismatch-diagnostics.rs:50:5 + --> $DIR/fn-arg-count-mismatch-diagnostics.rs:52:5 | LL | foo(1, 2, 3); | ^^^--------- argument #4 of type `isize` is missing @@ -68,7 +68,7 @@ LL | foo(1, 2, 3, /* isize */); | +++++++++++++ error[E0061]: this function takes 6 arguments but 3 arguments were supplied - --> $DIR/fn-arg-count-mismatch-diagnostics.rs:52:5 + --> $DIR/fn-arg-count-mismatch-diagnostics.rs:54:5 | LL | bar(1, 2, 3); | ^^^--------- three arguments of type `i32`, `i32`, and `i32` are missing @@ -83,6 +83,32 @@ help: provide the arguments LL | bar(1, 2, 3, /* i32 */, /* i32 */, /* i32 */); | +++++++++++++++++++++++++++++++++ -error: aborting due to 5 previous errors +error[E0061]: this function takes 6 arguments but 5 arguments were supplied + --> $DIR/fn-arg-count-mismatch-diagnostics.rs:58:5 + | +LL | function_with_lots_of_arguments( + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | variable_name, +LL | variable_name, + | ------------- argument #2 of type `char` is missing + | +note: function defined here + --> $DIR/fn-arg-count-mismatch-diagnostics.rs:49:4 + | +LL | fn function_with_lots_of_arguments(a: i32, b: char, c: i32, d: i32, e: i32, f: i32) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ------- +help: provide the argument + | +LL - function_with_lots_of_arguments( +LL - variable_name, +LL - variable_name, +LL - variable_name, +LL - variable_name, +LL - variable_name, +LL - ); +LL + function_with_lots_of_arguments(variable_name, /* char */, variable_name, variable_name, variable_name, variable_name); + | + +error: aborting due to 6 previous errors For more information about this error, try `rustc --explain E0061`. From bcefc2ee977e38a4a5ecd0956e19b0968d33052b Mon Sep 17 00:00:00 2001 From: Amanda Stjerna Date: Wed, 6 Aug 2025 11:12:51 +0200 Subject: [PATCH 016/195] Add annotations to the graphviz region graph on region origins This adds - (ex) for regions whose origin is existential, - (p) for regoins whose origin is a placeholder, and - (p for ) if the originating placeholder is named. This has helped _my_ debugging and it doesn't create too bad clutter, I feel. The change is ridiculously small, but I turned it into a separate PR so we can bikeshed the format. --- .../rustc_borrowck/src/region_infer/graphviz.rs | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_borrowck/src/region_infer/graphviz.rs b/compiler/rustc_borrowck/src/region_infer/graphviz.rs index a3e29982e90f..899dde9ecd47 100644 --- a/compiler/rustc_borrowck/src/region_infer/graphviz.rs +++ b/compiler/rustc_borrowck/src/region_infer/graphviz.rs @@ -41,7 +41,21 @@ fn render_region_vid<'tcx>( "".to_string() }; - format!("{:?}{universe_str}{external_name_str}", rvid) + let extra_info = match regioncx.region_definition(rvid).origin { + NllRegionVariableOrigin::FreeRegion => "".to_string(), + NllRegionVariableOrigin::Placeholder(p) => match p.bound.kind { + ty::BoundRegionKind::Named(def_id) => { + format!("(p for {})", tcx.item_name(def_id)) + } + ty::BoundRegionKind::ClosureEnv | ty::BoundRegionKind::Anon => "(p)".to_string(), + ty::BoundRegionKind::NamedAnon(_) => { + bug!("only used for pretty printing") + } + }, + NllRegionVariableOrigin::Existential { .. } => "(ex)".to_string(), + }; + + format!("{:?}{universe_str}{external_name_str}{extra_info}", rvid) } impl<'tcx> RegionInferenceContext<'tcx> { From 61e8869ed9aa8bf50a0ea2447a43bf856479feaa Mon Sep 17 00:00:00 2001 From: Amanda Stjerna Date: Wed, 6 Aug 2025 16:35:20 +0200 Subject: [PATCH 017/195] Proposed format --- compiler/rustc_borrowck/src/region_infer/graphviz.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_borrowck/src/region_infer/graphviz.rs b/compiler/rustc_borrowck/src/region_infer/graphviz.rs index 899dde9ecd47..ae4efddea4a9 100644 --- a/compiler/rustc_borrowck/src/region_infer/graphviz.rs +++ b/compiler/rustc_borrowck/src/region_infer/graphviz.rs @@ -45,14 +45,14 @@ fn render_region_vid<'tcx>( NllRegionVariableOrigin::FreeRegion => "".to_string(), NllRegionVariableOrigin::Placeholder(p) => match p.bound.kind { ty::BoundRegionKind::Named(def_id) => { - format!("(p for {})", tcx.item_name(def_id)) + format!(" (for<{}>)", tcx.item_name(def_id)) } - ty::BoundRegionKind::ClosureEnv | ty::BoundRegionKind::Anon => "(p)".to_string(), + ty::BoundRegionKind::ClosureEnv | ty::BoundRegionKind::Anon => " (for<'_>)".to_string(), ty::BoundRegionKind::NamedAnon(_) => { bug!("only used for pretty printing") } }, - NllRegionVariableOrigin::Existential { .. } => "(ex)".to_string(), + NllRegionVariableOrigin::Existential { .. } => " (ex<'?>)".to_string(), }; format!("{:?}{universe_str}{external_name_str}{extra_info}", rvid) From 648e3fc3938b4b5127e7591187ce523e660ade44 Mon Sep 17 00:00:00 2001 From: Amanda Stjerna Date: Wed, 6 Aug 2025 16:58:42 +0200 Subject: [PATCH 018/195] Track names of existentials --- compiler/rustc_borrowck/src/handle_placeholders.rs | 2 +- compiler/rustc_borrowck/src/region_infer/graphviz.rs | 3 ++- compiler/rustc_borrowck/src/region_infer/mod.rs | 4 ++-- compiler/rustc_borrowck/src/renumber.rs | 2 +- compiler/rustc_borrowck/src/type_check/relate_tys.rs | 2 +- compiler/rustc_infer/src/infer/mod.rs | 1 + 6 files changed, 8 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_borrowck/src/handle_placeholders.rs b/compiler/rustc_borrowck/src/handle_placeholders.rs index aaaf2f45c869..5eec121b8a73 100644 --- a/compiler/rustc_borrowck/src/handle_placeholders.rs +++ b/compiler/rustc_borrowck/src/handle_placeholders.rs @@ -157,7 +157,7 @@ fn region_definitions<'tcx>( for info in var_infos.iter() { let origin = match info.origin { RegionVariableOrigin::Nll(origin) => origin, - _ => NllRegionVariableOrigin::Existential { from_forall: false }, + _ => NllRegionVariableOrigin::Existential { from_forall: false, name: None }, }; let definition = RegionDefinition { origin, universe: info.universe, external_name: None }; diff --git a/compiler/rustc_borrowck/src/region_infer/graphviz.rs b/compiler/rustc_borrowck/src/region_infer/graphviz.rs index ae4efddea4a9..b2f67c43125d 100644 --- a/compiler/rustc_borrowck/src/region_infer/graphviz.rs +++ b/compiler/rustc_borrowck/src/region_infer/graphviz.rs @@ -52,7 +52,8 @@ fn render_region_vid<'tcx>( bug!("only used for pretty printing") } }, - NllRegionVariableOrigin::Existential { .. } => " (ex<'?>)".to_string(), + NllRegionVariableOrigin::Existential { name: Some(name), .. } => format!(" (ex<{name}>)"), + NllRegionVariableOrigin::Existential { .. } => format!(" (ex<'_>)"), }; format!("{:?}{universe_str}{external_name_str}{extra_info}", rvid) diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs index f13200485332..6ac908fd04c8 100644 --- a/compiler/rustc_borrowck/src/region_infer/mod.rs +++ b/compiler/rustc_borrowck/src/region_infer/mod.rs @@ -1940,9 +1940,9 @@ impl<'tcx> RegionInferenceContext<'tcx> { // and here we prefer to blame the source (the y = x statement). let blame_source = match from_region_origin { NllRegionVariableOrigin::FreeRegion - | NllRegionVariableOrigin::Existential { from_forall: false } => true, + | NllRegionVariableOrigin::Existential { from_forall: false, name: _ } => true, NllRegionVariableOrigin::Placeholder(_) - | NllRegionVariableOrigin::Existential { from_forall: true } => false, + | NllRegionVariableOrigin::Existential { from_forall: true, name: _ } => false, }; // To pick a constraint to blame, we organize constraints by how interesting we expect them diff --git a/compiler/rustc_borrowck/src/renumber.rs b/compiler/rustc_borrowck/src/renumber.rs index ff92b4168a86..100d30704b9e 100644 --- a/compiler/rustc_borrowck/src/renumber.rs +++ b/compiler/rustc_borrowck/src/renumber.rs @@ -66,7 +66,7 @@ impl<'a, 'tcx> RegionRenumberer<'a, 'tcx> { T: TypeFoldable>, F: Fn() -> RegionCtxt, { - let origin = NllRegionVariableOrigin::Existential { from_forall: false }; + let origin = NllRegionVariableOrigin::Existential { from_forall: false, name: None }; fold_regions(self.infcx.tcx, value, |_region, _depth| { self.infcx.next_nll_region_var(origin, || region_ctxt_fn()) }) diff --git a/compiler/rustc_borrowck/src/type_check/relate_tys.rs b/compiler/rustc_borrowck/src/type_check/relate_tys.rs index bb72d1d52f37..393adafea0b0 100644 --- a/compiler/rustc_borrowck/src/type_check/relate_tys.rs +++ b/compiler/rustc_borrowck/src/type_check/relate_tys.rs @@ -249,7 +249,7 @@ impl<'a, 'b, 'tcx> NllTypeRelating<'a, 'b, 'tcx> { from_forall: bool, name: Option, ) -> ty::Region<'tcx> { - let origin = NllRegionVariableOrigin::Existential { from_forall }; + let origin = NllRegionVariableOrigin::Existential { name, from_forall }; let reg_var = self.type_checker.infcx.next_nll_region_var(origin, || RegionCtxt::Existential(name)); diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index 7379df7c7c7d..a2afdc45fa87 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -484,6 +484,7 @@ pub enum NllRegionVariableOrigin { Placeholder(ty::PlaceholderRegion), Existential { + name: Option, /// If this is true, then this variable was created to represent a lifetime /// bound in a `for` binder. For example, it might have been created to /// represent the lifetime `'a` in a type like `for<'a> fn(&'a u32)`. From b0619327ef6afb89492b2fe0bd86f987c39f6c25 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Fri, 18 Jul 2025 14:16:08 +0000 Subject: [PATCH 019/195] Change adjust_for_rust_scalar into arg_attrs_for_rust_scalar Directly creating the ArgAttributes rather than adjusting one is a bit clearer. --- compiler/rustc_ty_utils/src/abi.rs | 25 +++++++++---------------- 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/compiler/rustc_ty_utils/src/abi.rs b/compiler/rustc_ty_utils/src/abi.rs index af2e000e3402..a0b333718357 100644 --- a/compiler/rustc_ty_utils/src/abi.rs +++ b/compiler/rustc_ty_utils/src/abi.rs @@ -268,20 +268,21 @@ fn fn_abi_of_instance<'tcx>( } // Handle safe Rust thin and wide pointers. -fn adjust_for_rust_scalar<'tcx>( +fn arg_attrs_for_rust_scalar<'tcx>( cx: LayoutCx<'tcx>, - attrs: &mut ArgAttributes, scalar: Scalar, layout: TyAndLayout<'tcx>, offset: Size, is_return: bool, drop_target_pointee: Option>, -) { +) -> ArgAttributes { + let mut attrs = ArgAttributes::new(); + // Booleans are always a noundef i1 that needs to be zero-extended. if scalar.is_bool() { attrs.ext(ArgExtension::Zext); attrs.set(ArgAttribute::NoUndef); - return; + return attrs; } if !scalar.is_uninit_valid() { @@ -289,7 +290,7 @@ fn adjust_for_rust_scalar<'tcx>( } // Only pointer types handled below. - let Scalar::Initialized { value: Pointer(_), valid_range } = scalar else { return }; + let Scalar::Initialized { value: Pointer(_), valid_range } = scalar else { return attrs }; // Set `nonnull` if the validity range excludes zero, or for the argument to `drop_in_place`, // which must be nonnull per its documented safety requirements. @@ -358,6 +359,8 @@ fn adjust_for_rust_scalar<'tcx>( } } } + + attrs } /// Ensure that the ABI makes basic sense. @@ -530,17 +533,7 @@ fn fn_abi_new_uncached<'tcx>( }; let mut arg = ArgAbi::new(cx, layout, |layout, scalar, offset| { - let mut attrs = ArgAttributes::new(); - adjust_for_rust_scalar( - *cx, - &mut attrs, - scalar, - *layout, - offset, - is_return, - drop_target_pointee, - ); - attrs + arg_attrs_for_rust_scalar(*cx, scalar, *layout, offset, is_return, drop_target_pointee) }); if arg.layout.is_zst() { From e2acc6c5f4474b3610ce9f1cde84ecb854a354a7 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Fri, 18 Jul 2025 14:25:56 +0000 Subject: [PATCH 020/195] Avoid using unadjusted ABI for the thread-local shim This restricts the uses of the unadjusted ABI to LLVM intrinsics. The Rust ABI works fine for the thread-local shim as it always returns pointers directly like the backend expects. --- compiler/rustc_ty_utils/src/abi.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_ty_utils/src/abi.rs b/compiler/rustc_ty_utils/src/abi.rs index a0b333718357..439d18a69d4d 100644 --- a/compiler/rustc_ty_utils/src/abi.rs +++ b/compiler/rustc_ty_utils/src/abi.rs @@ -39,7 +39,7 @@ fn fn_sig_for_fn_abi<'tcx>( tcx.thread_local_ptr_ty(instance.def_id()), false, hir::Safety::Safe, - rustc_abi::ExternAbi::Unadjusted, + rustc_abi::ExternAbi::Rust, ); } From 1e271d6ed13756db08160daaa327b99b7fe6099e Mon Sep 17 00:00:00 2001 From: Sasha Pourcelot Date: Tue, 5 Aug 2025 00:11:01 +0200 Subject: [PATCH 021/195] Allow function argument mismatch suggestion to be multiline --- .../rustc_hir_typeck/src/fn_ctxt/checks.rs | 50 ++++++++++++++++--- .../argument-suggestions/issue-100478.stderr | 16 +++--- .../fn-arg-count-mismatch-diagnostics.stderr | 12 ++--- 3 files changed, 58 insertions(+), 20 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index 36abd7c85550..b80a2af31007 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -1589,26 +1589,64 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // e.g. `reuse HasSelf::method;` should suggest `reuse HasSelf::method($args);`. full_call_span.shrink_to_hi() }; + + // Controls how the arguments should be listed in the suggestion. + enum ArgumentsFormatting { + SingleLine, + Multiline { fallback_indent: String, brace_indent: String }, + } + let arguments_formatting = { + let mut provided_inputs = matched_inputs.iter().filter_map(|a| *a); + if let Some(brace_indent) = source_map.indentation_before(suggestion_span) + && let Some(first_idx) = provided_inputs.by_ref().next() + && let Some(last_idx) = provided_inputs.by_ref().next() + && let (_, first_span) = provided_arg_tys[first_idx] + && let (_, last_span) = provided_arg_tys[last_idx] + && source_map.is_multiline(first_span.to(last_span)) + && let Some(fallback_indent) = source_map.indentation_before(first_span) + { + ArgumentsFormatting::Multiline { fallback_indent, brace_indent } + } else { + ArgumentsFormatting::SingleLine + } + }; + let mut suggestion = "(".to_owned(); let mut needs_comma = false; for (expected_idx, provided_idx) in matched_inputs.iter_enumerated() { if needs_comma { - suggestion += ", "; - } else { - needs_comma = true; + suggestion += ","; } - let suggestion_text = if let Some(provided_idx) = provided_idx + match &arguments_formatting { + ArgumentsFormatting::SingleLine if needs_comma => suggestion += " ", + ArgumentsFormatting::SingleLine => {} + ArgumentsFormatting::Multiline { .. } => suggestion += "\n", + } + needs_comma = true; + let (suggestion_span, suggestion_text) = if let Some(provided_idx) = provided_idx && let (_, provided_span) = provided_arg_tys[*provided_idx] && let Ok(arg_text) = source_map.span_to_snippet(provided_span) { - arg_text + (Some(provided_span), arg_text) } else { // Propose a placeholder of the correct type let (_, expected_ty) = formal_and_expected_inputs[expected_idx]; - ty_to_snippet(expected_ty, expected_idx) + (None, ty_to_snippet(expected_ty, expected_idx)) }; + if let ArgumentsFormatting::Multiline { fallback_indent, .. } = + &arguments_formatting + { + let indent = suggestion_span + .and_then(|span| source_map.indentation_before(span)) + .unwrap_or_else(|| fallback_indent.clone()); + suggestion += &indent; + } suggestion += &suggestion_text; } + if let ArgumentsFormatting::Multiline { brace_indent, .. } = arguments_formatting { + suggestion += ",\n"; + suggestion += &brace_indent; + } suggestion += ")"; err.span_suggestion_verbose( suggestion_span, diff --git a/tests/ui/argument-suggestions/issue-100478.stderr b/tests/ui/argument-suggestions/issue-100478.stderr index 8889a0ab5df0..81dbff2f0008 100644 --- a/tests/ui/argument-suggestions/issue-100478.stderr +++ b/tests/ui/argument-suggestions/issue-100478.stderr @@ -75,12 +75,16 @@ LL | fn foo(p1: T1, p2: Arc, p3: T3, p4: Arc, p5: T5, p6: T6, p7: T7, p8 | ^^^ ----------- help: provide the argument | -LL - foo( -LL - -LL - p1, //p2, -LL - p3, p4, p5, p6, p7, p8, -LL - ); -LL + foo(p1, /* Arc */, p3, p4, p5, p6, p7, p8); +LL ~ foo( +LL + p1, +LL + /* Arc */, +LL + p3, +LL + p4, +LL + p5, +LL + p6, +LL + p7, +LL + p8, +LL ~ ); | error: aborting due to 4 previous errors diff --git a/tests/ui/fn/fn-arg-count-mismatch-diagnostics.stderr b/tests/ui/fn/fn-arg-count-mismatch-diagnostics.stderr index 494d9bf60a65..dda9b398a833 100644 --- a/tests/ui/fn/fn-arg-count-mismatch-diagnostics.stderr +++ b/tests/ui/fn/fn-arg-count-mismatch-diagnostics.stderr @@ -99,14 +99,10 @@ LL | fn function_with_lots_of_arguments(a: i32, b: char, c: i32, d: i32, e: i32, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ------- help: provide the argument | -LL - function_with_lots_of_arguments( -LL - variable_name, -LL - variable_name, -LL - variable_name, -LL - variable_name, -LL - variable_name, -LL - ); -LL + function_with_lots_of_arguments(variable_name, /* char */, variable_name, variable_name, variable_name, variable_name); +LL | function_with_lots_of_arguments( +LL | variable_name, +LL ~ /* char */, +LL ~ variable_name, | error: aborting due to 6 previous errors From 7d88f657e993a3ec394f0b29dea0e5008633a6ba Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Wed, 6 Aug 2025 18:31:58 +0000 Subject: [PATCH 022/195] Fix ICE --- compiler/rustc_ty_utils/src/abi.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_ty_utils/src/abi.rs b/compiler/rustc_ty_utils/src/abi.rs index 439d18a69d4d..89d1dd8cf231 100644 --- a/compiler/rustc_ty_utils/src/abi.rs +++ b/compiler/rustc_ty_utils/src/abi.rs @@ -474,9 +474,10 @@ fn fn_abi_new_uncached<'tcx>( let (caller_location, determined_fn_def_id, is_virtual_call) = if let Some(instance) = instance { let is_virtual_call = matches!(instance.def, ty::InstanceKind::Virtual(..)); + let is_tls_shim_call = matches!(instance.def, ty::InstanceKind::ThreadLocalShim(_)); ( instance.def.requires_caller_location(tcx).then(|| tcx.caller_location_ty()), - if is_virtual_call { None } else { Some(instance.def_id()) }, + if is_virtual_call || is_tls_shim_call { None } else { Some(instance.def_id()) }, is_virtual_call, ) } else { @@ -556,6 +557,7 @@ fn fn_abi_new_uncached<'tcx>( c_variadic: sig.c_variadic, fixed_count: inputs.len() as u32, conv, + // FIXME return false for tls shim can_unwind: fn_can_unwind( tcx, // Since `#[rustc_nounwind]` can change unwinding, we cannot infer unwinding by `fn_def_id` for a virtual call. @@ -568,8 +570,9 @@ fn fn_abi_new_uncached<'tcx>( &mut fn_abi, sig.abi, // If this is a virtual call, we cannot pass the `fn_def_id`, as it might call other - // functions from vtable. Internally, `deduced_param_attrs` attempts to infer attributes by - // visit the function body. + // functions from vtable. And for a tls shim, passing the `fn_def_id` would refer to + // the underlying static. Internally, `deduced_param_attrs` attempts to infer attributes + // by visit the function body. determined_fn_def_id, ); debug!("fn_abi_new_uncached = {:?}", fn_abi); From 0bba9bd55c60a108c89cb32971ca2ebbf7386ba7 Mon Sep 17 00:00:00 2001 From: Paul Murphy Date: Wed, 6 Aug 2025 13:37:27 -0500 Subject: [PATCH 023/195] Explicitly disable vector feature on s390x baseline of bad-reg test If the baseline s390x cpu is changed to a newer variant, such as z13, the vector feature may be enabled by default. When rust is packaged on fedora 38 and newer, it is set to z13. Explicitly disable vector support on the baseline test for consistent results across s390x cpus. --- tests/ui/asm/s390x/bad-reg.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ui/asm/s390x/bad-reg.rs b/tests/ui/asm/s390x/bad-reg.rs index 56b2d7093726..eb9138755e76 100644 --- a/tests/ui/asm/s390x/bad-reg.rs +++ b/tests/ui/asm/s390x/bad-reg.rs @@ -1,7 +1,7 @@ //@ add-core-stubs //@ needs-asm-support //@ revisions: s390x s390x_vector s390x_vector_stable -//@[s390x] compile-flags: --target s390x-unknown-linux-gnu +//@[s390x] compile-flags: --target s390x-unknown-linux-gnu -C target-feature=-vector //@[s390x] needs-llvm-components: systemz //@[s390x_vector] compile-flags: --target s390x-unknown-linux-gnu -C target-feature=+vector //@[s390x_vector] needs-llvm-components: systemz From bf5020937f17fd4c2f8ae5e7703fd9b8d15f0502 Mon Sep 17 00:00:00 2001 From: Karl Meakin Date: Thu, 7 Aug 2025 00:05:22 +0100 Subject: [PATCH 024/195] Optimize `char::is_alphanumeric` Avoid an unnecessary call to `unicode::Alphabetic` when `self` is an ASCII digit (ie `0..=9`). --- library/core/src/char/methods.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/library/core/src/char/methods.rs b/library/core/src/char/methods.rs index b0752a85faf1..7ee0962721f5 100644 --- a/library/core/src/char/methods.rs +++ b/library/core/src/char/methods.rs @@ -920,7 +920,11 @@ impl char { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn is_alphanumeric(self) -> bool { - self.is_alphabetic() || self.is_numeric() + if self.is_ascii() { + self.is_ascii_alphanumeric() + } else { + unicode::Alphabetic(self) || unicode::N(self) + } } /// Returns `true` if this `char` has the general category for control codes. From 064d5886544de3f28b0804fb15654ea49eb6b9c7 Mon Sep 17 00:00:00 2001 From: lcnr Date: Thu, 7 Aug 2025 14:20:19 +0200 Subject: [PATCH 025/195] move member-constraints tests --- .../member-constraints/min-choice-reject-ambiguous.rs | 0 .../member-constraints/min-choice-reject-ambiguous.stderr | 0 tests/ui/{nll => impl-trait}/member-constraints/min-choice.rs | 0 .../member-constraints/nested-impl-trait-fail.rs | 0 .../member-constraints/nested-impl-trait-fail.stderr | 0 .../member-constraints/nested-impl-trait-pass.rs | 0 6 files changed, 0 insertions(+), 0 deletions(-) rename tests/ui/{nll => impl-trait}/member-constraints/min-choice-reject-ambiguous.rs (100%) rename tests/ui/{nll => impl-trait}/member-constraints/min-choice-reject-ambiguous.stderr (100%) rename tests/ui/{nll => impl-trait}/member-constraints/min-choice.rs (100%) rename tests/ui/{nll => impl-trait}/member-constraints/nested-impl-trait-fail.rs (100%) rename tests/ui/{nll => impl-trait}/member-constraints/nested-impl-trait-fail.stderr (100%) rename tests/ui/{nll => impl-trait}/member-constraints/nested-impl-trait-pass.rs (100%) diff --git a/tests/ui/nll/member-constraints/min-choice-reject-ambiguous.rs b/tests/ui/impl-trait/member-constraints/min-choice-reject-ambiguous.rs similarity index 100% rename from tests/ui/nll/member-constraints/min-choice-reject-ambiguous.rs rename to tests/ui/impl-trait/member-constraints/min-choice-reject-ambiguous.rs diff --git a/tests/ui/nll/member-constraints/min-choice-reject-ambiguous.stderr b/tests/ui/impl-trait/member-constraints/min-choice-reject-ambiguous.stderr similarity index 100% rename from tests/ui/nll/member-constraints/min-choice-reject-ambiguous.stderr rename to tests/ui/impl-trait/member-constraints/min-choice-reject-ambiguous.stderr diff --git a/tests/ui/nll/member-constraints/min-choice.rs b/tests/ui/impl-trait/member-constraints/min-choice.rs similarity index 100% rename from tests/ui/nll/member-constraints/min-choice.rs rename to tests/ui/impl-trait/member-constraints/min-choice.rs diff --git a/tests/ui/nll/member-constraints/nested-impl-trait-fail.rs b/tests/ui/impl-trait/member-constraints/nested-impl-trait-fail.rs similarity index 100% rename from tests/ui/nll/member-constraints/nested-impl-trait-fail.rs rename to tests/ui/impl-trait/member-constraints/nested-impl-trait-fail.rs diff --git a/tests/ui/nll/member-constraints/nested-impl-trait-fail.stderr b/tests/ui/impl-trait/member-constraints/nested-impl-trait-fail.stderr similarity index 100% rename from tests/ui/nll/member-constraints/nested-impl-trait-fail.stderr rename to tests/ui/impl-trait/member-constraints/nested-impl-trait-fail.stderr diff --git a/tests/ui/nll/member-constraints/nested-impl-trait-pass.rs b/tests/ui/impl-trait/member-constraints/nested-impl-trait-pass.rs similarity index 100% rename from tests/ui/nll/member-constraints/nested-impl-trait-pass.rs rename to tests/ui/impl-trait/member-constraints/nested-impl-trait-pass.rs From 8441f95ec70a2ee5cde4d1b8c81203860e4f5bb9 Mon Sep 17 00:00:00 2001 From: lcnr Date: Thu, 7 Aug 2025 14:29:11 +0200 Subject: [PATCH 026/195] add opaque type member constraint tests --- .../apply_member_constraint-no-req-eq.rs | 24 +++++++++++++ .../incomplete-constraint.rs | 21 ++++++++++++ .../nested-impl-trait-pass.rs | 5 ++- .../reject-choice-due-to-prev-constraint.rs | 34 +++++++++++++++++++ tests/ui/impl-trait/no-anonymize-regions.rs | 18 ++++++++++ 5 files changed, 101 insertions(+), 1 deletion(-) create mode 100644 tests/ui/impl-trait/member-constraints/apply_member_constraint-no-req-eq.rs create mode 100644 tests/ui/impl-trait/member-constraints/incomplete-constraint.rs create mode 100644 tests/ui/impl-trait/member-constraints/reject-choice-due-to-prev-constraint.rs create mode 100644 tests/ui/impl-trait/no-anonymize-regions.rs diff --git a/tests/ui/impl-trait/member-constraints/apply_member_constraint-no-req-eq.rs b/tests/ui/impl-trait/member-constraints/apply_member_constraint-no-req-eq.rs new file mode 100644 index 000000000000..3aa52fe26447 --- /dev/null +++ b/tests/ui/impl-trait/member-constraints/apply_member_constraint-no-req-eq.rs @@ -0,0 +1,24 @@ +//@ check-pass +// FIXME(-Znext-solver): enable this test + +trait Id { + type This; +} +impl Id for T { + type This = T; +} + +// We have two member constraints here: +// +// - 'unconstrained member ['a, 'static] +// - 'unconstrained member ['static] +// +// Applying the first constraint results in `'unconstrained: 'a` +// while the second then adds `'unconstrained: 'static`. If applying +// member constraints were to require the member region equal to the +// choice region, applying the first constraint first and then the +// second would result in a `'a: 'static` requirement. +fn test<'a>() -> impl Id> + use<'a> { + &() +} +fn main() {} diff --git a/tests/ui/impl-trait/member-constraints/incomplete-constraint.rs b/tests/ui/impl-trait/member-constraints/incomplete-constraint.rs new file mode 100644 index 000000000000..92fd31516d72 --- /dev/null +++ b/tests/ui/impl-trait/member-constraints/incomplete-constraint.rs @@ -0,0 +1,21 @@ +//@ check-pass +// FIXME(-Znext-solver): enable this test + +// These functions currently does not normalize the opaque type but will do +// so in the future. At this point we've got a new use of the opaque will fully +// universal arguments but for which lifetimes in the hidden type are unconstrained. +// +// Applying the member constraints would then incompletely infer `'unconstrained` to `'static`. +fn new_defining_use R, T, R>(_: F) {} + +fn rpit1<'a, 'b: 'b>(x: &'b ()) -> impl Sized + use<'a, 'b> { + new_defining_use(rpit1::<'a, 'b>); + x +} + +struct Inv<'a, 'b>(*mut (&'a (), &'b ())); +fn rpit2<'a>(_: ()) -> impl Sized + use<'a> { + new_defining_use(rpit2::<'a>); + Inv::<'a, 'static>(std::ptr::null_mut()) +} +fn main() {} diff --git a/tests/ui/impl-trait/member-constraints/nested-impl-trait-pass.rs b/tests/ui/impl-trait/member-constraints/nested-impl-trait-pass.rs index 4633ad682302..6860fc5e6a53 100644 --- a/tests/ui/impl-trait/member-constraints/nested-impl-trait-pass.rs +++ b/tests/ui/impl-trait/member-constraints/nested-impl-trait-pass.rs @@ -1,6 +1,7 @@ // Nested impl-traits can impose different member constraints on the same region variable. //@ check-pass +// FIXME(-Znext-solver): enable this test trait Cap<'a> {} impl Cap<'_> for T {} @@ -8,7 +9,9 @@ impl Cap<'_> for T {} // Assuming the hidden type is `[&'?15 u8; 1]`, we have two distinct member constraints: // - '?15 member ['static, 'a, 'b] // from outer impl-trait // - '?15 member ['static, 'a] // from inner impl-trait -// To satisfy both we can only choose 'a. +// To satisfy both we can only choose 'a. Concretely, first member constraint requires ?15 +// to outlive at least 'b while the second requires ?15 to outlive 'a. As 'a outlives 'b we +// end up with 'a as the final member region. fn pass_early_bound<'s, 'a, 'b>(a: &'s u8) -> impl IntoIterator> + Cap<'b> where 's: 'a, diff --git a/tests/ui/impl-trait/member-constraints/reject-choice-due-to-prev-constraint.rs b/tests/ui/impl-trait/member-constraints/reject-choice-due-to-prev-constraint.rs new file mode 100644 index 000000000000..4018256b2c84 --- /dev/null +++ b/tests/ui/impl-trait/member-constraints/reject-choice-due-to-prev-constraint.rs @@ -0,0 +1,34 @@ +//@ revisions: current next +//@[next] compile-flags: -Znext-solver +//@ ignore-compare-mode-next-solver (explicit revisions) +//@ check-pass + +// We've got `'0 member ['a, 'b, 'static]` and `'1 member ['a, 'b, 'static]`. +// +// As '0 gets outlived by 'a - its "upper bound" - the only applicable choice +// region is 'a. +// +// '1 has to outlive 'b so the only applicabel choice regions are 'b and 'static. +// Considering this member constraint by itself would choose 'b as it is the +// smaller of the two regions. +// +// However, this is only the case when ignoring the member constraint on '0. +// After applying this constraint and requiring '0 to outlive 'a. As '1 outlives +// '0, the region 'b is no longer an applicable choice region for '1 as 'b does +// does not outlive 'a. We would therefore choose 'static. +// +// This means applying member constraints is order dependent. We handle this by +// first applying member constraints for regions 'x and then consider the resulting +// constraints when applying member constraints for regions 'y with 'y: 'x. +fn with_constraints<'r0, 'r1, 'a, 'b>() -> *mut (&'r0 (), &'r1 ()) +where + 'r1: 'r0, + 'a: 'r0, + 'r1: 'b, +{ + loop {} +} +fn foo<'a, 'b>() -> impl Sized + use<'a, 'b> { + with_constraints::<'_, '_, 'a, 'b>() +} +fn main() {} diff --git a/tests/ui/impl-trait/no-anonymize-regions.rs b/tests/ui/impl-trait/no-anonymize-regions.rs new file mode 100644 index 000000000000..4f7f7c0641c6 --- /dev/null +++ b/tests/ui/impl-trait/no-anonymize-regions.rs @@ -0,0 +1,18 @@ +//@ check-pass +// FIXME(-Znext-solver): enable this test + +// A regression test for an error in `redis` while working on #139587. +// +// We check for structural equality when adding defining uses of opaques. +// In this test one defining use had anonymized regions while the other +// one did not, causing an error. +struct W(T); +fn constrain R, T, R>(f: F) -> R { + loop {} +} +fn foo<'a>(x: for<'b> fn(&'b ())) -> impl Sized + use<'a> { + let mut r = constrain(foo::<'_>); + r = W(x); + r +} +fn main() {} From 163594c8f81fc6bb8bb37645eac2f3b7da0be518 Mon Sep 17 00:00:00 2001 From: okaneco <47607823+okaneco@users.noreply.github.com> Date: Thu, 7 Aug 2025 14:22:58 -0400 Subject: [PATCH 027/195] Add regression test for `saturating_sub` bounds check issue Add codegen test for issue where `valid_index.saturating_sub(X)` produced an extra bounds check. This was fixed by the LLVM upgrade. --- .../issues/saturating-sub-index-139759.rs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 tests/codegen-llvm/issues/saturating-sub-index-139759.rs diff --git a/tests/codegen-llvm/issues/saturating-sub-index-139759.rs b/tests/codegen-llvm/issues/saturating-sub-index-139759.rs new file mode 100644 index 000000000000..eac2f4d306b9 --- /dev/null +++ b/tests/codegen-llvm/issues/saturating-sub-index-139759.rs @@ -0,0 +1,19 @@ +// Test that calculating an index with saturating subtraction from an in-bounds +// index doesn't generate another bounds check. + +//@ compile-flags: -Copt-level=3 +//@ min-llvm-version: 21 + +#![crate_type = "lib"] + +// CHECK-LABEL: @bounds_check_is_elided +#[no_mangle] +pub fn bounds_check_is_elided(s: &[i32], index: usize) -> i32 { + // CHECK-NOT: panic_bounds_check + if index < s.len() { + let lower_bound = index.saturating_sub(1); + s[lower_bound] + } else { + -1 + } +} From b60f75c9261e0968fbb6f3aa435660db189e0ab5 Mon Sep 17 00:00:00 2001 From: George Tokmaji Date: Sat, 31 May 2025 21:18:41 +0200 Subject: [PATCH 028/195] Add diagnostic explaining STATUS_STACK_BUFFER_OVERRUN not only being used for stack buffer overruns if link.exe exits with that exit code `STATUS_STACK_BUFFER_OVERRUN` is also used for fast abnormal program termination, e.g. by abort(). Emit a special diagnostic to let people know that this most likely doesn't indicate a stack buffer overrun. --- compiler/rustc_codegen_ssa/messages.ftl | 3 +++ compiler/rustc_codegen_ssa/src/back/link.rs | 8 ++++++++ compiler/rustc_codegen_ssa/src/errors.rs | 5 +++++ 3 files changed, 16 insertions(+) diff --git a/compiler/rustc_codegen_ssa/messages.ftl b/compiler/rustc_codegen_ssa/messages.ftl index a70d0011d161..53a52736a97c 100644 --- a/compiler/rustc_codegen_ssa/messages.ftl +++ b/compiler/rustc_codegen_ssa/messages.ftl @@ -178,6 +178,9 @@ codegen_ssa_ld64_unimplemented_modifier = `as-needed` modifier not implemented y codegen_ssa_lib_def_write_failure = failed to write lib.def file: {$error} +codegen_ssa_link_exe_status_stack_buffer_overrun = 0xc0000409 is `STATUS_STACK_BUFFER_OVERRUN` + .note = this may have been caused by a program abort and not a stack buffer overrun + codegen_ssa_link_exe_unexpected_error = `link.exe` returned an unexpected error codegen_ssa_link_script_unavailable = can only use link script when linking with GNU-like linker diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index b69fbf61185d..c82af3d9d1e2 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -880,6 +880,14 @@ fn link_natively( windows_registry::find_tool(&sess.target.arch, "link.exe").is_some(); sess.dcx().emit_note(errors::LinkExeUnexpectedError); + + // STATUS_STACK_BUFFER_OVERRUN is also used for fast abnormal program termination, e.g. abort(). + // Emit a special diagnostic to let people know that this most likely doesn't indicate a stack buffer overrun. + const STATUS_STACK_BUFFER_OVERRUN: i32 = 0xc0000409u32 as _; + if code == STATUS_STACK_BUFFER_OVERRUN { + sess.dcx().emit_note(errors::LinkExeStatusStackBufferOverrun); + } + if is_vs_installed && has_linker { // the linker is broken sess.dcx().emit_note(errors::RepairVSBuildTools); diff --git a/compiler/rustc_codegen_ssa/src/errors.rs b/compiler/rustc_codegen_ssa/src/errors.rs index 3d787d8bdbde..9ef1473e538e 100644 --- a/compiler/rustc_codegen_ssa/src/errors.rs +++ b/compiler/rustc_codegen_ssa/src/errors.rs @@ -550,6 +550,11 @@ impl Diagnostic<'_, G> for LinkingFailed<'_> { #[diag(codegen_ssa_link_exe_unexpected_error)] pub(crate) struct LinkExeUnexpectedError; +#[derive(Diagnostic)] +#[diag(codegen_ssa_link_exe_status_stack_buffer_overrun)] +#[note] +pub(crate) struct LinkExeStatusStackBufferOverrun; + #[derive(Diagnostic)] #[diag(codegen_ssa_repair_vs_build_tools)] pub(crate) struct RepairVSBuildTools; From 7e5acb91d70d2d6f77ed4e12041ef00a1e4582fe Mon Sep 17 00:00:00 2001 From: George Tokmaji Date: Thu, 7 Aug 2025 23:42:17 +0200 Subject: [PATCH 029/195] Add note mentioning the event log to LinkExeStatusStackBufferOverrun --- compiler/rustc_codegen_ssa/messages.ftl | 3 ++- compiler/rustc_codegen_ssa/src/errors.rs | 13 ++++++++++--- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_codegen_ssa/messages.ftl b/compiler/rustc_codegen_ssa/messages.ftl index 53a52736a97c..71212253afc6 100644 --- a/compiler/rustc_codegen_ssa/messages.ftl +++ b/compiler/rustc_codegen_ssa/messages.ftl @@ -179,7 +179,8 @@ codegen_ssa_ld64_unimplemented_modifier = `as-needed` modifier not implemented y codegen_ssa_lib_def_write_failure = failed to write lib.def file: {$error} codegen_ssa_link_exe_status_stack_buffer_overrun = 0xc0000409 is `STATUS_STACK_BUFFER_OVERRUN` - .note = this may have been caused by a program abort and not a stack buffer overrun + .abort_note = this may have been caused by a program abort and not a stack buffer overrun + .event_log_note = consider checking the Application Event Log for Windows Error Reporting events to see the fail fast error code codegen_ssa_link_exe_unexpected_error = `link.exe` returned an unexpected error diff --git a/compiler/rustc_codegen_ssa/src/errors.rs b/compiler/rustc_codegen_ssa/src/errors.rs index 9ef1473e538e..fc1092f79a5d 100644 --- a/compiler/rustc_codegen_ssa/src/errors.rs +++ b/compiler/rustc_codegen_ssa/src/errors.rs @@ -550,11 +550,18 @@ impl Diagnostic<'_, G> for LinkingFailed<'_> { #[diag(codegen_ssa_link_exe_unexpected_error)] pub(crate) struct LinkExeUnexpectedError; -#[derive(Diagnostic)] -#[diag(codegen_ssa_link_exe_status_stack_buffer_overrun)] -#[note] pub(crate) struct LinkExeStatusStackBufferOverrun; +impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for LinkExeStatusStackBufferOverrun { + fn into_diag(self, dcx: rustc_errors::DiagCtxtHandle<'a>, level: Level) -> Diag<'a, G> { + let mut diag = + Diag::new(dcx, level, fluent::codegen_ssa_link_exe_status_stack_buffer_overrun); + diag.note(fluent::codegen_ssa_abort_note); + diag.note(fluent::codegen_ssa_event_log_note); + diag + } +} + #[derive(Diagnostic)] #[diag(codegen_ssa_repair_vs_build_tools)] pub(crate) struct RepairVSBuildTools; From 4ec688110f21b04ab243c5a3e56997ea6c33dbcc Mon Sep 17 00:00:00 2001 From: dianne Date: Thu, 3 Jul 2025 03:31:55 -0700 Subject: [PATCH 030/195] add more tests for `if let` guard drop order --- tests/ui/drop/if-let-guards.rs | 89 ++++++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 tests/ui/drop/if-let-guards.rs diff --git a/tests/ui/drop/if-let-guards.rs b/tests/ui/drop/if-let-guards.rs new file mode 100644 index 000000000000..918245e029ca --- /dev/null +++ b/tests/ui/drop/if-let-guards.rs @@ -0,0 +1,89 @@ +//! Tests drop order for `if let` guard bindings and temporaries. This is for behavior specific to +//! `match` expressions, whereas `tests/ui/drop/drop-order-comparisons.rs` compares `let` chains in +//! guards to `let` chains in `if` expressions. Drop order for `let` chains in guards shouldn't +//! differ between Editions, so we test on both 2021 and 2024, expecting the same results. +//@ revisions: e2021 e2024 +//@ [e2021] edition: 2021 +//@ [e2024] edition: 2024 +//@ run-pass + +#![feature(if_let_guard)] +#![deny(rust_2024_compatibility)] + +use core::{cell::RefCell, ops::Drop}; + +fn main() { + // Test that `let` guard bindings and temps are dropped before the arm's pattern's bindings. + // TODO: this is currently the old behavior (`let` bindings dropped after arm bindings). + assert_drop_order(1..=6, |o| { + // We move out of the scrutinee, so the drop order of the array's elements are based on + // binding declaration order, and they're dropped in the arm's scope. + match [o.log(3), o.log(2)] { + // Partially move from the guard temporary to test drops both for temps and the binding. + [_x, _y] if let [_, _z, _] = [o.log(6), o.log(4), o.log(5)] + && true => { let _a = o.log(1); } + _ => unreachable!(), + } + }); + + // Sanity check: we don't move out of the match scrutinee when the guard fails. + assert_drop_order(1..=4, |o| { + // The scrutinee uses the drop order for arrays since its elements aren't moved. + match [o.log(3), o.log(4)] { + [_x, _y] if let _z = o.log(1) + && false => unreachable!(), + _ => { let _a = o.log(2); } + } + }); + + // Test `let` guards' temporaries are dropped immediately when a guard fails, even if the guard + // is lowered and run multiple times on the same arm due to or-patterns. + assert_drop_order(1..=8, |o| { + let mut _x = 1; + // The match's scrutinee isn't bound by-move, so it outlives the match. + match o.log(8) { + // Failing a guard breaks out of the arm's scope, dropping the `let` guard's scrutinee. + _ | _ | _ if let _ = o.log(_x) + && { _x += 1; false } => unreachable!(), + // The temporaries from a failed guard are dropped before testing the next guard. + _ if let _ = o.log(5) + && { o.push(4); false } => unreachable!(), + // If the guard succeeds, we stay in the arm's scope to execute its body. + _ if let _ = o.log(7) + && true => { o.log(6); } + _ => unreachable!(), + } + }); +} + +// # Test scaffolding... + +struct DropOrder(RefCell>); +struct LogDrop<'o>(&'o DropOrder, u64); + +impl DropOrder { + fn log(&self, n: u64) -> LogDrop<'_> { + LogDrop(self, n) + } + fn push(&self, n: u64) { + self.0.borrow_mut().push(n); + } +} + +impl<'o> Drop for LogDrop<'o> { + fn drop(&mut self) { + self.0.push(self.1); + } +} + +#[track_caller] +fn assert_drop_order( + ex: impl IntoIterator, + f: impl Fn(&DropOrder), +) { + let order = DropOrder(RefCell::new(Vec::new())); + f(&order); + let order = order.0.into_inner(); + let expected: Vec = ex.into_iter().collect(); + assert_eq!(order, expected); +} From c00881bcb9a02906fbbfff9fe447053914a273ee Mon Sep 17 00:00:00 2001 From: Manuel Drehwald Date: Thu, 7 Aug 2025 16:31:00 -0700 Subject: [PATCH 031/195] update enzyme submodule to handle llvm 21 --- src/bootstrap/src/core/build_steps/llvm.rs | 1 + src/tools/enzyme | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/bootstrap/src/core/build_steps/llvm.rs b/src/bootstrap/src/core/build_steps/llvm.rs index 721ba6ca459e..065a39a4e714 100644 --- a/src/bootstrap/src/core/build_steps/llvm.rs +++ b/src/bootstrap/src/core/build_steps/llvm.rs @@ -986,6 +986,7 @@ impl Step for Enzyme { .env("LLVM_CONFIG_REAL", &llvm_config) .define("LLVM_ENABLE_ASSERTIONS", "ON") .define("ENZYME_EXTERNAL_SHARED_LIB", "ON") + .define("ENZYME_BC_LOADER", "OFF") .define("LLVM_DIR", builder.llvm_out(target)); cfg.build(); diff --git a/src/tools/enzyme b/src/tools/enzyme index 2cccfba93c16..58af4e9e6c04 160000 --- a/src/tools/enzyme +++ b/src/tools/enzyme @@ -1 +1 @@ -Subproject commit 2cccfba93c1650f26f1cf8be8aa875a7c1d23fb3 +Subproject commit 58af4e9e6c047534ba059b12af17cecb8a2e9f9e From b2241c78c86a15b8e7e842f036135df4478de0fe Mon Sep 17 00:00:00 2001 From: dianne Date: Thu, 3 Jul 2025 06:10:37 -0700 Subject: [PATCH 032/195] add a scope for `if let` guard temporaries and bindings This ensures `if let` guard temporaries and bindings are dropped before the match arm's pattern's bindings. --- .../rustc_hir_analysis/src/check/region.rs | 5 ++ compiler/rustc_middle/src/middle/region.rs | 6 ++ compiler/rustc_middle/src/ty/rvalue_scopes.rs | 2 +- .../src/builder/matches/mod.rs | 76 ++++++++++--------- compiler/rustc_mir_build/src/builder/scope.rs | 23 ++++-- tests/ui/drop/if-let-guards.rs | 5 +- .../ui/thir-print/thir-tree-loop-match.stdout | 16 ++-- tests/ui/thir-print/thir-tree-match.stdout | 12 +-- 8 files changed, 83 insertions(+), 62 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/check/region.rs b/compiler/rustc_hir_analysis/src/check/region.rs index 95f6fba6487a..d43ac0537582 100644 --- a/compiler/rustc_hir_analysis/src/check/region.rs +++ b/compiler/rustc_hir_analysis/src/check/region.rs @@ -198,6 +198,11 @@ fn resolve_arm<'tcx>(visitor: &mut ScopeResolutionVisitor<'tcx>, arm: &'tcx hir: visitor.cx.var_parent = visitor.cx.parent; resolve_pat(visitor, arm.pat); + // We introduce a new scope to contain bindings and temporaries from `if let` guards, to + // ensure they're dropped before the arm's pattern's bindings. This extends to the end of + // the arm body and is the scope of its locals as well. + visitor.enter_scope(Scope { local_id: arm.hir_id.local_id, data: ScopeData::MatchGuard }); + visitor.cx.var_parent = visitor.cx.parent; if let Some(guard) = arm.guard { resolve_cond(visitor, guard); } diff --git a/compiler/rustc_middle/src/middle/region.rs b/compiler/rustc_middle/src/middle/region.rs index 800c1af660a4..857d041224fa 100644 --- a/compiler/rustc_middle/src/middle/region.rs +++ b/compiler/rustc_middle/src/middle/region.rs @@ -96,6 +96,7 @@ impl fmt::Debug for Scope { ScopeData::Destruction => write!(fmt, "Destruction({:?})", self.local_id), ScopeData::IfThen => write!(fmt, "IfThen({:?})", self.local_id), ScopeData::IfThenRescope => write!(fmt, "IfThen[edition2024]({:?})", self.local_id), + ScopeData::MatchGuard => write!(fmt, "MatchGuard({:?})", self.local_id), ScopeData::Remainder(fsi) => write!( fmt, "Remainder {{ block: {:?}, first_statement_index: {}}}", @@ -131,6 +132,11 @@ pub enum ScopeData { /// whose lifetimes do not cross beyond this scope. IfThenRescope, + /// Scope of the condition and body of a match arm with a guard + /// Used for variables introduced in an if-let guard, + /// whose lifetimes do not cross beyond this scope. + MatchGuard, + /// Scope following a `let id = expr;` binding in a block. Remainder(FirstStatementIndex), } diff --git a/compiler/rustc_middle/src/ty/rvalue_scopes.rs b/compiler/rustc_middle/src/ty/rvalue_scopes.rs index 9bf6e3a75900..7dfe2d280514 100644 --- a/compiler/rustc_middle/src/ty/rvalue_scopes.rs +++ b/compiler/rustc_middle/src/ty/rvalue_scopes.rs @@ -44,7 +44,7 @@ impl RvalueScopes { debug!("temporary_scope({expr_id:?}) = {id:?} [enclosing]"); return (Some(id), backwards_incompatible); } - ScopeData::IfThenRescope => { + ScopeData::IfThenRescope | ScopeData::MatchGuard => { debug!("temporary_scope({expr_id:?}) = {p:?} [enclosing]"); return (Some(p), backwards_incompatible); } diff --git a/compiler/rustc_mir_build/src/builder/matches/mod.rs b/compiler/rustc_mir_build/src/builder/matches/mod.rs index aebd78515a25..c9a561fc54a2 100644 --- a/compiler/rustc_mir_build/src/builder/matches/mod.rs +++ b/compiler/rustc_mir_build/src/builder/matches/mod.rs @@ -435,46 +435,50 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let arm_scope = (arm.scope, arm_source_info); let match_scope = self.local_scope(); self.in_scope(arm_scope, arm.lint_level, |this| { - let old_dedup_scope = - mem::replace(&mut this.fixed_temps_scope, Some(arm.scope)); + let guard_scope = + region::Scope { data: region::ScopeData::MatchGuard, ..arm.scope }; + this.in_scope((guard_scope, arm_source_info), LintLevel::Inherited, |this| { + let old_dedup_scope = + mem::replace(&mut this.fixed_temps_scope, Some(guard_scope)); - // `try_to_place` may fail if it is unable to resolve the given - // `PlaceBuilder` inside a closure. In this case, we don't want to include - // a scrutinee place. `scrutinee_place_builder` will fail to be resolved - // if the only match arm is a wildcard (`_`). - // Example: - // ``` - // let foo = (0, 1); - // let c = || { - // match foo { _ => () }; - // }; - // ``` - let scrutinee_place = scrutinee_place_builder.try_to_place(this); - let opt_scrutinee_place = - scrutinee_place.as_ref().map(|place| (Some(place), scrutinee_span)); - let scope = this.declare_bindings( - None, - arm.span, - &arm.pattern, - arm.guard, - opt_scrutinee_place, - ); + // `try_to_place` may fail if it is unable to resolve the given + // `PlaceBuilder` inside a closure. In this case, we don't want to include + // a scrutinee place. `scrutinee_place_builder` will fail to be resolved + // if the only match arm is a wildcard (`_`). + // Example: + // ``` + // let foo = (0, 1); + // let c = || { + // match foo { _ => () }; + // }; + // ``` + let scrutinee_place = scrutinee_place_builder.try_to_place(this); + let opt_scrutinee_place = + scrutinee_place.as_ref().map(|place| (Some(place), scrutinee_span)); + let scope = this.declare_bindings( + None, + arm.span, + &arm.pattern, + arm.guard, + opt_scrutinee_place, + ); - let arm_block = this.bind_pattern( - outer_source_info, - branch, - &built_match_tree.fake_borrow_temps, - scrutinee_span, - Some((arm, match_scope)), - ); + let arm_block = this.bind_pattern( + outer_source_info, + branch, + &built_match_tree.fake_borrow_temps, + scrutinee_span, + Some((arm, match_scope)), + ); - this.fixed_temps_scope = old_dedup_scope; + this.fixed_temps_scope = old_dedup_scope; - if let Some(source_scope) = scope { - this.source_scope = source_scope; - } + if let Some(source_scope) = scope { + this.source_scope = source_scope; + } - this.expr_into_dest(destination, arm_block, arm.body) + this.expr_into_dest(destination, arm_block, arm.body) + }) }) .into_block() }) @@ -2523,7 +2527,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // bindings and temporaries created for and by the guard. As a result, the drop order // for the arm will correspond to the binding order of the final sub-branch lowered. if matches!(schedule_drops, ScheduleDrops::No) { - self.clear_top_scope(arm.scope); + self.clear_match_arm_and_guard_scopes(arm.scope); } let source_info = self.source_info(guard_span); diff --git a/compiler/rustc_mir_build/src/builder/scope.rs b/compiler/rustc_mir_build/src/builder/scope.rs index 1240b34cf9d4..403e7167a69c 100644 --- a/compiler/rustc_mir_build/src/builder/scope.rs +++ b/compiler/rustc_mir_build/src/builder/scope.rs @@ -1750,17 +1750,24 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { success_block } - /// Unschedules any drops in the top scope. + /// Unschedules any drops in the top two scopes. /// - /// This is only needed for `match` arm scopes, because they have one - /// entrance per pattern, but only one exit. - pub(crate) fn clear_top_scope(&mut self, region_scope: region::Scope) { - let top_scope = self.scopes.scopes.last_mut().unwrap(); + /// This is only needed for pattern-matches combining guards and or-patterns: or-patterns lead + /// to guards being lowered multiple times before lowering the arm body, so we unschedle drops + /// for guards' temporaries and bindings between lowering each instance of an match arm's guard. + pub(crate) fn clear_match_arm_and_guard_scopes(&mut self, region_scope: region::Scope) { + let [.., arm_scope, guard_scope] = &mut *self.scopes.scopes else { + bug!("matches with guards should introduce separate scopes for the pattern and guard"); + }; - assert_eq!(top_scope.region_scope, region_scope); + assert_eq!(arm_scope.region_scope, region_scope); + assert_eq!(guard_scope.region_scope.data, region::ScopeData::MatchGuard); + assert_eq!(guard_scope.region_scope.local_id, region_scope.local_id); - top_scope.drops.clear(); - top_scope.invalidate_cache(); + arm_scope.drops.clear(); + arm_scope.invalidate_cache(); + guard_scope.drops.clear(); + guard_scope.invalidate_cache(); } } diff --git a/tests/ui/drop/if-let-guards.rs b/tests/ui/drop/if-let-guards.rs index 918245e029ca..bd353112c09b 100644 --- a/tests/ui/drop/if-let-guards.rs +++ b/tests/ui/drop/if-let-guards.rs @@ -14,13 +14,12 @@ use core::{cell::RefCell, ops::Drop}; fn main() { // Test that `let` guard bindings and temps are dropped before the arm's pattern's bindings. - // TODO: this is currently the old behavior (`let` bindings dropped after arm bindings). assert_drop_order(1..=6, |o| { // We move out of the scrutinee, so the drop order of the array's elements are based on // binding declaration order, and they're dropped in the arm's scope. - match [o.log(3), o.log(2)] { + match [o.log(6), o.log(5)] { // Partially move from the guard temporary to test drops both for temps and the binding. - [_x, _y] if let [_, _z, _] = [o.log(6), o.log(4), o.log(5)] + [_x, _y] if let [_, _z, _] = [o.log(4), o.log(2), o.log(3)] && true => { let _a = o.log(1); } _ => unreachable!(), } diff --git a/tests/ui/thir-print/thir-tree-loop-match.stdout b/tests/ui/thir-print/thir-tree-loop-match.stdout index 5c4c50cb1562..86d37e987e2c 100644 --- a/tests/ui/thir-print/thir-tree-loop-match.stdout +++ b/tests/ui/thir-print/thir-tree-loop-match.stdout @@ -129,7 +129,7 @@ body: body: Expr { ty: bool - temp_lifetime: TempLifetime { temp_lifetime: Some(Node(16)), backwards_incompatible: None } + temp_lifetime: TempLifetime { temp_lifetime: Some(MatchGuard(16)), backwards_incompatible: None } span: $DIR/thir-tree-loop-match.rs:12:25: 15:18 (#0) kind: Scope { @@ -138,14 +138,14 @@ body: value: Expr { ty: bool - temp_lifetime: TempLifetime { temp_lifetime: Some(Node(16)), backwards_incompatible: None } + temp_lifetime: TempLifetime { temp_lifetime: Some(MatchGuard(16)), backwards_incompatible: None } span: $DIR/thir-tree-loop-match.rs:12:25: 15:18 (#0) kind: NeverToAny { source: Expr { ty: ! - temp_lifetime: TempLifetime { temp_lifetime: Some(Node(16)), backwards_incompatible: None } + temp_lifetime: TempLifetime { temp_lifetime: Some(MatchGuard(16)), backwards_incompatible: None } span: $DIR/thir-tree-loop-match.rs:12:25: 15:18 (#0) kind: Block { @@ -227,7 +227,7 @@ body: body: Expr { ty: bool - temp_lifetime: TempLifetime { temp_lifetime: Some(Node(24)), backwards_incompatible: None } + temp_lifetime: TempLifetime { temp_lifetime: Some(MatchGuard(24)), backwards_incompatible: None } span: $DIR/thir-tree-loop-match.rs:16:26: 16:38 (#0) kind: Scope { @@ -236,21 +236,21 @@ body: value: Expr { ty: bool - temp_lifetime: TempLifetime { temp_lifetime: Some(Node(24)), backwards_incompatible: None } + temp_lifetime: TempLifetime { temp_lifetime: Some(MatchGuard(24)), backwards_incompatible: None } span: $DIR/thir-tree-loop-match.rs:16:26: 16:38 (#0) kind: NeverToAny { source: Expr { ty: ! - temp_lifetime: TempLifetime { temp_lifetime: Some(Node(24)), backwards_incompatible: None } + temp_lifetime: TempLifetime { temp_lifetime: Some(MatchGuard(24)), backwards_incompatible: None } span: $DIR/thir-tree-loop-match.rs:16:26: 16:38 (#0) kind: Return { value: Expr { ty: bool - temp_lifetime: TempLifetime { temp_lifetime: Some(Node(24)), backwards_incompatible: None } + temp_lifetime: TempLifetime { temp_lifetime: Some(MatchGuard(24)), backwards_incompatible: None } span: $DIR/thir-tree-loop-match.rs:16:33: 16:38 (#0) kind: Scope { @@ -259,7 +259,7 @@ body: value: Expr { ty: bool - temp_lifetime: TempLifetime { temp_lifetime: Some(Node(24)), backwards_incompatible: None } + temp_lifetime: TempLifetime { temp_lifetime: Some(MatchGuard(24)), backwards_incompatible: None } span: $DIR/thir-tree-loop-match.rs:16:33: 16:38 (#0) kind: VarRef { diff --git a/tests/ui/thir-print/thir-tree-match.stdout b/tests/ui/thir-print/thir-tree-match.stdout index 2049c531abd9..31b9b3b99955 100644 --- a/tests/ui/thir-print/thir-tree-match.stdout +++ b/tests/ui/thir-print/thir-tree-match.stdout @@ -123,7 +123,7 @@ body: body: Expr { ty: bool - temp_lifetime: TempLifetime { temp_lifetime: Some(Node(14)), backwards_incompatible: None } + temp_lifetime: TempLifetime { temp_lifetime: Some(MatchGuard(14)), backwards_incompatible: None } span: $DIR/thir-tree-match.rs:17:36: 17:40 (#0) kind: Scope { @@ -132,7 +132,7 @@ body: value: Expr { ty: bool - temp_lifetime: TempLifetime { temp_lifetime: Some(Node(14)), backwards_incompatible: None } + temp_lifetime: TempLifetime { temp_lifetime: Some(MatchGuard(14)), backwards_incompatible: None } span: $DIR/thir-tree-match.rs:17:36: 17:40 (#0) kind: Literal( lit: Spanned { node: Bool(true), span: $DIR/thir-tree-match.rs:17:36: 17:40 (#0) }, neg: false) @@ -175,7 +175,7 @@ body: body: Expr { ty: bool - temp_lifetime: TempLifetime { temp_lifetime: Some(Node(20)), backwards_incompatible: None } + temp_lifetime: TempLifetime { temp_lifetime: Some(MatchGuard(20)), backwards_incompatible: None } span: $DIR/thir-tree-match.rs:18:27: 18:32 (#0) kind: Scope { @@ -184,7 +184,7 @@ body: value: Expr { ty: bool - temp_lifetime: TempLifetime { temp_lifetime: Some(Node(20)), backwards_incompatible: None } + temp_lifetime: TempLifetime { temp_lifetime: Some(MatchGuard(20)), backwards_incompatible: None } span: $DIR/thir-tree-match.rs:18:27: 18:32 (#0) kind: Literal( lit: Spanned { node: Bool(false), span: $DIR/thir-tree-match.rs:18:27: 18:32 (#0) }, neg: false) @@ -219,7 +219,7 @@ body: body: Expr { ty: bool - temp_lifetime: TempLifetime { temp_lifetime: Some(Node(26)), backwards_incompatible: None } + temp_lifetime: TempLifetime { temp_lifetime: Some(MatchGuard(26)), backwards_incompatible: None } span: $DIR/thir-tree-match.rs:19:24: 19:28 (#0) kind: Scope { @@ -228,7 +228,7 @@ body: value: Expr { ty: bool - temp_lifetime: TempLifetime { temp_lifetime: Some(Node(26)), backwards_incompatible: None } + temp_lifetime: TempLifetime { temp_lifetime: Some(MatchGuard(26)), backwards_incompatible: None } span: $DIR/thir-tree-match.rs:19:24: 19:28 (#0) kind: Literal( lit: Spanned { node: Bool(true), span: $DIR/thir-tree-match.rs:19:24: 19:28 (#0) }, neg: false) From 0bdaef5b63fe2d557483e72732e463b86ffb041b Mon Sep 17 00:00:00 2001 From: dianne Date: Thu, 7 Aug 2025 16:17:54 -0700 Subject: [PATCH 033/195] only introduce a guard scope for arms with guards --- compiler/rustc_hir_analysis/src/check/region.rs | 10 +++++----- .../rustc_mir_build/src/builder/matches/mod.rs | 10 ++++++---- compiler/rustc_mir_build/src/builder/scope.rs | 14 ++++++++++++++ tests/ui/thir-print/thir-tree-loop-match.stdout | 16 ++++++++-------- tests/ui/thir-print/thir-tree-match.stdout | 12 ++++++------ 5 files changed, 39 insertions(+), 23 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/check/region.rs b/compiler/rustc_hir_analysis/src/check/region.rs index d43ac0537582..f5770b7312dd 100644 --- a/compiler/rustc_hir_analysis/src/check/region.rs +++ b/compiler/rustc_hir_analysis/src/check/region.rs @@ -198,12 +198,12 @@ fn resolve_arm<'tcx>(visitor: &mut ScopeResolutionVisitor<'tcx>, arm: &'tcx hir: visitor.cx.var_parent = visitor.cx.parent; resolve_pat(visitor, arm.pat); - // We introduce a new scope to contain bindings and temporaries from `if let` guards, to - // ensure they're dropped before the arm's pattern's bindings. This extends to the end of - // the arm body and is the scope of its locals as well. - visitor.enter_scope(Scope { local_id: arm.hir_id.local_id, data: ScopeData::MatchGuard }); - visitor.cx.var_parent = visitor.cx.parent; if let Some(guard) = arm.guard { + // We introduce a new scope to contain bindings and temporaries from `if let` guards, to + // ensure they're dropped before the arm's pattern's bindings. This extends to the end of + // the arm body and is the scope of its locals as well. + visitor.enter_scope(Scope { local_id: arm.hir_id.local_id, data: ScopeData::MatchGuard }); + visitor.cx.var_parent = visitor.cx.parent; resolve_cond(visitor, guard); } resolve_expr(visitor, arm.body, false); diff --git a/compiler/rustc_mir_build/src/builder/matches/mod.rs b/compiler/rustc_mir_build/src/builder/matches/mod.rs index c9a561fc54a2..d5822a65b178 100644 --- a/compiler/rustc_mir_build/src/builder/matches/mod.rs +++ b/compiler/rustc_mir_build/src/builder/matches/mod.rs @@ -434,12 +434,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let arm_source_info = self.source_info(arm.span); let arm_scope = (arm.scope, arm_source_info); let match_scope = self.local_scope(); + let guard_scope = arm + .guard + .map(|_| region::Scope { data: region::ScopeData::MatchGuard, ..arm.scope }); self.in_scope(arm_scope, arm.lint_level, |this| { - let guard_scope = - region::Scope { data: region::ScopeData::MatchGuard, ..arm.scope }; - this.in_scope((guard_scope, arm_source_info), LintLevel::Inherited, |this| { + this.opt_in_scope(guard_scope.map(|scope| (scope, arm_source_info)), |this| { + // `if let` guard temps needing deduplicating will be in the guard scope. let old_dedup_scope = - mem::replace(&mut this.fixed_temps_scope, Some(guard_scope)); + mem::replace(&mut this.fixed_temps_scope, guard_scope); // `try_to_place` may fail if it is unable to resolve the given // `PlaceBuilder` inside a closure. In this case, we don't want to include diff --git a/compiler/rustc_mir_build/src/builder/scope.rs b/compiler/rustc_mir_build/src/builder/scope.rs index 403e7167a69c..966598736220 100644 --- a/compiler/rustc_mir_build/src/builder/scope.rs +++ b/compiler/rustc_mir_build/src/builder/scope.rs @@ -697,6 +697,20 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { block.and(rv) } + /// Convenience wrapper that executes `f` either within the current scope or a new scope. + /// Used for pattern matching, which introduces an additional scope for patterns with guards. + pub(crate) fn opt_in_scope( + &mut self, + opt_region_scope: Option<(region::Scope, SourceInfo)>, + f: impl FnOnce(&mut Builder<'a, 'tcx>) -> BlockAnd, + ) -> BlockAnd { + if let Some(region_scope) = opt_region_scope { + self.in_scope(region_scope, LintLevel::Inherited, f) + } else { + f(self) + } + } + /// Push a scope onto the stack. You can then build code in this /// scope and call `pop_scope` afterwards. Note that these two /// calls must be paired; using `in_scope` as a convenience diff --git a/tests/ui/thir-print/thir-tree-loop-match.stdout b/tests/ui/thir-print/thir-tree-loop-match.stdout index 86d37e987e2c..5c4c50cb1562 100644 --- a/tests/ui/thir-print/thir-tree-loop-match.stdout +++ b/tests/ui/thir-print/thir-tree-loop-match.stdout @@ -129,7 +129,7 @@ body: body: Expr { ty: bool - temp_lifetime: TempLifetime { temp_lifetime: Some(MatchGuard(16)), backwards_incompatible: None } + temp_lifetime: TempLifetime { temp_lifetime: Some(Node(16)), backwards_incompatible: None } span: $DIR/thir-tree-loop-match.rs:12:25: 15:18 (#0) kind: Scope { @@ -138,14 +138,14 @@ body: value: Expr { ty: bool - temp_lifetime: TempLifetime { temp_lifetime: Some(MatchGuard(16)), backwards_incompatible: None } + temp_lifetime: TempLifetime { temp_lifetime: Some(Node(16)), backwards_incompatible: None } span: $DIR/thir-tree-loop-match.rs:12:25: 15:18 (#0) kind: NeverToAny { source: Expr { ty: ! - temp_lifetime: TempLifetime { temp_lifetime: Some(MatchGuard(16)), backwards_incompatible: None } + temp_lifetime: TempLifetime { temp_lifetime: Some(Node(16)), backwards_incompatible: None } span: $DIR/thir-tree-loop-match.rs:12:25: 15:18 (#0) kind: Block { @@ -227,7 +227,7 @@ body: body: Expr { ty: bool - temp_lifetime: TempLifetime { temp_lifetime: Some(MatchGuard(24)), backwards_incompatible: None } + temp_lifetime: TempLifetime { temp_lifetime: Some(Node(24)), backwards_incompatible: None } span: $DIR/thir-tree-loop-match.rs:16:26: 16:38 (#0) kind: Scope { @@ -236,21 +236,21 @@ body: value: Expr { ty: bool - temp_lifetime: TempLifetime { temp_lifetime: Some(MatchGuard(24)), backwards_incompatible: None } + temp_lifetime: TempLifetime { temp_lifetime: Some(Node(24)), backwards_incompatible: None } span: $DIR/thir-tree-loop-match.rs:16:26: 16:38 (#0) kind: NeverToAny { source: Expr { ty: ! - temp_lifetime: TempLifetime { temp_lifetime: Some(MatchGuard(24)), backwards_incompatible: None } + temp_lifetime: TempLifetime { temp_lifetime: Some(Node(24)), backwards_incompatible: None } span: $DIR/thir-tree-loop-match.rs:16:26: 16:38 (#0) kind: Return { value: Expr { ty: bool - temp_lifetime: TempLifetime { temp_lifetime: Some(MatchGuard(24)), backwards_incompatible: None } + temp_lifetime: TempLifetime { temp_lifetime: Some(Node(24)), backwards_incompatible: None } span: $DIR/thir-tree-loop-match.rs:16:33: 16:38 (#0) kind: Scope { @@ -259,7 +259,7 @@ body: value: Expr { ty: bool - temp_lifetime: TempLifetime { temp_lifetime: Some(MatchGuard(24)), backwards_incompatible: None } + temp_lifetime: TempLifetime { temp_lifetime: Some(Node(24)), backwards_incompatible: None } span: $DIR/thir-tree-loop-match.rs:16:33: 16:38 (#0) kind: VarRef { diff --git a/tests/ui/thir-print/thir-tree-match.stdout b/tests/ui/thir-print/thir-tree-match.stdout index 31b9b3b99955..2049c531abd9 100644 --- a/tests/ui/thir-print/thir-tree-match.stdout +++ b/tests/ui/thir-print/thir-tree-match.stdout @@ -123,7 +123,7 @@ body: body: Expr { ty: bool - temp_lifetime: TempLifetime { temp_lifetime: Some(MatchGuard(14)), backwards_incompatible: None } + temp_lifetime: TempLifetime { temp_lifetime: Some(Node(14)), backwards_incompatible: None } span: $DIR/thir-tree-match.rs:17:36: 17:40 (#0) kind: Scope { @@ -132,7 +132,7 @@ body: value: Expr { ty: bool - temp_lifetime: TempLifetime { temp_lifetime: Some(MatchGuard(14)), backwards_incompatible: None } + temp_lifetime: TempLifetime { temp_lifetime: Some(Node(14)), backwards_incompatible: None } span: $DIR/thir-tree-match.rs:17:36: 17:40 (#0) kind: Literal( lit: Spanned { node: Bool(true), span: $DIR/thir-tree-match.rs:17:36: 17:40 (#0) }, neg: false) @@ -175,7 +175,7 @@ body: body: Expr { ty: bool - temp_lifetime: TempLifetime { temp_lifetime: Some(MatchGuard(20)), backwards_incompatible: None } + temp_lifetime: TempLifetime { temp_lifetime: Some(Node(20)), backwards_incompatible: None } span: $DIR/thir-tree-match.rs:18:27: 18:32 (#0) kind: Scope { @@ -184,7 +184,7 @@ body: value: Expr { ty: bool - temp_lifetime: TempLifetime { temp_lifetime: Some(MatchGuard(20)), backwards_incompatible: None } + temp_lifetime: TempLifetime { temp_lifetime: Some(Node(20)), backwards_incompatible: None } span: $DIR/thir-tree-match.rs:18:27: 18:32 (#0) kind: Literal( lit: Spanned { node: Bool(false), span: $DIR/thir-tree-match.rs:18:27: 18:32 (#0) }, neg: false) @@ -219,7 +219,7 @@ body: body: Expr { ty: bool - temp_lifetime: TempLifetime { temp_lifetime: Some(MatchGuard(26)), backwards_incompatible: None } + temp_lifetime: TempLifetime { temp_lifetime: Some(Node(26)), backwards_incompatible: None } span: $DIR/thir-tree-match.rs:19:24: 19:28 (#0) kind: Scope { @@ -228,7 +228,7 @@ body: value: Expr { ty: bool - temp_lifetime: TempLifetime { temp_lifetime: Some(MatchGuard(26)), backwards_incompatible: None } + temp_lifetime: TempLifetime { temp_lifetime: Some(Node(26)), backwards_incompatible: None } span: $DIR/thir-tree-match.rs:19:24: 19:28 (#0) kind: Literal( lit: Spanned { node: Bool(true), span: $DIR/thir-tree-match.rs:19:24: 19:28 (#0) }, neg: false) From b4a3d3014e128fcc8dd0e11136929863a3768cae Mon Sep 17 00:00:00 2001 From: Roger Curley Date: Sun, 13 Jul 2025 22:04:06 -0400 Subject: [PATCH 034/195] Consolidate abs tests This clobbers the existing generic abs test, but it covers strictly more, so that seems fine. --- library/coretests/tests/floats/f128.rs | 13 ------------- library/coretests/tests/floats/f16.rs | 13 ------------- library/coretests/tests/floats/f32.rs | 12 ------------ library/coretests/tests/floats/f64.rs | 12 ------------ library/coretests/tests/floats/mod.rs | 12 +++++++++--- 5 files changed, 9 insertions(+), 53 deletions(-) diff --git a/library/coretests/tests/floats/f128.rs b/library/coretests/tests/floats/f128.rs index 36d6a20a9442..bd064443b2a9 100644 --- a/library/coretests/tests/floats/f128.rs +++ b/library/coretests/tests/floats/f128.rs @@ -39,19 +39,6 @@ const NAN_MASK2: u128 = 0x00005555555555555555555555555555; // FIXME(f16_f128,miri): many of these have to be disabled since miri does not yet support // the intrinsics. -#[test] -#[cfg(any(miri, target_has_reliable_f128_math))] -fn test_abs() { - assert_biteq!(f128::INFINITY.abs(), f128::INFINITY); - assert_biteq!(1f128.abs(), 1f128); - assert_biteq!(0f128.abs(), 0f128); - assert_biteq!((-0f128).abs(), 0f128); - assert_biteq!((-1f128).abs(), 1f128); - assert_biteq!(f128::NEG_INFINITY.abs(), f128::INFINITY); - assert_biteq!((1f128 / f128::NEG_INFINITY).abs(), 0f128); - assert!(f128::NAN.abs().is_nan()); -} - #[test] fn test_is_sign_positive() { assert!(f128::INFINITY.is_sign_positive()); diff --git a/library/coretests/tests/floats/f16.rs b/library/coretests/tests/floats/f16.rs index 351c008a37ba..11b4a38a7ed8 100644 --- a/library/coretests/tests/floats/f16.rs +++ b/library/coretests/tests/floats/f16.rs @@ -45,19 +45,6 @@ const NAN_MASK2: u16 = 0x0155; // FIXME(f16_f128,miri): many of these have to be disabled since miri does not yet support // the intrinsics. -#[test] -#[cfg(any(miri, target_has_reliable_f16_math))] -fn test_abs() { - assert_biteq!(f16::INFINITY.abs(), f16::INFINITY); - assert_biteq!(1f16.abs(), 1f16); - assert_biteq!(0f16.abs(), 0f16); - assert_biteq!((-0f16).abs(), 0f16); - assert_biteq!((-1f16).abs(), 1f16); - assert_biteq!(f16::NEG_INFINITY.abs(), f16::INFINITY); - assert_biteq!((1f16 / f16::NEG_INFINITY).abs(), 0f16); - assert!(f16::NAN.abs().is_nan()); -} - #[test] fn test_is_sign_positive() { assert!(f16::INFINITY.is_sign_positive()); diff --git a/library/coretests/tests/floats/f32.rs b/library/coretests/tests/floats/f32.rs index 267b0e4e2943..ab68b9dbfbec 100644 --- a/library/coretests/tests/floats/f32.rs +++ b/library/coretests/tests/floats/f32.rs @@ -29,18 +29,6 @@ const NAN_MASK2: u32 = 0x0055_5555; /// They serve as a way to get an idea of the real precision of floating point operations on different platforms. const APPROX_DELTA: f32 = if cfg!(miri) { 1e-4 } else { 1e-6 }; -#[test] -fn test_abs() { - assert_biteq!(f32::INFINITY.abs(), f32::INFINITY); - assert_biteq!(1f32.abs(), 1f32); - assert_biteq!(0f32.abs(), 0f32); - assert_biteq!((-0f32).abs(), 0f32); - assert_biteq!((-1f32).abs(), 1f32); - assert_biteq!(f32::NEG_INFINITY.abs(), f32::INFINITY); - assert_biteq!((1f32 / f32::NEG_INFINITY).abs(), 0f32); - assert!(f32::NAN.abs().is_nan()); -} - #[test] fn test_signum() { assert_biteq!(f32::INFINITY.signum(), 1f32); diff --git a/library/coretests/tests/floats/f64.rs b/library/coretests/tests/floats/f64.rs index 735b7a765151..05c2df085d52 100644 --- a/library/coretests/tests/floats/f64.rs +++ b/library/coretests/tests/floats/f64.rs @@ -24,18 +24,6 @@ const NAN_MASK1: u64 = 0x000a_aaaa_aaaa_aaaa; /// Second pattern over the mantissa const NAN_MASK2: u64 = 0x0005_5555_5555_5555; -#[test] -fn test_abs() { - assert_biteq!(f64::INFINITY.abs(), f64::INFINITY); - assert_biteq!(1f64.abs(), 1f64); - assert_biteq!(0f64.abs(), 0f64); - assert_biteq!((-0f64).abs(), 0f64); - assert_biteq!((-1f64).abs(), 1f64); - assert_biteq!(f64::NEG_INFINITY.abs(), f64::INFINITY); - assert_biteq!((1f64 / f64::NEG_INFINITY).abs(), 0f64); - assert!(f64::NAN.abs().is_nan()); -} - #[test] fn test_signum() { assert_biteq!(f64::INFINITY.signum(), 1f64); diff --git a/library/coretests/tests/floats/mod.rs b/library/coretests/tests/floats/mod.rs index 43431bba6954..abfcfdd99911 100644 --- a/library/coretests/tests/floats/mod.rs +++ b/library/coretests/tests/floats/mod.rs @@ -720,10 +720,16 @@ float_test! { f128: #[cfg(any(miri, target_has_reliable_f128_math))], }, test { - assert_biteq!((-1.0 as Float).abs(), 1.0); - assert_biteq!((1.0 as Float).abs(), 1.0); - assert_biteq!(Float::NEG_INFINITY.abs(), Float::INFINITY); + let one: Float = 1.0; + let zero: Float = 0.0; assert_biteq!(Float::INFINITY.abs(), Float::INFINITY); + assert_biteq!(one.abs(), one); + assert_biteq!(zero.abs(), zero); + assert_biteq!((-zero).abs(), zero); + assert_biteq!((-one).abs(), one); + assert_biteq!(Float::NEG_INFINITY.abs(), Float::INFINITY); + assert_biteq!((one / Float::NEG_INFINITY).abs(), zero); + assert!(Float::NAN.abs().is_nan()); } } From 83878ea2286f2b2e21411d134f390b31bbfaec7b Mon Sep 17 00:00:00 2001 From: Roger Curley Date: Sun, 13 Jul 2025 22:36:56 -0400 Subject: [PATCH 035/195] Consolidate signum tests --- library/coretests/tests/floats/f32.rs | 12 ------------ library/coretests/tests/floats/f64.rs | 12 ------------ library/coretests/tests/floats/mod.rs | 20 ++++++++++++++++++++ 3 files changed, 20 insertions(+), 24 deletions(-) diff --git a/library/coretests/tests/floats/f32.rs b/library/coretests/tests/floats/f32.rs index ab68b9dbfbec..23af02cada0d 100644 --- a/library/coretests/tests/floats/f32.rs +++ b/library/coretests/tests/floats/f32.rs @@ -29,18 +29,6 @@ const NAN_MASK2: u32 = 0x0055_5555; /// They serve as a way to get an idea of the real precision of floating point operations on different platforms. const APPROX_DELTA: f32 = if cfg!(miri) { 1e-4 } else { 1e-6 }; -#[test] -fn test_signum() { - assert_biteq!(f32::INFINITY.signum(), 1f32); - assert_biteq!(1f32.signum(), 1f32); - assert_biteq!(0f32.signum(), 1f32); - assert_biteq!((-0f32).signum(), -1f32); - assert_biteq!((-1f32).signum(), -1f32); - assert_biteq!(f32::NEG_INFINITY.signum(), -1f32); - assert_biteq!((1f32 / f32::NEG_INFINITY).signum(), -1f32); - assert!(f32::NAN.signum().is_nan()); -} - #[test] fn test_is_sign_positive() { assert!(f32::INFINITY.is_sign_positive()); diff --git a/library/coretests/tests/floats/f64.rs b/library/coretests/tests/floats/f64.rs index 05c2df085d52..cc3311aaa7be 100644 --- a/library/coretests/tests/floats/f64.rs +++ b/library/coretests/tests/floats/f64.rs @@ -24,18 +24,6 @@ const NAN_MASK1: u64 = 0x000a_aaaa_aaaa_aaaa; /// Second pattern over the mantissa const NAN_MASK2: u64 = 0x0005_5555_5555_5555; -#[test] -fn test_signum() { - assert_biteq!(f64::INFINITY.signum(), 1f64); - assert_biteq!(1f64.signum(), 1f64); - assert_biteq!(0f64.signum(), 1f64); - assert_biteq!((-0f64).signum(), -1f64); - assert_biteq!((-1f64).signum(), -1f64); - assert_biteq!(f64::NEG_INFINITY.signum(), -1f64); - assert_biteq!((1f64 / f64::NEG_INFINITY).signum(), -1f64); - assert!(f64::NAN.signum().is_nan()); -} - #[test] fn test_is_sign_positive() { assert!(f64::INFINITY.is_sign_positive()); diff --git a/library/coretests/tests/floats/mod.rs b/library/coretests/tests/floats/mod.rs index abfcfdd99911..26735bbeb0d2 100644 --- a/library/coretests/tests/floats/mod.rs +++ b/library/coretests/tests/floats/mod.rs @@ -957,3 +957,23 @@ float_test! { assert!(Float::NEG_INFINITY.fract().is_nan()); } } + +float_test! { + name: signum, + attrs: { + f16: #[cfg(any(miri, target_has_reliable_f16_math))], + f128: #[cfg(any(miri, target_has_reliable_f128_math))], + }, + test { + let one: Float = 1.0; + let zero: Float = 0.0; + assert_biteq!(Float::INFINITY.signum(), one); + assert_biteq!(one.signum(), one); + assert_biteq!(zero.signum(), one); + assert_biteq!((-zero).signum(), -one); + assert_biteq!((-one).signum(), -one); + assert_biteq!(Float::NEG_INFINITY.signum(), -one); + assert_biteq!((one / Float::NEG_INFINITY).signum(), -one); + assert!(Float::NAN.signum().is_nan()); + } +} From 951ef57b85bfa119b34378fdd55bdabd6d22e6d6 Mon Sep 17 00:00:00 2001 From: Roger Curley Date: Sun, 13 Jul 2025 22:46:18 -0400 Subject: [PATCH 036/195] Consolidate is_positive tests --- library/coretests/tests/floats/f128.rs | 13 ------------- library/coretests/tests/floats/f16.rs | 13 ------------- library/coretests/tests/floats/f32.rs | 13 ------------- library/coretests/tests/floats/f64.rs | 13 ------------- library/coretests/tests/floats/mod.rs | 21 +++++++++++++++++++++ 5 files changed, 21 insertions(+), 52 deletions(-) diff --git a/library/coretests/tests/floats/f128.rs b/library/coretests/tests/floats/f128.rs index bd064443b2a9..d67cae19bb79 100644 --- a/library/coretests/tests/floats/f128.rs +++ b/library/coretests/tests/floats/f128.rs @@ -39,19 +39,6 @@ const NAN_MASK2: u128 = 0x00005555555555555555555555555555; // FIXME(f16_f128,miri): many of these have to be disabled since miri does not yet support // the intrinsics. -#[test] -fn test_is_sign_positive() { - assert!(f128::INFINITY.is_sign_positive()); - assert!(1f128.is_sign_positive()); - assert!(0f128.is_sign_positive()); - assert!(!(-0f128).is_sign_positive()); - assert!(!(-1f128).is_sign_positive()); - assert!(!f128::NEG_INFINITY.is_sign_positive()); - assert!(!(1f128 / f128::NEG_INFINITY).is_sign_positive()); - assert!(f128::NAN.is_sign_positive()); - assert!(!(-f128::NAN).is_sign_positive()); -} - #[test] fn test_is_sign_negative() { assert!(!f128::INFINITY.is_sign_negative()); diff --git a/library/coretests/tests/floats/f16.rs b/library/coretests/tests/floats/f16.rs index 11b4a38a7ed8..9ec43f012a2c 100644 --- a/library/coretests/tests/floats/f16.rs +++ b/library/coretests/tests/floats/f16.rs @@ -45,19 +45,6 @@ const NAN_MASK2: u16 = 0x0155; // FIXME(f16_f128,miri): many of these have to be disabled since miri does not yet support // the intrinsics. -#[test] -fn test_is_sign_positive() { - assert!(f16::INFINITY.is_sign_positive()); - assert!(1f16.is_sign_positive()); - assert!(0f16.is_sign_positive()); - assert!(!(-0f16).is_sign_positive()); - assert!(!(-1f16).is_sign_positive()); - assert!(!f16::NEG_INFINITY.is_sign_positive()); - assert!(!(1f16 / f16::NEG_INFINITY).is_sign_positive()); - assert!(f16::NAN.is_sign_positive()); - assert!(!(-f16::NAN).is_sign_positive()); -} - #[test] fn test_is_sign_negative() { assert!(!f16::INFINITY.is_sign_negative()); diff --git a/library/coretests/tests/floats/f32.rs b/library/coretests/tests/floats/f32.rs index 23af02cada0d..c45974382501 100644 --- a/library/coretests/tests/floats/f32.rs +++ b/library/coretests/tests/floats/f32.rs @@ -29,19 +29,6 @@ const NAN_MASK2: u32 = 0x0055_5555; /// They serve as a way to get an idea of the real precision of floating point operations on different platforms. const APPROX_DELTA: f32 = if cfg!(miri) { 1e-4 } else { 1e-6 }; -#[test] -fn test_is_sign_positive() { - assert!(f32::INFINITY.is_sign_positive()); - assert!(1f32.is_sign_positive()); - assert!(0f32.is_sign_positive()); - assert!(!(-0f32).is_sign_positive()); - assert!(!(-1f32).is_sign_positive()); - assert!(!f32::NEG_INFINITY.is_sign_positive()); - assert!(!(1f32 / f32::NEG_INFINITY).is_sign_positive()); - assert!(f32::NAN.is_sign_positive()); - assert!(!(-f32::NAN).is_sign_positive()); -} - #[test] fn test_is_sign_negative() { assert!(!f32::INFINITY.is_sign_negative()); diff --git a/library/coretests/tests/floats/f64.rs b/library/coretests/tests/floats/f64.rs index cc3311aaa7be..d20f45d872d8 100644 --- a/library/coretests/tests/floats/f64.rs +++ b/library/coretests/tests/floats/f64.rs @@ -24,19 +24,6 @@ const NAN_MASK1: u64 = 0x000a_aaaa_aaaa_aaaa; /// Second pattern over the mantissa const NAN_MASK2: u64 = 0x0005_5555_5555_5555; -#[test] -fn test_is_sign_positive() { - assert!(f64::INFINITY.is_sign_positive()); - assert!(1f64.is_sign_positive()); - assert!(0f64.is_sign_positive()); - assert!(!(-0f64).is_sign_positive()); - assert!(!(-1f64).is_sign_positive()); - assert!(!f64::NEG_INFINITY.is_sign_positive()); - assert!(!(1f64 / f64::NEG_INFINITY).is_sign_positive()); - assert!(f64::NAN.is_sign_positive()); - assert!(!(-f64::NAN).is_sign_positive()); -} - #[test] fn test_is_sign_negative() { assert!(!f64::INFINITY.is_sign_negative()); diff --git a/library/coretests/tests/floats/mod.rs b/library/coretests/tests/floats/mod.rs index 26735bbeb0d2..d45cc73174a2 100644 --- a/library/coretests/tests/floats/mod.rs +++ b/library/coretests/tests/floats/mod.rs @@ -977,3 +977,24 @@ float_test! { assert!(Float::NAN.signum().is_nan()); } } + +float_test! { + name: is_sign_positive, + attrs: { + f16: #[cfg(any(miri, target_has_reliable_f16))], + f128: #[cfg(any(miri, target_has_reliable_f128))], + }, + test { + let one: Float = 1.0; + let zero: Float = 0.0; + assert!(Float::INFINITY.is_sign_positive()); + assert!(one.is_sign_positive()); + assert!(zero.is_sign_positive()); + assert!(!(-zero).is_sign_positive()); + assert!(!(-one).is_sign_positive()); + assert!(!Float::NEG_INFINITY.is_sign_positive()); + assert!(!(one / Float::NEG_INFINITY).is_sign_positive()); + assert!(Float::NAN.is_sign_positive()); + assert!(!(-Float::NAN).is_sign_positive()); + } +} From 71973fcbdb1c6681b603c675bb3177724ae2e403 Mon Sep 17 00:00:00 2001 From: Roger Curley Date: Sun, 3 Aug 2025 22:25:49 -0400 Subject: [PATCH 037/195] Consolidate is_sign_negative tests --- library/coretests/tests/floats/f128.rs | 13 ------------- library/coretests/tests/floats/f16.rs | 13 ------------- library/coretests/tests/floats/f32.rs | 13 ------------- library/coretests/tests/floats/f64.rs | 13 ------------- library/coretests/tests/floats/mod.rs | 21 +++++++++++++++++++++ 5 files changed, 21 insertions(+), 52 deletions(-) diff --git a/library/coretests/tests/floats/f128.rs b/library/coretests/tests/floats/f128.rs index d67cae19bb79..d3f8e8ef1d29 100644 --- a/library/coretests/tests/floats/f128.rs +++ b/library/coretests/tests/floats/f128.rs @@ -39,19 +39,6 @@ const NAN_MASK2: u128 = 0x00005555555555555555555555555555; // FIXME(f16_f128,miri): many of these have to be disabled since miri does not yet support // the intrinsics. -#[test] -fn test_is_sign_negative() { - assert!(!f128::INFINITY.is_sign_negative()); - assert!(!1f128.is_sign_negative()); - assert!(!0f128.is_sign_negative()); - assert!((-0f128).is_sign_negative()); - assert!((-1f128).is_sign_negative()); - assert!(f128::NEG_INFINITY.is_sign_negative()); - assert!((1f128 / f128::NEG_INFINITY).is_sign_negative()); - assert!(!f128::NAN.is_sign_negative()); - assert!((-f128::NAN).is_sign_negative()); -} - #[test] fn test_next_up() { let tiny = f128::from_bits(TINY_BITS); diff --git a/library/coretests/tests/floats/f16.rs b/library/coretests/tests/floats/f16.rs index 9ec43f012a2c..db46bff1571e 100644 --- a/library/coretests/tests/floats/f16.rs +++ b/library/coretests/tests/floats/f16.rs @@ -45,19 +45,6 @@ const NAN_MASK2: u16 = 0x0155; // FIXME(f16_f128,miri): many of these have to be disabled since miri does not yet support // the intrinsics. -#[test] -fn test_is_sign_negative() { - assert!(!f16::INFINITY.is_sign_negative()); - assert!(!1f16.is_sign_negative()); - assert!(!0f16.is_sign_negative()); - assert!((-0f16).is_sign_negative()); - assert!((-1f16).is_sign_negative()); - assert!(f16::NEG_INFINITY.is_sign_negative()); - assert!((1f16 / f16::NEG_INFINITY).is_sign_negative()); - assert!(!f16::NAN.is_sign_negative()); - assert!((-f16::NAN).is_sign_negative()); -} - #[test] fn test_next_up() { let tiny = f16::from_bits(TINY_BITS); diff --git a/library/coretests/tests/floats/f32.rs b/library/coretests/tests/floats/f32.rs index c45974382501..6994e8134081 100644 --- a/library/coretests/tests/floats/f32.rs +++ b/library/coretests/tests/floats/f32.rs @@ -29,19 +29,6 @@ const NAN_MASK2: u32 = 0x0055_5555; /// They serve as a way to get an idea of the real precision of floating point operations on different platforms. const APPROX_DELTA: f32 = if cfg!(miri) { 1e-4 } else { 1e-6 }; -#[test] -fn test_is_sign_negative() { - assert!(!f32::INFINITY.is_sign_negative()); - assert!(!1f32.is_sign_negative()); - assert!(!0f32.is_sign_negative()); - assert!((-0f32).is_sign_negative()); - assert!((-1f32).is_sign_negative()); - assert!(f32::NEG_INFINITY.is_sign_negative()); - assert!((1f32 / f32::NEG_INFINITY).is_sign_negative()); - assert!(!f32::NAN.is_sign_negative()); - assert!((-f32::NAN).is_sign_negative()); -} - #[test] fn test_next_up() { let tiny = f32::from_bits(TINY_BITS); diff --git a/library/coretests/tests/floats/f64.rs b/library/coretests/tests/floats/f64.rs index d20f45d872d8..6b5fc8f69c3f 100644 --- a/library/coretests/tests/floats/f64.rs +++ b/library/coretests/tests/floats/f64.rs @@ -24,19 +24,6 @@ const NAN_MASK1: u64 = 0x000a_aaaa_aaaa_aaaa; /// Second pattern over the mantissa const NAN_MASK2: u64 = 0x0005_5555_5555_5555; -#[test] -fn test_is_sign_negative() { - assert!(!f64::INFINITY.is_sign_negative()); - assert!(!1f64.is_sign_negative()); - assert!(!0f64.is_sign_negative()); - assert!((-0f64).is_sign_negative()); - assert!((-1f64).is_sign_negative()); - assert!(f64::NEG_INFINITY.is_sign_negative()); - assert!((1f64 / f64::NEG_INFINITY).is_sign_negative()); - assert!(!f64::NAN.is_sign_negative()); - assert!((-f64::NAN).is_sign_negative()); -} - #[test] fn test_next_up() { let tiny = f64::from_bits(TINY_BITS); diff --git a/library/coretests/tests/floats/mod.rs b/library/coretests/tests/floats/mod.rs index d45cc73174a2..b8600b30fd77 100644 --- a/library/coretests/tests/floats/mod.rs +++ b/library/coretests/tests/floats/mod.rs @@ -998,3 +998,24 @@ float_test! { assert!(!(-Float::NAN).is_sign_positive()); } } + +float_test! { + name: is_sign_negative, + attrs: { + f16: #[cfg(any(miri, target_has_reliable_f16))], + f128: #[cfg(any(miri, target_has_reliable_f128))], + }, + test { + let one: Float = 1.0; + let zero: Float = 0.0; + assert!(!Float::INFINITY.is_sign_negative()); + assert!(!one.is_sign_negative()); + assert!(!zero.is_sign_negative()); + assert!((-zero).is_sign_negative()); + assert!((-one).is_sign_negative()); + assert!(Float::NEG_INFINITY.is_sign_negative()); + assert!((one / Float::NEG_INFINITY).is_sign_negative()); + assert!(!Float::NAN.is_sign_negative()); + assert!((-Float::NAN).is_sign_negative()); + } +} From f51f68b49ae63c44f9be8b3e2a5774e177cdc5ed Mon Sep 17 00:00:00 2001 From: Roger Curley Date: Sun, 3 Aug 2025 23:08:36 -0400 Subject: [PATCH 038/195] Consolidate test_next_up Note that the behaviour of the f128 test is slightly changed to use the same nan mask as is used in test_float_bits_conv, which is the behaviour used by f16,f32,and f64. --- library/coretests/tests/floats/f128.rs | 30 ----------- library/coretests/tests/floats/f16.rs | 30 ----------- library/coretests/tests/floats/f32.rs | 30 ----------- library/coretests/tests/floats/f64.rs | 29 ----------- library/coretests/tests/floats/mod.rs | 69 ++++++++++++++++++++++++++ 5 files changed, 69 insertions(+), 119 deletions(-) diff --git a/library/coretests/tests/floats/f128.rs b/library/coretests/tests/floats/f128.rs index d3f8e8ef1d29..71ca2abf3030 100644 --- a/library/coretests/tests/floats/f128.rs +++ b/library/coretests/tests/floats/f128.rs @@ -39,36 +39,6 @@ const NAN_MASK2: u128 = 0x00005555555555555555555555555555; // FIXME(f16_f128,miri): many of these have to be disabled since miri does not yet support // the intrinsics. -#[test] -fn test_next_up() { - let tiny = f128::from_bits(TINY_BITS); - let tiny_up = f128::from_bits(TINY_UP_BITS); - let max_down = f128::from_bits(MAX_DOWN_BITS); - let largest_subnormal = f128::from_bits(LARGEST_SUBNORMAL_BITS); - let smallest_normal = f128::from_bits(SMALLEST_NORMAL_BITS); - assert_biteq!(f128::NEG_INFINITY.next_up(), f128::MIN); - assert_biteq!(f128::MIN.next_up(), -max_down); - assert_biteq!((-1.0 - f128::EPSILON).next_up(), -1.0f128); - assert_biteq!((-smallest_normal).next_up(), -largest_subnormal); - assert_biteq!((-tiny_up).next_up(), -tiny); - assert_biteq!((-tiny).next_up(), -0.0f128); - assert_biteq!((-0.0f128).next_up(), tiny); - assert_biteq!(0.0f128.next_up(), tiny); - assert_biteq!(tiny.next_up(), tiny_up); - assert_biteq!(largest_subnormal.next_up(), smallest_normal); - assert_biteq!(1.0f128.next_up(), 1.0 + f128::EPSILON); - assert_biteq!(f128::MAX.next_up(), f128::INFINITY); - assert_biteq!(f128::INFINITY.next_up(), f128::INFINITY); - - // Check that NaNs roundtrip. - let nan0 = f128::NAN; - let nan1 = f128::from_bits(f128::NAN.to_bits() ^ 0x002a_aaaa); - let nan2 = f128::from_bits(f128::NAN.to_bits() ^ 0x0055_5555); - assert_biteq!(nan0.next_up(), nan0); - assert_biteq!(nan1.next_up(), nan1); - assert_biteq!(nan2.next_up(), nan2); -} - #[test] fn test_next_down() { let tiny = f128::from_bits(TINY_BITS); diff --git a/library/coretests/tests/floats/f16.rs b/library/coretests/tests/floats/f16.rs index db46bff1571e..e739e6fd0c95 100644 --- a/library/coretests/tests/floats/f16.rs +++ b/library/coretests/tests/floats/f16.rs @@ -45,36 +45,6 @@ const NAN_MASK2: u16 = 0x0155; // FIXME(f16_f128,miri): many of these have to be disabled since miri does not yet support // the intrinsics. -#[test] -fn test_next_up() { - let tiny = f16::from_bits(TINY_BITS); - let tiny_up = f16::from_bits(TINY_UP_BITS); - let max_down = f16::from_bits(MAX_DOWN_BITS); - let largest_subnormal = f16::from_bits(LARGEST_SUBNORMAL_BITS); - let smallest_normal = f16::from_bits(SMALLEST_NORMAL_BITS); - assert_biteq!(f16::NEG_INFINITY.next_up(), f16::MIN); - assert_biteq!(f16::MIN.next_up(), -max_down); - assert_biteq!((-1.0 - f16::EPSILON).next_up(), -1.0f16); - assert_biteq!((-smallest_normal).next_up(), -largest_subnormal); - assert_biteq!((-tiny_up).next_up(), -tiny); - assert_biteq!((-tiny).next_up(), -0.0f16); - assert_biteq!((-0.0f16).next_up(), tiny); - assert_biteq!(0.0f16.next_up(), tiny); - assert_biteq!(tiny.next_up(), tiny_up); - assert_biteq!(largest_subnormal.next_up(), smallest_normal); - assert_biteq!(1.0f16.next_up(), 1.0 + f16::EPSILON); - assert_biteq!(f16::MAX.next_up(), f16::INFINITY); - assert_biteq!(f16::INFINITY.next_up(), f16::INFINITY); - - // Check that NaNs roundtrip. - let nan0 = f16::NAN; - let nan1 = f16::from_bits(f16::NAN.to_bits() ^ NAN_MASK1); - let nan2 = f16::from_bits(f16::NAN.to_bits() ^ NAN_MASK2); - assert_biteq!(nan0.next_up(), nan0); - assert_biteq!(nan1.next_up(), nan1); - assert_biteq!(nan2.next_up(), nan2); -} - #[test] fn test_next_down() { let tiny = f16::from_bits(TINY_BITS); diff --git a/library/coretests/tests/floats/f32.rs b/library/coretests/tests/floats/f32.rs index 6994e8134081..9612660108c1 100644 --- a/library/coretests/tests/floats/f32.rs +++ b/library/coretests/tests/floats/f32.rs @@ -29,36 +29,6 @@ const NAN_MASK2: u32 = 0x0055_5555; /// They serve as a way to get an idea of the real precision of floating point operations on different platforms. const APPROX_DELTA: f32 = if cfg!(miri) { 1e-4 } else { 1e-6 }; -#[test] -fn test_next_up() { - let tiny = f32::from_bits(TINY_BITS); - let tiny_up = f32::from_bits(TINY_UP_BITS); - let max_down = f32::from_bits(MAX_DOWN_BITS); - let largest_subnormal = f32::from_bits(LARGEST_SUBNORMAL_BITS); - let smallest_normal = f32::from_bits(SMALLEST_NORMAL_BITS); - assert_biteq!(f32::NEG_INFINITY.next_up(), f32::MIN); - assert_biteq!(f32::MIN.next_up(), -max_down); - assert_biteq!((-1.0f32 - f32::EPSILON).next_up(), -1.0f32); - assert_biteq!((-smallest_normal).next_up(), -largest_subnormal); - assert_biteq!((-tiny_up).next_up(), -tiny); - assert_biteq!((-tiny).next_up(), -0.0f32); - assert_biteq!((-0.0f32).next_up(), tiny); - assert_biteq!(0.0f32.next_up(), tiny); - assert_biteq!(tiny.next_up(), tiny_up); - assert_biteq!(largest_subnormal.next_up(), smallest_normal); - assert_biteq!(1.0f32.next_up(), 1.0 + f32::EPSILON); - assert_biteq!(f32::MAX.next_up(), f32::INFINITY); - assert_biteq!(f32::INFINITY.next_up(), f32::INFINITY); - - // Check that NaNs roundtrip. - let nan0 = f32::NAN; - let nan1 = f32::from_bits(f32::NAN.to_bits() ^ NAN_MASK1); - let nan2 = f32::from_bits(f32::NAN.to_bits() ^ NAN_MASK2); - assert_biteq!(nan0.next_up(), nan0); - assert_biteq!(nan1.next_up(), nan1); - assert_biteq!(nan2.next_up(), nan2); -} - #[test] fn test_next_down() { let tiny = f32::from_bits(TINY_BITS); diff --git a/library/coretests/tests/floats/f64.rs b/library/coretests/tests/floats/f64.rs index 6b5fc8f69c3f..cd535da681d7 100644 --- a/library/coretests/tests/floats/f64.rs +++ b/library/coretests/tests/floats/f64.rs @@ -24,35 +24,6 @@ const NAN_MASK1: u64 = 0x000a_aaaa_aaaa_aaaa; /// Second pattern over the mantissa const NAN_MASK2: u64 = 0x0005_5555_5555_5555; -#[test] -fn test_next_up() { - let tiny = f64::from_bits(TINY_BITS); - let tiny_up = f64::from_bits(TINY_UP_BITS); - let max_down = f64::from_bits(MAX_DOWN_BITS); - let largest_subnormal = f64::from_bits(LARGEST_SUBNORMAL_BITS); - let smallest_normal = f64::from_bits(SMALLEST_NORMAL_BITS); - assert_biteq!(f64::NEG_INFINITY.next_up(), f64::MIN); - assert_biteq!(f64::MIN.next_up(), -max_down); - assert_biteq!((-1.0 - f64::EPSILON).next_up(), -1.0f64); - assert_biteq!((-smallest_normal).next_up(), -largest_subnormal); - assert_biteq!((-tiny_up).next_up(), -tiny); - assert_biteq!((-tiny).next_up(), -0.0f64); - assert_biteq!((-0.0f64).next_up(), tiny); - assert_biteq!(0.0f64.next_up(), tiny); - assert_biteq!(tiny.next_up(), tiny_up); - assert_biteq!(largest_subnormal.next_up(), smallest_normal); - assert_biteq!(1.0f64.next_up(), 1.0 + f64::EPSILON); - assert_biteq!(f64::MAX.next_up(), f64::INFINITY); - assert_biteq!(f64::INFINITY.next_up(), f64::INFINITY); - - let nan0 = f64::NAN; - let nan1 = f64::from_bits(f64::NAN.to_bits() ^ NAN_MASK1); - let nan2 = f64::from_bits(f64::NAN.to_bits() ^ NAN_MASK2); - assert_biteq!(nan0.next_up(), nan0); - assert_biteq!(nan1.next_up(), nan1); - assert_biteq!(nan2.next_up(), nan2); -} - #[test] fn test_next_down() { let tiny = f64::from_bits(TINY_BITS); diff --git a/library/coretests/tests/floats/mod.rs b/library/coretests/tests/floats/mod.rs index b8600b30fd77..beb016ce4d02 100644 --- a/library/coretests/tests/floats/mod.rs +++ b/library/coretests/tests/floats/mod.rs @@ -2,34 +2,70 @@ use std::num::FpCategory as Fp; use std::ops::{Add, Div, Mul, Rem, Sub}; trait TestableFloat { + /// Unsigned int with the same size, for converting to/from bits. + type Int; /// Set the default tolerance for float comparison based on the type. const APPROX: Self; const MIN_POSITIVE_NORMAL: Self; const MAX_SUBNORMAL: Self; + /// Smallest number + const TINY: Self; + /// Next smallest number + const TINY_UP: Self; + /// Exponent = 0b11...10, Significand 0b1111..10. Min val > 0 + const MAX_DOWN: Self; + /// First pattern over the mantissa + const NAN_MASK1: Self::Int; + /// Second pattern over the mantissa + const NAN_MASK2: Self::Int; } impl TestableFloat for f16 { + type Int = u16; const APPROX: Self = 1e-3; const MIN_POSITIVE_NORMAL: Self = Self::MIN_POSITIVE; const MAX_SUBNORMAL: Self = Self::MIN_POSITIVE.next_down(); + const TINY: Self = Self::from_bits(0x1); + const TINY_UP: Self = Self::from_bits(0x2); + const MAX_DOWN: Self = Self::from_bits(0x7bfe); + const NAN_MASK1: Self::Int = 0x02aa; + const NAN_MASK2: Self::Int = 0x0155; } impl TestableFloat for f32 { + type Int = u32; const APPROX: Self = 1e-6; const MIN_POSITIVE_NORMAL: Self = Self::MIN_POSITIVE; const MAX_SUBNORMAL: Self = Self::MIN_POSITIVE.next_down(); + const TINY: Self = Self::from_bits(0x1); + const TINY_UP: Self = Self::from_bits(0x2); + const MAX_DOWN: Self = Self::from_bits(0x7f7f_fffe); + const NAN_MASK1: Self::Int = 0x002a_aaaa; + const NAN_MASK2: Self::Int = 0x0055_5555; } impl TestableFloat for f64 { + type Int = u64; const APPROX: Self = 1e-6; const MIN_POSITIVE_NORMAL: Self = Self::MIN_POSITIVE; const MAX_SUBNORMAL: Self = Self::MIN_POSITIVE.next_down(); + const TINY: Self = Self::from_bits(0x1); + const TINY_UP: Self = Self::from_bits(0x2); + const MAX_DOWN: Self = Self::from_bits(0x7fef_ffff_ffff_fffe); + const NAN_MASK1: Self::Int = 0x000a_aaaa_aaaa_aaaa; + const NAN_MASK2: Self::Int = 0x0005_5555_5555_5555; } impl TestableFloat for f128 { + type Int = u128; const APPROX: Self = 1e-9; const MIN_POSITIVE_NORMAL: Self = Self::MIN_POSITIVE; const MAX_SUBNORMAL: Self = Self::MIN_POSITIVE.next_down(); + const TINY: Self = Self::from_bits(0x1); + const TINY_UP: Self = Self::from_bits(0x2); + const MAX_DOWN: Self = Self::from_bits(0x7ffefffffffffffffffffffffffffffe); + const NAN_MASK1: Self::Int = 0x0000aaaaaaaaaaaaaaaaaaaaaaaaaaaa; + const NAN_MASK2: Self::Int = 0x00005555555555555555555555555555; } /// Determine the tolerance for values of the argument type. @@ -1019,3 +1055,36 @@ float_test! { assert!((-Float::NAN).is_sign_negative()); } } + +float_test! { + name: next_up, + attrs: { + f16: #[cfg(any(miri, target_has_reliable_f16))], + f128: #[cfg(any(miri, target_has_reliable_f128))], + }, + test { + let one: Float = 1.0; + let zero: Float = 0.0; + assert_biteq!(Float::NEG_INFINITY.next_up(), Float::MIN); + assert_biteq!(Float::MIN.next_up(), -Float::MAX_DOWN); + assert_biteq!((-one - Float::EPSILON).next_up(), -one); + assert_biteq!((-Float::MIN_POSITIVE_NORMAL).next_up(), -Float::MAX_SUBNORMAL); + assert_biteq!((-Float::TINY_UP).next_up(), -Float::TINY); + assert_biteq!((-Float::TINY).next_up(), -zero); + assert_biteq!((-zero).next_up(), Float::TINY); + assert_biteq!(zero.next_up(), Float::TINY); + assert_biteq!(Float::TINY.next_up(), Float::TINY_UP); + assert_biteq!(Float::MAX_SUBNORMAL.next_up(), Float::MIN_POSITIVE_NORMAL); + assert_biteq!(one.next_up(), 1.0 + Float::EPSILON); + assert_biteq!(Float::MAX.next_up(), Float::INFINITY); + assert_biteq!(Float::INFINITY.next_up(), Float::INFINITY); + + // Check that NaNs roundtrip. + let nan0 = Float::NAN; + let nan1 = Float::from_bits(Float::NAN.to_bits() ^ Float::NAN_MASK1); + let nan2 = Float::from_bits(Float::NAN.to_bits() ^ Float::NAN_MASK2); + assert_biteq!(nan0.next_up(), nan0); + assert_biteq!(nan1.next_up(), nan1); + assert_biteq!(nan2.next_up(), nan2); + } +} From 8a65ce4360eced99b7878e0f05363143a3ecfd53 Mon Sep 17 00:00:00 2001 From: Roger Curley Date: Mon, 4 Aug 2025 11:31:05 -0400 Subject: [PATCH 039/195] Consolidate test_next_down --- library/coretests/tests/floats/f128.rs | 46 -------------------------- library/coretests/tests/floats/f16.rs | 46 -------------------------- library/coretests/tests/floats/f32.rs | 46 -------------------------- library/coretests/tests/floats/f64.rs | 45 ------------------------- library/coretests/tests/floats/mod.rs | 34 +++++++++++++++++++ 5 files changed, 34 insertions(+), 183 deletions(-) diff --git a/library/coretests/tests/floats/f128.rs b/library/coretests/tests/floats/f128.rs index 71ca2abf3030..551241c6c824 100644 --- a/library/coretests/tests/floats/f128.rs +++ b/library/coretests/tests/floats/f128.rs @@ -15,21 +15,6 @@ const TOL: f128 = 1e-12; /// signs. const TOL_PRECISE: f128 = 1e-28; -/// Smallest number -const TINY_BITS: u128 = 0x1; - -/// Next smallest number -const TINY_UP_BITS: u128 = 0x2; - -/// Exponent = 0b11...10, Sifnificand 0b1111..10. Min val > 0 -const MAX_DOWN_BITS: u128 = 0x7ffefffffffffffffffffffffffffffe; - -/// Zeroed exponent, full significant -const LARGEST_SUBNORMAL_BITS: u128 = 0x0000ffffffffffffffffffffffffffff; - -/// Exponent = 0b1, zeroed significand -const SMALLEST_NORMAL_BITS: u128 = 0x00010000000000000000000000000000; - /// First pattern over the mantissa const NAN_MASK1: u128 = 0x0000aaaaaaaaaaaaaaaaaaaaaaaaaaaa; @@ -39,37 +24,6 @@ const NAN_MASK2: u128 = 0x00005555555555555555555555555555; // FIXME(f16_f128,miri): many of these have to be disabled since miri does not yet support // the intrinsics. -#[test] -fn test_next_down() { - let tiny = f128::from_bits(TINY_BITS); - let tiny_up = f128::from_bits(TINY_UP_BITS); - let max_down = f128::from_bits(MAX_DOWN_BITS); - let largest_subnormal = f128::from_bits(LARGEST_SUBNORMAL_BITS); - let smallest_normal = f128::from_bits(SMALLEST_NORMAL_BITS); - assert_biteq!(f128::NEG_INFINITY.next_down(), f128::NEG_INFINITY); - assert_biteq!(f128::MIN.next_down(), f128::NEG_INFINITY); - assert_biteq!((-max_down).next_down(), f128::MIN); - assert_biteq!((-1.0f128).next_down(), -1.0 - f128::EPSILON); - assert_biteq!((-largest_subnormal).next_down(), -smallest_normal); - assert_biteq!((-tiny).next_down(), -tiny_up); - assert_biteq!((-0.0f128).next_down(), -tiny); - assert_biteq!((0.0f128).next_down(), -tiny); - assert_biteq!(tiny.next_down(), 0.0f128); - assert_biteq!(tiny_up.next_down(), tiny); - assert_biteq!(smallest_normal.next_down(), largest_subnormal); - assert_biteq!((1.0 + f128::EPSILON).next_down(), 1.0f128); - assert_biteq!(f128::MAX.next_down(), max_down); - assert_biteq!(f128::INFINITY.next_down(), f128::MAX); - - // Check that NaNs roundtrip. - let nan0 = f128::NAN; - let nan1 = f128::from_bits(f128::NAN.to_bits() ^ 0x002a_aaaa); - let nan2 = f128::from_bits(f128::NAN.to_bits() ^ 0x0055_5555); - assert_biteq!(nan0.next_down(), nan0); - assert_biteq!(nan1.next_down(), nan1); - assert_biteq!(nan2.next_down(), nan2); -} - #[test] #[cfg(not(miri))] #[cfg(target_has_reliable_f128_math)] diff --git a/library/coretests/tests/floats/f16.rs b/library/coretests/tests/floats/f16.rs index e739e6fd0c95..23c2c7acb693 100644 --- a/library/coretests/tests/floats/f16.rs +++ b/library/coretests/tests/floats/f16.rs @@ -21,21 +21,6 @@ const TOL_P2: f16 = 0.5; #[allow(unused)] const TOL_P4: f16 = 10.0; -/// Smallest number -const TINY_BITS: u16 = 0x1; - -/// Next smallest number -const TINY_UP_BITS: u16 = 0x2; - -/// Exponent = 0b11...10, Sifnificand 0b1111..10. Min val > 0 -const MAX_DOWN_BITS: u16 = 0x7bfe; - -/// Zeroed exponent, full significant -const LARGEST_SUBNORMAL_BITS: u16 = 0x03ff; - -/// Exponent = 0b1, zeroed significand -const SMALLEST_NORMAL_BITS: u16 = 0x0400; - /// First pattern over the mantissa const NAN_MASK1: u16 = 0x02aa; @@ -45,37 +30,6 @@ const NAN_MASK2: u16 = 0x0155; // FIXME(f16_f128,miri): many of these have to be disabled since miri does not yet support // the intrinsics. -#[test] -fn test_next_down() { - let tiny = f16::from_bits(TINY_BITS); - let tiny_up = f16::from_bits(TINY_UP_BITS); - let max_down = f16::from_bits(MAX_DOWN_BITS); - let largest_subnormal = f16::from_bits(LARGEST_SUBNORMAL_BITS); - let smallest_normal = f16::from_bits(SMALLEST_NORMAL_BITS); - assert_biteq!(f16::NEG_INFINITY.next_down(), f16::NEG_INFINITY); - assert_biteq!(f16::MIN.next_down(), f16::NEG_INFINITY); - assert_biteq!((-max_down).next_down(), f16::MIN); - assert_biteq!((-1.0f16).next_down(), -1.0 - f16::EPSILON); - assert_biteq!((-largest_subnormal).next_down(), -smallest_normal); - assert_biteq!((-tiny).next_down(), -tiny_up); - assert_biteq!((-0.0f16).next_down(), -tiny); - assert_biteq!((0.0f16).next_down(), -tiny); - assert_biteq!(tiny.next_down(), 0.0f16); - assert_biteq!(tiny_up.next_down(), tiny); - assert_biteq!(smallest_normal.next_down(), largest_subnormal); - assert_biteq!((1.0 + f16::EPSILON).next_down(), 1.0f16); - assert_biteq!(f16::MAX.next_down(), max_down); - assert_biteq!(f16::INFINITY.next_down(), f16::MAX); - - // Check that NaNs roundtrip. - let nan0 = f16::NAN; - let nan1 = f16::from_bits(f16::NAN.to_bits() ^ NAN_MASK1); - let nan2 = f16::from_bits(f16::NAN.to_bits() ^ NAN_MASK2); - assert_biteq!(nan0.next_down(), nan0); - assert_biteq!(nan1.next_down(), nan1); - assert_biteq!(nan2.next_down(), nan2); -} - #[test] #[cfg(not(miri))] #[cfg(target_has_reliable_f16_math)] diff --git a/library/coretests/tests/floats/f32.rs b/library/coretests/tests/floats/f32.rs index 9612660108c1..552cac942f9e 100644 --- a/library/coretests/tests/floats/f32.rs +++ b/library/coretests/tests/floats/f32.rs @@ -3,21 +3,6 @@ use core::f32::consts; use super::{assert_approx_eq, assert_biteq}; -/// Smallest number -const TINY_BITS: u32 = 0x1; - -/// Next smallest number -const TINY_UP_BITS: u32 = 0x2; - -/// Exponent = 0b11...10, Sifnificand 0b1111..10. Min val > 0 -const MAX_DOWN_BITS: u32 = 0x7f7f_fffe; - -/// Zeroed exponent, full significant -const LARGEST_SUBNORMAL_BITS: u32 = 0x007f_ffff; - -/// Exponent = 0b1, zeroed significand -const SMALLEST_NORMAL_BITS: u32 = 0x0080_0000; - /// First pattern over the mantissa const NAN_MASK1: u32 = 0x002a_aaaa; @@ -29,37 +14,6 @@ const NAN_MASK2: u32 = 0x0055_5555; /// They serve as a way to get an idea of the real precision of floating point operations on different platforms. const APPROX_DELTA: f32 = if cfg!(miri) { 1e-4 } else { 1e-6 }; -#[test] -fn test_next_down() { - let tiny = f32::from_bits(TINY_BITS); - let tiny_up = f32::from_bits(TINY_UP_BITS); - let max_down = f32::from_bits(MAX_DOWN_BITS); - let largest_subnormal = f32::from_bits(LARGEST_SUBNORMAL_BITS); - let smallest_normal = f32::from_bits(SMALLEST_NORMAL_BITS); - assert_biteq!(f32::NEG_INFINITY.next_down(), f32::NEG_INFINITY); - assert_biteq!(f32::MIN.next_down(), f32::NEG_INFINITY); - assert_biteq!((-max_down).next_down(), f32::MIN); - assert_biteq!((-1.0f32).next_down(), -1.0 - f32::EPSILON); - assert_biteq!((-largest_subnormal).next_down(), -smallest_normal); - assert_biteq!((-tiny).next_down(), -tiny_up); - assert_biteq!((-0.0f32).next_down(), -tiny); - assert_biteq!((0.0f32).next_down(), -tiny); - assert_biteq!(tiny.next_down(), 0.0f32); - assert_biteq!(tiny_up.next_down(), tiny); - assert_biteq!(smallest_normal.next_down(), largest_subnormal); - assert_biteq!((1.0 + f32::EPSILON).next_down(), 1.0f32); - assert_biteq!(f32::MAX.next_down(), max_down); - assert_biteq!(f32::INFINITY.next_down(), f32::MAX); - - // Check that NaNs roundtrip. - let nan0 = f32::NAN; - let nan1 = f32::from_bits(f32::NAN.to_bits() ^ NAN_MASK1); - let nan2 = f32::from_bits(f32::NAN.to_bits() ^ NAN_MASK2); - assert_biteq!(nan0.next_down(), nan0); - assert_biteq!(nan1.next_down(), nan1); - assert_biteq!(nan2.next_down(), nan2); -} - // FIXME(#140515): mingw has an incorrect fma https://sourceforge.net/p/mingw-w64/bugs/848/ #[cfg_attr(all(target_os = "windows", target_env = "gnu", not(target_abi = "llvm")), ignore)] #[test] diff --git a/library/coretests/tests/floats/f64.rs b/library/coretests/tests/floats/f64.rs index cd535da681d7..1c82406a91e4 100644 --- a/library/coretests/tests/floats/f64.rs +++ b/library/coretests/tests/floats/f64.rs @@ -3,57 +3,12 @@ use core::f64::consts; use super::{assert_approx_eq, assert_biteq}; -/// Smallest number -const TINY_BITS: u64 = 0x1; - -/// Next smallest number -const TINY_UP_BITS: u64 = 0x2; - -/// Exponent = 0b11...10, Sifnificand 0b1111..10. Min val > 0 -const MAX_DOWN_BITS: u64 = 0x7fef_ffff_ffff_fffe; - -/// Zeroed exponent, full significant -const LARGEST_SUBNORMAL_BITS: u64 = 0x000f_ffff_ffff_ffff; - -/// Exponent = 0b1, zeroed significand -const SMALLEST_NORMAL_BITS: u64 = 0x0010_0000_0000_0000; - /// First pattern over the mantissa const NAN_MASK1: u64 = 0x000a_aaaa_aaaa_aaaa; /// Second pattern over the mantissa const NAN_MASK2: u64 = 0x0005_5555_5555_5555; -#[test] -fn test_next_down() { - let tiny = f64::from_bits(TINY_BITS); - let tiny_up = f64::from_bits(TINY_UP_BITS); - let max_down = f64::from_bits(MAX_DOWN_BITS); - let largest_subnormal = f64::from_bits(LARGEST_SUBNORMAL_BITS); - let smallest_normal = f64::from_bits(SMALLEST_NORMAL_BITS); - assert_biteq!(f64::NEG_INFINITY.next_down(), f64::NEG_INFINITY); - assert_biteq!(f64::MIN.next_down(), f64::NEG_INFINITY); - assert_biteq!((-max_down).next_down(), f64::MIN); - assert_biteq!((-1.0f64).next_down(), -1.0 - f64::EPSILON); - assert_biteq!((-largest_subnormal).next_down(), -smallest_normal); - assert_biteq!((-tiny).next_down(), -tiny_up); - assert_biteq!((-0.0f64).next_down(), -tiny); - assert_biteq!((0.0f64).next_down(), -tiny); - assert_biteq!(tiny.next_down(), 0.0f64); - assert_biteq!(tiny_up.next_down(), tiny); - assert_biteq!(smallest_normal.next_down(), largest_subnormal); - assert_biteq!((1.0 + f64::EPSILON).next_down(), 1.0f64); - assert_biteq!(f64::MAX.next_down(), max_down); - assert_biteq!(f64::INFINITY.next_down(), f64::MAX); - - let nan0 = f64::NAN; - let nan1 = f64::from_bits(f64::NAN.to_bits() ^ NAN_MASK1); - let nan2 = f64::from_bits(f64::NAN.to_bits() ^ NAN_MASK2); - assert_biteq!(nan0.next_down(), nan0); - assert_biteq!(nan1.next_down(), nan1); - assert_biteq!(nan2.next_down(), nan2); -} - // FIXME(#140515): mingw has an incorrect fma https://sourceforge.net/p/mingw-w64/bugs/848/ #[cfg_attr(all(target_os = "windows", target_env = "gnu", not(target_abi = "llvm")), ignore)] #[test] diff --git a/library/coretests/tests/floats/mod.rs b/library/coretests/tests/floats/mod.rs index beb016ce4d02..e9210c558f14 100644 --- a/library/coretests/tests/floats/mod.rs +++ b/library/coretests/tests/floats/mod.rs @@ -1088,3 +1088,37 @@ float_test! { assert_biteq!(nan2.next_up(), nan2); } } + +float_test! { + name: next_down, + attrs: { + f16: #[cfg(any(miri, target_has_reliable_f16))], + f128: #[cfg(any(miri, target_has_reliable_f128))], + }, + test { + let one: Float = 1.0; + let zero: Float = 0.0; + assert_biteq!(Float::NEG_INFINITY.next_down(), Float::NEG_INFINITY); + assert_biteq!(Float::MIN.next_down(), Float::NEG_INFINITY); + assert_biteq!((-Float::MAX_DOWN).next_down(), Float::MIN); + assert_biteq!((-one).next_down(), -1.0 - Float::EPSILON); + assert_biteq!((-Float::MAX_SUBNORMAL).next_down(), -Float::MIN_POSITIVE_NORMAL); + assert_biteq!((-Float::TINY).next_down(), -Float::TINY_UP); + assert_biteq!((-zero).next_down(), -Float::TINY); + assert_biteq!((zero).next_down(), -Float::TINY); + assert_biteq!(Float::TINY.next_down(), zero); + assert_biteq!(Float::TINY_UP.next_down(), Float::TINY); + assert_biteq!(Float::MIN_POSITIVE_NORMAL.next_down(), Float::MAX_SUBNORMAL); + assert_biteq!((1.0 + Float::EPSILON).next_down(), one); + assert_biteq!(Float::MAX.next_down(), Float::MAX_DOWN); + assert_biteq!(Float::INFINITY.next_down(), Float::MAX); + + // Check that NaNs roundtrip. + let nan0 = Float::NAN; + let nan1 = Float::from_bits(Float::NAN.to_bits() ^ Float::NAN_MASK1); + let nan2 = Float::from_bits(Float::NAN.to_bits() ^ Float::NAN_MASK2); + assert_biteq!(nan0.next_down(), nan0); + assert_biteq!(nan1.next_down(), nan1); + assert_biteq!(nan2.next_down(), nan2); + } +} From 666bfcae214eccdef07b64671df8609a99a216eb Mon Sep 17 00:00:00 2001 From: Roger Curley Date: Mon, 4 Aug 2025 13:27:47 -0400 Subject: [PATCH 040/195] Consolidate sqrt_domain tests --- library/coretests/tests/floats/f128.rs | 13 ------------- library/coretests/tests/floats/f16.rs | 13 ------------- library/coretests/tests/floats/f32.rs | 11 ----------- library/coretests/tests/floats/f64.rs | 11 ----------- library/coretests/tests/floats/mod.rs | 23 +++++++++++++++++++++++ 5 files changed, 23 insertions(+), 48 deletions(-) diff --git a/library/coretests/tests/floats/f128.rs b/library/coretests/tests/floats/f128.rs index 551241c6c824..43882ad89a2d 100644 --- a/library/coretests/tests/floats/f128.rs +++ b/library/coretests/tests/floats/f128.rs @@ -78,19 +78,6 @@ fn test_powi() { assert_biteq!(neg_inf.powi(2), inf); } -#[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f128_math)] -fn test_sqrt_domain() { - assert!(f128::NAN.sqrt().is_nan()); - assert!(f128::NEG_INFINITY.sqrt().is_nan()); - assert!((-1.0f128).sqrt().is_nan()); - assert_biteq!((-0.0f128).sqrt(), -0.0); - assert_biteq!(0.0f128.sqrt(), 0.0); - assert_biteq!(1.0f128.sqrt(), 1.0); - assert_biteq!(f128::INFINITY.sqrt(), f128::INFINITY); -} - #[test] fn test_to_degrees() { let pi: f128 = consts::PI; diff --git a/library/coretests/tests/floats/f16.rs b/library/coretests/tests/floats/f16.rs index 23c2c7acb693..c405e4e5c85a 100644 --- a/library/coretests/tests/floats/f16.rs +++ b/library/coretests/tests/floats/f16.rs @@ -80,19 +80,6 @@ fn test_powi() { assert_biteq!(neg_inf.powi(2), inf); } -#[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f16_math)] -fn test_sqrt_domain() { - assert!(f16::NAN.sqrt().is_nan()); - assert!(f16::NEG_INFINITY.sqrt().is_nan()); - assert!((-1.0f16).sqrt().is_nan()); - assert_biteq!((-0.0f16).sqrt(), -0.0); - assert_biteq!(0.0f16.sqrt(), 0.0); - assert_biteq!(1.0f16.sqrt(), 1.0); - assert_biteq!(f16::INFINITY.sqrt(), f16::INFINITY); -} - #[test] fn test_to_degrees() { let pi: f16 = consts::PI; diff --git a/library/coretests/tests/floats/f32.rs b/library/coretests/tests/floats/f32.rs index 552cac942f9e..f08b033fb5ea 100644 --- a/library/coretests/tests/floats/f32.rs +++ b/library/coretests/tests/floats/f32.rs @@ -60,17 +60,6 @@ fn test_powi() { assert_biteq!(neg_inf.powi(2), inf); } -#[test] -fn test_sqrt_domain() { - assert!(f32::NAN.sqrt().is_nan()); - assert!(f32::NEG_INFINITY.sqrt().is_nan()); - assert!((-1.0f32).sqrt().is_nan()); - assert_biteq!((-0.0f32).sqrt(), -0.0); - assert_biteq!(0.0f32.sqrt(), 0.0); - assert_biteq!(1.0f32.sqrt(), 1.0); - assert_biteq!(f32::INFINITY.sqrt(), f32::INFINITY); -} - #[test] fn test_to_degrees() { let pi: f32 = consts::PI; diff --git a/library/coretests/tests/floats/f64.rs b/library/coretests/tests/floats/f64.rs index 1c82406a91e4..8d7d31b57c18 100644 --- a/library/coretests/tests/floats/f64.rs +++ b/library/coretests/tests/floats/f64.rs @@ -55,17 +55,6 @@ fn test_powi() { assert_biteq!(neg_inf.powi(2), inf); } -#[test] -fn test_sqrt_domain() { - assert!(f64::NAN.sqrt().is_nan()); - assert!(f64::NEG_INFINITY.sqrt().is_nan()); - assert!((-1.0f64).sqrt().is_nan()); - assert_biteq!((-0.0f64).sqrt(), -0.0); - assert_biteq!(0.0f64.sqrt(), 0.0); - assert_biteq!(1.0f64.sqrt(), 1.0); - assert_biteq!(f64::INFINITY.sqrt(), f64::INFINITY); -} - #[test] fn test_to_degrees() { let pi: f64 = consts::PI; diff --git a/library/coretests/tests/floats/mod.rs b/library/coretests/tests/floats/mod.rs index e9210c558f14..68ed8d47094f 100644 --- a/library/coretests/tests/floats/mod.rs +++ b/library/coretests/tests/floats/mod.rs @@ -1122,3 +1122,26 @@ float_test! { assert_biteq!(nan2.next_down(), nan2); } } + +// FIXME(f16_f128,miri): many of these have to be disabled since miri does not yet support +// the intrinsics. + +float_test! { + name: sqrt_domain, + attrs: { + const: #[cfg(false)], + f16: #[cfg(all(not(miri), target_has_reliable_f16_math))], + f128: #[cfg(all(not(miri), target_has_reliable_f128_math))], + }, + test { + let one: Float = 1.0; + let zero: Float = 0.0; + assert!(Float::NAN.sqrt().is_nan()); + assert!(Float::NEG_INFINITY.sqrt().is_nan()); + assert!((-one).sqrt().is_nan()); + assert_biteq!((-zero).sqrt(), -zero); + assert_biteq!(zero.sqrt(), zero); + assert_biteq!(one.sqrt(), one); + assert_biteq!(Float::INFINITY.sqrt(), Float::INFINITY); + } +} From a5e4e7ab52a031153ae7cef169ea7fa50cc6ae1c Mon Sep 17 00:00:00 2001 From: Roger Curley Date: Mon, 4 Aug 2025 13:59:47 -0400 Subject: [PATCH 041/195] Consolidate clamp tests --- library/coretests/tests/floats/f128.rs | 18 ----------- library/coretests/tests/floats/f16.rs | 18 ----------- library/coretests/tests/floats/f32.rs | 18 ----------- library/coretests/tests/floats/f64.rs | 18 ----------- library/coretests/tests/floats/mod.rs | 45 ++++++++++++++++++++++++++ 5 files changed, 45 insertions(+), 72 deletions(-) diff --git a/library/coretests/tests/floats/f128.rs b/library/coretests/tests/floats/f128.rs index 43882ad89a2d..0ac297d50523 100644 --- a/library/coretests/tests/floats/f128.rs +++ b/library/coretests/tests/floats/f128.rs @@ -132,24 +132,6 @@ fn test_float_bits_conv() { assert_eq!(f128::from_bits(masked_nan2).to_bits(), masked_nan2); } -#[test] -#[should_panic] -fn test_clamp_min_greater_than_max() { - let _ = 1.0f128.clamp(3.0, 1.0); -} - -#[test] -#[should_panic] -fn test_clamp_min_is_nan() { - let _ = 1.0f128.clamp(f128::NAN, 1.0); -} - -#[test] -#[should_panic] -fn test_clamp_max_is_nan() { - let _ = 1.0f128.clamp(3.0, f128::NAN); -} - #[test] fn test_total_cmp() { use core::cmp::Ordering; diff --git a/library/coretests/tests/floats/f16.rs b/library/coretests/tests/floats/f16.rs index c405e4e5c85a..4088ed79aaf3 100644 --- a/library/coretests/tests/floats/f16.rs +++ b/library/coretests/tests/floats/f16.rs @@ -131,24 +131,6 @@ fn test_float_bits_conv() { assert_eq!(f16::from_bits(masked_nan2).to_bits(), masked_nan2); } -#[test] -#[should_panic] -fn test_clamp_min_greater_than_max() { - let _ = 1.0f16.clamp(3.0, 1.0); -} - -#[test] -#[should_panic] -fn test_clamp_min_is_nan() { - let _ = 1.0f16.clamp(f16::NAN, 1.0); -} - -#[test] -#[should_panic] -fn test_clamp_max_is_nan() { - let _ = 1.0f16.clamp(3.0, f16::NAN); -} - #[test] #[cfg(not(miri))] #[cfg(target_has_reliable_f16_math)] diff --git a/library/coretests/tests/floats/f32.rs b/library/coretests/tests/floats/f32.rs index f08b033fb5ea..20d04fcf35af 100644 --- a/library/coretests/tests/floats/f32.rs +++ b/library/coretests/tests/floats/f32.rs @@ -112,24 +112,6 @@ fn test_float_bits_conv() { assert_eq!(f32::from_bits(masked_nan2).to_bits(), masked_nan2); } -#[test] -#[should_panic] -fn test_clamp_min_greater_than_max() { - let _ = 1.0f32.clamp(3.0, 1.0); -} - -#[test] -#[should_panic] -fn test_clamp_min_is_nan() { - let _ = 1.0f32.clamp(f32::NAN, 1.0); -} - -#[test] -#[should_panic] -fn test_clamp_max_is_nan() { - let _ = 1.0f32.clamp(3.0, f32::NAN); -} - #[test] fn test_total_cmp() { use core::cmp::Ordering; diff --git a/library/coretests/tests/floats/f64.rs b/library/coretests/tests/floats/f64.rs index 8d7d31b57c18..9d97a61e762c 100644 --- a/library/coretests/tests/floats/f64.rs +++ b/library/coretests/tests/floats/f64.rs @@ -105,24 +105,6 @@ fn test_float_bits_conv() { assert_eq!(f64::from_bits(masked_nan2).to_bits(), masked_nan2); } -#[test] -#[should_panic] -fn test_clamp_min_greater_than_max() { - let _ = 1.0f64.clamp(3.0, 1.0); -} - -#[test] -#[should_panic] -fn test_clamp_min_is_nan() { - let _ = 1.0f64.clamp(f64::NAN, 1.0); -} - -#[test] -#[should_panic] -fn test_clamp_max_is_nan() { - let _ = 1.0f64.clamp(3.0, f64::NAN); -} - #[test] fn test_total_cmp() { use core::cmp::Ordering; diff --git a/library/coretests/tests/floats/mod.rs b/library/coretests/tests/floats/mod.rs index 68ed8d47094f..ad25b49d8a31 100644 --- a/library/coretests/tests/floats/mod.rs +++ b/library/coretests/tests/floats/mod.rs @@ -1145,3 +1145,48 @@ float_test! { assert_biteq!(Float::INFINITY.sqrt(), Float::INFINITY); } } + +float_test! { + name: clamp_min_greater_than_max, + attrs: { + const: #[cfg(false)], + f16: #[should_panic, cfg(any(miri, target_has_reliable_f16))], + f32: #[should_panic], + f64: #[should_panic], + f128: #[should_panic, cfg(any(miri, target_has_reliable_f128))], + }, + test { + let one : Float = 1.0; + let _ = one.clamp(3.0, 1.0); + } +} + +float_test! { + name: clamp_min_is_nan, + attrs: { + const: #[cfg(false)], + f16: #[should_panic, cfg(any(miri, target_has_reliable_f16))], + f32: #[should_panic], + f64: #[should_panic], + f128: #[should_panic, cfg(any(miri, target_has_reliable_f128))], + }, + test { + let one : Float = 1.0; + let _ = one.clamp(Float::NAN, 1.0); + } +} + +float_test! { + name: clamp_max_is_nan, + attrs: { + const: #[cfg(false)], + f16: #[should_panic, cfg(any(miri, target_has_reliable_f16))], + f32: #[should_panic], + f64: #[should_panic], + f128: #[should_panic, cfg(any(miri, target_has_reliable_f128))], + }, + test { + let one : Float = 1.0; + let _ = one.clamp(3.0, Float::NAN); + } +} From 80bcd1a61f4132f47978978c648dc964f679749c Mon Sep 17 00:00:00 2001 From: Roger Curley Date: Mon, 4 Aug 2025 15:06:47 -0400 Subject: [PATCH 042/195] Consolidate total_cmp tests This standardizes how max and min subnormals are generated. Since the new method doesn't use powf, it also enables some of the tests for f128 that were previously disabled due to issues with powf (although it looks like those issues were already fixed anyway). f16 signalling nan tests previously disabled are not re-enabled, since the underlying LLVM issue has not been closed. --- library/coretests/tests/floats/f128.rs | 144 --------------------- library/coretests/tests/floats/f16.rs | 148 ---------------------- library/coretests/tests/floats/f32.rs | 143 --------------------- library/coretests/tests/floats/f64.rs | 143 --------------------- library/coretests/tests/floats/mod.rs | 166 +++++++++++++++++++++++++ 5 files changed, 166 insertions(+), 578 deletions(-) diff --git a/library/coretests/tests/floats/f128.rs b/library/coretests/tests/floats/f128.rs index 0ac297d50523..ac4a20665305 100644 --- a/library/coretests/tests/floats/f128.rs +++ b/library/coretests/tests/floats/f128.rs @@ -132,150 +132,6 @@ fn test_float_bits_conv() { assert_eq!(f128::from_bits(masked_nan2).to_bits(), masked_nan2); } -#[test] -fn test_total_cmp() { - use core::cmp::Ordering; - - fn quiet_bit_mask() -> u128 { - 1 << (f128::MANTISSA_DIGITS - 2) - } - - // FIXME(f16_f128): test subnormals when powf is available - // fn min_subnorm() -> f128 { - // f128::MIN_POSITIVE / f128::powf(2.0, f128::MANTISSA_DIGITS as f128 - 1.0) - // } - - // fn max_subnorm() -> f128 { - // f128::MIN_POSITIVE - min_subnorm() - // } - - fn q_nan() -> f128 { - f128::from_bits(f128::NAN.to_bits() | quiet_bit_mask()) - } - - fn s_nan() -> f128 { - f128::from_bits((f128::NAN.to_bits() & !quiet_bit_mask()) + 42) - } - - assert_eq!(Ordering::Equal, (-q_nan()).total_cmp(&-q_nan())); - assert_eq!(Ordering::Equal, (-s_nan()).total_cmp(&-s_nan())); - assert_eq!(Ordering::Equal, (-f128::INFINITY).total_cmp(&-f128::INFINITY)); - assert_eq!(Ordering::Equal, (-f128::MAX).total_cmp(&-f128::MAX)); - assert_eq!(Ordering::Equal, (-2.5_f128).total_cmp(&-2.5)); - assert_eq!(Ordering::Equal, (-1.0_f128).total_cmp(&-1.0)); - assert_eq!(Ordering::Equal, (-1.5_f128).total_cmp(&-1.5)); - assert_eq!(Ordering::Equal, (-0.5_f128).total_cmp(&-0.5)); - assert_eq!(Ordering::Equal, (-f128::MIN_POSITIVE).total_cmp(&-f128::MIN_POSITIVE)); - // assert_eq!(Ordering::Equal, (-max_subnorm()).total_cmp(&-max_subnorm())); - // assert_eq!(Ordering::Equal, (-min_subnorm()).total_cmp(&-min_subnorm())); - assert_eq!(Ordering::Equal, (-0.0_f128).total_cmp(&-0.0)); - assert_eq!(Ordering::Equal, 0.0_f128.total_cmp(&0.0)); - // assert_eq!(Ordering::Equal, min_subnorm().total_cmp(&min_subnorm())); - // assert_eq!(Ordering::Equal, max_subnorm().total_cmp(&max_subnorm())); - assert_eq!(Ordering::Equal, f128::MIN_POSITIVE.total_cmp(&f128::MIN_POSITIVE)); - assert_eq!(Ordering::Equal, 0.5_f128.total_cmp(&0.5)); - assert_eq!(Ordering::Equal, 1.0_f128.total_cmp(&1.0)); - assert_eq!(Ordering::Equal, 1.5_f128.total_cmp(&1.5)); - assert_eq!(Ordering::Equal, 2.5_f128.total_cmp(&2.5)); - assert_eq!(Ordering::Equal, f128::MAX.total_cmp(&f128::MAX)); - assert_eq!(Ordering::Equal, f128::INFINITY.total_cmp(&f128::INFINITY)); - assert_eq!(Ordering::Equal, s_nan().total_cmp(&s_nan())); - assert_eq!(Ordering::Equal, q_nan().total_cmp(&q_nan())); - - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-s_nan())); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f128::INFINITY)); - assert_eq!(Ordering::Less, (-f128::INFINITY).total_cmp(&-f128::MAX)); - assert_eq!(Ordering::Less, (-f128::MAX).total_cmp(&-2.5)); - assert_eq!(Ordering::Less, (-2.5_f128).total_cmp(&-1.5)); - assert_eq!(Ordering::Less, (-1.5_f128).total_cmp(&-1.0)); - assert_eq!(Ordering::Less, (-1.0_f128).total_cmp(&-0.5)); - assert_eq!(Ordering::Less, (-0.5_f128).total_cmp(&-f128::MIN_POSITIVE)); - // assert_eq!(Ordering::Less, (-f128::MIN_POSITIVE).total_cmp(&-max_subnorm())); - // assert_eq!(Ordering::Less, (-max_subnorm()).total_cmp(&-min_subnorm())); - // assert_eq!(Ordering::Less, (-min_subnorm()).total_cmp(&-0.0)); - assert_eq!(Ordering::Less, (-0.0_f128).total_cmp(&0.0)); - // assert_eq!(Ordering::Less, 0.0_f128.total_cmp(&min_subnorm())); - // assert_eq!(Ordering::Less, min_subnorm().total_cmp(&max_subnorm())); - // assert_eq!(Ordering::Less, max_subnorm().total_cmp(&f128::MIN_POSITIVE)); - assert_eq!(Ordering::Less, f128::MIN_POSITIVE.total_cmp(&0.5)); - assert_eq!(Ordering::Less, 0.5_f128.total_cmp(&1.0)); - assert_eq!(Ordering::Less, 1.0_f128.total_cmp(&1.5)); - assert_eq!(Ordering::Less, 1.5_f128.total_cmp(&2.5)); - assert_eq!(Ordering::Less, 2.5_f128.total_cmp(&f128::MAX)); - assert_eq!(Ordering::Less, f128::MAX.total_cmp(&f128::INFINITY)); - assert_eq!(Ordering::Less, f128::INFINITY.total_cmp(&s_nan())); - assert_eq!(Ordering::Less, s_nan().total_cmp(&q_nan())); - - assert_eq!(Ordering::Greater, (-s_nan()).total_cmp(&-q_nan())); - assert_eq!(Ordering::Greater, (-f128::INFINITY).total_cmp(&-s_nan())); - assert_eq!(Ordering::Greater, (-f128::MAX).total_cmp(&-f128::INFINITY)); - assert_eq!(Ordering::Greater, (-2.5_f128).total_cmp(&-f128::MAX)); - assert_eq!(Ordering::Greater, (-1.5_f128).total_cmp(&-2.5)); - assert_eq!(Ordering::Greater, (-1.0_f128).total_cmp(&-1.5)); - assert_eq!(Ordering::Greater, (-0.5_f128).total_cmp(&-1.0)); - assert_eq!(Ordering::Greater, (-f128::MIN_POSITIVE).total_cmp(&-0.5)); - // assert_eq!(Ordering::Greater, (-max_subnorm()).total_cmp(&-f128::MIN_POSITIVE)); - // assert_eq!(Ordering::Greater, (-min_subnorm()).total_cmp(&-max_subnorm())); - // assert_eq!(Ordering::Greater, (-0.0_f128).total_cmp(&-min_subnorm())); - assert_eq!(Ordering::Greater, 0.0_f128.total_cmp(&-0.0)); - // assert_eq!(Ordering::Greater, min_subnorm().total_cmp(&0.0)); - // assert_eq!(Ordering::Greater, max_subnorm().total_cmp(&min_subnorm())); - // assert_eq!(Ordering::Greater, f128::MIN_POSITIVE.total_cmp(&max_subnorm())); - assert_eq!(Ordering::Greater, 0.5_f128.total_cmp(&f128::MIN_POSITIVE)); - assert_eq!(Ordering::Greater, 1.0_f128.total_cmp(&0.5)); - assert_eq!(Ordering::Greater, 1.5_f128.total_cmp(&1.0)); - assert_eq!(Ordering::Greater, 2.5_f128.total_cmp(&1.5)); - assert_eq!(Ordering::Greater, f128::MAX.total_cmp(&2.5)); - assert_eq!(Ordering::Greater, f128::INFINITY.total_cmp(&f128::MAX)); - assert_eq!(Ordering::Greater, s_nan().total_cmp(&f128::INFINITY)); - assert_eq!(Ordering::Greater, q_nan().total_cmp(&s_nan())); - - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-s_nan())); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f128::INFINITY)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f128::MAX)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-2.5)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-1.5)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-1.0)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-0.5)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f128::MIN_POSITIVE)); - // assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-max_subnorm())); - // assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-min_subnorm())); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-0.0)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&0.0)); - // assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&min_subnorm())); - // assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&max_subnorm())); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f128::MIN_POSITIVE)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&0.5)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&1.0)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&1.5)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&2.5)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f128::MAX)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f128::INFINITY)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&s_nan())); - - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f128::INFINITY)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f128::MAX)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-2.5)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-1.5)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-1.0)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-0.5)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f128::MIN_POSITIVE)); - // assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-max_subnorm())); - // assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-min_subnorm())); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-0.0)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&0.0)); - // assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&min_subnorm())); - // assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&max_subnorm())); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f128::MIN_POSITIVE)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&0.5)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&1.0)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&1.5)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&2.5)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f128::MAX)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f128::INFINITY)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&s_nan())); -} - #[test] fn test_algebraic() { let a: f128 = 123.0; diff --git a/library/coretests/tests/floats/f16.rs b/library/coretests/tests/floats/f16.rs index 4088ed79aaf3..bb9c8a002fe8 100644 --- a/library/coretests/tests/floats/f16.rs +++ b/library/coretests/tests/floats/f16.rs @@ -131,154 +131,6 @@ fn test_float_bits_conv() { assert_eq!(f16::from_bits(masked_nan2).to_bits(), masked_nan2); } -#[test] -#[cfg(not(miri))] -#[cfg(target_has_reliable_f16_math)] -fn test_total_cmp() { - use core::cmp::Ordering; - - fn quiet_bit_mask() -> u16 { - 1 << (f16::MANTISSA_DIGITS - 2) - } - - fn min_subnorm() -> f16 { - f16::MIN_POSITIVE / f16::powf(2.0, f16::MANTISSA_DIGITS as f16 - 1.0) - } - - fn max_subnorm() -> f16 { - f16::MIN_POSITIVE - min_subnorm() - } - - fn q_nan() -> f16 { - f16::from_bits(f16::NAN.to_bits() | quiet_bit_mask()) - } - - // FIXME(f16_f128): Tests involving sNaN are disabled because without optimizations, - // `total_cmp` is getting incorrectly lowered to code that includes a `extend`/`trunc` round - // trip, which quiets sNaNs. See: https://github.com/llvm/llvm-project/issues/104915 - // fn s_nan() -> f16 { - // f16::from_bits((f16::NAN.to_bits() & !quiet_bit_mask()) + 42) - // } - - assert_eq!(Ordering::Equal, (-q_nan()).total_cmp(&-q_nan())); - // assert_eq!(Ordering::Equal, (-s_nan()).total_cmp(&-s_nan())); - assert_eq!(Ordering::Equal, (-f16::INFINITY).total_cmp(&-f16::INFINITY)); - assert_eq!(Ordering::Equal, (-f16::MAX).total_cmp(&-f16::MAX)); - assert_eq!(Ordering::Equal, (-2.5_f16).total_cmp(&-2.5)); - assert_eq!(Ordering::Equal, (-1.0_f16).total_cmp(&-1.0)); - assert_eq!(Ordering::Equal, (-1.5_f16).total_cmp(&-1.5)); - assert_eq!(Ordering::Equal, (-0.5_f16).total_cmp(&-0.5)); - assert_eq!(Ordering::Equal, (-f16::MIN_POSITIVE).total_cmp(&-f16::MIN_POSITIVE)); - assert_eq!(Ordering::Equal, (-max_subnorm()).total_cmp(&-max_subnorm())); - assert_eq!(Ordering::Equal, (-min_subnorm()).total_cmp(&-min_subnorm())); - assert_eq!(Ordering::Equal, (-0.0_f16).total_cmp(&-0.0)); - assert_eq!(Ordering::Equal, 0.0_f16.total_cmp(&0.0)); - assert_eq!(Ordering::Equal, min_subnorm().total_cmp(&min_subnorm())); - assert_eq!(Ordering::Equal, max_subnorm().total_cmp(&max_subnorm())); - assert_eq!(Ordering::Equal, f16::MIN_POSITIVE.total_cmp(&f16::MIN_POSITIVE)); - assert_eq!(Ordering::Equal, 0.5_f16.total_cmp(&0.5)); - assert_eq!(Ordering::Equal, 1.0_f16.total_cmp(&1.0)); - assert_eq!(Ordering::Equal, 1.5_f16.total_cmp(&1.5)); - assert_eq!(Ordering::Equal, 2.5_f16.total_cmp(&2.5)); - assert_eq!(Ordering::Equal, f16::MAX.total_cmp(&f16::MAX)); - assert_eq!(Ordering::Equal, f16::INFINITY.total_cmp(&f16::INFINITY)); - // assert_eq!(Ordering::Equal, s_nan().total_cmp(&s_nan())); - assert_eq!(Ordering::Equal, q_nan().total_cmp(&q_nan())); - - // assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-s_nan())); - // assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f16::INFINITY)); - assert_eq!(Ordering::Less, (-f16::INFINITY).total_cmp(&-f16::MAX)); - assert_eq!(Ordering::Less, (-f16::MAX).total_cmp(&-2.5)); - assert_eq!(Ordering::Less, (-2.5_f16).total_cmp(&-1.5)); - assert_eq!(Ordering::Less, (-1.5_f16).total_cmp(&-1.0)); - assert_eq!(Ordering::Less, (-1.0_f16).total_cmp(&-0.5)); - assert_eq!(Ordering::Less, (-0.5_f16).total_cmp(&-f16::MIN_POSITIVE)); - assert_eq!(Ordering::Less, (-f16::MIN_POSITIVE).total_cmp(&-max_subnorm())); - assert_eq!(Ordering::Less, (-max_subnorm()).total_cmp(&-min_subnorm())); - assert_eq!(Ordering::Less, (-min_subnorm()).total_cmp(&-0.0)); - assert_eq!(Ordering::Less, (-0.0_f16).total_cmp(&0.0)); - assert_eq!(Ordering::Less, 0.0_f16.total_cmp(&min_subnorm())); - assert_eq!(Ordering::Less, min_subnorm().total_cmp(&max_subnorm())); - assert_eq!(Ordering::Less, max_subnorm().total_cmp(&f16::MIN_POSITIVE)); - assert_eq!(Ordering::Less, f16::MIN_POSITIVE.total_cmp(&0.5)); - assert_eq!(Ordering::Less, 0.5_f16.total_cmp(&1.0)); - assert_eq!(Ordering::Less, 1.0_f16.total_cmp(&1.5)); - assert_eq!(Ordering::Less, 1.5_f16.total_cmp(&2.5)); - assert_eq!(Ordering::Less, 2.5_f16.total_cmp(&f16::MAX)); - assert_eq!(Ordering::Less, f16::MAX.total_cmp(&f16::INFINITY)); - // assert_eq!(Ordering::Less, f16::INFINITY.total_cmp(&s_nan())); - // assert_eq!(Ordering::Less, s_nan().total_cmp(&q_nan())); - - // assert_eq!(Ordering::Greater, (-s_nan()).total_cmp(&-q_nan())); - // assert_eq!(Ordering::Greater, (-f16::INFINITY).total_cmp(&-s_nan())); - assert_eq!(Ordering::Greater, (-f16::MAX).total_cmp(&-f16::INFINITY)); - assert_eq!(Ordering::Greater, (-2.5_f16).total_cmp(&-f16::MAX)); - assert_eq!(Ordering::Greater, (-1.5_f16).total_cmp(&-2.5)); - assert_eq!(Ordering::Greater, (-1.0_f16).total_cmp(&-1.5)); - assert_eq!(Ordering::Greater, (-0.5_f16).total_cmp(&-1.0)); - assert_eq!(Ordering::Greater, (-f16::MIN_POSITIVE).total_cmp(&-0.5)); - assert_eq!(Ordering::Greater, (-max_subnorm()).total_cmp(&-f16::MIN_POSITIVE)); - assert_eq!(Ordering::Greater, (-min_subnorm()).total_cmp(&-max_subnorm())); - assert_eq!(Ordering::Greater, (-0.0_f16).total_cmp(&-min_subnorm())); - assert_eq!(Ordering::Greater, 0.0_f16.total_cmp(&-0.0)); - assert_eq!(Ordering::Greater, min_subnorm().total_cmp(&0.0)); - assert_eq!(Ordering::Greater, max_subnorm().total_cmp(&min_subnorm())); - assert_eq!(Ordering::Greater, f16::MIN_POSITIVE.total_cmp(&max_subnorm())); - assert_eq!(Ordering::Greater, 0.5_f16.total_cmp(&f16::MIN_POSITIVE)); - assert_eq!(Ordering::Greater, 1.0_f16.total_cmp(&0.5)); - assert_eq!(Ordering::Greater, 1.5_f16.total_cmp(&1.0)); - assert_eq!(Ordering::Greater, 2.5_f16.total_cmp(&1.5)); - assert_eq!(Ordering::Greater, f16::MAX.total_cmp(&2.5)); - assert_eq!(Ordering::Greater, f16::INFINITY.total_cmp(&f16::MAX)); - // assert_eq!(Ordering::Greater, s_nan().total_cmp(&f16::INFINITY)); - // assert_eq!(Ordering::Greater, q_nan().total_cmp(&s_nan())); - - // assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-s_nan())); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f16::INFINITY)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f16::MAX)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-2.5)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-1.5)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-1.0)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-0.5)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f16::MIN_POSITIVE)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-max_subnorm())); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-min_subnorm())); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-0.0)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&0.0)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&min_subnorm())); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&max_subnorm())); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f16::MIN_POSITIVE)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&0.5)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&1.0)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&1.5)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&2.5)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f16::MAX)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f16::INFINITY)); - // assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&s_nan())); - - // assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f16::INFINITY)); - // assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f16::MAX)); - // assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-2.5)); - // assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-1.5)); - // assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-1.0)); - // assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-0.5)); - // assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f16::MIN_POSITIVE)); - // assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-max_subnorm())); - // assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-min_subnorm())); - // assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-0.0)); - // assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&0.0)); - // assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&min_subnorm())); - // assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&max_subnorm())); - // assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f16::MIN_POSITIVE)); - // assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&0.5)); - // assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&1.0)); - // assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&1.5)); - // assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&2.5)); - // assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f16::MAX)); - // assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f16::INFINITY)); - // assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&s_nan())); -} - #[test] fn test_algebraic() { let a: f16 = 123.0; diff --git a/library/coretests/tests/floats/f32.rs b/library/coretests/tests/floats/f32.rs index 20d04fcf35af..e77e44655dc8 100644 --- a/library/coretests/tests/floats/f32.rs +++ b/library/coretests/tests/floats/f32.rs @@ -112,149 +112,6 @@ fn test_float_bits_conv() { assert_eq!(f32::from_bits(masked_nan2).to_bits(), masked_nan2); } -#[test] -fn test_total_cmp() { - use core::cmp::Ordering; - - fn quiet_bit_mask() -> u32 { - 1 << (f32::MANTISSA_DIGITS - 2) - } - - fn min_subnorm() -> f32 { - f32::MIN_POSITIVE / f32::powf(2.0, f32::MANTISSA_DIGITS as f32 - 1.0) - } - - fn max_subnorm() -> f32 { - f32::MIN_POSITIVE - min_subnorm() - } - - fn q_nan() -> f32 { - f32::from_bits(f32::NAN.to_bits() | quiet_bit_mask()) - } - - fn s_nan() -> f32 { - f32::from_bits((f32::NAN.to_bits() & !quiet_bit_mask()) + 42) - } - - assert_eq!(Ordering::Equal, (-q_nan()).total_cmp(&-q_nan())); - assert_eq!(Ordering::Equal, (-s_nan()).total_cmp(&-s_nan())); - assert_eq!(Ordering::Equal, (-f32::INFINITY).total_cmp(&-f32::INFINITY)); - assert_eq!(Ordering::Equal, (-f32::MAX).total_cmp(&-f32::MAX)); - assert_eq!(Ordering::Equal, (-2.5_f32).total_cmp(&-2.5)); - assert_eq!(Ordering::Equal, (-1.0_f32).total_cmp(&-1.0)); - assert_eq!(Ordering::Equal, (-1.5_f32).total_cmp(&-1.5)); - assert_eq!(Ordering::Equal, (-0.5_f32).total_cmp(&-0.5)); - assert_eq!(Ordering::Equal, (-f32::MIN_POSITIVE).total_cmp(&-f32::MIN_POSITIVE)); - assert_eq!(Ordering::Equal, (-max_subnorm()).total_cmp(&-max_subnorm())); - assert_eq!(Ordering::Equal, (-min_subnorm()).total_cmp(&-min_subnorm())); - assert_eq!(Ordering::Equal, (-0.0_f32).total_cmp(&-0.0)); - assert_eq!(Ordering::Equal, 0.0_f32.total_cmp(&0.0)); - assert_eq!(Ordering::Equal, min_subnorm().total_cmp(&min_subnorm())); - assert_eq!(Ordering::Equal, max_subnorm().total_cmp(&max_subnorm())); - assert_eq!(Ordering::Equal, f32::MIN_POSITIVE.total_cmp(&f32::MIN_POSITIVE)); - assert_eq!(Ordering::Equal, 0.5_f32.total_cmp(&0.5)); - assert_eq!(Ordering::Equal, 1.0_f32.total_cmp(&1.0)); - assert_eq!(Ordering::Equal, 1.5_f32.total_cmp(&1.5)); - assert_eq!(Ordering::Equal, 2.5_f32.total_cmp(&2.5)); - assert_eq!(Ordering::Equal, f32::MAX.total_cmp(&f32::MAX)); - assert_eq!(Ordering::Equal, f32::INFINITY.total_cmp(&f32::INFINITY)); - assert_eq!(Ordering::Equal, s_nan().total_cmp(&s_nan())); - assert_eq!(Ordering::Equal, q_nan().total_cmp(&q_nan())); - - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-s_nan())); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f32::INFINITY)); - assert_eq!(Ordering::Less, (-f32::INFINITY).total_cmp(&-f32::MAX)); - assert_eq!(Ordering::Less, (-f32::MAX).total_cmp(&-2.5)); - assert_eq!(Ordering::Less, (-2.5_f32).total_cmp(&-1.5)); - assert_eq!(Ordering::Less, (-1.5_f32).total_cmp(&-1.0)); - assert_eq!(Ordering::Less, (-1.0_f32).total_cmp(&-0.5)); - assert_eq!(Ordering::Less, (-0.5_f32).total_cmp(&-f32::MIN_POSITIVE)); - assert_eq!(Ordering::Less, (-f32::MIN_POSITIVE).total_cmp(&-max_subnorm())); - assert_eq!(Ordering::Less, (-max_subnorm()).total_cmp(&-min_subnorm())); - assert_eq!(Ordering::Less, (-min_subnorm()).total_cmp(&-0.0)); - assert_eq!(Ordering::Less, (-0.0_f32).total_cmp(&0.0)); - assert_eq!(Ordering::Less, 0.0_f32.total_cmp(&min_subnorm())); - assert_eq!(Ordering::Less, min_subnorm().total_cmp(&max_subnorm())); - assert_eq!(Ordering::Less, max_subnorm().total_cmp(&f32::MIN_POSITIVE)); - assert_eq!(Ordering::Less, f32::MIN_POSITIVE.total_cmp(&0.5)); - assert_eq!(Ordering::Less, 0.5_f32.total_cmp(&1.0)); - assert_eq!(Ordering::Less, 1.0_f32.total_cmp(&1.5)); - assert_eq!(Ordering::Less, 1.5_f32.total_cmp(&2.5)); - assert_eq!(Ordering::Less, 2.5_f32.total_cmp(&f32::MAX)); - assert_eq!(Ordering::Less, f32::MAX.total_cmp(&f32::INFINITY)); - assert_eq!(Ordering::Less, f32::INFINITY.total_cmp(&s_nan())); - assert_eq!(Ordering::Less, s_nan().total_cmp(&q_nan())); - - assert_eq!(Ordering::Greater, (-s_nan()).total_cmp(&-q_nan())); - assert_eq!(Ordering::Greater, (-f32::INFINITY).total_cmp(&-s_nan())); - assert_eq!(Ordering::Greater, (-f32::MAX).total_cmp(&-f32::INFINITY)); - assert_eq!(Ordering::Greater, (-2.5_f32).total_cmp(&-f32::MAX)); - assert_eq!(Ordering::Greater, (-1.5_f32).total_cmp(&-2.5)); - assert_eq!(Ordering::Greater, (-1.0_f32).total_cmp(&-1.5)); - assert_eq!(Ordering::Greater, (-0.5_f32).total_cmp(&-1.0)); - assert_eq!(Ordering::Greater, (-f32::MIN_POSITIVE).total_cmp(&-0.5)); - assert_eq!(Ordering::Greater, (-max_subnorm()).total_cmp(&-f32::MIN_POSITIVE)); - assert_eq!(Ordering::Greater, (-min_subnorm()).total_cmp(&-max_subnorm())); - assert_eq!(Ordering::Greater, (-0.0_f32).total_cmp(&-min_subnorm())); - assert_eq!(Ordering::Greater, 0.0_f32.total_cmp(&-0.0)); - assert_eq!(Ordering::Greater, min_subnorm().total_cmp(&0.0)); - assert_eq!(Ordering::Greater, max_subnorm().total_cmp(&min_subnorm())); - assert_eq!(Ordering::Greater, f32::MIN_POSITIVE.total_cmp(&max_subnorm())); - assert_eq!(Ordering::Greater, 0.5_f32.total_cmp(&f32::MIN_POSITIVE)); - assert_eq!(Ordering::Greater, 1.0_f32.total_cmp(&0.5)); - assert_eq!(Ordering::Greater, 1.5_f32.total_cmp(&1.0)); - assert_eq!(Ordering::Greater, 2.5_f32.total_cmp(&1.5)); - assert_eq!(Ordering::Greater, f32::MAX.total_cmp(&2.5)); - assert_eq!(Ordering::Greater, f32::INFINITY.total_cmp(&f32::MAX)); - assert_eq!(Ordering::Greater, s_nan().total_cmp(&f32::INFINITY)); - assert_eq!(Ordering::Greater, q_nan().total_cmp(&s_nan())); - - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-s_nan())); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f32::INFINITY)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f32::MAX)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-2.5)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-1.5)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-1.0)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-0.5)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f32::MIN_POSITIVE)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-max_subnorm())); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-min_subnorm())); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-0.0)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&0.0)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&min_subnorm())); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&max_subnorm())); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f32::MIN_POSITIVE)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&0.5)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&1.0)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&1.5)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&2.5)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f32::MAX)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f32::INFINITY)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&s_nan())); - - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f32::INFINITY)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f32::MAX)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-2.5)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-1.5)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-1.0)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-0.5)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f32::MIN_POSITIVE)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-max_subnorm())); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-min_subnorm())); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-0.0)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&0.0)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&min_subnorm())); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&max_subnorm())); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f32::MIN_POSITIVE)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&0.5)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&1.0)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&1.5)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&2.5)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f32::MAX)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f32::INFINITY)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&s_nan())); -} - #[test] fn test_algebraic() { let a: f32 = 123.0; diff --git a/library/coretests/tests/floats/f64.rs b/library/coretests/tests/floats/f64.rs index 9d97a61e762c..fea9cc19b39f 100644 --- a/library/coretests/tests/floats/f64.rs +++ b/library/coretests/tests/floats/f64.rs @@ -105,149 +105,6 @@ fn test_float_bits_conv() { assert_eq!(f64::from_bits(masked_nan2).to_bits(), masked_nan2); } -#[test] -fn test_total_cmp() { - use core::cmp::Ordering; - - fn quiet_bit_mask() -> u64 { - 1 << (f64::MANTISSA_DIGITS - 2) - } - - fn min_subnorm() -> f64 { - f64::MIN_POSITIVE / f64::powf(2.0, f64::MANTISSA_DIGITS as f64 - 1.0) - } - - fn max_subnorm() -> f64 { - f64::MIN_POSITIVE - min_subnorm() - } - - fn q_nan() -> f64 { - f64::from_bits(f64::NAN.to_bits() | quiet_bit_mask()) - } - - fn s_nan() -> f64 { - f64::from_bits((f64::NAN.to_bits() & !quiet_bit_mask()) + 42) - } - - assert_eq!(Ordering::Equal, (-q_nan()).total_cmp(&-q_nan())); - assert_eq!(Ordering::Equal, (-s_nan()).total_cmp(&-s_nan())); - assert_eq!(Ordering::Equal, (-f64::INFINITY).total_cmp(&-f64::INFINITY)); - assert_eq!(Ordering::Equal, (-f64::MAX).total_cmp(&-f64::MAX)); - assert_eq!(Ordering::Equal, (-2.5_f64).total_cmp(&-2.5)); - assert_eq!(Ordering::Equal, (-1.0_f64).total_cmp(&-1.0)); - assert_eq!(Ordering::Equal, (-1.5_f64).total_cmp(&-1.5)); - assert_eq!(Ordering::Equal, (-0.5_f64).total_cmp(&-0.5)); - assert_eq!(Ordering::Equal, (-f64::MIN_POSITIVE).total_cmp(&-f64::MIN_POSITIVE)); - assert_eq!(Ordering::Equal, (-max_subnorm()).total_cmp(&-max_subnorm())); - assert_eq!(Ordering::Equal, (-min_subnorm()).total_cmp(&-min_subnorm())); - assert_eq!(Ordering::Equal, (-0.0_f64).total_cmp(&-0.0)); - assert_eq!(Ordering::Equal, 0.0_f64.total_cmp(&0.0)); - assert_eq!(Ordering::Equal, min_subnorm().total_cmp(&min_subnorm())); - assert_eq!(Ordering::Equal, max_subnorm().total_cmp(&max_subnorm())); - assert_eq!(Ordering::Equal, f64::MIN_POSITIVE.total_cmp(&f64::MIN_POSITIVE)); - assert_eq!(Ordering::Equal, 0.5_f64.total_cmp(&0.5)); - assert_eq!(Ordering::Equal, 1.0_f64.total_cmp(&1.0)); - assert_eq!(Ordering::Equal, 1.5_f64.total_cmp(&1.5)); - assert_eq!(Ordering::Equal, 2.5_f64.total_cmp(&2.5)); - assert_eq!(Ordering::Equal, f64::MAX.total_cmp(&f64::MAX)); - assert_eq!(Ordering::Equal, f64::INFINITY.total_cmp(&f64::INFINITY)); - assert_eq!(Ordering::Equal, s_nan().total_cmp(&s_nan())); - assert_eq!(Ordering::Equal, q_nan().total_cmp(&q_nan())); - - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-s_nan())); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f64::INFINITY)); - assert_eq!(Ordering::Less, (-f64::INFINITY).total_cmp(&-f64::MAX)); - assert_eq!(Ordering::Less, (-f64::MAX).total_cmp(&-2.5)); - assert_eq!(Ordering::Less, (-2.5_f64).total_cmp(&-1.5)); - assert_eq!(Ordering::Less, (-1.5_f64).total_cmp(&-1.0)); - assert_eq!(Ordering::Less, (-1.0_f64).total_cmp(&-0.5)); - assert_eq!(Ordering::Less, (-0.5_f64).total_cmp(&-f64::MIN_POSITIVE)); - assert_eq!(Ordering::Less, (-f64::MIN_POSITIVE).total_cmp(&-max_subnorm())); - assert_eq!(Ordering::Less, (-max_subnorm()).total_cmp(&-min_subnorm())); - assert_eq!(Ordering::Less, (-min_subnorm()).total_cmp(&-0.0)); - assert_eq!(Ordering::Less, (-0.0_f64).total_cmp(&0.0)); - assert_eq!(Ordering::Less, 0.0_f64.total_cmp(&min_subnorm())); - assert_eq!(Ordering::Less, min_subnorm().total_cmp(&max_subnorm())); - assert_eq!(Ordering::Less, max_subnorm().total_cmp(&f64::MIN_POSITIVE)); - assert_eq!(Ordering::Less, f64::MIN_POSITIVE.total_cmp(&0.5)); - assert_eq!(Ordering::Less, 0.5_f64.total_cmp(&1.0)); - assert_eq!(Ordering::Less, 1.0_f64.total_cmp(&1.5)); - assert_eq!(Ordering::Less, 1.5_f64.total_cmp(&2.5)); - assert_eq!(Ordering::Less, 2.5_f64.total_cmp(&f64::MAX)); - assert_eq!(Ordering::Less, f64::MAX.total_cmp(&f64::INFINITY)); - assert_eq!(Ordering::Less, f64::INFINITY.total_cmp(&s_nan())); - assert_eq!(Ordering::Less, s_nan().total_cmp(&q_nan())); - - assert_eq!(Ordering::Greater, (-s_nan()).total_cmp(&-q_nan())); - assert_eq!(Ordering::Greater, (-f64::INFINITY).total_cmp(&-s_nan())); - assert_eq!(Ordering::Greater, (-f64::MAX).total_cmp(&-f64::INFINITY)); - assert_eq!(Ordering::Greater, (-2.5_f64).total_cmp(&-f64::MAX)); - assert_eq!(Ordering::Greater, (-1.5_f64).total_cmp(&-2.5)); - assert_eq!(Ordering::Greater, (-1.0_f64).total_cmp(&-1.5)); - assert_eq!(Ordering::Greater, (-0.5_f64).total_cmp(&-1.0)); - assert_eq!(Ordering::Greater, (-f64::MIN_POSITIVE).total_cmp(&-0.5)); - assert_eq!(Ordering::Greater, (-max_subnorm()).total_cmp(&-f64::MIN_POSITIVE)); - assert_eq!(Ordering::Greater, (-min_subnorm()).total_cmp(&-max_subnorm())); - assert_eq!(Ordering::Greater, (-0.0_f64).total_cmp(&-min_subnorm())); - assert_eq!(Ordering::Greater, 0.0_f64.total_cmp(&-0.0)); - assert_eq!(Ordering::Greater, min_subnorm().total_cmp(&0.0)); - assert_eq!(Ordering::Greater, max_subnorm().total_cmp(&min_subnorm())); - assert_eq!(Ordering::Greater, f64::MIN_POSITIVE.total_cmp(&max_subnorm())); - assert_eq!(Ordering::Greater, 0.5_f64.total_cmp(&f64::MIN_POSITIVE)); - assert_eq!(Ordering::Greater, 1.0_f64.total_cmp(&0.5)); - assert_eq!(Ordering::Greater, 1.5_f64.total_cmp(&1.0)); - assert_eq!(Ordering::Greater, 2.5_f64.total_cmp(&1.5)); - assert_eq!(Ordering::Greater, f64::MAX.total_cmp(&2.5)); - assert_eq!(Ordering::Greater, f64::INFINITY.total_cmp(&f64::MAX)); - assert_eq!(Ordering::Greater, s_nan().total_cmp(&f64::INFINITY)); - assert_eq!(Ordering::Greater, q_nan().total_cmp(&s_nan())); - - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-s_nan())); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f64::INFINITY)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f64::MAX)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-2.5)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-1.5)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-1.0)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-0.5)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f64::MIN_POSITIVE)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-max_subnorm())); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-min_subnorm())); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-0.0)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&0.0)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&min_subnorm())); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&max_subnorm())); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f64::MIN_POSITIVE)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&0.5)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&1.0)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&1.5)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&2.5)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f64::MAX)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f64::INFINITY)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&s_nan())); - - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f64::INFINITY)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f64::MAX)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-2.5)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-1.5)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-1.0)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-0.5)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f64::MIN_POSITIVE)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-max_subnorm())); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-min_subnorm())); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-0.0)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&0.0)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&min_subnorm())); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&max_subnorm())); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f64::MIN_POSITIVE)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&0.5)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&1.0)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&1.5)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&2.5)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f64::MAX)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f64::INFINITY)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&s_nan())); -} - #[test] fn test_algebraic() { let a: f64 = 123.0; diff --git a/library/coretests/tests/floats/mod.rs b/library/coretests/tests/floats/mod.rs index ad25b49d8a31..60c2914c4245 100644 --- a/library/coretests/tests/floats/mod.rs +++ b/library/coretests/tests/floats/mod.rs @@ -1190,3 +1190,169 @@ float_test! { let _ = one.clamp(3.0, Float::NAN); } } + +float_test! { + name: total_cmp, + attrs: { + const: #[cfg(false)], + f16: #[cfg(all(not(miri), target_has_reliable_f16_math))], + f128: #[cfg(all(not(miri), target_has_reliable_f128_math))], + }, + test { + use core::cmp::Ordering; + + fn quiet_bit_mask() -> ::Int { + 1 << (Float::MANTISSA_DIGITS - 2) + } + + fn q_nan() -> Float { + Float::from_bits(Float::NAN.to_bits() | quiet_bit_mask()) + } + + assert_eq!(Ordering::Equal, Float::total_cmp(&-q_nan(), &-q_nan())); + assert_eq!(Ordering::Equal, Float::total_cmp(&-Float::INFINITY, &-Float::INFINITY)); + assert_eq!(Ordering::Equal, Float::total_cmp(&-Float::MAX, &-Float::MAX)); + assert_eq!(Ordering::Equal, Float::total_cmp(&-2.5, &-2.5)); + assert_eq!(Ordering::Equal, Float::total_cmp(&-1.0, &-1.0)); + assert_eq!(Ordering::Equal, Float::total_cmp(&-1.5, &-1.5)); + assert_eq!(Ordering::Equal, Float::total_cmp(&-0.5, &-0.5)); + assert_eq!(Ordering::Equal, Float::total_cmp(&-Float::MIN_POSITIVE, &-Float::MIN_POSITIVE)); + assert_eq!(Ordering::Equal, Float::total_cmp(&-Float::MAX_SUBNORMAL, &-Float::MAX_SUBNORMAL)); + assert_eq!(Ordering::Equal, Float::total_cmp(&-Float::TINY, &-Float::TINY)); + assert_eq!(Ordering::Equal, Float::total_cmp(&-0.0, &-0.0)); + assert_eq!(Ordering::Equal, Float::total_cmp(&0.0, &0.0)); + assert_eq!(Ordering::Equal, Float::total_cmp(&Float::TINY, &Float::TINY)); + assert_eq!(Ordering::Equal, Float::total_cmp(&Float::MAX_SUBNORMAL, &Float::MAX_SUBNORMAL)); + assert_eq!(Ordering::Equal, Float::total_cmp(&Float::MIN_POSITIVE, &Float::MIN_POSITIVE)); + assert_eq!(Ordering::Equal, Float::total_cmp(&0.5, &0.5)); + assert_eq!(Ordering::Equal, Float::total_cmp(&1.0, &1.0)); + assert_eq!(Ordering::Equal, Float::total_cmp(&1.5, &1.5)); + assert_eq!(Ordering::Equal, Float::total_cmp(&2.5, &2.5)); + assert_eq!(Ordering::Equal, Float::total_cmp(&Float::MAX, &Float::MAX)); + assert_eq!(Ordering::Equal, Float::total_cmp(&Float::INFINITY, &Float::INFINITY)); + assert_eq!(Ordering::Equal, Float::total_cmp(&q_nan(), &q_nan())); + + assert_eq!(Ordering::Less, Float::total_cmp(&-Float::INFINITY, &-Float::MAX)); + assert_eq!(Ordering::Less, Float::total_cmp(&-Float::MAX, &-2.5)); + assert_eq!(Ordering::Less, Float::total_cmp(&-2.5, &-1.5)); + assert_eq!(Ordering::Less, Float::total_cmp(&-1.5, &-1.0)); + assert_eq!(Ordering::Less, Float::total_cmp(&-1.0, &-0.5)); + assert_eq!(Ordering::Less, Float::total_cmp(&-0.5, &-Float::MIN_POSITIVE)); + assert_eq!(Ordering::Less, Float::total_cmp(&-Float::MIN_POSITIVE, &-Float::MAX_SUBNORMAL)); + assert_eq!(Ordering::Less, Float::total_cmp(&-Float::MAX_SUBNORMAL, &-Float::TINY)); + assert_eq!(Ordering::Less, Float::total_cmp(&-Float::TINY, &-0.0)); + assert_eq!(Ordering::Less, Float::total_cmp(&-0.0, &0.0)); + assert_eq!(Ordering::Less, Float::total_cmp(&0.0, &Float::TINY)); + assert_eq!(Ordering::Less, Float::total_cmp(&Float::TINY, &Float::MAX_SUBNORMAL)); + assert_eq!(Ordering::Less, Float::total_cmp(&Float::MAX_SUBNORMAL, &Float::MIN_POSITIVE)); + assert_eq!(Ordering::Less, Float::total_cmp(&Float::MIN_POSITIVE, &0.5)); + assert_eq!(Ordering::Less, Float::total_cmp(&0.5, &1.0)); + assert_eq!(Ordering::Less, Float::total_cmp(&1.0, &1.5)); + assert_eq!(Ordering::Less, Float::total_cmp(&1.5, &2.5)); + assert_eq!(Ordering::Less, Float::total_cmp(&2.5, &Float::MAX)); + assert_eq!(Ordering::Less, Float::total_cmp(&Float::MAX, &Float::INFINITY)); + + assert_eq!(Ordering::Greater, Float::total_cmp(&-Float::MAX, &-Float::INFINITY)); + assert_eq!(Ordering::Greater, Float::total_cmp(&-2.5, &-Float::MAX)); + assert_eq!(Ordering::Greater, Float::total_cmp(&-1.5, &-2.5)); + assert_eq!(Ordering::Greater, Float::total_cmp(&-1.0, &-1.5)); + assert_eq!(Ordering::Greater, Float::total_cmp(&-0.5, &-1.0)); + assert_eq!(Ordering::Greater, Float::total_cmp(&-Float::MIN_POSITIVE, &-0.5)); + assert_eq!(Ordering::Greater, Float::total_cmp(&-Float::MAX_SUBNORMAL, &-Float::MIN_POSITIVE)); + assert_eq!(Ordering::Greater, Float::total_cmp(&-Float::TINY, &-Float::MAX_SUBNORMAL)); + assert_eq!(Ordering::Greater, Float::total_cmp(&-0.0, &-Float::TINY)); + assert_eq!(Ordering::Greater, Float::total_cmp(&0.0, &-0.0)); + assert_eq!(Ordering::Greater, Float::total_cmp(&Float::TINY, &0.0)); + assert_eq!(Ordering::Greater, Float::total_cmp(&Float::MAX_SUBNORMAL, &Float::TINY)); + assert_eq!(Ordering::Greater, Float::total_cmp(&Float::MIN_POSITIVE, &Float::MAX_SUBNORMAL)); + assert_eq!(Ordering::Greater, Float::total_cmp(&0.5, &Float::MIN_POSITIVE)); + assert_eq!(Ordering::Greater, Float::total_cmp(&1.0, &0.5)); + assert_eq!(Ordering::Greater, Float::total_cmp(&1.5, &1.0)); + assert_eq!(Ordering::Greater, Float::total_cmp(&2.5, &1.5)); + assert_eq!(Ordering::Greater, Float::total_cmp(&Float::MAX, &2.5)); + assert_eq!(Ordering::Greater, Float::total_cmp(&Float::INFINITY, &Float::MAX)); + + assert_eq!(Ordering::Less, Float::total_cmp(&-q_nan(), &-Float::INFINITY)); + assert_eq!(Ordering::Less, Float::total_cmp(&-q_nan(), &-Float::MAX)); + assert_eq!(Ordering::Less, Float::total_cmp(&-q_nan(), &-2.5)); + assert_eq!(Ordering::Less, Float::total_cmp(&-q_nan(), &-1.5)); + assert_eq!(Ordering::Less, Float::total_cmp(&-q_nan(), &-1.0)); + assert_eq!(Ordering::Less, Float::total_cmp(&-q_nan(), &-0.5)); + assert_eq!(Ordering::Less, Float::total_cmp(&-q_nan(), &-Float::MIN_POSITIVE)); + assert_eq!(Ordering::Less, Float::total_cmp(&-q_nan(), &-Float::MAX_SUBNORMAL)); + assert_eq!(Ordering::Less, Float::total_cmp(&-q_nan(), &-Float::TINY)); + assert_eq!(Ordering::Less, Float::total_cmp(&-q_nan(), &-0.0)); + assert_eq!(Ordering::Less, Float::total_cmp(&-q_nan(), &0.0)); + assert_eq!(Ordering::Less, Float::total_cmp(&-q_nan(), &Float::TINY)); + assert_eq!(Ordering::Less, Float::total_cmp(&-q_nan(), &Float::MAX_SUBNORMAL)); + assert_eq!(Ordering::Less, Float::total_cmp(&-q_nan(), &Float::MIN_POSITIVE)); + assert_eq!(Ordering::Less, Float::total_cmp(&-q_nan(), &0.5)); + assert_eq!(Ordering::Less, Float::total_cmp(&-q_nan(), &1.0)); + assert_eq!(Ordering::Less, Float::total_cmp(&-q_nan(), &1.5)); + assert_eq!(Ordering::Less, Float::total_cmp(&-q_nan(), &2.5)); + assert_eq!(Ordering::Less, Float::total_cmp(&-q_nan(), &Float::MAX)); + assert_eq!(Ordering::Less, Float::total_cmp(&-q_nan(), &Float::INFINITY)); + + } +} + +// FIXME(f16): Tests involving sNaN are disabled because without optimizations, `total_cmp` is +// getting incorrectly lowered to code that includes a `extend`/`trunc` round trip, which quiets +// sNaNs. See: https://github.com/llvm/llvm-project/issues/104915 + +float_test! { + name: total_cmp_s_nan, + attrs: { + const: #[cfg(false)], + f16: #[cfg(false)], + f128: #[cfg(all(not(miri), target_has_reliable_f128_math))], + }, + test { + use core::cmp::Ordering; + + fn quiet_bit_mask() -> ::Int { + 1 << (Float::MANTISSA_DIGITS - 2) + } + + fn q_nan() -> Float { + Float::from_bits(Float::NAN.to_bits() | quiet_bit_mask()) + } + + fn s_nan() -> Float { + Float::from_bits((Float::NAN.to_bits() & !quiet_bit_mask()) + 42) + } + assert_eq!(Ordering::Equal, Float::total_cmp(&-s_nan(), &-s_nan())); + assert_eq!(Ordering::Equal, Float::total_cmp(&s_nan(), &s_nan())); + assert_eq!(Ordering::Less, Float::total_cmp(&-q_nan(), &-s_nan())); + assert_eq!(Ordering::Less, Float::total_cmp(&-s_nan(), &-Float::INFINITY)); + assert_eq!(Ordering::Less, Float::total_cmp(&Float::INFINITY, &s_nan())); + assert_eq!(Ordering::Less, Float::total_cmp(&s_nan(), &q_nan())); + assert_eq!(Ordering::Greater, Float::total_cmp(&-s_nan(), &-q_nan())); + assert_eq!(Ordering::Greater, Float::total_cmp(&-Float::INFINITY, &-s_nan())); + assert_eq!(Ordering::Greater, Float::total_cmp(&s_nan(), &Float::INFINITY)); + assert_eq!(Ordering::Greater, Float::total_cmp(&q_nan(), &s_nan())); + assert_eq!(Ordering::Less, Float::total_cmp(&-q_nan(), &-s_nan())); + assert_eq!(Ordering::Less, Float::total_cmp(&-q_nan(), &s_nan())); + assert_eq!(Ordering::Less, Float::total_cmp(&-s_nan(), &-Float::INFINITY)); + assert_eq!(Ordering::Less, Float::total_cmp(&-s_nan(), &-Float::MAX)); + assert_eq!(Ordering::Less, Float::total_cmp(&-s_nan(), &-2.5)); + assert_eq!(Ordering::Less, Float::total_cmp(&-s_nan(), &-1.5)); + assert_eq!(Ordering::Less, Float::total_cmp(&-s_nan(), &-1.0)); + assert_eq!(Ordering::Less, Float::total_cmp(&-s_nan(), &-0.5)); + assert_eq!(Ordering::Less, Float::total_cmp(&-s_nan(), &-Float::MIN_POSITIVE)); + assert_eq!(Ordering::Less, Float::total_cmp(&-s_nan(), &-Float::MAX_SUBNORMAL)); + assert_eq!(Ordering::Less, Float::total_cmp(&-s_nan(), &-Float::TINY)); + assert_eq!(Ordering::Less, Float::total_cmp(&-s_nan(), &-0.0)); + assert_eq!(Ordering::Less, Float::total_cmp(&-s_nan(), &0.0)); + assert_eq!(Ordering::Less, Float::total_cmp(&-s_nan(), &Float::TINY)); + assert_eq!(Ordering::Less, Float::total_cmp(&-s_nan(), &Float::MAX_SUBNORMAL)); + assert_eq!(Ordering::Less, Float::total_cmp(&-s_nan(), &Float::MIN_POSITIVE)); + assert_eq!(Ordering::Less, Float::total_cmp(&-s_nan(), &0.5)); + assert_eq!(Ordering::Less, Float::total_cmp(&-s_nan(), &1.0)); + assert_eq!(Ordering::Less, Float::total_cmp(&-s_nan(), &1.5)); + assert_eq!(Ordering::Less, Float::total_cmp(&-s_nan(), &2.5)); + assert_eq!(Ordering::Less, Float::total_cmp(&-s_nan(), &Float::MAX)); + assert_eq!(Ordering::Less, Float::total_cmp(&-s_nan(), &Float::INFINITY)); + assert_eq!(Ordering::Less, Float::total_cmp(&-s_nan(), &s_nan())); + } +} From 57cf40cd82592f004736d955e87253b4346ba2fc Mon Sep 17 00:00:00 2001 From: Roger Curley Date: Thu, 7 Aug 2025 22:51:46 -0400 Subject: [PATCH 043/195] Hoist zero and one out into TestableFloat --- library/coretests/tests/floats/mod.rs | 160 ++++++++++++-------------- 1 file changed, 72 insertions(+), 88 deletions(-) diff --git a/library/coretests/tests/floats/mod.rs b/library/coretests/tests/floats/mod.rs index 60c2914c4245..2c2a07920d06 100644 --- a/library/coretests/tests/floats/mod.rs +++ b/library/coretests/tests/floats/mod.rs @@ -6,6 +6,8 @@ trait TestableFloat { type Int; /// Set the default tolerance for float comparison based on the type. const APPROX: Self; + const ZERO: Self; + const ONE: Self; const MIN_POSITIVE_NORMAL: Self; const MAX_SUBNORMAL: Self; /// Smallest number @@ -23,6 +25,8 @@ trait TestableFloat { impl TestableFloat for f16 { type Int = u16; const APPROX: Self = 1e-3; + const ZERO: Self = 0.0; + const ONE: Self = 1.0; const MIN_POSITIVE_NORMAL: Self = Self::MIN_POSITIVE; const MAX_SUBNORMAL: Self = Self::MIN_POSITIVE.next_down(); const TINY: Self = Self::from_bits(0x1); @@ -35,6 +39,8 @@ impl TestableFloat for f16 { impl TestableFloat for f32 { type Int = u32; const APPROX: Self = 1e-6; + const ZERO: Self = 0.0; + const ONE: Self = 1.0; const MIN_POSITIVE_NORMAL: Self = Self::MIN_POSITIVE; const MAX_SUBNORMAL: Self = Self::MIN_POSITIVE.next_down(); const TINY: Self = Self::from_bits(0x1); @@ -47,6 +53,8 @@ impl TestableFloat for f32 { impl TestableFloat for f64 { type Int = u64; const APPROX: Self = 1e-6; + const ZERO: Self = 0.0; + const ONE: Self = 1.0; const MIN_POSITIVE_NORMAL: Self = Self::MIN_POSITIVE; const MAX_SUBNORMAL: Self = Self::MIN_POSITIVE.next_down(); const TINY: Self = Self::from_bits(0x1); @@ -59,6 +67,8 @@ impl TestableFloat for f64 { impl TestableFloat for f128 { type Int = u128; const APPROX: Self = 1e-9; + const ZERO: Self = 0.0; + const ONE: Self = 1.0; const MIN_POSITIVE_NORMAL: Self = Self::MIN_POSITIVE; const MAX_SUBNORMAL: Self = Self::MIN_POSITIVE.next_down(); const TINY: Self = Self::from_bits(0x1); @@ -378,15 +388,14 @@ float_test! { f128: #[cfg(any(miri, target_has_reliable_f128))], }, test { - let zero: Float = 0.0; - assert_biteq!(0.0, zero); - assert!(!zero.is_infinite()); - assert!(zero.is_finite()); - assert!(zero.is_sign_positive()); - assert!(!zero.is_sign_negative()); - assert!(!zero.is_nan()); - assert!(!zero.is_normal()); - assert!(matches!(zero.classify(), Fp::Zero)); + assert_biteq!(0.0, Float::ZERO); + assert!(!Float::ZERO.is_infinite()); + assert!(Float::ZERO.is_finite()); + assert!(Float::ZERO.is_sign_positive()); + assert!(!Float::ZERO.is_sign_negative()); + assert!(!Float::ZERO.is_nan()); + assert!(!Float::ZERO.is_normal()); + assert!(matches!(Float::ZERO.classify(), Fp::Zero)); } } @@ -417,15 +426,14 @@ float_test! { f128: #[cfg(any(miri, target_has_reliable_f128))], }, test { - let one: Float = 1.0; - assert_biteq!(1.0, one); - assert!(!one.is_infinite()); - assert!(one.is_finite()); - assert!(one.is_sign_positive()); - assert!(!one.is_sign_negative()); - assert!(!one.is_nan()); - assert!(one.is_normal()); - assert!(matches!(one.classify(), Fp::Normal)); + assert_biteq!(1.0, Float::ONE); + assert!(!Float::ONE.is_infinite()); + assert!(Float::ONE.is_finite()); + assert!(Float::ONE.is_sign_positive()); + assert!(!Float::ONE.is_sign_negative()); + assert!(!Float::ONE.is_nan()); + assert!(Float::ONE.is_normal()); + assert!(matches!(Float::ONE.classify(), Fp::Normal)); } } @@ -439,11 +447,10 @@ float_test! { let nan: Float = Float::NAN; let inf: Float = Float::INFINITY; let neg_inf: Float = Float::NEG_INFINITY; - let zero: Float = 0.0; let pos: Float = 5.3; let neg: Float = -10.732; assert!(nan.is_nan()); - assert!(!zero.is_nan()); + assert!(!Float::ZERO.is_nan()); assert!(!pos.is_nan()); assert!(!neg.is_nan()); assert!(!inf.is_nan()); @@ -461,13 +468,12 @@ float_test! { let nan: Float = Float::NAN; let inf: Float = Float::INFINITY; let neg_inf: Float = Float::NEG_INFINITY; - let zero: Float = 0.0; let pos: Float = 42.8; let neg: Float = -109.2; assert!(!nan.is_infinite()); assert!(inf.is_infinite()); assert!(neg_inf.is_infinite()); - assert!(!zero.is_infinite()); + assert!(!Float::ZERO.is_infinite()); assert!(!pos.is_infinite()); assert!(!neg.is_infinite()); } @@ -483,13 +489,12 @@ float_test! { let nan: Float = Float::NAN; let inf: Float = Float::INFINITY; let neg_inf: Float = Float::NEG_INFINITY; - let zero: Float = 0.0; let pos: Float = 42.8; let neg: Float = -109.2; assert!(!nan.is_finite()); assert!(!inf.is_finite()); assert!(!neg_inf.is_finite()); - assert!(zero.is_finite()); + assert!(Float::ZERO.is_finite()); assert!(pos.is_finite()); assert!(neg.is_finite()); } @@ -505,15 +510,13 @@ float_test! { let nan: Float = Float::NAN; let inf: Float = Float::INFINITY; let neg_inf: Float = Float::NEG_INFINITY; - let zero: Float = 0.0; let neg_zero: Float = -0.0; - let one : Float = 1.0; assert!(!nan.is_normal()); assert!(!inf.is_normal()); assert!(!neg_inf.is_normal()); - assert!(!zero.is_normal()); + assert!(!Float::ZERO.is_normal()); assert!(!neg_zero.is_normal()); - assert!(one.is_normal()); + assert!(Float::ONE.is_normal()); assert!(Float::MIN_POSITIVE_NORMAL.is_normal()); assert!(!Float::MAX_SUBNORMAL.is_normal()); } @@ -528,15 +531,13 @@ float_test! { let nan: Float = Float::NAN; let inf: Float = Float::INFINITY; let neg_inf: Float = Float::NEG_INFINITY; - let zero: Float = 0.0; let neg_zero: Float = -0.0; - let one: Float = 1.0; assert!(matches!(nan.classify(), Fp::Nan)); assert!(matches!(inf.classify(), Fp::Infinite)); assert!(matches!(neg_inf.classify(), Fp::Infinite)); - assert!(matches!(zero.classify(), Fp::Zero)); + assert!(matches!(Float::ZERO.classify(), Fp::Zero)); assert!(matches!(neg_zero.classify(), Fp::Zero)); - assert!(matches!(one.classify(), Fp::Normal)); + assert!(matches!(Float::ONE.classify(), Fp::Normal)); assert!(matches!(Float::MIN_POSITIVE_NORMAL.classify(), Fp::Normal)); assert!(matches!(Float::MAX_SUBNORMAL.classify(), Fp::Subnormal)); } @@ -756,15 +757,13 @@ float_test! { f128: #[cfg(any(miri, target_has_reliable_f128_math))], }, test { - let one: Float = 1.0; - let zero: Float = 0.0; assert_biteq!(Float::INFINITY.abs(), Float::INFINITY); - assert_biteq!(one.abs(), one); - assert_biteq!(zero.abs(), zero); - assert_biteq!((-zero).abs(), zero); - assert_biteq!((-one).abs(), one); + assert_biteq!(Float::ONE.abs(), Float::ONE); + assert_biteq!(Float::ZERO.abs(), Float::ZERO); + assert_biteq!((-Float::ZERO).abs(), Float::ZERO); + assert_biteq!((-Float::ONE).abs(), Float::ONE); assert_biteq!(Float::NEG_INFINITY.abs(), Float::INFINITY); - assert_biteq!((one / Float::NEG_INFINITY).abs(), zero); + assert_biteq!((Float::ONE / Float::NEG_INFINITY).abs(), Float::ZERO); assert!(Float::NAN.abs().is_nan()); } } @@ -1001,15 +1000,13 @@ float_test! { f128: #[cfg(any(miri, target_has_reliable_f128_math))], }, test { - let one: Float = 1.0; - let zero: Float = 0.0; - assert_biteq!(Float::INFINITY.signum(), one); - assert_biteq!(one.signum(), one); - assert_biteq!(zero.signum(), one); - assert_biteq!((-zero).signum(), -one); - assert_biteq!((-one).signum(), -one); - assert_biteq!(Float::NEG_INFINITY.signum(), -one); - assert_biteq!((one / Float::NEG_INFINITY).signum(), -one); + assert_biteq!(Float::INFINITY.signum(), Float::ONE); + assert_biteq!(Float::ONE.signum(), Float::ONE); + assert_biteq!(Float::ZERO.signum(), Float::ONE); + assert_biteq!((-Float::ZERO).signum(), -Float::ONE); + assert_biteq!((-Float::ONE).signum(), -Float::ONE); + assert_biteq!(Float::NEG_INFINITY.signum(), -Float::ONE); + assert_biteq!((Float::ONE / Float::NEG_INFINITY).signum(), -Float::ONE); assert!(Float::NAN.signum().is_nan()); } } @@ -1021,15 +1018,13 @@ float_test! { f128: #[cfg(any(miri, target_has_reliable_f128))], }, test { - let one: Float = 1.0; - let zero: Float = 0.0; assert!(Float::INFINITY.is_sign_positive()); - assert!(one.is_sign_positive()); - assert!(zero.is_sign_positive()); - assert!(!(-zero).is_sign_positive()); - assert!(!(-one).is_sign_positive()); + assert!(Float::ONE.is_sign_positive()); + assert!(Float::ZERO.is_sign_positive()); + assert!(!(-Float::ZERO).is_sign_positive()); + assert!(!(-Float::ONE).is_sign_positive()); assert!(!Float::NEG_INFINITY.is_sign_positive()); - assert!(!(one / Float::NEG_INFINITY).is_sign_positive()); + assert!(!(Float::ONE / Float::NEG_INFINITY).is_sign_positive()); assert!(Float::NAN.is_sign_positive()); assert!(!(-Float::NAN).is_sign_positive()); } @@ -1042,15 +1037,13 @@ float_test! { f128: #[cfg(any(miri, target_has_reliable_f128))], }, test { - let one: Float = 1.0; - let zero: Float = 0.0; assert!(!Float::INFINITY.is_sign_negative()); - assert!(!one.is_sign_negative()); - assert!(!zero.is_sign_negative()); - assert!((-zero).is_sign_negative()); - assert!((-one).is_sign_negative()); + assert!(!Float::ONE.is_sign_negative()); + assert!(!Float::ZERO.is_sign_negative()); + assert!((-Float::ZERO).is_sign_negative()); + assert!((-Float::ONE).is_sign_negative()); assert!(Float::NEG_INFINITY.is_sign_negative()); - assert!((one / Float::NEG_INFINITY).is_sign_negative()); + assert!((Float::ONE / Float::NEG_INFINITY).is_sign_negative()); assert!(!Float::NAN.is_sign_negative()); assert!((-Float::NAN).is_sign_negative()); } @@ -1063,19 +1056,17 @@ float_test! { f128: #[cfg(any(miri, target_has_reliable_f128))], }, test { - let one: Float = 1.0; - let zero: Float = 0.0; assert_biteq!(Float::NEG_INFINITY.next_up(), Float::MIN); assert_biteq!(Float::MIN.next_up(), -Float::MAX_DOWN); - assert_biteq!((-one - Float::EPSILON).next_up(), -one); + assert_biteq!((-Float::ONE - Float::EPSILON).next_up(), -Float::ONE); assert_biteq!((-Float::MIN_POSITIVE_NORMAL).next_up(), -Float::MAX_SUBNORMAL); assert_biteq!((-Float::TINY_UP).next_up(), -Float::TINY); - assert_biteq!((-Float::TINY).next_up(), -zero); - assert_biteq!((-zero).next_up(), Float::TINY); - assert_biteq!(zero.next_up(), Float::TINY); + assert_biteq!((-Float::TINY).next_up(), -Float::ZERO); + assert_biteq!((-Float::ZERO).next_up(), Float::TINY); + assert_biteq!(Float::ZERO.next_up(), Float::TINY); assert_biteq!(Float::TINY.next_up(), Float::TINY_UP); assert_biteq!(Float::MAX_SUBNORMAL.next_up(), Float::MIN_POSITIVE_NORMAL); - assert_biteq!(one.next_up(), 1.0 + Float::EPSILON); + assert_biteq!(Float::ONE.next_up(), 1.0 + Float::EPSILON); assert_biteq!(Float::MAX.next_up(), Float::INFINITY); assert_biteq!(Float::INFINITY.next_up(), Float::INFINITY); @@ -1096,20 +1087,18 @@ float_test! { f128: #[cfg(any(miri, target_has_reliable_f128))], }, test { - let one: Float = 1.0; - let zero: Float = 0.0; assert_biteq!(Float::NEG_INFINITY.next_down(), Float::NEG_INFINITY); assert_biteq!(Float::MIN.next_down(), Float::NEG_INFINITY); assert_biteq!((-Float::MAX_DOWN).next_down(), Float::MIN); - assert_biteq!((-one).next_down(), -1.0 - Float::EPSILON); + assert_biteq!((-Float::ONE).next_down(), -1.0 - Float::EPSILON); assert_biteq!((-Float::MAX_SUBNORMAL).next_down(), -Float::MIN_POSITIVE_NORMAL); assert_biteq!((-Float::TINY).next_down(), -Float::TINY_UP); - assert_biteq!((-zero).next_down(), -Float::TINY); - assert_biteq!((zero).next_down(), -Float::TINY); - assert_biteq!(Float::TINY.next_down(), zero); + assert_biteq!((-Float::ZERO).next_down(), -Float::TINY); + assert_biteq!((Float::ZERO).next_down(), -Float::TINY); + assert_biteq!(Float::TINY.next_down(), Float::ZERO); assert_biteq!(Float::TINY_UP.next_down(), Float::TINY); assert_biteq!(Float::MIN_POSITIVE_NORMAL.next_down(), Float::MAX_SUBNORMAL); - assert_biteq!((1.0 + Float::EPSILON).next_down(), one); + assert_biteq!((1.0 + Float::EPSILON).next_down(), Float::ONE); assert_biteq!(Float::MAX.next_down(), Float::MAX_DOWN); assert_biteq!(Float::INFINITY.next_down(), Float::MAX); @@ -1134,14 +1123,12 @@ float_test! { f128: #[cfg(all(not(miri), target_has_reliable_f128_math))], }, test { - let one: Float = 1.0; - let zero: Float = 0.0; assert!(Float::NAN.sqrt().is_nan()); assert!(Float::NEG_INFINITY.sqrt().is_nan()); - assert!((-one).sqrt().is_nan()); - assert_biteq!((-zero).sqrt(), -zero); - assert_biteq!(zero.sqrt(), zero); - assert_biteq!(one.sqrt(), one); + assert!((-Float::ONE).sqrt().is_nan()); + assert_biteq!((-Float::ZERO).sqrt(), -Float::ZERO); + assert_biteq!(Float::ZERO.sqrt(), Float::ZERO); + assert_biteq!(Float::ONE.sqrt(), Float::ONE); assert_biteq!(Float::INFINITY.sqrt(), Float::INFINITY); } } @@ -1156,8 +1143,7 @@ float_test! { f128: #[should_panic, cfg(any(miri, target_has_reliable_f128))], }, test { - let one : Float = 1.0; - let _ = one.clamp(3.0, 1.0); + let _ = Float::ONE.clamp(3.0, 1.0); } } @@ -1171,8 +1157,7 @@ float_test! { f128: #[should_panic, cfg(any(miri, target_has_reliable_f128))], }, test { - let one : Float = 1.0; - let _ = one.clamp(Float::NAN, 1.0); + let _ = Float::ONE.clamp(Float::NAN, 1.0); } } @@ -1186,8 +1171,7 @@ float_test! { f128: #[should_panic, cfg(any(miri, target_has_reliable_f128))], }, test { - let one : Float = 1.0; - let _ = one.clamp(3.0, Float::NAN); + let _ = Float::ONE.clamp(3.0, Float::NAN); } } From 347cd4e6cb8a189c12a89e89a3951a5b1043308c Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 8 Aug 2025 14:04:17 +1000 Subject: [PATCH 044/195] Augment the test. Some cases that are currently handled incorrectly. --- tests/ui/stats/macro-stats.rs | 30 ++++++++++++++++++++++++++---- tests/ui/stats/macro-stats.stderr | 7 ++++++- 2 files changed, 32 insertions(+), 5 deletions(-) diff --git a/tests/ui/stats/macro-stats.rs b/tests/ui/stats/macro-stats.rs index d986904ddd67..1b7d58bf0585 100644 --- a/tests/ui/stats/macro-stats.rs +++ b/tests/ui/stats/macro-stats.rs @@ -49,10 +49,32 @@ fn opt(x: Option) { } } -macro_rules! long_name_that_fits_on_a_single_line { - () => {} -} -long_name_that_fits_on_a_single_line!(); +macro_rules! long_name_that_fits_on_one_line { () => {} } +long_name_that_fits_on_one_line!(); +long_name_that_fits_on_one_line!(); +long_name_that_fits_on_one_line!(); +long_name_that_fits_on_one_line!(); +long_name_that_fits_on_one_line!(); +long_name_that_fits_on_one_line!(); +long_name_that_fits_on_one_line!(); +long_name_that_fits_on_one_line!(); +long_name_that_fits_on_one_line!(); +long_name_that_fits_on_one_line!(); + +macro_rules! long_name_that_fits_on_one_line_ { () => {} } +long_name_that_fits_on_one_line_!(); + +macro_rules! long_name_that_fits_on_one_line__ { () => {} } +long_name_that_fits_on_one_line__!(); + +macro_rules! long_name_that_fits_on_one_line___ { () => {} } +long_name_that_fits_on_one_line___!(); + +macro_rules! long_name_that_fits_on_one_line____ { () => {} } +long_name_that_fits_on_one_line____!(); + +macro_rules! long_name_that_fits_on_one_line_____ { () => {} } +long_name_that_fits_on_one_line_____!(); macro_rules! long_name_that_doesnt_fit_on_one_line { ($t:ty) => { diff --git a/tests/ui/stats/macro-stats.stderr b/tests/ui/stats/macro-stats.stderr index 8d0fdb8958a8..0ef9adc6dd40 100644 --- a/tests/ui/stats/macro-stats.stderr +++ b/tests/ui/stats/macro-stats.stderr @@ -22,6 +22,11 @@ macro-stats trait_tys! 1 2 2.0 macro-stats n99! 2 2 1.0 4 2.0 macro-stats none! 1 1 1.0 4 4.0 macro-stats u32! 1 1 1.0 3 3.0 -macro-stats long_name_that_fits_on_a_single_line! 1 1 1.0 0 0.0 +macro-stats long_name_that_fits_on_one_line! 10 10 1.0 0 0.0 +macro-stats long_name_that_fits_on_one_line_____! 1 1 1.0 0 0.0 +macro-stats long_name_that_fits_on_one_line____! 1 1 1.0 0 0.0 +macro-stats long_name_that_fits_on_one_line___! 1 1 1.0 0 0.0 +macro-stats long_name_that_fits_on_one_line__! 1 1 1.0 0 0.0 +macro-stats long_name_that_fits_on_one_line_! 1 1 1.0 0 0.0 macro-stats #[test] 1 1 1.0 0 0.0 macro-stats =================================================================================== From 0239e464876c8785e8601712220f17d9c58bcba6 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 8 Aug 2025 14:10:54 +1000 Subject: [PATCH 045/195] Fix some bad formatting in `-Zmacro-stats` output. I also double-checked that everything looks good on some real-world crates. --- compiler/rustc_interface/src/passes.rs | 2 +- tests/ui/stats/macro-stats.stderr | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index 8dec8069bc7b..8bbd0e614921 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -371,7 +371,7 @@ fn print_macro_stats(ecx: &ExtCtxt<'_>) { // The name won't abut or overlap with the uses value, but it does // overlap with the empty part of the uses column. Shrink the width // of the uses column to account for the excess name length. - uses_w = uses_with_underscores.len() + 1 + uses_w -= name.len() - name_w; }; _ = writeln!( diff --git a/tests/ui/stats/macro-stats.stderr b/tests/ui/stats/macro-stats.stderr index 0ef9adc6dd40..75b90df6466f 100644 --- a/tests/ui/stats/macro-stats.stderr +++ b/tests/ui/stats/macro-stats.stderr @@ -22,11 +22,11 @@ macro-stats trait_tys! 1 2 2.0 macro-stats n99! 2 2 1.0 4 2.0 macro-stats none! 1 1 1.0 4 4.0 macro-stats u32! 1 1 1.0 3 3.0 -macro-stats long_name_that_fits_on_one_line! 10 10 1.0 0 0.0 +macro-stats long_name_that_fits_on_one_line! 10 10 1.0 0 0.0 macro-stats long_name_that_fits_on_one_line_____! 1 1 1.0 0 0.0 -macro-stats long_name_that_fits_on_one_line____! 1 1 1.0 0 0.0 -macro-stats long_name_that_fits_on_one_line___! 1 1 1.0 0 0.0 -macro-stats long_name_that_fits_on_one_line__! 1 1 1.0 0 0.0 -macro-stats long_name_that_fits_on_one_line_! 1 1 1.0 0 0.0 +macro-stats long_name_that_fits_on_one_line____! 1 1 1.0 0 0.0 +macro-stats long_name_that_fits_on_one_line___! 1 1 1.0 0 0.0 +macro-stats long_name_that_fits_on_one_line__! 1 1 1.0 0 0.0 +macro-stats long_name_that_fits_on_one_line_! 1 1 1.0 0 0.0 macro-stats #[test] 1 1 1.0 0 0.0 macro-stats =================================================================================== From 997c6a88216ac6a11bb864ab92fa4cf3da72f019 Mon Sep 17 00:00:00 2001 From: Amogh Shivaram Date: Fri, 8 Aug 2025 00:20:55 -0500 Subject: [PATCH 046/195] Escape diff strings in graphviz --- compiler/rustc_mir_dataflow/src/framework/graphviz.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/compiler/rustc_mir_dataflow/src/framework/graphviz.rs b/compiler/rustc_mir_dataflow/src/framework/graphviz.rs index a7d5422a3d72..f86a15a8f921 100644 --- a/compiler/rustc_mir_dataflow/src/framework/graphviz.rs +++ b/compiler/rustc_mir_dataflow/src/framework/graphviz.rs @@ -800,6 +800,7 @@ where let re = regex!("\t?\u{001f}([+-])"); let raw_diff = format!("{:#?}", DebugDiffWithAdapter { new, old, ctxt }); + let raw_diff = dot::escape_html(&raw_diff); // Replace newlines in the `Debug` output with `
` let raw_diff = raw_diff.replace('\n', r#"
"#); From 3abe5f9dbef7046d8c63b7c2f782ebe6142881ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Fri, 8 Aug 2025 08:56:51 +0200 Subject: [PATCH 047/195] Add snapshot test for cross-compilation cargo build --- src/bootstrap/src/core/builder/tests.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs index 5361347da904..41fdb905e6a9 100644 --- a/src/bootstrap/src/core/builder/tests.rs +++ b/src/bootstrap/src/core/builder/tests.rs @@ -975,6 +975,21 @@ mod snapshot { .render_steps(), @"[build] rustc 0 -> cargo 1 "); } + #[test] + fn build_cargo_cross() { + let ctx = TestCtx::new(); + insta::assert_snapshot!( + ctx.config("build") + .paths(&["cargo"]) + .hosts(&[TEST_TRIPLE_1]) + .render_steps(), @r" + [build] llvm + [build] rustc 0 -> rustc 1 + [build] rustc 1 -> std 1 + [build] rustc 1 -> cargo 2 + "); + } + #[test] fn dist_default_stage() { let ctx = TestCtx::new(); From 9eb9b5a191807e823ade467902f5e663f6ba9341 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Fri, 8 Aug 2025 08:57:44 +0200 Subject: [PATCH 048/195] Build host library when cross-compiling `ToolTarget` tools --- src/bootstrap/src/core/build_steps/tool.rs | 8 ++++++-- src/bootstrap/src/core/builder/tests.rs | 1 + 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/tool.rs b/src/bootstrap/src/core/build_steps/tool.rs index 7b0ef942abec..aa00cd03c5bb 100644 --- a/src/bootstrap/src/core/build_steps/tool.rs +++ b/src/bootstrap/src/core/build_steps/tool.rs @@ -334,7 +334,8 @@ pub fn prepare_tool_cargo( /// Determines how to build a `ToolTarget`, i.e. which compiler should be used to compile it. /// The compiler stage is automatically bumped if we need to cross-compile a stage 1 tool. pub enum ToolTargetBuildMode { - /// Build the tool using rustc that corresponds to the selected CLI stage. + /// Build the tool for the given `target` using rustc that corresponds to the top CLI + /// stage. Build(TargetSelection), /// Build the tool so that it can be attached to the sysroot of the passed compiler. /// Since we always dist stage 2+, the compiler that builds the tool in this case has to be @@ -366,7 +367,10 @@ pub(crate) fn get_tool_target_compiler( } else { // If we are cross-compiling a stage 1 tool, we cannot do that with a stage 0 compiler, // so we auto-bump the tool's stage to 2, which means we need a stage 1 compiler. - builder.compiler(build_compiler_stage.max(1), builder.host_target) + let build_compiler = builder.compiler(build_compiler_stage.max(1), builder.host_target); + // We also need the host stdlib to compile host code (proc macros/build scripts) + builder.std(build_compiler, builder.host_target); + build_compiler }; builder.std(compiler, target); compiler diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs index 41fdb905e6a9..1914e24fa35a 100644 --- a/src/bootstrap/src/core/builder/tests.rs +++ b/src/bootstrap/src/core/builder/tests.rs @@ -985,6 +985,7 @@ mod snapshot { .render_steps(), @r" [build] llvm [build] rustc 0 -> rustc 1 + [build] rustc 1 -> std 1 [build] rustc 1 -> std 1 [build] rustc 1 -> cargo 2 "); From b6fe04defc37e1754c71ffe67ab79eff422d12c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Fri, 1 Aug 2025 17:01:07 +0200 Subject: [PATCH 049/195] Split CodegenBackend step into two and handle stamp sysroot copy explicitly --- src/bootstrap/src/core/build_steps/compile.rs | 346 ++++++++++-------- src/bootstrap/src/core/builder/mod.rs | 3 +- 2 files changed, 191 insertions(+), 158 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs index e1ee0773107e..dc350c5c34af 100644 --- a/src/bootstrap/src/core/build_steps/compile.rs +++ b/src/bootstrap/src/core/build_steps/compile.rs @@ -23,8 +23,7 @@ use crate::core::build_steps::tool::{RustcPrivateCompilers, SourceType, copy_lld use crate::core::build_steps::{dist, llvm}; use crate::core::builder; use crate::core::builder::{ - Builder, Cargo, Kind, PathSet, RunConfig, ShouldRun, Step, StepMetadata, TaskPath, - crate_description, + Builder, Cargo, Kind, RunConfig, ShouldRun, Step, StepMetadata, crate_description, }; use crate::core::config::{DebuginfoLevel, LlvmLibunwind, RustcLto, TargetSelection}; use crate::utils::build_stamp; @@ -1539,99 +1538,46 @@ impl Step for RustcLink { } #[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct CodegenBackend { +pub struct GccCodegenBackend { compilers: RustcPrivateCompilers, - backend: CodegenBackendKind, } -fn needs_codegen_config(run: &RunConfig<'_>) -> bool { - let mut needs_codegen_cfg = false; - for path_set in &run.paths { - needs_codegen_cfg = match path_set { - PathSet::Set(set) => set.iter().any(|p| is_codegen_cfg_needed(p, run)), - PathSet::Suite(suite) => is_codegen_cfg_needed(suite, run), - } - } - needs_codegen_cfg -} - -pub(crate) const CODEGEN_BACKEND_PREFIX: &str = "rustc_codegen_"; - -fn is_codegen_cfg_needed(path: &TaskPath, run: &RunConfig<'_>) -> bool { - let path = path.path.to_str().unwrap(); - - let is_explicitly_called = |p| -> bool { run.builder.paths.contains(p) }; - let should_enforce = run.builder.kind == Kind::Dist || run.builder.kind == Kind::Install; - - if path.contains(CODEGEN_BACKEND_PREFIX) { - let mut needs_codegen_backend_config = true; - for backend in run.builder.config.codegen_backends(run.target) { - if path.ends_with(&(CODEGEN_BACKEND_PREFIX.to_owned() + backend.name())) { - needs_codegen_backend_config = false; - } - } - if (is_explicitly_called(&PathBuf::from(path)) || should_enforce) - && needs_codegen_backend_config - { - run.builder.info( - "WARNING: no codegen-backends config matched the requested path to build a codegen backend. \ - HELP: add backend to codegen-backends in bootstrap.toml.", - ); - return true; - } - } - - false -} - -impl Step for CodegenBackend { - type Output = (); +impl Step for GccCodegenBackend { + type Output = BuildStamp; const ONLY_HOSTS: bool = true; - /// Only the backends specified in the `codegen-backends` entry of `bootstrap.toml` are built. - const DEFAULT: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { - run.paths(&["compiler/rustc_codegen_cranelift", "compiler/rustc_codegen_gcc"]) + run.alias("rustc_codegen_gcc").alias("cg_gcc") } fn make_run(run: RunConfig<'_>) { - if needs_codegen_config(&run) { - return; - } - - for backend in run.builder.config.codegen_backends(run.target) { - if backend.is_llvm() { - continue; // Already built as part of rustc - } - - run.builder.ensure(CodegenBackend { - compilers: RustcPrivateCompilers::new( - run.builder, - run.builder.top_stage, - run.target, - ), - backend: backend.clone(), - }); - } + run.builder.ensure(GccCodegenBackend { + compilers: RustcPrivateCompilers::new(run.builder, run.builder.top_stage, run.target), + }); } #[cfg_attr( feature = "tracing", instrument( level = "debug", - name = "CodegenBackend::run", + name = "GccCodegenBackend::run", skip_all, fields( compilers = ?self.compilers, - backend = ?self.backend, ), ), )] - fn run(self, builder: &Builder<'_>) { - let backend = self.backend; + fn run(self, builder: &Builder<'_>) -> Self::Output { let target = self.compilers.target(); let build_compiler = self.compilers.build_compiler(); + let stamp = build_stamp::codegen_backend_stamp( + builder, + build_compiler, + target, + &CodegenBackendKind::Gcc, + ); + if builder.config.keep_stage.contains(&build_compiler.stage) { trace!("`keep-stage` requested"); builder.info( @@ -1640,10 +1586,93 @@ impl Step for CodegenBackend { ); // Codegen backends are linked separately from this step today, so we don't do // anything here. - return; + return stamp; } - let out_dir = builder.cargo_out(build_compiler, Mode::Codegen, target); + let mut cargo = builder::Cargo::new( + builder, + build_compiler, + Mode::Codegen, + SourceType::InTree, + target, + Kind::Build, + ); + cargo.arg("--manifest-path").arg(builder.src.join("compiler/rustc_codegen_gcc/Cargo.toml")); + rustc_cargo_env(builder, &mut cargo, target); + + let gcc = builder.ensure(Gcc { target }); + add_cg_gcc_cargo_flags(&mut cargo, &gcc); + + let _guard = builder.msg_rustc_tool( + Kind::Build, + build_compiler.stage, + format_args!("codegen backend gcc"), + build_compiler.host, + target, + ); + let files = run_cargo(builder, cargo, vec![], &stamp, vec![], false, false); + write_codegen_backend_stamp(stamp, files, builder.config.dry_run()) + } + + fn metadata(&self) -> Option { + Some( + StepMetadata::build("rustc_codegen_gcc", self.compilers.target()) + .built_by(self.compilers.build_compiler()), + ) + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct CraneliftCodegenBackend { + compilers: RustcPrivateCompilers, +} + +impl Step for CraneliftCodegenBackend { + type Output = BuildStamp; + const ONLY_HOSTS: bool = true; + + fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { + run.alias("rustc_codegen_cranelift").alias("cg_clif") + } + + fn make_run(run: RunConfig<'_>) { + run.builder.ensure(CraneliftCodegenBackend { + compilers: RustcPrivateCompilers::new(run.builder, run.builder.top_stage, run.target), + }); + } + + #[cfg_attr( + feature = "tracing", + instrument( + level = "debug", + name = "CraneliftCodegenBackend::run", + skip_all, + fields( + compilers = ?self.compilers, + ), + ), + )] + fn run(self, builder: &Builder<'_>) -> Self::Output { + let target = self.compilers.target(); + let build_compiler = self.compilers.build_compiler(); + + let stamp = build_stamp::codegen_backend_stamp( + builder, + build_compiler, + target, + &CodegenBackendKind::Cranelift, + ); + + if builder.config.keep_stage.contains(&build_compiler.stage) { + trace!("`keep-stage` requested"); + builder.info( + "WARNING: Using a potentially old codegen backend. \ + This may not behave well.", + ); + // Codegen backends are linked separately from this step today, so we don't do + // anything here. + return stamp; + } let mut cargo = builder::Cargo::new( builder, @@ -1655,30 +1684,35 @@ impl Step for CodegenBackend { ); cargo .arg("--manifest-path") - .arg(builder.src.join(format!("compiler/{}/Cargo.toml", backend.crate_name()))); + .arg(builder.src.join("compiler/rustc_codegen_cranelift/Cargo.toml")); rustc_cargo_env(builder, &mut cargo, target); - // Ideally, we'd have a separate step for the individual codegen backends, - // like we have in tests (test::CodegenGCC) but that would require a lot of restructuring. - // If the logic gets more complicated, it should probably be done. - if backend.is_gcc() { - let gcc = builder.ensure(Gcc { target }); - add_cg_gcc_cargo_flags(&mut cargo, &gcc); - } - - let tmp_stamp = BuildStamp::new(&out_dir).with_prefix("tmp"); - let _guard = builder.msg_rustc_tool( Kind::Build, build_compiler.stage, - format_args!("codegen backend {}", backend.name()), + format_args!("codegen backend cranelift"), build_compiler.host, target, ); - let files = run_cargo(builder, cargo, vec![], &tmp_stamp, vec![], false, false); - if builder.config.dry_run() { - return; - } + let files = run_cargo(builder, cargo, vec![], &stamp, vec![], false, false); + write_codegen_backend_stamp(stamp, files, builder.config.dry_run()) + } + + fn metadata(&self) -> Option { + Some( + StepMetadata::build("rustc_codegen_cranelift", self.compilers.target()) + .built_by(self.compilers.build_compiler()), + ) + } +} + +/// Write filtered `files` into the passed build stamp and returns it. +fn write_codegen_backend_stamp( + mut stamp: BuildStamp, + files: Vec, + dry_run: bool, +) -> BuildStamp { + if !dry_run { let mut files = files.into_iter().filter(|f| { let filename = f.file_name().unwrap().to_str().unwrap(); is_dylib(f) && filename.contains("rustc_codegen_") @@ -1694,32 +1728,27 @@ impl Step for CodegenBackend { f.display() ); } - let stamp = build_stamp::codegen_backend_stamp(builder, build_compiler, target, &backend); - let codegen_backend = codegen_backend.to_str().unwrap(); - t!(stamp.add_stamp(codegen_backend).write()); - } - fn metadata(&self) -> Option { - Some( - StepMetadata::build(&self.backend.crate_name(), self.compilers.target()) - .built_by(self.compilers.build_compiler()), - ) + let codegen_backend = codegen_backend.to_str().unwrap(); + stamp = stamp.add_stamp(codegen_backend); + t!(stamp.write()); } + stamp } +pub(crate) const CODEGEN_BACKEND_PREFIX: &str = "rustc_codegen_"; + /// Creates the `codegen-backends` folder for a compiler that's about to be /// assembled as a complete compiler. /// -/// This will take the codegen artifacts produced by `compiler` and link them +/// This will take the codegen artifacts recorded in the given `stamp` and link them /// into an appropriate location for `target_compiler` to be a functional /// compiler. fn copy_codegen_backends_to_sysroot( builder: &Builder<'_>, - compiler: Compiler, + stamp: BuildStamp, target_compiler: Compiler, ) { - let target = target_compiler.host; - // Note that this step is different than all the other `*Link` steps in // that it's not assembling a bunch of libraries but rather is primarily // moving the codegen backend into place. The codegen backend of rustc is @@ -1735,25 +1764,18 @@ fn copy_codegen_backends_to_sysroot( return; } - for backend in builder.config.codegen_backends(target) { - if backend.is_llvm() { - continue; // Already built as part of rustc - } - - let stamp = build_stamp::codegen_backend_stamp(builder, compiler, target, backend); - if stamp.path().exists() { - let dylib = t!(fs::read_to_string(stamp.path())); - let file = Path::new(&dylib); - let filename = file.file_name().unwrap().to_str().unwrap(); - // change `librustc_codegen_cranelift-xxxxxx.so` to - // `librustc_codegen_cranelift-release.so` - let target_filename = { - let dash = filename.find('-').unwrap(); - let dot = filename.find('.').unwrap(); - format!("{}-{}{}", &filename[..dash], builder.rust_release(), &filename[dot..]) - }; - builder.copy_link(file, &dst.join(target_filename), FileType::NativeLibrary); - } + if stamp.path().exists() { + let dylib = t!(fs::read_to_string(stamp.path())); + let file = Path::new(&dylib); + let filename = file.file_name().unwrap().to_str().unwrap(); + // change `librustc_codegen_cranelift-xxxxxx.so` to + // `librustc_codegen_cranelift-release.so` + let target_filename = { + let dash = filename.find('-').unwrap(); + let dot = filename.find('.').unwrap(); + format!("{}-{}{}", &filename[..dash], builder.rust_release(), &filename[dot..]) + }; + builder.copy_link(file, &dst.join(target_filename), FileType::NativeLibrary); } } @@ -2162,44 +2184,52 @@ impl Step for Assemble { ); build_compiler.stage = actual_stage; - #[cfg(feature = "tracing")] - let _codegen_backend_span = - span!(tracing::Level::DEBUG, "building requested codegen backends").entered(); - for backend in builder.config.codegen_backends(target_compiler.host) { - if backend.is_llvm() { - debug!("llvm codegen backend is already built as part of rustc"); - continue; // Already built as part of rustc - } + let mut codegen_backend_stamps = vec![]; + { + #[cfg(feature = "tracing")] + let _codegen_backend_span = + span!(tracing::Level::DEBUG, "building requested codegen backends").entered(); - // FIXME: this is a horrible hack used to make `x check` work when other codegen - // backends are enabled. - // `x check` will check stage 1 rustc, which copies its rmetas to the stage0 sysroot. - // Then it checks codegen backends, which correctly use these rmetas. - // Then it needs to check std, but for that it needs to build stage 1 rustc. - // This copies the build rmetas into the stage0 sysroot, effectively poisoning it, - // because we then have both check and build rmetas in the same sysroot. - // That would be fine on its own. However, when another codegen backend is enabled, - // then building stage 1 rustc implies also building stage 1 codegen backend (even if - // it isn't used for anything). And since that tries to use the poisoned - // rmetas, it fails to build. - // We don't actually need to build rustc-private codegen backends for checking std, - // so instead we skip that. - // Note: this would be also an issue for other rustc-private tools, but that is "solved" - // by check::Std being last in the list of checked things (see - // `Builder::get_step_descriptions`). - if builder.kind == Kind::Check && builder.top_stage == 1 { - continue; + for backend in builder.config.codegen_backends(target_compiler.host) { + // FIXME: this is a horrible hack used to make `x check` work when other codegen + // backends are enabled. + // `x check` will check stage 1 rustc, which copies its rmetas to the stage0 sysroot. + // Then it checks codegen backends, which correctly use these rmetas. + // Then it needs to check std, but for that it needs to build stage 1 rustc. + // This copies the build rmetas into the stage0 sysroot, effectively poisoning it, + // because we then have both check and build rmetas in the same sysroot. + // That would be fine on its own. However, when another codegen backend is enabled, + // then building stage 1 rustc implies also building stage 1 codegen backend (even if + // it isn't used for anything). And since that tries to use the poisoned + // rmetas, it fails to build. + // We don't actually need to build rustc-private codegen backends for checking std, + // so instead we skip that. + // Note: this would be also an issue for other rustc-private tools, but that is "solved" + // by check::Std being last in the list of checked things (see + // `Builder::get_step_descriptions`). + if builder.kind == Kind::Check && builder.top_stage == 1 { + continue; + } + + let prepare_compilers = || { + RustcPrivateCompilers::from_build_and_target_compiler( + build_compiler, + target_compiler, + ) + }; + + let stamp = match backend { + CodegenBackendKind::Cranelift => { + builder.ensure(CraneliftCodegenBackend { compilers: prepare_compilers() }) + } + CodegenBackendKind::Gcc => { + builder.ensure(GccCodegenBackend { compilers: prepare_compilers() }) + } + CodegenBackendKind::Llvm | CodegenBackendKind::Custom(_) => continue, + }; + codegen_backend_stamps.push(stamp); } - builder.ensure(CodegenBackend { - compilers: RustcPrivateCompilers::from_build_and_target_compiler( - build_compiler, - target_compiler, - ), - backend: backend.clone(), - }); } - #[cfg(feature = "tracing")] - drop(_codegen_backend_span); let stage = target_compiler.stage; let host = target_compiler.host; @@ -2260,7 +2290,9 @@ impl Step for Assemble { } debug!("copying codegen backends to sysroot"); - copy_codegen_backends_to_sysroot(builder, build_compiler, target_compiler); + for stamp in codegen_backend_stamps { + copy_codegen_backends_to_sysroot(builder, stamp, target_compiler); + } if builder.config.lld_enabled { let lld_wrapper = diff --git a/src/bootstrap/src/core/builder/mod.rs b/src/bootstrap/src/core/builder/mod.rs index e15941938f19..d16ffbe34ceb 100644 --- a/src/bootstrap/src/core/builder/mod.rs +++ b/src/bootstrap/src/core/builder/mod.rs @@ -959,7 +959,8 @@ impl<'a> Builder<'a> { compile::Std, compile::Rustc, compile::Assemble, - compile::CodegenBackend, + compile::CraneliftCodegenBackend, + compile::GccCodegenBackend, compile::StartupObjects, tool::BuildManifest, tool::Rustbook, From 929b3bb4c3dead8dffef42c3541b552cfb014d22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Fri, 1 Aug 2025 17:39:49 +0200 Subject: [PATCH 050/195] Refactor `dist::CraneliftCodegenBackend` Make it clear that it only works for the Cranelift backend, add step metadata, add a test and change the default enablement logic for this step. --- src/bootstrap/src/core/build_steps/compile.rs | 2 +- src/bootstrap/src/core/build_steps/dist.rs | 92 +++++++++---------- src/bootstrap/src/core/build_steps/install.rs | 7 +- src/bootstrap/src/core/builder/mod.rs | 2 +- src/bootstrap/src/core/builder/tests.rs | 34 +++++++ 5 files changed, 85 insertions(+), 52 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs index dc350c5c34af..0fb4b95e40a3 100644 --- a/src/bootstrap/src/core/build_steps/compile.rs +++ b/src/bootstrap/src/core/build_steps/compile.rs @@ -1624,7 +1624,7 @@ impl Step for GccCodegenBackend { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct CraneliftCodegenBackend { - compilers: RustcPrivateCompilers, + pub compilers: RustcPrivateCompilers, } impl Step for CraneliftCodegenBackend { diff --git a/src/bootstrap/src/core/build_steps/dist.rs b/src/bootstrap/src/core/build_steps/dist.rs index cbbfb6b6a110..8143bec05cfc 100644 --- a/src/bootstrap/src/core/build_steps/dist.rs +++ b/src/bootstrap/src/core/build_steps/dist.rs @@ -1389,38 +1389,39 @@ impl Step for Miri { } #[derive(Debug, Clone, Hash, PartialEq, Eq)] -pub struct CodegenBackend { - pub compiler: Compiler, - pub backend: CodegenBackendKind, +pub struct CraneliftCodegenBackend { + pub build_compiler: Compiler, } -impl Step for CodegenBackend { +impl Step for CraneliftCodegenBackend { type Output = Option; const DEFAULT: bool = true; const ONLY_HOSTS: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { - run.path("compiler/rustc_codegen_cranelift") + // We only want to build the cranelift backend in `x dist` if the backend was enabled + // in rust.codegen-backends. + // Sadly, we don't have access to the actual for which we're disting clif here.. + // So we just use the host target. + let clif_enabled_by_default = run + .builder + .config + .codegen_backends(run.builder.host_target) + .contains(&CodegenBackendKind::Cranelift); + run.alias("rustc_codegen_cranelift").default_condition(clif_enabled_by_default) } fn make_run(run: RunConfig<'_>) { - for backend in run.builder.config.codegen_backends(run.target) { - if backend.is_llvm() { - continue; // Already built as part of rustc - } - - run.builder.ensure(CodegenBackend { - compiler: run.builder.compiler(run.builder.top_stage, run.target), - backend: backend.clone(), - }); - } + run.builder.ensure(CraneliftCodegenBackend { + build_compiler: run.builder.compiler_for( + run.builder.top_stage, + run.builder.config.host_target, + run.target, + ), + }); } fn run(self, builder: &Builder<'_>) -> Option { - if builder.config.dry_run() { - return None; - } - // This prevents rustc_codegen_cranelift from being built for "dist" // or "install" on the stable/beta channels. It is not yet stable and // should not be included. @@ -1428,46 +1429,39 @@ impl Step for CodegenBackend { return None; } - if !builder.config.codegen_backends(self.compiler.host).contains(&self.backend) { - return None; - } - - if self.backend.is_cranelift() && !target_supports_cranelift_backend(self.compiler.host) { + let target = self.build_compiler.host; + let compilers = + RustcPrivateCompilers::from_build_compiler(builder, self.build_compiler, target); + if !target_supports_cranelift_backend(target) { builder.info("target not supported by rustc_codegen_cranelift. skipping"); return None; } - let compiler = self.compiler; - let backend = self.backend; - - let mut tarball = Tarball::new( - builder, - &format!("rustc-codegen-{}", backend.name()), - &compiler.host.triple, - ); - if backend.is_cranelift() { - tarball.set_overlay(OverlayKind::RustcCodegenCranelift); - } else { - panic!("Unknown codegen backend {}", backend.name()); - } + let mut tarball = Tarball::new(builder, &"rustc-codegen-cranelift", &target.triple); + tarball.set_overlay(OverlayKind::RustcCodegenCranelift); tarball.is_preview(true); - tarball.add_legal_and_readme_to(format!("share/doc/{}", backend.crate_name())); + tarball.add_legal_and_readme_to("share/doc/rustc_codegen_cranelift"); - let src = builder.sysroot(compiler); - let backends_src = builder.sysroot_codegen_backends(compiler); + builder.ensure(compile::CraneliftCodegenBackend { compilers }); + + if builder.config.dry_run() { + return None; + } + + let src = builder.sysroot(self.build_compiler); + let backends_src = builder.sysroot_codegen_backends(self.build_compiler); let backends_rel = backends_src .strip_prefix(src) .unwrap() - .strip_prefix(builder.sysroot_libdir_relative(compiler)) + .strip_prefix(builder.sysroot_libdir_relative(self.build_compiler)) .unwrap(); // Don't use custom libdir here because ^lib/ will be resolved again with installer let backends_dst = PathBuf::from("lib").join(backends_rel); - let backend_name = backend.crate_name(); let mut found_backend = false; for backend in fs::read_dir(&backends_src).unwrap() { let file_name = backend.unwrap().file_name(); - if file_name.to_str().unwrap().contains(&backend_name) { + if file_name.to_str().unwrap().contains("rustc_codegen_cranelift") { tarball.add_file( backends_src.join(file_name), &backends_dst, @@ -1480,6 +1474,13 @@ impl Step for CodegenBackend { Some(tarball.generate()) } + + fn metadata(&self) -> Option { + Some( + StepMetadata::dist("rustc_codegen_cranelift", self.build_compiler.host) + .built_by(self.build_compiler), + ) + } } #[derive(Debug, Clone, Hash, PartialEq, Eq)] @@ -1590,9 +1591,8 @@ impl Step for Extended { add_component!("clippy" => Clippy { build_compiler: compiler, target }); add_component!("miri" => Miri { build_compiler: compiler, target }); add_component!("analysis" => Analysis { compiler, target }); - add_component!("rustc-codegen-cranelift" => CodegenBackend { - compiler: builder.compiler(stage, target), - backend: CodegenBackendKind::Cranelift, + add_component!("rustc-codegen-cranelift" => CraneliftCodegenBackend { + build_compiler: compiler, }); add_component!("llvm-bitcode-linker" => LlvmBitcodeLinker { build_compiler: compiler, diff --git a/src/bootstrap/src/core/build_steps/install.rs b/src/bootstrap/src/core/build_steps/install.rs index f628330e9ed4..acee78dcf59b 100644 --- a/src/bootstrap/src/core/build_steps/install.rs +++ b/src/bootstrap/src/core/build_steps/install.rs @@ -12,7 +12,7 @@ use crate::core::config::{Config, TargetSelection}; use crate::utils::exec::command; use crate::utils::helpers::t; use crate::utils::tarball::GeneratedTarball; -use crate::{CodegenBackendKind, Compiler, Kind}; +use crate::{Compiler, Kind}; #[cfg(target_os = "illumos")] const SHELL: &str = "bash"; @@ -274,9 +274,8 @@ install!((self, builder, _config), install_sh(builder, "rustc", self.compiler.stage, Some(self.target), &tarball); }; RustcCodegenCranelift, alias = "rustc-codegen-cranelift", Self::should_build(_config), only_hosts: true, { - if let Some(tarball) = builder.ensure(dist::CodegenBackend { - compiler: self.compiler, - backend: CodegenBackendKind::Cranelift, + if let Some(tarball) = builder.ensure(dist::CraneliftCodegenBackend { + build_compiler: self.compiler, }) { install_sh(builder, "rustc-codegen-cranelift", self.compiler.stage, Some(self.target), &tarball); } else { diff --git a/src/bootstrap/src/core/builder/mod.rs b/src/bootstrap/src/core/builder/mod.rs index d16ffbe34ceb..163a498d4b48 100644 --- a/src/bootstrap/src/core/builder/mod.rs +++ b/src/bootstrap/src/core/builder/mod.rs @@ -1151,7 +1151,7 @@ impl<'a> Builder<'a> { dist::JsonDocs, dist::Mingw, dist::Rustc, - dist::CodegenBackend, + dist::CraneliftCodegenBackend, dist::Std, dist::RustcDev, dist::Analysis, diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs index 5361347da904..eb8672106930 100644 --- a/src/bootstrap/src/core/builder/tests.rs +++ b/src/bootstrap/src/core/builder/tests.rs @@ -1272,6 +1272,40 @@ mod snapshot { "); } + // Enable dist cranelift tarball by default with `x dist` if cranelift is enabled in + // `rust.codegen-backends`. + #[test] + fn dist_cranelift_by_default() { + let ctx = TestCtx::new(); + insta::assert_snapshot!( + ctx + .config("dist") + .args(&["--set", "rust.codegen-backends=['llvm', 'cranelift']"]) + .render_steps(), @r" + [build] rustc 0 -> UnstableBookGen 1 + [build] rustc 0 -> Rustbook 1 + [build] llvm + [build] rustc 0 -> rustc 1 + [build] rustc 0 -> rustc_codegen_cranelift 1 + [build] rustc 1 -> std 1 + [build] rustc 1 -> rustc 2 + [build] rustc 1 -> rustc_codegen_cranelift 2 + [build] rustdoc 2 + [doc] std 2 crates=[alloc,compiler_builtins,core,panic_abort,panic_unwind,proc_macro,rustc-std-workspace-core,std,std_detect,sysroot,test,unwind] + [build] rustc 2 -> std 2 + [build] rustc 0 -> LintDocs 1 + [build] rustc 0 -> RustInstaller 1 + [dist] docs + [doc] std 2 crates=[] + [dist] mingw + [build] rustc 0 -> GenerateCopyright 1 + [dist] rustc + [dist] rustc 1 -> rustc_codegen_cranelift 2 + [dist] rustc 1 -> std 1 + [dist] src <> + "); + } + #[test] fn check_compiler_no_explicit_stage() { let ctx = TestCtx::new(); From ca6e367e19a96726d36876670b3a2c326046a56d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Fri, 1 Aug 2025 17:48:53 +0200 Subject: [PATCH 051/195] Clarify the behavior of `rust.codegen-backends` --- bootstrap.example.toml | 18 +++++++++++++----- src/bootstrap/src/core/build_steps/compile.rs | 2 +- src/bootstrap/src/core/build_steps/dist.rs | 4 ++-- src/bootstrap/src/core/build_steps/test.rs | 8 ++++++-- src/bootstrap/src/core/config/config.rs | 17 +++++++++++------ 5 files changed, 33 insertions(+), 16 deletions(-) diff --git a/bootstrap.example.toml b/bootstrap.example.toml index 31966af33012..89da6eeb5315 100644 --- a/bootstrap.example.toml +++ b/bootstrap.example.toml @@ -740,11 +740,19 @@ # result (broken, compiling, testing) into this JSON file. #rust.save-toolstates = (path) -# This is an array of the codegen backends that will be compiled for the rustc -# that's being compiled. The default is to only build the LLVM codegen backend, -# and currently the only standard options supported are `"llvm"`, `"cranelift"` -# and `"gcc"`. The first backend in this list will be used as default by rustc -# when no explicit backend is specified. +# This array serves three distinct purposes: +# - Backends in this list will be automatically compiled and included in the sysroot of each +# rustc compiled by bootstrap. +# - The first backend in this list will be configured as the **default codegen backend** by each +# rustc compiled by bootstrap. In other words, if the first backend is e.g. cranelift, then when +# we build a stage 1 rustc, it will by default compile Rust programs using the Cranelift backend. +# This also means that stage 2 rustc would get built by the Cranelift backend. +# - Running `x dist` (without additional arguments, or with `--include-default-paths`) will produce +# a dist component/tarball for the Cranelift backend if it is included in this array. +# +# Note that the LLVM codegen backend is special and will always be built and distributed. +# +# Currently, the only standard options supported here are `"llvm"`, `"cranelift"` and `"gcc"`. #rust.codegen-backends = ["llvm"] # Indicates whether LLD will be compiled and made available in the sysroot for rustc to execute, and diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs index 0fb4b95e40a3..1dfbf64a9557 100644 --- a/src/bootstrap/src/core/build_steps/compile.rs +++ b/src/bootstrap/src/core/build_steps/compile.rs @@ -2190,7 +2190,7 @@ impl Step for Assemble { let _codegen_backend_span = span!(tracing::Level::DEBUG, "building requested codegen backends").entered(); - for backend in builder.config.codegen_backends(target_compiler.host) { + for backend in builder.config.enabled_codegen_backends(target_compiler.host) { // FIXME: this is a horrible hack used to make `x check` work when other codegen // backends are enabled. // `x check` will check stage 1 rustc, which copies its rmetas to the stage0 sysroot. diff --git a/src/bootstrap/src/core/build_steps/dist.rs b/src/bootstrap/src/core/build_steps/dist.rs index 8143bec05cfc..b98f1e6dc1dd 100644 --- a/src/bootstrap/src/core/build_steps/dist.rs +++ b/src/bootstrap/src/core/build_steps/dist.rs @@ -1406,7 +1406,7 @@ impl Step for CraneliftCodegenBackend { let clif_enabled_by_default = run .builder .config - .codegen_backends(run.builder.host_target) + .enabled_codegen_backends(run.builder.host_target) .contains(&CodegenBackendKind::Cranelift); run.alias("rustc_codegen_cranelift").default_condition(clif_enabled_by_default) } @@ -1437,7 +1437,7 @@ impl Step for CraneliftCodegenBackend { return None; } - let mut tarball = Tarball::new(builder, &"rustc-codegen-cranelift", &target.triple); + let mut tarball = Tarball::new(builder, "rustc-codegen-cranelift", &target.triple); tarball.set_overlay(OverlayKind::RustcCodegenCranelift); tarball.is_preview(true); tarball.add_legal_and_readme_to("share/doc/rustc_codegen_cranelift"); diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index 66332c0b3e83..32c3ef53e128 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -3478,7 +3478,11 @@ impl Step for CodegenCranelift { return; } - if !builder.config.codegen_backends(run.target).contains(&CodegenBackendKind::Cranelift) { + if !builder + .config + .enabled_codegen_backends(run.target) + .contains(&CodegenBackendKind::Cranelift) + { builder.info("cranelift not in rust.codegen-backends. skipping"); return; } @@ -3605,7 +3609,7 @@ impl Step for CodegenGCC { return; } - if !builder.config.codegen_backends(run.target).contains(&CodegenBackendKind::Gcc) { + if !builder.config.enabled_codegen_backends(run.target).contains(&CodegenBackendKind::Gcc) { builder.info("gcc not in rust.codegen-backends. skipping"); return; } diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index 2c008f957d98..eb1ac8c637ff 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -1977,19 +1977,24 @@ impl Config { .unwrap_or(self.profiler) } - pub fn codegen_backends(&self, target: TargetSelection) -> &[CodegenBackendKind] { + /// Returns codegen backends that should be: + /// - Built and added to the sysroot when we build the compiler. + /// - Distributed when `x dist` is executed (if the codegen backend has a dist step). + pub fn enabled_codegen_backends(&self, target: TargetSelection) -> &[CodegenBackendKind] { self.target_config .get(&target) .and_then(|cfg| cfg.codegen_backends.as_deref()) .unwrap_or(&self.rust_codegen_backends) } - pub fn jemalloc(&self, target: TargetSelection) -> bool { - self.target_config.get(&target).and_then(|cfg| cfg.jemalloc).unwrap_or(self.jemalloc) + /// Returns the codegen backend that should be configured as the *default* codegen backend + /// for a rustc compiled by bootstrap. + pub fn default_codegen_backend(&self, target: TargetSelection) -> Option { + self.enabled_codegen_backends(target).first().cloned() } - pub fn default_codegen_backend(&self, target: TargetSelection) -> Option { - self.codegen_backends(target).first().cloned() + pub fn jemalloc(&self, target: TargetSelection) -> bool { + self.target_config.get(&target).and_then(|cfg| cfg.jemalloc).unwrap_or(self.jemalloc) } pub fn rpath_enabled(&self, target: TargetSelection) -> bool { @@ -2004,7 +2009,7 @@ impl Config { } pub fn llvm_enabled(&self, target: TargetSelection) -> bool { - self.codegen_backends(target).contains(&CodegenBackendKind::Llvm) + self.enabled_codegen_backends(target).contains(&CodegenBackendKind::Llvm) } pub fn llvm_libunwind(&self, target: TargetSelection) -> LlvmLibunwind { From b92b35204ba0ee6a97848d1eb600be5cc9ef457d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Wed, 6 Aug 2025 15:35:10 +0200 Subject: [PATCH 052/195] Review remarks --- src/bootstrap/src/core/build_steps/compile.rs | 44 +++++++++---------- src/bootstrap/src/core/build_steps/dist.rs | 2 +- src/bootstrap/src/core/config/toml/rust.rs | 3 +- 3 files changed, 23 insertions(+), 26 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs index 1dfbf64a9557..79174eb281f6 100644 --- a/src/bootstrap/src/core/build_steps/compile.rs +++ b/src/bootstrap/src/core/build_steps/compile.rs @@ -1606,7 +1606,7 @@ impl Step for GccCodegenBackend { let _guard = builder.msg_rustc_tool( Kind::Build, build_compiler.stage, - format_args!("codegen backend gcc"), + "codegen backend gcc", build_compiler.host, target, ); @@ -1690,7 +1690,7 @@ impl Step for CraneliftCodegenBackend { let _guard = builder.msg_rustc_tool( Kind::Build, build_compiler.stage, - format_args!("codegen backend cranelift"), + "codegen backend cranelift", build_compiler.host, target, ); @@ -1712,32 +1712,28 @@ fn write_codegen_backend_stamp( files: Vec, dry_run: bool, ) -> BuildStamp { - if !dry_run { - let mut files = files.into_iter().filter(|f| { - let filename = f.file_name().unwrap().to_str().unwrap(); - is_dylib(f) && filename.contains("rustc_codegen_") - }); - let codegen_backend = match files.next() { - Some(f) => f, - None => panic!("no dylibs built for codegen backend?"), - }; - if let Some(f) = files.next() { - panic!( - "codegen backend built two dylibs:\n{}\n{}", - codegen_backend.display(), - f.display() - ); - } - - let codegen_backend = codegen_backend.to_str().unwrap(); - stamp = stamp.add_stamp(codegen_backend); - t!(stamp.write()); + if dry_run { + return stamp; } + + let mut files = files.into_iter().filter(|f| { + let filename = f.file_name().unwrap().to_str().unwrap(); + is_dylib(f) && filename.contains("rustc_codegen_") + }); + let codegen_backend = match files.next() { + Some(f) => f, + None => panic!("no dylibs built for codegen backend?"), + }; + if let Some(f) = files.next() { + panic!("codegen backend built two dylibs:\n{}\n{}", codegen_backend.display(), f.display()); + } + + let codegen_backend = codegen_backend.to_str().unwrap(); + stamp = stamp.add_stamp(codegen_backend); + t!(stamp.write()); stamp } -pub(crate) const CODEGEN_BACKEND_PREFIX: &str = "rustc_codegen_"; - /// Creates the `codegen-backends` folder for a compiler that's about to be /// assembled as a complete compiler. /// diff --git a/src/bootstrap/src/core/build_steps/dist.rs b/src/bootstrap/src/core/build_steps/dist.rs index b98f1e6dc1dd..3fbc8cdbcd5a 100644 --- a/src/bootstrap/src/core/build_steps/dist.rs +++ b/src/bootstrap/src/core/build_steps/dist.rs @@ -1401,7 +1401,7 @@ impl Step for CraneliftCodegenBackend { fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { // We only want to build the cranelift backend in `x dist` if the backend was enabled // in rust.codegen-backends. - // Sadly, we don't have access to the actual for which we're disting clif here.. + // Sadly, we don't have access to the actual target for which we're disting clif here.. // So we just use the host target. let clif_enabled_by_default = run .builder diff --git a/src/bootstrap/src/core/config/toml/rust.rs b/src/bootstrap/src/core/config/toml/rust.rs index b95fb236fa16..3dab8d1d96d5 100644 --- a/src/bootstrap/src/core/config/toml/rust.rs +++ b/src/bootstrap/src/core/config/toml/rust.rs @@ -3,7 +3,6 @@ use serde::{Deserialize, Deserializer}; -use crate::core::build_steps::compile::CODEGEN_BACKEND_PREFIX; use crate::core::config::toml::TomlConfig; use crate::core::config::{DebuginfoLevel, Merge, ReplaceOpt, StringOrBool}; use crate::{BTreeSet, CodegenBackendKind, HashSet, PathBuf, TargetSelection, define_config, exit}; @@ -391,6 +390,8 @@ pub(crate) fn parse_codegen_backends( backends: Vec, section: &str, ) -> Vec { + const CODEGEN_BACKEND_PREFIX: &str = "rustc_codegen_"; + let mut found_backends = vec![]; for backend in &backends { if let Some(stripped) = backend.strip_prefix(CODEGEN_BACKEND_PREFIX) { From 1a2ee808c5aadee0e81b803f117d8b3a5dbcd844 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Fri, 8 Aug 2025 11:36:56 +0200 Subject: [PATCH 053/195] Bless snapshot tests --- src/bootstrap/src/core/builder/tests.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs index eb8672106930..6f9d5cbe6b11 100644 --- a/src/bootstrap/src/core/builder/tests.rs +++ b/src/bootstrap/src/core/builder/tests.rs @@ -1292,6 +1292,8 @@ mod snapshot { [build] rustc 1 -> rustc_codegen_cranelift 2 [build] rustdoc 2 [doc] std 2 crates=[alloc,compiler_builtins,core,panic_abort,panic_unwind,proc_macro,rustc-std-workspace-core,std,std_detect,sysroot,test,unwind] + [build] rustc 1 -> error-index 2 + [doc] rustc 1 -> error-index 2 [build] rustc 2 -> std 2 [build] rustc 0 -> LintDocs 1 [build] rustc 0 -> RustInstaller 1 From 7671b5af71deb6c43cb466b333c821b03eb98b65 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Sat, 2 Aug 2025 22:33:04 +1000 Subject: [PATCH 054/195] coverage: Extract HIR-related helper code out of the main module --- .../src/coverage/hir_info.rs | 125 +++++++++++++++ .../src/coverage/mappings.rs | 2 +- .../rustc_mir_transform/src/coverage/mod.rs | 145 ++---------------- .../rustc_mir_transform/src/coverage/spans.rs | 3 +- 4 files changed, 139 insertions(+), 136 deletions(-) create mode 100644 compiler/rustc_mir_transform/src/coverage/hir_info.rs diff --git a/compiler/rustc_mir_transform/src/coverage/hir_info.rs b/compiler/rustc_mir_transform/src/coverage/hir_info.rs new file mode 100644 index 000000000000..7d720159295b --- /dev/null +++ b/compiler/rustc_mir_transform/src/coverage/hir_info.rs @@ -0,0 +1,125 @@ +use rustc_hir as hir; +use rustc_hir::intravisit::{Visitor, walk_expr}; +use rustc_middle::hir::nested_filter; +use rustc_middle::ty::TyCtxt; +use rustc_span::Span; +use rustc_span::def_id::LocalDefId; + +/// Function information extracted from HIR by the coverage instrumentor. +#[derive(Debug)] +pub(crate) struct ExtractedHirInfo { + pub(crate) function_source_hash: u64, + pub(crate) is_async_fn: bool, + /// The span of the function's signature, if available. + /// Must have the same context and filename as the body span. + pub(crate) fn_sig_span: Option, + pub(crate) body_span: Span, + /// "Holes" are regions within the function body (or its expansions) that + /// should not be included in coverage spans for this function + /// (e.g. closures and nested items). + pub(crate) hole_spans: Vec, +} + +pub(crate) fn extract_hir_info<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> ExtractedHirInfo { + // FIXME(#79625): Consider improving MIR to provide the information needed, to avoid going back + // to HIR for it. + + // HACK: For synthetic MIR bodies (async closures), use the def id of the HIR body. + if tcx.is_synthetic_mir(def_id) { + return extract_hir_info(tcx, tcx.local_parent(def_id)); + } + + let hir_node = tcx.hir_node_by_def_id(def_id); + let fn_body_id = hir_node.body_id().expect("HIR node is a function with body"); + let hir_body = tcx.hir_body(fn_body_id); + + let maybe_fn_sig = hir_node.fn_sig(); + let is_async_fn = maybe_fn_sig.is_some_and(|fn_sig| fn_sig.header.is_async()); + + let mut body_span = hir_body.value.span; + + use hir::{Closure, Expr, ExprKind, Node}; + // Unexpand a closure's body span back to the context of its declaration. + // This helps with closure bodies that consist of just a single bang-macro, + // and also with closure bodies produced by async desugaring. + if let Node::Expr(&Expr { kind: ExprKind::Closure(&Closure { fn_decl_span, .. }), .. }) = + hir_node + { + body_span = body_span.find_ancestor_in_same_ctxt(fn_decl_span).unwrap_or(body_span); + } + + // The actual signature span is only used if it has the same context and + // filename as the body, and precedes the body. + let fn_sig_span = maybe_fn_sig.map(|fn_sig| fn_sig.span).filter(|&fn_sig_span| { + let source_map = tcx.sess.source_map(); + let file_idx = |span: Span| source_map.lookup_source_file_idx(span.lo()); + + fn_sig_span.eq_ctxt(body_span) + && fn_sig_span.hi() <= body_span.lo() + && file_idx(fn_sig_span) == file_idx(body_span) + }); + + let function_source_hash = hash_mir_source(tcx, hir_body); + + let hole_spans = extract_hole_spans_from_hir(tcx, hir_body); + + ExtractedHirInfo { function_source_hash, is_async_fn, fn_sig_span, body_span, hole_spans } +} + +fn hash_mir_source<'tcx>(tcx: TyCtxt<'tcx>, hir_body: &'tcx hir::Body<'tcx>) -> u64 { + // FIXME(cjgillot) Stop hashing HIR manually here. + let owner = hir_body.id().hir_id.owner; + tcx.hir_owner_nodes(owner).opt_hash_including_bodies.unwrap().to_smaller_hash().as_u64() +} + +fn extract_hole_spans_from_hir<'tcx>(tcx: TyCtxt<'tcx>, hir_body: &hir::Body<'tcx>) -> Vec { + struct HolesVisitor<'tcx> { + tcx: TyCtxt<'tcx>, + hole_spans: Vec, + } + + impl<'tcx> Visitor<'tcx> for HolesVisitor<'tcx> { + /// We have special handling for nested items, but we still want to + /// traverse into nested bodies of things that are not considered items, + /// such as "anon consts" (e.g. array lengths). + type NestedFilter = nested_filter::OnlyBodies; + + fn maybe_tcx(&mut self) -> TyCtxt<'tcx> { + self.tcx + } + + /// We override `visit_nested_item` instead of `visit_item` because we + /// only need the item's span, not the item itself. + fn visit_nested_item(&mut self, id: hir::ItemId) -> Self::Result { + let span = self.tcx.def_span(id.owner_id.def_id); + self.visit_hole_span(span); + // Having visited this item, we don't care about its children, + // so don't call `walk_item`. + } + + // We override `visit_expr` instead of the more specific expression + // visitors, so that we have direct access to the expression span. + fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) { + match expr.kind { + hir::ExprKind::Closure(_) | hir::ExprKind::ConstBlock(_) => { + self.visit_hole_span(expr.span); + // Having visited this expression, we don't care about its + // children, so don't call `walk_expr`. + } + + // For other expressions, recursively visit as normal. + _ => walk_expr(self, expr), + } + } + } + impl HolesVisitor<'_> { + fn visit_hole_span(&mut self, hole_span: Span) { + self.hole_spans.push(hole_span); + } + } + + let mut visitor = HolesVisitor { tcx, hole_spans: vec![] }; + + visitor.visit_body(hir_body); + visitor.hole_spans +} diff --git a/compiler/rustc_mir_transform/src/coverage/mappings.rs b/compiler/rustc_mir_transform/src/coverage/mappings.rs index 399978b5915a..46fe7c40826a 100644 --- a/compiler/rustc_mir_transform/src/coverage/mappings.rs +++ b/compiler/rustc_mir_transform/src/coverage/mappings.rs @@ -4,8 +4,8 @@ use rustc_middle::mir::{self, BasicBlock, StatementKind}; use rustc_middle::ty::TyCtxt; use rustc_span::Span; -use crate::coverage::ExtractedHirInfo; use crate::coverage::graph::{BasicCoverageBlock, CoverageGraph}; +use crate::coverage::hir_info::ExtractedHirInfo; use crate::coverage::spans::extract_refined_covspans; use crate::coverage::unexpand::unexpand_into_body_span; diff --git a/compiler/rustc_mir_transform/src/coverage/mod.rs b/compiler/rustc_mir_transform/src/coverage/mod.rs index f6945a95a7c3..47a87a2e94d8 100644 --- a/compiler/rustc_mir_transform/src/coverage/mod.rs +++ b/compiler/rustc_mir_transform/src/coverage/mod.rs @@ -1,5 +1,15 @@ +use rustc_middle::mir::coverage::{CoverageKind, FunctionCoverageInfo, Mapping, MappingKind}; +use rustc_middle::mir::{self, BasicBlock, Statement, StatementKind, TerminatorKind}; +use rustc_middle::ty::TyCtxt; +use tracing::{debug, debug_span, trace}; + +use crate::coverage::counters::BcbCountersData; +use crate::coverage::graph::CoverageGraph; +use crate::coverage::mappings::ExtractedMappings; + mod counters; mod graph; +mod hir_info; mod mappings; pub(super) mod query; mod spans; @@ -7,20 +17,6 @@ mod spans; mod tests; mod unexpand; -use rustc_hir as hir; -use rustc_hir::intravisit::{Visitor, walk_expr}; -use rustc_middle::hir::nested_filter; -use rustc_middle::mir::coverage::{CoverageKind, FunctionCoverageInfo, Mapping, MappingKind}; -use rustc_middle::mir::{self, BasicBlock, Statement, StatementKind, TerminatorKind}; -use rustc_middle::ty::TyCtxt; -use rustc_span::Span; -use rustc_span::def_id::LocalDefId; -use tracing::{debug, debug_span, trace}; - -use crate::coverage::counters::BcbCountersData; -use crate::coverage::graph::CoverageGraph; -use crate::coverage::mappings::ExtractedMappings; - /// Inserts `StatementKind::Coverage` statements that either instrument the binary with injected /// counters, via intrinsic `llvm.instrprof.increment`, and/or inject metadata used during codegen /// to construct the coverage map. @@ -67,7 +63,7 @@ fn instrument_function_for_coverage<'tcx>(tcx: TyCtxt<'tcx>, mir_body: &mut mir: let def_id = mir_body.source.def_id(); let _span = debug_span!("instrument_function_for_coverage", ?def_id).entered(); - let hir_info = extract_hir_info(tcx, def_id.expect_local()); + let hir_info = hir_info::extract_hir_info(tcx, def_id.expect_local()); // Build the coverage graph, which is a simplified view of the MIR control-flow // graph that ignores some details not relevant to coverage instrumentation. @@ -147,122 +143,3 @@ fn inject_statement(mir_body: &mut mir::Body<'_>, counter_kind: CoverageKind, bb let statement = Statement::new(source_info, StatementKind::Coverage(counter_kind)); data.statements.insert(0, statement); } - -/// Function information extracted from HIR by the coverage instrumentor. -#[derive(Debug)] -struct ExtractedHirInfo { - function_source_hash: u64, - is_async_fn: bool, - /// The span of the function's signature, if available. - /// Must have the same context and filename as the body span. - fn_sig_span: Option, - body_span: Span, - /// "Holes" are regions within the function body (or its expansions) that - /// should not be included in coverage spans for this function - /// (e.g. closures and nested items). - hole_spans: Vec, -} - -fn extract_hir_info<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> ExtractedHirInfo { - // FIXME(#79625): Consider improving MIR to provide the information needed, to avoid going back - // to HIR for it. - - // HACK: For synthetic MIR bodies (async closures), use the def id of the HIR body. - if tcx.is_synthetic_mir(def_id) { - return extract_hir_info(tcx, tcx.local_parent(def_id)); - } - - let hir_node = tcx.hir_node_by_def_id(def_id); - let fn_body_id = hir_node.body_id().expect("HIR node is a function with body"); - let hir_body = tcx.hir_body(fn_body_id); - - let maybe_fn_sig = hir_node.fn_sig(); - let is_async_fn = maybe_fn_sig.is_some_and(|fn_sig| fn_sig.header.is_async()); - - let mut body_span = hir_body.value.span; - - use hir::{Closure, Expr, ExprKind, Node}; - // Unexpand a closure's body span back to the context of its declaration. - // This helps with closure bodies that consist of just a single bang-macro, - // and also with closure bodies produced by async desugaring. - if let Node::Expr(&Expr { kind: ExprKind::Closure(&Closure { fn_decl_span, .. }), .. }) = - hir_node - { - body_span = body_span.find_ancestor_in_same_ctxt(fn_decl_span).unwrap_or(body_span); - } - - // The actual signature span is only used if it has the same context and - // filename as the body, and precedes the body. - let fn_sig_span = maybe_fn_sig.map(|fn_sig| fn_sig.span).filter(|&fn_sig_span| { - let source_map = tcx.sess.source_map(); - let file_idx = |span: Span| source_map.lookup_source_file_idx(span.lo()); - - fn_sig_span.eq_ctxt(body_span) - && fn_sig_span.hi() <= body_span.lo() - && file_idx(fn_sig_span) == file_idx(body_span) - }); - - let function_source_hash = hash_mir_source(tcx, hir_body); - - let hole_spans = extract_hole_spans_from_hir(tcx, hir_body); - - ExtractedHirInfo { function_source_hash, is_async_fn, fn_sig_span, body_span, hole_spans } -} - -fn hash_mir_source<'tcx>(tcx: TyCtxt<'tcx>, hir_body: &'tcx hir::Body<'tcx>) -> u64 { - // FIXME(cjgillot) Stop hashing HIR manually here. - let owner = hir_body.id().hir_id.owner; - tcx.hir_owner_nodes(owner).opt_hash_including_bodies.unwrap().to_smaller_hash().as_u64() -} - -fn extract_hole_spans_from_hir<'tcx>(tcx: TyCtxt<'tcx>, hir_body: &hir::Body<'tcx>) -> Vec { - struct HolesVisitor<'tcx> { - tcx: TyCtxt<'tcx>, - hole_spans: Vec, - } - - impl<'tcx> Visitor<'tcx> for HolesVisitor<'tcx> { - /// We have special handling for nested items, but we still want to - /// traverse into nested bodies of things that are not considered items, - /// such as "anon consts" (e.g. array lengths). - type NestedFilter = nested_filter::OnlyBodies; - - fn maybe_tcx(&mut self) -> TyCtxt<'tcx> { - self.tcx - } - - /// We override `visit_nested_item` instead of `visit_item` because we - /// only need the item's span, not the item itself. - fn visit_nested_item(&mut self, id: hir::ItemId) -> Self::Result { - let span = self.tcx.def_span(id.owner_id.def_id); - self.visit_hole_span(span); - // Having visited this item, we don't care about its children, - // so don't call `walk_item`. - } - - // We override `visit_expr` instead of the more specific expression - // visitors, so that we have direct access to the expression span. - fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) { - match expr.kind { - hir::ExprKind::Closure(_) | hir::ExprKind::ConstBlock(_) => { - self.visit_hole_span(expr.span); - // Having visited this expression, we don't care about its - // children, so don't call `walk_expr`. - } - - // For other expressions, recursively visit as normal. - _ => walk_expr(self, expr), - } - } - } - impl HolesVisitor<'_> { - fn visit_hole_span(&mut self, hole_span: Span) { - self.hole_spans.push(hole_span); - } - } - - let mut visitor = HolesVisitor { tcx, hole_spans: vec![] }; - - visitor.visit_body(hir_body); - visitor.hole_spans -} diff --git a/compiler/rustc_mir_transform/src/coverage/spans.rs b/compiler/rustc_mir_transform/src/coverage/spans.rs index 0ee42abb1956..ae9459dee842 100644 --- a/compiler/rustc_mir_transform/src/coverage/spans.rs +++ b/compiler/rustc_mir_transform/src/coverage/spans.rs @@ -7,8 +7,9 @@ use rustc_span::{BytePos, DesugaringKind, ExpnKind, MacroKind, Span}; use tracing::instrument; use crate::coverage::graph::{BasicCoverageBlock, CoverageGraph}; +use crate::coverage::hir_info::ExtractedHirInfo; use crate::coverage::spans::from_mir::{Hole, RawSpanFromMir, SpanFromMir}; -use crate::coverage::{ExtractedHirInfo, mappings, unexpand}; +use crate::coverage::{mappings, unexpand}; mod from_mir; From db9f0bb5326b8726c2760ffb2f17977118534c2c Mon Sep 17 00:00:00 2001 From: Zalathar Date: Sat, 2 Aug 2025 22:39:54 +1000 Subject: [PATCH 055/195] coverage: Remove obsolete comment about hashing HIR This code does not hash HIR manually (and has not done so for some time); it merely obtains a hash returned as part of `hir_owner_nodes`. --- compiler/rustc_mir_transform/src/coverage/hir_info.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_mir_transform/src/coverage/hir_info.rs b/compiler/rustc_mir_transform/src/coverage/hir_info.rs index 7d720159295b..28fdc52b06cb 100644 --- a/compiler/rustc_mir_transform/src/coverage/hir_info.rs +++ b/compiler/rustc_mir_transform/src/coverage/hir_info.rs @@ -67,9 +67,12 @@ pub(crate) fn extract_hir_info<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> E } fn hash_mir_source<'tcx>(tcx: TyCtxt<'tcx>, hir_body: &'tcx hir::Body<'tcx>) -> u64 { - // FIXME(cjgillot) Stop hashing HIR manually here. let owner = hir_body.id().hir_id.owner; - tcx.hir_owner_nodes(owner).opt_hash_including_bodies.unwrap().to_smaller_hash().as_u64() + tcx.hir_owner_nodes(owner) + .opt_hash_including_bodies + .expect("hash should be present when coverage instrumentation is enabled") + .to_smaller_hash() + .as_u64() } fn extract_hole_spans_from_hir<'tcx>(tcx: TyCtxt<'tcx>, hir_body: &hir::Body<'tcx>) -> Vec { From ac862c0ffb7b9df22b24603925ce299f8638b712 Mon Sep 17 00:00:00 2001 From: lcnr Date: Fri, 8 Aug 2025 13:49:27 +0200 Subject: [PATCH 056/195] fix typos MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Rémy Rakic --- .../ui/impl-trait/member-constraints/incomplete-constraint.rs | 4 ++-- .../reject-choice-due-to-prev-constraint.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/ui/impl-trait/member-constraints/incomplete-constraint.rs b/tests/ui/impl-trait/member-constraints/incomplete-constraint.rs index 92fd31516d72..4c085cc1eedf 100644 --- a/tests/ui/impl-trait/member-constraints/incomplete-constraint.rs +++ b/tests/ui/impl-trait/member-constraints/incomplete-constraint.rs @@ -1,8 +1,8 @@ //@ check-pass // FIXME(-Znext-solver): enable this test -// These functions currently does not normalize the opaque type but will do -// so in the future. At this point we've got a new use of the opaque will fully +// These functions currently do not normalize the opaque type but will do +// so in the future. At this point we've got a new use of the opaque with fully // universal arguments but for which lifetimes in the hidden type are unconstrained. // // Applying the member constraints would then incompletely infer `'unconstrained` to `'static`. diff --git a/tests/ui/impl-trait/member-constraints/reject-choice-due-to-prev-constraint.rs b/tests/ui/impl-trait/member-constraints/reject-choice-due-to-prev-constraint.rs index 4018256b2c84..33f2d277fe58 100644 --- a/tests/ui/impl-trait/member-constraints/reject-choice-due-to-prev-constraint.rs +++ b/tests/ui/impl-trait/member-constraints/reject-choice-due-to-prev-constraint.rs @@ -8,7 +8,7 @@ // As '0 gets outlived by 'a - its "upper bound" - the only applicable choice // region is 'a. // -// '1 has to outlive 'b so the only applicabel choice regions are 'b and 'static. +// '1 has to outlive 'b so the only applicable choice regions are 'b and 'static. // Considering this member constraint by itself would choose 'b as it is the // smaller of the two regions. // From 988569f3371f7cba75d91c46bece11813b4ace4f Mon Sep 17 00:00:00 2001 From: lcnr Date: Fri, 8 Aug 2025 15:29:37 +0200 Subject: [PATCH 057/195] remove unnecessary `TypeFoldable` impls --- compiler/rustc_middle/src/ty/mod.rs | 2 ++ compiler/rustc_type_ir/src/canonical.rs | 1 - compiler/rustc_type_ir/src/const_kind.rs | 7 +------ compiler/rustc_type_ir/src/infer_ctxt.rs | 2 -- 4 files changed, 3 insertions(+), 9 deletions(-) diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 8c0277055cd2..73e1661106ea 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -1031,6 +1031,8 @@ pub struct ParamEnvAnd<'tcx, T> { #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, HashStable)] #[derive(TypeVisitable, TypeFoldable)] pub struct TypingEnv<'tcx> { + #[type_foldable(identity)] + #[type_visitable(ignore)] pub typing_mode: TypingMode<'tcx>, pub param_env: ParamEnv<'tcx>, } diff --git a/compiler/rustc_type_ir/src/canonical.rs b/compiler/rustc_type_ir/src/canonical.rs index c66a83662d76..0cb8d8902c6f 100644 --- a/compiler/rustc_type_ir/src/canonical.rs +++ b/compiler/rustc_type_ir/src/canonical.rs @@ -199,7 +199,6 @@ impl CanonicalVarKind { /// usize or f32). In order to faithfully reproduce a type, we need to /// know what set of types a given type variable can be unified with. #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] -#[derive(TypeVisitable_Generic, TypeFoldable_Generic)] #[cfg_attr( feature = "nightly", derive(Decodable_NoContext, Encodable_NoContext, HashStable_NoContext) diff --git a/compiler/rustc_type_ir/src/const_kind.rs b/compiler/rustc_type_ir/src/const_kind.rs index 4be38d4e702d..70a8509b5133 100644 --- a/compiler/rustc_type_ir/src/const_kind.rs +++ b/compiler/rustc_type_ir/src/const_kind.rs @@ -92,15 +92,10 @@ rustc_index::newtype_index! { /// An inference variable for a const, for use in const generics. #[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Hash)] -#[derive(TypeVisitable_Generic, TypeFoldable_Generic)] #[cfg_attr(feature = "nightly", derive(Encodable_NoContext, Decodable_NoContext))] pub enum InferConst { /// Infer the value of the const. - Var( - #[type_foldable(identity)] - #[type_visitable(ignore)] - ConstVid, - ), + Var(ConstVid), /// A fresh const variable. See `infer::freshen` for more details. Fresh(u32), } diff --git a/compiler/rustc_type_ir/src/infer_ctxt.rs b/compiler/rustc_type_ir/src/infer_ctxt.rs index b4873c8c71cb..e6462a97f8ab 100644 --- a/compiler/rustc_type_ir/src/infer_ctxt.rs +++ b/compiler/rustc_type_ir/src/infer_ctxt.rs @@ -1,7 +1,6 @@ use derive_where::derive_where; #[cfg(feature = "nightly")] use rustc_macros::{Decodable_NoContext, Encodable_NoContext, HashStable_NoContext}; -use rustc_type_ir_macros::{TypeFoldable_Generic, TypeVisitable_Generic}; use crate::fold::TypeFoldable; use crate::inherent::*; @@ -20,7 +19,6 @@ use crate::{self as ty, Interner}; /// If neither of these functions are available, feel free to reach out to /// t-types for help. #[derive_where(Clone, Copy, Hash, PartialEq, Eq, Debug; I: Interner)] -#[derive(TypeVisitable_Generic, TypeFoldable_Generic)] #[cfg_attr( feature = "nightly", derive(Encodable_NoContext, Decodable_NoContext, HashStable_NoContext) From d9b725abb02c5c648b847b0b4d13b213567df0e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Fri, 8 Aug 2025 13:16:07 +0200 Subject: [PATCH 058/195] Improve error output when a command fails in bootstrap --- src/bootstrap/src/utils/exec.rs | 165 ++++++++++++++++---------------- 1 file changed, 83 insertions(+), 82 deletions(-) diff --git a/src/bootstrap/src/utils/exec.rs b/src/bootstrap/src/utils/exec.rs index 7527dff9cd84..03760faec690 100644 --- a/src/bootstrap/src/utils/exec.rs +++ b/src/bootstrap/src/utils/exec.rs @@ -80,11 +80,21 @@ impl CommandFingerprint { /// Helper method to format both Command and BootstrapCommand as a short execution line, /// without all the other details (e.g. environment variables). pub fn format_short_cmd(&self) -> String { - let program = Path::new(&self.program); - let mut line = vec![program.file_name().unwrap().to_str().unwrap().to_owned()]; - line.extend(self.args.iter().map(|arg| arg.to_string_lossy().into_owned())); - line.extend(self.cwd.iter().map(|p| p.to_string_lossy().into_owned())); - line.join(" ") + use std::fmt::Write; + + let mut cmd = self.program.to_string_lossy().to_string(); + for arg in &self.args { + let arg = arg.to_string_lossy(); + if arg.contains(' ') { + write!(cmd, " '{arg}'").unwrap(); + } else { + write!(cmd, " {arg}").unwrap(); + } + } + if let Some(cwd) = &self.cwd { + write!(cmd, " [workdir={}]", cwd.to_string_lossy()).unwrap(); + } + cmd } } @@ -434,8 +444,8 @@ impl From for BootstrapCommand { enum CommandStatus { /// The command has started and finished with some status. Finished(ExitStatus), - /// It was not even possible to start the command. - DidNotStart, + /// It was not even possible to start the command or wait for it to finish. + DidNotStartOrFinish, } /// Create a new BootstrapCommand. This is a helper function to make command creation @@ -456,9 +466,9 @@ pub struct CommandOutput { impl CommandOutput { #[must_use] - pub fn did_not_start(stdout: OutputMode, stderr: OutputMode) -> Self { + pub fn not_finished(stdout: OutputMode, stderr: OutputMode) -> Self { Self { - status: CommandStatus::DidNotStart, + status: CommandStatus::DidNotStartOrFinish, stdout: match stdout { OutputMode::Print => None, OutputMode::Capture => Some(vec![]), @@ -489,7 +499,7 @@ impl CommandOutput { pub fn is_success(&self) -> bool { match self.status { CommandStatus::Finished(status) => status.success(), - CommandStatus::DidNotStart => false, + CommandStatus::DidNotStartOrFinish => false, } } @@ -501,7 +511,7 @@ impl CommandOutput { pub fn status(&self) -> Option { match self.status { CommandStatus::Finished(status) => Some(status), - CommandStatus::DidNotStart => None, + CommandStatus::DidNotStartOrFinish => None, } } @@ -745,25 +755,11 @@ impl ExecutionContext { self.start(command, stdout, stderr).wait_for_output(self) } - fn fail(&self, message: &str, output: CommandOutput) -> ! { - if self.is_verbose() { - println!("{message}"); - } else { - let (stdout, stderr) = (output.stdout_if_present(), output.stderr_if_present()); - // If the command captures output, the user would not see any indication that - // it has failed. In this case, print a more verbose error, since to provide more - // context. - if stdout.is_some() || stderr.is_some() { - if let Some(stdout) = output.stdout_if_present().take_if(|s| !s.trim().is_empty()) { - println!("STDOUT:\n{stdout}\n"); - } - if let Some(stderr) = output.stderr_if_present().take_if(|s| !s.trim().is_empty()) { - println!("STDERR:\n{stderr}\n"); - } - println!("Command has failed. Rerun with -v to see more details."); - } else { - println!("Command has failed. Rerun with -v to see more details."); - } + fn fail(&self, message: &str) -> ! { + println!("{message}"); + + if !self.is_verbose() { + println!("Command has failed. Rerun with -v to see more details."); } exit!(1); } @@ -856,7 +852,7 @@ impl<'a> DeferredCommand<'a> { && command.should_cache { exec_ctx.command_cache.insert(fingerprint.clone(), output.clone()); - exec_ctx.profiler.record_execution(fingerprint.clone(), start_time); + exec_ctx.profiler.record_execution(fingerprint, start_time); } output @@ -872,6 +868,8 @@ impl<'a> DeferredCommand<'a> { executed_at: &'a std::panic::Location<'a>, exec_ctx: &ExecutionContext, ) -> CommandOutput { + use std::fmt::Write; + command.mark_as_executed(); let process = match process.take() { @@ -881,79 +879,82 @@ impl<'a> DeferredCommand<'a> { let created_at = command.get_created_location(); - let mut message = String::new(); + #[allow(clippy::enum_variant_names)] + enum FailureReason { + FailedAtRuntime(ExitStatus), + FailedToFinish(std::io::Error), + FailedToStart(std::io::Error), + } - let output = match process { + let (output, fail_reason) = match process { Ok(child) => match child.wait_with_output() { - Ok(result) if result.status.success() => { + Ok(output) if output.status.success() => { // Successful execution - CommandOutput::from_output(result, stdout, stderr) + (CommandOutput::from_output(output, stdout, stderr), None) } - Ok(result) => { - // Command ran but failed - use std::fmt::Write; - - writeln!( - message, - r#" -Command {command:?} did not execute successfully. -Expected success, got {} -Created at: {created_at} -Executed at: {executed_at}"#, - result.status, + Ok(output) => { + // Command started, but then it failed + let status = output.status; + ( + CommandOutput::from_output(output, stdout, stderr), + Some(FailureReason::FailedAtRuntime(status)), ) - .unwrap(); - - let output = CommandOutput::from_output(result, stdout, stderr); - - if stdout.captures() { - writeln!(message, "\nSTDOUT ----\n{}", output.stdout().trim()).unwrap(); - } - if stderr.captures() { - writeln!(message, "\nSTDERR ----\n{}", output.stderr().trim()).unwrap(); - } - - output } Err(e) => { // Failed to wait for output - use std::fmt::Write; - - writeln!( - message, - "\n\nCommand {command:?} did not execute successfully.\ - \nIt was not possible to execute the command: {e:?}" + ( + CommandOutput::not_finished(stdout, stderr), + Some(FailureReason::FailedToFinish(e)), ) - .unwrap(); - - CommandOutput::did_not_start(stdout, stderr) } }, Err(e) => { // Failed to spawn the command - use std::fmt::Write; - - writeln!( - message, - "\n\nCommand {command:?} did not execute successfully.\ - \nIt was not possible to execute the command: {e:?}" - ) - .unwrap(); - - CommandOutput::did_not_start(stdout, stderr) + (CommandOutput::not_finished(stdout, stderr), Some(FailureReason::FailedToStart(e))) } }; - if !output.is_success() { + if let Some(fail_reason) = fail_reason { + let mut error_message = String::new(); + let command_str = if exec_ctx.is_verbose() { + format!("{command:?}") + } else { + command.fingerprint().format_short_cmd() + }; + let action = match fail_reason { + FailureReason::FailedAtRuntime(e) => { + format!("failed with exit code {}", e.code().unwrap_or(1)) + } + FailureReason::FailedToFinish(e) => { + format!("failed to finish: {e:?}") + } + FailureReason::FailedToStart(e) => { + format!("failed to start: {e:?}") + } + }; + writeln!( + error_message, + r#"Command `{command_str}` {action} +Created at: {created_at} +Executed at: {executed_at}"#, + ) + .unwrap(); + if stdout.captures() { + writeln!(error_message, "\n--- STDOUT vvv\n{}", output.stdout().trim()).unwrap(); + } + if stderr.captures() { + writeln!(error_message, "\n--- STDERR vvv\n{}", output.stderr().trim()).unwrap(); + } + match command.failure_behavior { BehaviorOnFailure::DelayFail => { if exec_ctx.fail_fast { - exec_ctx.fail(&message, output); + exec_ctx.fail(&error_message); } - exec_ctx.add_to_delay_failure(message); + exec_ctx.add_to_delay_failure(error_message); } BehaviorOnFailure::Exit => { - exec_ctx.fail(&message, output); + exec_ctx.fail(&error_message); } BehaviorOnFailure::Ignore => { // If failures are allowed, either the error has been printed already From 43327b5da6bc4ccbddebb935b1bcf362ccb8f993 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Rakic?= Date: Fri, 14 Feb 2025 11:06:06 +0000 Subject: [PATCH 059/195] switch polonius compare-mode to polonius=next --- src/tools/compiletest/src/runtest.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index f283a625f977..debf67e2741d 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -1766,7 +1766,7 @@ impl<'test> TestCx<'test> { match self.config.compare_mode { Some(CompareMode::Polonius) => { - rustc.args(&["-Zpolonius"]); + rustc.args(&["-Zpolonius=next"]); } Some(CompareMode::NextSolver) => { rustc.args(&["-Znext-solver"]); From f4094ea25236bc4ba3d9b7b03d2674ba7ab92907 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Rakic?= Date: Mon, 24 Mar 2025 07:09:18 +0000 Subject: [PATCH 060/195] update test expectations for boring locals + dropckoutlives interactions The suboptimal error only appears with NLLs due to liveness differences where polonius cannot have as many boring locals. Sometimes this causes NLLs to emit a duplicate error as well. --- ...err => dropck-normalize-errors.nll.stderr} | 24 +++---- .../dropck-normalize-errors.polonius.stderr | 64 +++++++++++++++++++ tests/ui/drop/dropck-normalize-errors.rs | 6 +- ... => hir-wf-check-erase-regions.nll.stderr} | 8 +-- ...hir-wf-check-erase-regions.polonius.stderr | 39 +++++++++++ tests/ui/wf/hir-wf-check-erase-regions.rs | 6 +- 6 files changed, 129 insertions(+), 18 deletions(-) rename tests/ui/drop/{dropck-normalize-errors.stderr => dropck-normalize-errors.nll.stderr} (82%) create mode 100644 tests/ui/drop/dropck-normalize-errors.polonius.stderr rename tests/ui/wf/{hir-wf-check-erase-regions.stderr => hir-wf-check-erase-regions.nll.stderr} (91%) create mode 100644 tests/ui/wf/hir-wf-check-erase-regions.polonius.stderr diff --git a/tests/ui/drop/dropck-normalize-errors.stderr b/tests/ui/drop/dropck-normalize-errors.nll.stderr similarity index 82% rename from tests/ui/drop/dropck-normalize-errors.stderr rename to tests/ui/drop/dropck-normalize-errors.nll.stderr index 2bb5909c6b22..b008daa51a33 100644 --- a/tests/ui/drop/dropck-normalize-errors.stderr +++ b/tests/ui/drop/dropck-normalize-errors.nll.stderr @@ -1,44 +1,44 @@ error[E0277]: the trait bound `NonImplementedStruct: NonImplementedTrait` is not satisfied in `ADecoder<'a>` - --> $DIR/dropck-normalize-errors.rs:15:28 + --> $DIR/dropck-normalize-errors.rs:19:28 | LL | fn make_a_decoder<'a>() -> ADecoder<'a> { | ^^^^^^^^^^^^ within `ADecoder<'a>`, the trait `NonImplementedTrait` is not implemented for `NonImplementedStruct` | help: this trait has no implementations, consider adding one - --> $DIR/dropck-normalize-errors.rs:7:1 + --> $DIR/dropck-normalize-errors.rs:11:1 | LL | trait NonImplementedTrait { | ^^^^^^^^^^^^^^^^^^^^^^^^^ note: required because it appears within the type `BDecoder` - --> $DIR/dropck-normalize-errors.rs:26:12 + --> $DIR/dropck-normalize-errors.rs:30:12 | LL | pub struct BDecoder { | ^^^^^^^^ note: required because it appears within the type `ADecoder<'a>` - --> $DIR/dropck-normalize-errors.rs:12:12 + --> $DIR/dropck-normalize-errors.rs:16:12 | LL | pub struct ADecoder<'a> { | ^^^^^^^^ = note: the return type of a function must have a statically known size error[E0277]: the trait bound `NonImplementedStruct: NonImplementedTrait` is not satisfied in `BDecoder` - --> $DIR/dropck-normalize-errors.rs:23:20 + --> $DIR/dropck-normalize-errors.rs:27:20 | LL | type Decoder = BDecoder; | ^^^^^^^^ within `BDecoder`, the trait `NonImplementedTrait` is not implemented for `NonImplementedStruct` | help: this trait has no implementations, consider adding one - --> $DIR/dropck-normalize-errors.rs:7:1 + --> $DIR/dropck-normalize-errors.rs:11:1 | LL | trait NonImplementedTrait { | ^^^^^^^^^^^^^^^^^^^^^^^^^ note: required because it appears within the type `BDecoder` - --> $DIR/dropck-normalize-errors.rs:26:12 + --> $DIR/dropck-normalize-errors.rs:30:12 | LL | pub struct BDecoder { | ^^^^^^^^ note: required by a bound in `Decode::Decoder` - --> $DIR/dropck-normalize-errors.rs:4:5 + --> $DIR/dropck-normalize-errors.rs:8:5 | LL | type Decoder; | ^^^^^^^^^^^^^ required by this bound in `Decode::Decoder` @@ -48,25 +48,25 @@ LL | type Decoder: ?Sized; | ++++++++ error[E0277]: the trait bound `NonImplementedStruct: NonImplementedTrait` is not satisfied - --> $DIR/dropck-normalize-errors.rs:27:22 + --> $DIR/dropck-normalize-errors.rs:31:22 | LL | non_implemented: ::Assoc, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `NonImplementedTrait` is not implemented for `NonImplementedStruct` | help: this trait has no implementations, consider adding one - --> $DIR/dropck-normalize-errors.rs:7:1 + --> $DIR/dropck-normalize-errors.rs:11:1 | LL | trait NonImplementedTrait { | ^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0277]: the trait bound `NonImplementedStruct: NonImplementedTrait` is not satisfied - --> $DIR/dropck-normalize-errors.rs:15:28 + --> $DIR/dropck-normalize-errors.rs:19:28 | LL | fn make_a_decoder<'a>() -> ADecoder<'a> { | ^^^^^^^^^^^^ the trait `NonImplementedTrait` is not implemented for `NonImplementedStruct` | help: this trait has no implementations, consider adding one - --> $DIR/dropck-normalize-errors.rs:7:1 + --> $DIR/dropck-normalize-errors.rs:11:1 | LL | trait NonImplementedTrait { | ^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/drop/dropck-normalize-errors.polonius.stderr b/tests/ui/drop/dropck-normalize-errors.polonius.stderr new file mode 100644 index 000000000000..f8674b8e34a4 --- /dev/null +++ b/tests/ui/drop/dropck-normalize-errors.polonius.stderr @@ -0,0 +1,64 @@ +error[E0277]: the trait bound `NonImplementedStruct: NonImplementedTrait` is not satisfied in `ADecoder<'a>` + --> $DIR/dropck-normalize-errors.rs:19:28 + | +LL | fn make_a_decoder<'a>() -> ADecoder<'a> { + | ^^^^^^^^^^^^ within `ADecoder<'a>`, the trait `NonImplementedTrait` is not implemented for `NonImplementedStruct` + | +help: this trait has no implementations, consider adding one + --> $DIR/dropck-normalize-errors.rs:11:1 + | +LL | trait NonImplementedTrait { + | ^^^^^^^^^^^^^^^^^^^^^^^^^ +note: required because it appears within the type `BDecoder` + --> $DIR/dropck-normalize-errors.rs:30:12 + | +LL | pub struct BDecoder { + | ^^^^^^^^ +note: required because it appears within the type `ADecoder<'a>` + --> $DIR/dropck-normalize-errors.rs:16:12 + | +LL | pub struct ADecoder<'a> { + | ^^^^^^^^ + = note: the return type of a function must have a statically known size + +error[E0277]: the trait bound `NonImplementedStruct: NonImplementedTrait` is not satisfied in `BDecoder` + --> $DIR/dropck-normalize-errors.rs:27:20 + | +LL | type Decoder = BDecoder; + | ^^^^^^^^ within `BDecoder`, the trait `NonImplementedTrait` is not implemented for `NonImplementedStruct` + | +help: this trait has no implementations, consider adding one + --> $DIR/dropck-normalize-errors.rs:11:1 + | +LL | trait NonImplementedTrait { + | ^^^^^^^^^^^^^^^^^^^^^^^^^ +note: required because it appears within the type `BDecoder` + --> $DIR/dropck-normalize-errors.rs:30:12 + | +LL | pub struct BDecoder { + | ^^^^^^^^ +note: required by a bound in `Decode::Decoder` + --> $DIR/dropck-normalize-errors.rs:8:5 + | +LL | type Decoder; + | ^^^^^^^^^^^^^ required by this bound in `Decode::Decoder` +help: consider relaxing the implicit `Sized` restriction + | +LL | type Decoder: ?Sized; + | ++++++++ + +error[E0277]: the trait bound `NonImplementedStruct: NonImplementedTrait` is not satisfied + --> $DIR/dropck-normalize-errors.rs:31:22 + | +LL | non_implemented: ::Assoc, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `NonImplementedTrait` is not implemented for `NonImplementedStruct` + | +help: this trait has no implementations, consider adding one + --> $DIR/dropck-normalize-errors.rs:11:1 + | +LL | trait NonImplementedTrait { + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/drop/dropck-normalize-errors.rs b/tests/ui/drop/dropck-normalize-errors.rs index 793122bd33d4..2ade63f27c5f 100644 --- a/tests/ui/drop/dropck-normalize-errors.rs +++ b/tests/ui/drop/dropck-normalize-errors.rs @@ -1,5 +1,9 @@ // Test that we don't ICE when computing the drop types for +//@ ignore-compare-mode-polonius (explicit revisions) +//@ revisions: nll polonius +//@ [polonius] compile-flags: -Zpolonius=next + trait Decode<'a> { type Decoder; } @@ -14,7 +18,7 @@ pub struct ADecoder<'a> { } fn make_a_decoder<'a>() -> ADecoder<'a> { //~^ ERROR the trait bound - //~| ERROR the trait bound + //[nll]~| ERROR the trait bound panic!() } diff --git a/tests/ui/wf/hir-wf-check-erase-regions.stderr b/tests/ui/wf/hir-wf-check-erase-regions.nll.stderr similarity index 91% rename from tests/ui/wf/hir-wf-check-erase-regions.stderr rename to tests/ui/wf/hir-wf-check-erase-regions.nll.stderr index 07304cd448eb..dcade3aa3679 100644 --- a/tests/ui/wf/hir-wf-check-erase-regions.stderr +++ b/tests/ui/wf/hir-wf-check-erase-regions.nll.stderr @@ -1,5 +1,5 @@ error[E0277]: `&'a T` is not an iterator - --> $DIR/hir-wf-check-erase-regions.rs:7:21 + --> $DIR/hir-wf-check-erase-regions.rs:11:21 | LL | type IntoIter = std::iter::Flatten>; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `&'a T` is not an iterator @@ -11,7 +11,7 @@ note: required by a bound in `std::iter::IntoIterator::IntoIter` --> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL error[E0277]: `&'a T` is not an iterator - --> $DIR/hir-wf-check-erase-regions.rs:7:5 + --> $DIR/hir-wf-check-erase-regions.rs:11:5 | LL | type IntoIter = std::iter::Flatten>; | ^^^^^^^^^^^^^ `&'a T` is not an iterator @@ -23,7 +23,7 @@ note: required by a bound in `Flatten` --> $SRC_DIR/core/src/iter/adapters/flatten.rs:LL:COL error[E0277]: `&'a T` is not an iterator - --> $DIR/hir-wf-check-erase-regions.rs:11:27 + --> $DIR/hir-wf-check-erase-regions.rs:15:27 | LL | fn into_iter(self) -> Self::IntoIter { | ^^^^^^^^^^^^^^ `&'a T` is not an iterator @@ -35,7 +35,7 @@ note: required by a bound in `Flatten` --> $SRC_DIR/core/src/iter/adapters/flatten.rs:LL:COL error[E0277]: `&T` is not an iterator - --> $DIR/hir-wf-check-erase-regions.rs:11:27 + --> $DIR/hir-wf-check-erase-regions.rs:15:27 | LL | fn into_iter(self) -> Self::IntoIter { | ^^^^^^^^^^^^^^ `&T` is not an iterator diff --git a/tests/ui/wf/hir-wf-check-erase-regions.polonius.stderr b/tests/ui/wf/hir-wf-check-erase-regions.polonius.stderr new file mode 100644 index 000000000000..55728aa642b8 --- /dev/null +++ b/tests/ui/wf/hir-wf-check-erase-regions.polonius.stderr @@ -0,0 +1,39 @@ +error[E0277]: `&'a T` is not an iterator + --> $DIR/hir-wf-check-erase-regions.rs:11:21 + | +LL | type IntoIter = std::iter::Flatten>; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `&'a T` is not an iterator + | + = help: the trait `Iterator` is not implemented for `&'a T` + = help: the trait `Iterator` is implemented for `&mut I` + = note: required for `Flatten>` to implement `Iterator` +note: required by a bound in `std::iter::IntoIterator::IntoIter` + --> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL + +error[E0277]: `&'a T` is not an iterator + --> $DIR/hir-wf-check-erase-regions.rs:11:5 + | +LL | type IntoIter = std::iter::Flatten>; + | ^^^^^^^^^^^^^ `&'a T` is not an iterator + | + = help: the trait `Iterator` is not implemented for `&'a T` + = help: the trait `Iterator` is implemented for `&mut I` + = note: required for `&'a T` to implement `IntoIterator` +note: required by a bound in `Flatten` + --> $SRC_DIR/core/src/iter/adapters/flatten.rs:LL:COL + +error[E0277]: `&'a T` is not an iterator + --> $DIR/hir-wf-check-erase-regions.rs:15:27 + | +LL | fn into_iter(self) -> Self::IntoIter { + | ^^^^^^^^^^^^^^ `&'a T` is not an iterator + | + = help: the trait `Iterator` is not implemented for `&'a T` + = help: the trait `Iterator` is implemented for `&mut I` + = note: required for `&'a T` to implement `IntoIterator` +note: required by a bound in `Flatten` + --> $SRC_DIR/core/src/iter/adapters/flatten.rs:LL:COL + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/wf/hir-wf-check-erase-regions.rs b/tests/ui/wf/hir-wf-check-erase-regions.rs index 20cc1cfe7301..ef9132697efc 100644 --- a/tests/ui/wf/hir-wf-check-erase-regions.rs +++ b/tests/ui/wf/hir-wf-check-erase-regions.rs @@ -1,6 +1,10 @@ // Regression test for #87549. //@ incremental +//@ ignore-compare-mode-polonius (explicit revisions) +//@ revisions: nll polonius +//@ [polonius] compile-flags: -Zpolonius=next + pub struct Table([Option; N]); impl<'a, T, const N: usize> IntoIterator for &'a Table { @@ -10,7 +14,7 @@ impl<'a, T, const N: usize> IntoIterator for &'a Table { fn into_iter(self) -> Self::IntoIter { //~^ ERROR `&'a T` is not an iterator - //~| ERROR `&T` is not an iterator + //[nll]~| ERROR `&T` is not an iterator unimplemented!() } } From c3e504d399a51781f328d4195c79ce9b7d44388d Mon Sep 17 00:00:00 2001 From: Spxg Date: Fri, 8 Aug 2025 21:27:46 +0800 Subject: [PATCH 061/195] Fix wasm target build with atomics feature --- library/std/src/sys/pal/wasm/atomics/thread.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/library/std/src/sys/pal/wasm/atomics/thread.rs b/library/std/src/sys/pal/wasm/atomics/thread.rs index ebfabaafc794..42a7dbdf8b8b 100644 --- a/library/std/src/sys/pal/wasm/atomics/thread.rs +++ b/library/std/src/sys/pal/wasm/atomics/thread.rs @@ -56,6 +56,10 @@ impl Thread { pub fn join(self) {} } +pub(crate) fn current_os_id() -> Option { + None +} + pub fn available_parallelism() -> io::Result> { unsupported() } From a5adde8eaae4ad5d9510897b02117bf0a9f45d0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Rakic?= Date: Fri, 8 Aug 2025 15:14:17 +0000 Subject: [PATCH 062/195] simplify polonius=next Remove incomplete handling of kills during traversal for loan liveness to get to a simpler and actionable prototype. This handles the cases, on sufficiently simple examples, that were deferred from NLLs (NLL problem case 3, lending iterators), and is still a good step to put in people's hands without needing to wait for another full implementation. This is a practical cut in scope, but it also shows where are the areas of improvement, that we will explore in the future. --- .../src/polonius/constraints.rs | 4 +- .../src/polonius/liveness_constraints.rs | 12 +- .../src/polonius/loan_liveness.rs | 191 ++---------------- compiler/rustc_borrowck/src/polonius/mod.rs | 6 +- .../src/polonius/typeck_constraints.rs | 14 +- 5 files changed, 28 insertions(+), 199 deletions(-) diff --git a/compiler/rustc_borrowck/src/polonius/constraints.rs b/compiler/rustc_borrowck/src/polonius/constraints.rs index 50f59dd0dee6..525957578595 100644 --- a/compiler/rustc_borrowck/src/polonius/constraints.rs +++ b/compiler/rustc_borrowck/src/polonius/constraints.rs @@ -7,9 +7,7 @@ use rustc_mir_dataflow::points::PointIndex; /// /// This models two sources of constraints: /// - constraints that traverse the subsets between regions at a given point, `a@p: b@p`. These -/// depend on typeck constraints generated via assignments, calls, etc. (In practice there are -/// subtleties where a statement's effect only starts being visible at the successor point, via -/// the "result" of that statement). +/// depend on typeck constraints generated via assignments, calls, etc. /// - constraints that traverse the CFG via the same region, `a@p: a@q`, where `p` is a predecessor /// of `q`. These depend on the liveness of the regions at these points, as well as their /// variance. diff --git a/compiler/rustc_borrowck/src/polonius/liveness_constraints.rs b/compiler/rustc_borrowck/src/polonius/liveness_constraints.rs index 6ab09f731c07..2ba72180d66a 100644 --- a/compiler/rustc_borrowck/src/polonius/liveness_constraints.rs +++ b/compiler/rustc_borrowck/src/polonius/liveness_constraints.rs @@ -105,22 +105,14 @@ fn propagate_loans_between_points( }); } - let Some(current_live_regions) = live_regions.row(current_point) else { - // There are no constraints to add: there are no live regions at the current point. - return; - }; let Some(next_live_regions) = live_regions.row(next_point) else { // There are no constraints to add: there are no live regions at the next point. return; }; for region in next_live_regions.iter() { - if !current_live_regions.contains(region) { - continue; - } - - // `region` is indeed live at both points, add a constraint between them, according to - // variance. + // `region` could be live at the current point, and is live at the next point: add a + // constraint between them, according to variance. if let Some(&direction) = live_region_variances.get(®ion) { add_liveness_constraint( region, diff --git a/compiler/rustc_borrowck/src/polonius/loan_liveness.rs b/compiler/rustc_borrowck/src/polonius/loan_liveness.rs index 5cd265e0db92..bdc3047e5ba0 100644 --- a/compiler/rustc_borrowck/src/polonius/loan_liveness.rs +++ b/compiler/rustc_borrowck/src/polonius/loan_liveness.rs @@ -1,27 +1,18 @@ -use std::collections::{BTreeMap, BTreeSet}; - use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet}; -use rustc_middle::mir::visit::Visitor; -use rustc_middle::mir::{ - Body, Local, Location, Place, Rvalue, Statement, StatementKind, Terminator, TerminatorKind, -}; -use rustc_middle::ty::{RegionVid, TyCtxt}; +use rustc_middle::ty::RegionVid; use rustc_mir_dataflow::points::PointIndex; use super::{LiveLoans, LocalizedOutlivesConstraintSet}; +use crate::BorrowSet; use crate::constraints::OutlivesConstraint; -use crate::dataflow::BorrowIndex; use crate::region_infer::values::LivenessValues; use crate::type_check::Locations; -use crate::{BorrowSet, PlaceConflictBias, places_conflict}; -/// Compute loan reachability, stop at kills, and trace loan liveness throughout the CFG, by +/// Compute loan reachability to approximately trace loan liveness throughout the CFG, by /// traversing the full graph of constraints that combines: /// - the localized constraints (the physical edges), /// - with the constraints that hold at all points (the logical edges). pub(super) fn compute_loan_liveness<'tcx>( - tcx: TyCtxt<'tcx>, - body: &Body<'tcx>, liveness: &LivenessValues, outlives_constraints: impl Iterator>, borrow_set: &BorrowSet<'tcx>, @@ -29,11 +20,6 @@ pub(super) fn compute_loan_liveness<'tcx>( ) -> LiveLoans { let mut live_loans = LiveLoans::new(borrow_set.len()); - // FIXME: it may be preferable for kills to be encoded in the edges themselves, to simplify and - // likely make traversal (and constraint generation) more efficient. We also display kills on - // edges when visualizing the constraint graph anyways. - let kills = collect_kills(body, tcx, borrow_set); - // Create the full graph with the physical edges we've localized earlier, and the logical edges // of constraints that hold at all points. let logical_constraints = @@ -59,15 +45,15 @@ pub(super) fn compute_loan_liveness<'tcx>( continue; } - // Record the loan as being live on entry to this point. - live_loans.insert(node.point, loan_idx); - - // Here, we have a conundrum. There's currently a weakness in our theory, in that - // we're using a single notion of reachability to represent what used to be _two_ - // different transitive closures. It didn't seem impactful when coming up with the - // single-graph and reachability through space (regions) + time (CFG) concepts, but in - // practice the combination of time-traveling with kills is more impactful than - // initially anticipated. + // Record the loan as being live on entry to this point if it reaches a live region + // there. + // + // This is an approximation of liveness (which is the thing we want), in that we're + // using a single notion of reachability to represent what used to be _two_ different + // transitive closures. It didn't seem impactful when coming up with the single-graph + // and reachability through space (regions) + time (CFG) concepts, but in practice the + // combination of time-traveling with kills is more impactful than initially + // anticipated. // // Kills should prevent a loan from reaching its successor points in the CFG, but not // while time-traveling: we're not actually at that CFG point, but looking for @@ -92,40 +78,20 @@ pub(super) fn compute_loan_liveness<'tcx>( // two-step traversal described above: only kills encountered on exit via a backward // edge are ignored. // - // In our test suite, there are a couple of cases where kills are encountered while - // time-traveling, however as far as we can tell, always in cases where they would be - // unreachable. We have reason to believe that this is a property of the single-graph - // approach (but haven't proved it yet): - // - reachable kills while time-traveling would also be encountered via regular - // traversal - // - it makes _some_ sense to ignore unreachable kills, but subtleties around dead code - // in general need to be better thought through (like they were for NLLs). - // - ignoring kills is a conservative approximation: the loan is still live and could - // cause false positive errors at another place access. Soundness issues in this - // domain should look more like the absence of reachability instead. + // This version of the analysis, however, is enough in practice to pass the tests that + // we care about and NLLs reject, without regressions on crater, and is an actionable + // subset of the full analysis. It also naturally points to areas of improvement that we + // wish to explore later, namely handling kills appropriately during traversal, instead + // of continuing traversal to all the reachable nodes. // - // This is enough in practice to pass tests, and therefore is what we have implemented - // for now. - // - // FIXME: all of the above. Analyze potential unsoundness, possibly in concert with a - // borrowck implementation in a-mir-formality, fuzzing, or manually crafting - // counter-examples. + // FIXME: analyze potential unsoundness, possibly in concert with a borrowck + // implementation in a-mir-formality, fuzzing, or manually crafting counter-examples. - // Continuing traversal will depend on whether the loan is killed at this point, and - // whether we're time-traveling. - let current_location = liveness.location_from_point(node.point); - let is_loan_killed = - kills.get(¤t_location).is_some_and(|kills| kills.contains(&loan_idx)); + if liveness.is_live_at(node.region, liveness.location_from_point(node.point)) { + live_loans.insert(node.point, loan_idx); + } for succ in graph.outgoing_edges(node) { - // If the loan is killed at this point, it is killed _on exit_. But only during - // forward traversal. - if is_loan_killed { - let destination = liveness.location_from_point(succ.point); - if current_location.is_predecessor_of(destination, body) { - continue; - } - } stack.push(succ); } } @@ -192,116 +158,3 @@ impl LocalizedConstraintGraph { physical_edges.chain(materialized_edges) } } - -/// Traverses the MIR and collects kills. -fn collect_kills<'tcx>( - body: &Body<'tcx>, - tcx: TyCtxt<'tcx>, - borrow_set: &BorrowSet<'tcx>, -) -> BTreeMap> { - let mut collector = KillsCollector { borrow_set, tcx, body, kills: BTreeMap::default() }; - for (block, data) in body.basic_blocks.iter_enumerated() { - collector.visit_basic_block_data(block, data); - } - collector.kills -} - -struct KillsCollector<'a, 'tcx> { - body: &'a Body<'tcx>, - tcx: TyCtxt<'tcx>, - borrow_set: &'a BorrowSet<'tcx>, - - /// The set of loans killed at each location. - kills: BTreeMap>, -} - -// This visitor has a similar structure to the `Borrows` dataflow computation with respect to kills, -// and the datalog polonius fact generation for the `loan_killed_at` relation. -impl<'tcx> KillsCollector<'_, 'tcx> { - /// Records the borrows on the specified place as `killed`. For example, when assigning to a - /// local, or on a call's return destination. - fn record_killed_borrows_for_place(&mut self, place: Place<'tcx>, location: Location) { - // For the reasons described in graph traversal, we also filter out kills - // unreachable from the loan's introduction point, as they would stop traversal when - // e.g. checking for reachability in the subset graph through invariance constraints - // higher up. - let filter_unreachable_kills = |loan| { - let introduction = self.borrow_set[loan].reserve_location; - let reachable = introduction.is_predecessor_of(location, self.body); - reachable - }; - - let other_borrows_of_local = self - .borrow_set - .local_map - .get(&place.local) - .into_iter() - .flat_map(|bs| bs.iter()) - .copied(); - - // If the borrowed place is a local with no projections, all other borrows of this - // local must conflict. This is purely an optimization so we don't have to call - // `places_conflict` for every borrow. - if place.projection.is_empty() { - if !self.body.local_decls[place.local].is_ref_to_static() { - self.kills - .entry(location) - .or_default() - .extend(other_borrows_of_local.filter(|&loan| filter_unreachable_kills(loan))); - } - return; - } - - // By passing `PlaceConflictBias::NoOverlap`, we conservatively assume that any given - // pair of array indices are not equal, so that when `places_conflict` returns true, we - // will be assured that two places being compared definitely denotes the same sets of - // locations. - let definitely_conflicting_borrows = other_borrows_of_local - .filter(|&i| { - places_conflict( - self.tcx, - self.body, - self.borrow_set[i].borrowed_place, - place, - PlaceConflictBias::NoOverlap, - ) - }) - .filter(|&loan| filter_unreachable_kills(loan)); - - self.kills.entry(location).or_default().extend(definitely_conflicting_borrows); - } - - /// Records the borrows on the specified local as `killed`. - fn record_killed_borrows_for_local(&mut self, local: Local, location: Location) { - if let Some(borrow_indices) = self.borrow_set.local_map.get(&local) { - self.kills.entry(location).or_default().extend(borrow_indices.iter()); - } - } -} - -impl<'tcx> Visitor<'tcx> for KillsCollector<'_, 'tcx> { - fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) { - // Make sure there are no remaining borrows for locals that have gone out of scope. - if let StatementKind::StorageDead(local) = statement.kind { - self.record_killed_borrows_for_local(local, location); - } - - self.super_statement(statement, location); - } - - fn visit_assign(&mut self, place: &Place<'tcx>, rvalue: &Rvalue<'tcx>, location: Location) { - // When we see `X = ...`, then kill borrows of `(*X).foo` and so forth. - self.record_killed_borrows_for_place(*place, location); - self.super_assign(place, rvalue, location); - } - - fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) { - // A `Call` terminator's return value can be a local which has borrows, so we need to record - // those as killed as well. - if let TerminatorKind::Call { destination, .. } = terminator.kind { - self.record_killed_borrows_for_place(destination, location); - } - - self.super_terminator(terminator, location); - } -} diff --git a/compiler/rustc_borrowck/src/polonius/mod.rs b/compiler/rustc_borrowck/src/polonius/mod.rs index 142ef8ba28ef..a9092b1981e1 100644 --- a/compiler/rustc_borrowck/src/polonius/mod.rs +++ b/compiler/rustc_borrowck/src/polonius/mod.rs @@ -146,8 +146,8 @@ impl PoloniusContext { /// - converting NLL typeck constraints to be localized /// - encoding liveness constraints /// - /// Then, this graph is traversed, and combined with kills, reachability is recorded as loan - /// liveness, to be used by the loan scope and active loans computations. + /// Then, this graph is traversed, reachability is recorded as loan liveness, to be used by the + /// loan scope and active loans computations. /// /// The constraint data will be used to compute errors and diagnostics. pub(crate) fn compute_loan_liveness<'tcx>( @@ -182,8 +182,6 @@ impl PoloniusContext { // Now that we have a complete graph, we can compute reachability to trace the liveness of // loans for the next step in the chain, the NLL loan scope and active loans computations. let live_loans = compute_loan_liveness( - tcx, - body, regioncx.liveness_constraints(), regioncx.outlives_constraints(), borrow_set, diff --git a/compiler/rustc_borrowck/src/polonius/typeck_constraints.rs b/compiler/rustc_borrowck/src/polonius/typeck_constraints.rs index 1289b1899eb3..8106d1e4af06 100644 --- a/compiler/rustc_borrowck/src/polonius/typeck_constraints.rs +++ b/compiler/rustc_borrowck/src/polonius/typeck_constraints.rs @@ -47,9 +47,7 @@ pub(super) fn convert_typeck_constraints<'tcx>( tcx, body, stmt, - liveness, &outlives_constraint, - location, point, universal_regions, ) @@ -78,9 +76,7 @@ fn localize_statement_constraint<'tcx>( tcx: TyCtxt<'tcx>, body: &Body<'tcx>, stmt: &Statement<'tcx>, - liveness: &LivenessValues, outlives_constraint: &OutlivesConstraint<'tcx>, - current_location: Location, current_point: PointIndex, universal_regions: &UniversalRegions<'tcx>, ) -> LocalizedOutlivesConstraint { @@ -119,16 +115,8 @@ fn localize_statement_constraint<'tcx>( "there should be no common regions between the LHS and RHS of an assignment" ); - // As mentioned earlier, we should be tracking these better upstream but: we want to - // relate the types on entry to the type of the place on exit. That is, outlives - // constraints on the RHS are on entry, and outlives constraints to/from the LHS are on - // exit (i.e. on entry to the successor location). let lhs_ty = body.local_decls[lhs.local].ty; - let successor_location = Location { - block: current_location.block, - statement_index: current_location.statement_index + 1, - }; - let successor_point = liveness.point_from_location(successor_location); + let successor_point = current_point; compute_constraint_direction( tcx, outlives_constraint, From 13308988b051ebe014a061b9d595cbb8289348d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Rakic?= Date: Mon, 16 Jun 2025 14:39:32 +0000 Subject: [PATCH 063/195] new impl fixes crash test --- tests/crashes/135646.rs | 7 ------- .../ui/nll/polonius/array-literal-index-oob-2024.rs | 12 ++++++++++++ 2 files changed, 12 insertions(+), 7 deletions(-) delete mode 100644 tests/crashes/135646.rs create mode 100644 tests/ui/nll/polonius/array-literal-index-oob-2024.rs diff --git a/tests/crashes/135646.rs b/tests/crashes/135646.rs deleted file mode 100644 index 841ea5b81b41..000000000000 --- a/tests/crashes/135646.rs +++ /dev/null @@ -1,7 +0,0 @@ -//@ known-bug: #135646 -//@ compile-flags: -Zpolonius=next -//@ edition: 2024 - -fn main() { - &{ [1, 2, 3][4] }; -} diff --git a/tests/ui/nll/polonius/array-literal-index-oob-2024.rs b/tests/ui/nll/polonius/array-literal-index-oob-2024.rs new file mode 100644 index 000000000000..2054a32e5355 --- /dev/null +++ b/tests/ui/nll/polonius/array-literal-index-oob-2024.rs @@ -0,0 +1,12 @@ +// This test used to ICE under `-Zpolonius=next` when computing loan liveness +// and taking kills into account during reachability traversal of the localized +// constraint graph. Originally from another test but on edition 2024, as +// seen in issue #135646. + +//@ compile-flags: -Zpolonius=next +//@ edition: 2024 +//@ check-pass + +fn main() { + &{ [1, 2, 3][4] }; +} From d4bbd681bbdb2b47717922e46bd022dbf2535403 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Rakic?= Date: Wed, 18 Jun 2025 09:01:57 +0000 Subject: [PATCH 064/195] turn expensive assert into debug assertion --- compiler/rustc_borrowck/src/polonius/typeck_constraints.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_borrowck/src/polonius/typeck_constraints.rs b/compiler/rustc_borrowck/src/polonius/typeck_constraints.rs index 8106d1e4af06..e4e52962bf7f 100644 --- a/compiler/rustc_borrowck/src/polonius/typeck_constraints.rs +++ b/compiler/rustc_borrowck/src/polonius/typeck_constraints.rs @@ -94,8 +94,8 @@ fn localize_statement_constraint<'tcx>( // - and that should be impossible in MIR // // When we have a more complete implementation in the future, tested with crater, etc, - // we can relax this to a debug assert instead, or remove it. - assert!( + // we can remove this assertion. It's a debug assert because it can be expensive. + debug_assert!( { let mut lhs_regions = FxHashSet::default(); tcx.for_each_free_region(lhs, |region| { @@ -183,6 +183,7 @@ fn localize_terminator_constraint<'tcx>( } } } + /// For a given outlives constraint and CFG edge, returns the localized constraint with the /// appropriate `from`-`to` direction. This is computed according to whether the constraint flows to /// or from a free region in the given `value`, some kind of result for an effectful operation, like From b172980d5c0de4a917e0f28ebd118f20889e6036 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Rakic?= Date: Thu, 26 Jun 2025 13:06:48 +0000 Subject: [PATCH 065/195] add some test cases for overlapping yielded items These are just some sanity checks to ensure NLLs, the polonius alpha analysis, and the datalog implementation behave the same on these common examples. --- ...nding-iterator-sanity-checks.legacy.stderr | 28 ++++++++ .../lending-iterator-sanity-checks.nll.stderr | 28 ++++++++ ...ing-iterator-sanity-checks.polonius.stderr | 26 +++++++ .../lending-iterator-sanity-checks.rs | 71 +++++++++++++++++++ 4 files changed, 153 insertions(+) create mode 100644 tests/ui/nll/polonius/lending-iterator-sanity-checks.legacy.stderr create mode 100644 tests/ui/nll/polonius/lending-iterator-sanity-checks.nll.stderr create mode 100644 tests/ui/nll/polonius/lending-iterator-sanity-checks.polonius.stderr create mode 100644 tests/ui/nll/polonius/lending-iterator-sanity-checks.rs diff --git a/tests/ui/nll/polonius/lending-iterator-sanity-checks.legacy.stderr b/tests/ui/nll/polonius/lending-iterator-sanity-checks.legacy.stderr new file mode 100644 index 000000000000..fa201b89048a --- /dev/null +++ b/tests/ui/nll/polonius/lending-iterator-sanity-checks.legacy.stderr @@ -0,0 +1,28 @@ +error[E0499]: cannot borrow `*t` as mutable more than once at a time + --> $DIR/lending-iterator-sanity-checks.rs:19:19 + | +LL | fn use_live(t: &mut T) -> Option<(T::Item<'_>, T::Item<'_>)> { + | - let's call the lifetime of this reference `'1` +LL | let Some(i) = t.next() else { return None }; + | - first mutable borrow occurs here +LL | let Some(j) = t.next() else { return None }; + | ^ second mutable borrow occurs here +... +LL | Some((i, j)) + | ------------ returning this value requires that `*t` is borrowed for `'1` + +error[E0499]: cannot borrow `*t` as mutable more than once at a time + --> $DIR/lending-iterator-sanity-checks.rs:31:13 + | +LL | let i = t.next(); + | - first mutable borrow occurs here +... +LL | let j = t.next(); + | ^ second mutable borrow occurs here +LL | +LL | } + | - first borrow might be used here, when `i` is dropped and runs the destructor for type `Option<::Item<'_>>` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0499`. diff --git a/tests/ui/nll/polonius/lending-iterator-sanity-checks.nll.stderr b/tests/ui/nll/polonius/lending-iterator-sanity-checks.nll.stderr new file mode 100644 index 000000000000..fa201b89048a --- /dev/null +++ b/tests/ui/nll/polonius/lending-iterator-sanity-checks.nll.stderr @@ -0,0 +1,28 @@ +error[E0499]: cannot borrow `*t` as mutable more than once at a time + --> $DIR/lending-iterator-sanity-checks.rs:19:19 + | +LL | fn use_live(t: &mut T) -> Option<(T::Item<'_>, T::Item<'_>)> { + | - let's call the lifetime of this reference `'1` +LL | let Some(i) = t.next() else { return None }; + | - first mutable borrow occurs here +LL | let Some(j) = t.next() else { return None }; + | ^ second mutable borrow occurs here +... +LL | Some((i, j)) + | ------------ returning this value requires that `*t` is borrowed for `'1` + +error[E0499]: cannot borrow `*t` as mutable more than once at a time + --> $DIR/lending-iterator-sanity-checks.rs:31:13 + | +LL | let i = t.next(); + | - first mutable borrow occurs here +... +LL | let j = t.next(); + | ^ second mutable borrow occurs here +LL | +LL | } + | - first borrow might be used here, when `i` is dropped and runs the destructor for type `Option<::Item<'_>>` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0499`. diff --git a/tests/ui/nll/polonius/lending-iterator-sanity-checks.polonius.stderr b/tests/ui/nll/polonius/lending-iterator-sanity-checks.polonius.stderr new file mode 100644 index 000000000000..bf8546b63bf3 --- /dev/null +++ b/tests/ui/nll/polonius/lending-iterator-sanity-checks.polonius.stderr @@ -0,0 +1,26 @@ +error[E0499]: cannot borrow `*t` as mutable more than once at a time + --> $DIR/lending-iterator-sanity-checks.rs:19:19 + | +LL | let Some(i) = t.next() else { return None }; + | - first mutable borrow occurs here +LL | let Some(j) = t.next() else { return None }; + | ^ second mutable borrow occurs here +... +LL | } + | - first borrow might be used here, when `i` is dropped and runs the destructor for type `::Item<'_>` + +error[E0499]: cannot borrow `*t` as mutable more than once at a time + --> $DIR/lending-iterator-sanity-checks.rs:31:13 + | +LL | let i = t.next(); + | - first mutable borrow occurs here +... +LL | let j = t.next(); + | ^ second mutable borrow occurs here +LL | +LL | } + | - first borrow might be used here, when `i` is dropped and runs the destructor for type `Option<::Item<'_>>` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0499`. diff --git a/tests/ui/nll/polonius/lending-iterator-sanity-checks.rs b/tests/ui/nll/polonius/lending-iterator-sanity-checks.rs new file mode 100644 index 000000000000..ae8cc78957fe --- /dev/null +++ b/tests/ui/nll/polonius/lending-iterator-sanity-checks.rs @@ -0,0 +1,71 @@ +// Some sanity checks for lending iterators with GATs. This is just some non-regression tests +// ensuring the polonius alpha analysis, the datalog implementation, and NLLs agree in these common +// cases of overlapping yielded items. + +//@ ignore-compare-mode-polonius (explicit revisions) +//@ revisions: nll polonius legacy +//@ [polonius] compile-flags: -Z polonius=next +//@ [legacy] compile-flags: -Z polonius=legacy + +trait LendingIterator { + type Item<'a> + where + Self: 'a; + fn next(&mut self) -> Option>; +} + +fn use_live(t: &mut T) -> Option<(T::Item<'_>, T::Item<'_>)> { + let Some(i) = t.next() else { return None }; + let Some(j) = t.next() else { return None }; + //~^ ERROR cannot borrow `*t` as mutable more than once at a time + + // `i` is obviously still (use-)live here, but we called `next` again to get `j`. + Some((i, j)) +} + +fn drop_live(t: &mut T) { + let i = t.next(); + + // Now `i` is use-dead here, but we don't know if the iterator items have a `Drop` impl, so it's + // still drop-live. + let j = t.next(); + //~^ ERROR cannot borrow `*t` as mutable more than once at a time +} + +// But we can still manually serialize the lifetimes with scopes (or preventing the destructor from +// being called), so they're not overlapping. +fn manually_non_overlapping(t: &mut T) { + { + let i = t.next(); + } + + let j = t.next(); // i is dead + + drop(j); + let k = t.next(); // j is dead + + let k = std::mem::ManuallyDrop::new(k); + let l = t.next(); // we told the compiler that k is not drop-live +} + +// The cfg below is because there's a diagnostic ICE trying to explain the source of the error when +// using the datalog implementation. We're not fixing *that*, outside of removing the implementation +// in the future. +#[cfg(not(legacy))] // FIXME: remove this cfg when removing the datalog implementation +fn items_have_no_borrows(t: &mut T) +where + for<'a> T::Item<'a>: 'static, +{ + let i = t.next(); + let j = t.next(); +} + +fn items_are_copy(t: &mut T) +where + for<'a> T::Item<'a>: Copy, +{ + let i = t.next(); + let j = t.next(); +} + +fn main() {} From 48ebae9cef6fdb1af495e692a0811634f308e447 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Rakic?= Date: Thu, 26 Jun 2025 15:27:23 +0000 Subject: [PATCH 066/195] add NLL-like imprecision example This test showcases the same imprecision as NLLs, unlike the datalog implementation, when using reachability as a liveness approximation. --- .../flow-sensitive-invariance.nll.stderr | 36 +++++++++++++++++++ .../flow-sensitive-invariance.polonius.stderr | 36 +++++++++++++++++++ .../nll/polonius/flow-sensitive-invariance.rs | 34 ++++++++++++++++++ 3 files changed, 106 insertions(+) create mode 100644 tests/ui/nll/polonius/flow-sensitive-invariance.nll.stderr create mode 100644 tests/ui/nll/polonius/flow-sensitive-invariance.polonius.stderr create mode 100644 tests/ui/nll/polonius/flow-sensitive-invariance.rs diff --git a/tests/ui/nll/polonius/flow-sensitive-invariance.nll.stderr b/tests/ui/nll/polonius/flow-sensitive-invariance.nll.stderr new file mode 100644 index 000000000000..5756148f4eb2 --- /dev/null +++ b/tests/ui/nll/polonius/flow-sensitive-invariance.nll.stderr @@ -0,0 +1,36 @@ +error: lifetime may not live long enough + --> $DIR/flow-sensitive-invariance.rs:20:17 + | +LL | fn use_it<'a, 'b>(choice: bool) -> Result, Invariant<'b>> { + | -- -- lifetime `'b` defined here + | | + | lifetime `'a` defined here +LL | let returned_value = create_invariant(); +LL | if choice { Ok(returned_value) } else { Err(returned_value) } + | ^^^^^^^^^^^^^^^^^^ function was supposed to return data with lifetime `'a` but it is returning data with lifetime `'b` + | + = help: consider adding the following bound: `'b: 'a` + = note: requirement occurs because of the type `Invariant<'_>`, which makes the generic argument `'_` invariant + = note: the struct `Invariant<'l>` is invariant over the parameter `'l` + = help: see for more information about variance + +error: lifetime may not live long enough + --> $DIR/flow-sensitive-invariance.rs:20:45 + | +LL | fn use_it<'a, 'b>(choice: bool) -> Result, Invariant<'b>> { + | -- -- lifetime `'b` defined here + | | + | lifetime `'a` defined here +LL | let returned_value = create_invariant(); +LL | if choice { Ok(returned_value) } else { Err(returned_value) } + | ^^^^^^^^^^^^^^^^^^^ function was supposed to return data with lifetime `'b` but it is returning data with lifetime `'a` + | + = help: consider adding the following bound: `'a: 'b` + = note: requirement occurs because of the type `Invariant<'_>`, which makes the generic argument `'_` invariant + = note: the struct `Invariant<'l>` is invariant over the parameter `'l` + = help: see for more information about variance + +help: `'a` and `'b` must be the same: replace one with the other + +error: aborting due to 2 previous errors + diff --git a/tests/ui/nll/polonius/flow-sensitive-invariance.polonius.stderr b/tests/ui/nll/polonius/flow-sensitive-invariance.polonius.stderr new file mode 100644 index 000000000000..5756148f4eb2 --- /dev/null +++ b/tests/ui/nll/polonius/flow-sensitive-invariance.polonius.stderr @@ -0,0 +1,36 @@ +error: lifetime may not live long enough + --> $DIR/flow-sensitive-invariance.rs:20:17 + | +LL | fn use_it<'a, 'b>(choice: bool) -> Result, Invariant<'b>> { + | -- -- lifetime `'b` defined here + | | + | lifetime `'a` defined here +LL | let returned_value = create_invariant(); +LL | if choice { Ok(returned_value) } else { Err(returned_value) } + | ^^^^^^^^^^^^^^^^^^ function was supposed to return data with lifetime `'a` but it is returning data with lifetime `'b` + | + = help: consider adding the following bound: `'b: 'a` + = note: requirement occurs because of the type `Invariant<'_>`, which makes the generic argument `'_` invariant + = note: the struct `Invariant<'l>` is invariant over the parameter `'l` + = help: see for more information about variance + +error: lifetime may not live long enough + --> $DIR/flow-sensitive-invariance.rs:20:45 + | +LL | fn use_it<'a, 'b>(choice: bool) -> Result, Invariant<'b>> { + | -- -- lifetime `'b` defined here + | | + | lifetime `'a` defined here +LL | let returned_value = create_invariant(); +LL | if choice { Ok(returned_value) } else { Err(returned_value) } + | ^^^^^^^^^^^^^^^^^^^ function was supposed to return data with lifetime `'b` but it is returning data with lifetime `'a` + | + = help: consider adding the following bound: `'a: 'b` + = note: requirement occurs because of the type `Invariant<'_>`, which makes the generic argument `'_` invariant + = note: the struct `Invariant<'l>` is invariant over the parameter `'l` + = help: see for more information about variance + +help: `'a` and `'b` must be the same: replace one with the other + +error: aborting due to 2 previous errors + diff --git a/tests/ui/nll/polonius/flow-sensitive-invariance.rs b/tests/ui/nll/polonius/flow-sensitive-invariance.rs new file mode 100644 index 000000000000..c5571f131da3 --- /dev/null +++ b/tests/ui/nll/polonius/flow-sensitive-invariance.rs @@ -0,0 +1,34 @@ +// An example (from @steffahn) of reachability as an approximation of liveness where the polonius +// alpha analysis shows the same imprecision as NLLs, unlike the datalog implementation. + +//@ ignore-compare-mode-polonius (explicit revisions) +//@ revisions: nll polonius legacy +//@ [polonius] compile-flags: -Z polonius=next +//@ [legacy] check-pass +//@ [legacy] compile-flags: -Z polonius=legacy + +use std::cell::Cell; + +struct Invariant<'l>(Cell<&'l ()>); + +fn create_invariant<'l>() -> Invariant<'l> { + Invariant(Cell::new(&())) +} + +fn use_it<'a, 'b>(choice: bool) -> Result, Invariant<'b>> { + let returned_value = create_invariant(); + if choice { Ok(returned_value) } else { Err(returned_value) } + //[nll]~^ ERROR lifetime may not live long enough + //[nll]~| ERROR lifetime may not live long enough + //[polonius]~^^^ ERROR lifetime may not live long enough + //[polonius]~| ERROR lifetime may not live long enough +} + +fn use_it_but_its_the_same_region<'a: 'b, 'b: 'a>( + choice: bool, +) -> Result, Invariant<'b>> { + let returned_value = create_invariant(); + if choice { Ok(returned_value) } else { Err(returned_value) } +} + +fn main() {} From 9badbdf5f9272bf42699edabc14735a1ab42e3d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Rakic?= Date: Thu, 26 Jun 2025 15:29:03 +0000 Subject: [PATCH 067/195] add cursor-like example that works This is an example similar to the linked-list cursor examples where the alpha shows the same imprecision as NLLs, but that can work due to the loans not being live after the loop, or the constraint graph being simple enough that the cfg/subset relationships are the same for reachability and liveness. --- .../iterating-updating-mutref.nll.stderr | 15 ++++ .../nll/polonius/iterating-updating-mutref.rs | 87 +++++++++++++++++++ 2 files changed, 102 insertions(+) create mode 100644 tests/ui/nll/polonius/iterating-updating-mutref.nll.stderr create mode 100644 tests/ui/nll/polonius/iterating-updating-mutref.rs diff --git a/tests/ui/nll/polonius/iterating-updating-mutref.nll.stderr b/tests/ui/nll/polonius/iterating-updating-mutref.nll.stderr new file mode 100644 index 000000000000..941c736d8d25 --- /dev/null +++ b/tests/ui/nll/polonius/iterating-updating-mutref.nll.stderr @@ -0,0 +1,15 @@ +error[E0499]: cannot borrow `self.buf_read` as mutable more than once at a time + --> $DIR/iterating-updating-mutref.rs:61:23 + | +LL | pub fn next<'a>(&'a mut self) -> &'a str { + | -- lifetime `'a` defined here +LL | loop { +LL | let buf = self.buf_read.fill_buf(); + | ^^^^^^^^^^^^^ `self.buf_read` was mutably borrowed here in the previous iteration of the loop +LL | if let Some(s) = decode(buf) { +LL | return s; + | - returning this value requires that `self.buf_read` is borrowed for `'a` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0499`. diff --git a/tests/ui/nll/polonius/iterating-updating-mutref.rs b/tests/ui/nll/polonius/iterating-updating-mutref.rs new file mode 100644 index 000000000000..a315bf66279c --- /dev/null +++ b/tests/ui/nll/polonius/iterating-updating-mutref.rs @@ -0,0 +1,87 @@ +// These are some examples of iterating through and updating a mutable ref, similar in spirit to the +// linked-list-like pattern of #46859/#48001 where the polonius alpha analysis shows imprecision, +// unlike the datalog implementation. +// +// They differ in that after the loans prior to the loop are either not live after the loop, or with +// control flow and outlives relationships that are simple enough for the reachability +// approximation. They're thus accepted by the alpha analysis, like NLLs did for the simplest cases +// of flow-sensitivity. + +//@ ignore-compare-mode-polonius (explicit revisions) +//@ revisions: nll polonius legacy +//@ [nll] known-bug: #46859 +//@ [polonius] check-pass +//@ [polonius] compile-flags: -Z polonius=next +//@ [legacy] check-pass +//@ [legacy] compile-flags: -Z polonius=legacy + +// The #46859 OP +struct List { + value: T, + next: Option>>, +} + +fn to_refs(mut list: &mut List) -> Vec<&mut T> { + let mut result = vec![]; + loop { + result.push(&mut list.value); + if let Some(n) = list.next.as_mut() { + list = n; + } else { + return result; + } + } +} + +// A similar construction, where paths in the constraint graph are also clearly terminating, so it's +// fine even for NLLs. +fn to_refs2(mut list: &mut List) -> Vec<&mut T> { + let mut result = vec![]; + loop { + result.push(&mut list.value); + if let Some(n) = list.next.as_mut() { + list = n; + } else { + break; + } + } + + result +} + +// Another MCVE from the same issue, but was rejected by NLLs. +pub struct Decoder { + buf_read: BufRead, +} + +impl Decoder { + // NLLs fail here + pub fn next<'a>(&'a mut self) -> &'a str { + loop { + let buf = self.buf_read.fill_buf(); + if let Some(s) = decode(buf) { + return s; + } + // loop to get more input data + + // At this point `buf` is not used anymore. + // With NLL I would expect the borrow to end here, + // such that `self.buf_read` is not borrowed anymore + // by the time we start the next loop iteration. + } + } +} + +struct BufRead; + +impl BufRead { + fn fill_buf(&mut self) -> &[u8] { + unimplemented!() + } +} + +fn decode(_: &[u8]) -> Option<&str> { + unimplemented!() +} + +fn main() {} From b99fe2b72022f4fe7a6f2e55fd70cc8530566473 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Rakic?= Date: Fri, 8 Aug 2025 14:29:50 +0000 Subject: [PATCH 068/195] mark polonius=next's NLL imprecisions as known-bugs - linked-list cursor-like patterns - issue-46589 These are known-bugs for the polonius alpha, where they show the same imprecision as NLLs, but are supported by the old datalog implementation. --- tests/ui/nll/issue-46589.nll.stderr | 2 +- tests/ui/nll/issue-46589.polonius.stderr | 15 +++++++++++++ tests/ui/nll/issue-46589.rs | 12 +++++----- ...ng-updating-cursor-issue-108704.nll.stderr | 2 +- ...dating-cursor-issue-108704.polonius.stderr | 12 ++++++++++ .../iterating-updating-cursor-issue-108704.rs | 7 +++--- ...ing-updating-cursor-issue-57165.nll.stderr | 4 ++-- ...pdating-cursor-issue-57165.polonius.stderr | 22 +++++++++++++++++++ .../iterating-updating-cursor-issue-57165.rs | 7 +++--- ...ing-updating-cursor-issue-63908.nll.stderr | 2 +- ...pdating-cursor-issue-63908.polonius.stderr | 18 +++++++++++++++ .../iterating-updating-cursor-issue-63908.rs | 7 +++--- 12 files changed, 90 insertions(+), 20 deletions(-) create mode 100644 tests/ui/nll/issue-46589.polonius.stderr create mode 100644 tests/ui/nll/polonius/iterating-updating-cursor-issue-108704.polonius.stderr create mode 100644 tests/ui/nll/polonius/iterating-updating-cursor-issue-57165.polonius.stderr create mode 100644 tests/ui/nll/polonius/iterating-updating-cursor-issue-63908.polonius.stderr diff --git a/tests/ui/nll/issue-46589.nll.stderr b/tests/ui/nll/issue-46589.nll.stderr index dc80c1d08dec..f2d08d1f4c83 100644 --- a/tests/ui/nll/issue-46589.nll.stderr +++ b/tests/ui/nll/issue-46589.nll.stderr @@ -1,5 +1,5 @@ error[E0499]: cannot borrow `**other` as mutable more than once at a time - --> $DIR/issue-46589.rs:24:21 + --> $DIR/issue-46589.rs:25:21 | LL | *other = match (*other).get_self() { | -------- first mutable borrow occurs here diff --git a/tests/ui/nll/issue-46589.polonius.stderr b/tests/ui/nll/issue-46589.polonius.stderr new file mode 100644 index 000000000000..f2d08d1f4c83 --- /dev/null +++ b/tests/ui/nll/issue-46589.polonius.stderr @@ -0,0 +1,15 @@ +error[E0499]: cannot borrow `**other` as mutable more than once at a time + --> $DIR/issue-46589.rs:25:21 + | +LL | *other = match (*other).get_self() { + | -------- first mutable borrow occurs here +LL | Some(s) => s, +LL | None => (*other).new_self() + | ^^^^^^^^ + | | + | second mutable borrow occurs here + | first borrow later used here + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0499`. diff --git a/tests/ui/nll/issue-46589.rs b/tests/ui/nll/issue-46589.rs index 10aff0d7b4ef..9a23f61b8cb8 100644 --- a/tests/ui/nll/issue-46589.rs +++ b/tests/ui/nll/issue-46589.rs @@ -1,9 +1,10 @@ //@ ignore-compare-mode-polonius (explicit revisions) -//@ revisions: nll polonius_next polonius -//@ [polonius_next] check-pass -//@ [polonius_next] compile-flags: -Zpolonius=next -//@ [polonius] check-pass -//@ [polonius] compile-flags: -Zpolonius +//@ revisions: nll polonius legacy +//@ [nll] known-bug: #46589 +//@ [polonius] known-bug: #46589 +//@ [polonius] compile-flags: -Zpolonius=next +//@ [legacy] check-pass +//@ [legacy] compile-flags: -Zpolonius=legacy struct Foo; @@ -22,7 +23,6 @@ impl Foo { *other = match (*other).get_self() { Some(s) => s, None => (*other).new_self() - //[nll]~^ ERROR cannot borrow `**other` as mutable more than once at a time [E0499] }; let c = other; diff --git a/tests/ui/nll/polonius/iterating-updating-cursor-issue-108704.nll.stderr b/tests/ui/nll/polonius/iterating-updating-cursor-issue-108704.nll.stderr index 7ac4e5de2838..b768f60590cb 100644 --- a/tests/ui/nll/polonius/iterating-updating-cursor-issue-108704.nll.stderr +++ b/tests/ui/nll/polonius/iterating-updating-cursor-issue-108704.nll.stderr @@ -1,5 +1,5 @@ error[E0499]: cannot borrow `*elements` as mutable more than once at a time - --> $DIR/iterating-updating-cursor-issue-108704.rs:40:26 + --> $DIR/iterating-updating-cursor-issue-108704.rs:41:26 | LL | for (idx, el) in elements.iter_mut().enumerate() { | ^^^^^^^^ diff --git a/tests/ui/nll/polonius/iterating-updating-cursor-issue-108704.polonius.stderr b/tests/ui/nll/polonius/iterating-updating-cursor-issue-108704.polonius.stderr new file mode 100644 index 000000000000..b768f60590cb --- /dev/null +++ b/tests/ui/nll/polonius/iterating-updating-cursor-issue-108704.polonius.stderr @@ -0,0 +1,12 @@ +error[E0499]: cannot borrow `*elements` as mutable more than once at a time + --> $DIR/iterating-updating-cursor-issue-108704.rs:41:26 + | +LL | for (idx, el) in elements.iter_mut().enumerate() { + | ^^^^^^^^ + | | + | `*elements` was mutably borrowed here in the previous iteration of the loop + | first borrow used here, in later iteration of loop + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0499`. diff --git a/tests/ui/nll/polonius/iterating-updating-cursor-issue-108704.rs b/tests/ui/nll/polonius/iterating-updating-cursor-issue-108704.rs index 420cb73bed28..673efa8f2259 100644 --- a/tests/ui/nll/polonius/iterating-updating-cursor-issue-108704.rs +++ b/tests/ui/nll/polonius/iterating-updating-cursor-issue-108704.rs @@ -1,11 +1,12 @@ #![crate_type = "lib"] -// An example from #108704 of the linked-list cursor-like pattern of #46859/#48001. +// An example from #108704 of the linked-list cursor-like pattern of #46859/#48001, where the +// polonius alpha analysis shows the same imprecision as NLLs, unlike the datalog implementation. //@ ignore-compare-mode-polonius (explicit revisions) //@ revisions: nll polonius legacy //@ [nll] known-bug: #108704 -//@ [polonius] check-pass +//@ [polonius] known-bug: #108704 //@ [polonius] compile-flags: -Z polonius=next //@ [legacy] check-pass //@ [legacy] compile-flags: -Z polonius=legacy @@ -32,7 +33,7 @@ fn merge_tree_ok(root: &mut Root, path: Vec) { } } -// NLLs fail here +// NLLs and polonius alpha fail here fn merge_tree_ko(root: &mut Root, path: Vec) { let mut elements = &mut root.children; diff --git a/tests/ui/nll/polonius/iterating-updating-cursor-issue-57165.nll.stderr b/tests/ui/nll/polonius/iterating-updating-cursor-issue-57165.nll.stderr index 14e1726e158f..34b04e707dbd 100644 --- a/tests/ui/nll/polonius/iterating-updating-cursor-issue-57165.nll.stderr +++ b/tests/ui/nll/polonius/iterating-updating-cursor-issue-57165.nll.stderr @@ -1,5 +1,5 @@ error[E0499]: cannot borrow `p.0` as mutable more than once at a time - --> $DIR/iterating-updating-cursor-issue-57165.rs:29:20 + --> $DIR/iterating-updating-cursor-issue-57165.rs:30:20 | LL | while let Some(now) = p { | ^^^ - first borrow used here, in later iteration of loop @@ -7,7 +7,7 @@ LL | while let Some(now) = p { | `p.0` was mutably borrowed here in the previous iteration of the loop error[E0503]: cannot use `*p` because it was mutably borrowed - --> $DIR/iterating-updating-cursor-issue-57165.rs:29:27 + --> $DIR/iterating-updating-cursor-issue-57165.rs:30:27 | LL | while let Some(now) = p { | --- ^ diff --git a/tests/ui/nll/polonius/iterating-updating-cursor-issue-57165.polonius.stderr b/tests/ui/nll/polonius/iterating-updating-cursor-issue-57165.polonius.stderr new file mode 100644 index 000000000000..34b04e707dbd --- /dev/null +++ b/tests/ui/nll/polonius/iterating-updating-cursor-issue-57165.polonius.stderr @@ -0,0 +1,22 @@ +error[E0499]: cannot borrow `p.0` as mutable more than once at a time + --> $DIR/iterating-updating-cursor-issue-57165.rs:30:20 + | +LL | while let Some(now) = p { + | ^^^ - first borrow used here, in later iteration of loop + | | + | `p.0` was mutably borrowed here in the previous iteration of the loop + +error[E0503]: cannot use `*p` because it was mutably borrowed + --> $DIR/iterating-updating-cursor-issue-57165.rs:30:27 + | +LL | while let Some(now) = p { + | --- ^ + | | | + | | use of borrowed `p.0` + | | borrow later used here + | `p.0` is borrowed here + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0499, E0503. +For more information about an error, try `rustc --explain E0499`. diff --git a/tests/ui/nll/polonius/iterating-updating-cursor-issue-57165.rs b/tests/ui/nll/polonius/iterating-updating-cursor-issue-57165.rs index 63ec89146d4a..9670fc77e6fa 100644 --- a/tests/ui/nll/polonius/iterating-updating-cursor-issue-57165.rs +++ b/tests/ui/nll/polonius/iterating-updating-cursor-issue-57165.rs @@ -1,11 +1,12 @@ #![crate_type = "lib"] -// An example from #57165 of the linked-list cursor-like pattern of #46859/#48001. +// An example from #57165 of the linked-list cursor-like pattern of #46859/#48001, where the +// polonius alpha analysis shows the same imprecision as NLLs, unlike the datalog implementation. //@ ignore-compare-mode-polonius (explicit revisions) //@ revisions: nll polonius legacy //@ [nll] known-bug: #57165 -//@ [polonius] check-pass +//@ [polonius] known-bug: #57165 //@ [polonius] compile-flags: -Z polonius=next //@ [legacy] check-pass //@ [legacy] compile-flags: -Z polonius=legacy @@ -22,7 +23,7 @@ fn no_control_flow() { } } -// NLLs fail here +// NLLs and polonius alpha fail here fn conditional() { let mut b = Some(Box::new(X { next: None })); let mut p = &mut b; diff --git a/tests/ui/nll/polonius/iterating-updating-cursor-issue-63908.nll.stderr b/tests/ui/nll/polonius/iterating-updating-cursor-issue-63908.nll.stderr index bf38da566c69..b76e4ff63987 100644 --- a/tests/ui/nll/polonius/iterating-updating-cursor-issue-63908.nll.stderr +++ b/tests/ui/nll/polonius/iterating-updating-cursor-issue-63908.nll.stderr @@ -1,5 +1,5 @@ error[E0506]: cannot assign to `*node_ref` because it is borrowed - --> $DIR/iterating-updating-cursor-issue-63908.rs:42:5 + --> $DIR/iterating-updating-cursor-issue-63908.rs:43:5 | LL | fn remove_last_node_iterative(mut node_ref: &mut List) { | - let's call the lifetime of this reference `'1` diff --git a/tests/ui/nll/polonius/iterating-updating-cursor-issue-63908.polonius.stderr b/tests/ui/nll/polonius/iterating-updating-cursor-issue-63908.polonius.stderr new file mode 100644 index 000000000000..b76e4ff63987 --- /dev/null +++ b/tests/ui/nll/polonius/iterating-updating-cursor-issue-63908.polonius.stderr @@ -0,0 +1,18 @@ +error[E0506]: cannot assign to `*node_ref` because it is borrowed + --> $DIR/iterating-updating-cursor-issue-63908.rs:43:5 + | +LL | fn remove_last_node_iterative(mut node_ref: &mut List) { + | - let's call the lifetime of this reference `'1` +LL | loop { +LL | let next_ref = &mut node_ref.as_mut().unwrap().next; + | -------- `*node_ref` is borrowed here +... +LL | node_ref = next_ref; + | ------------------- assignment requires that `*node_ref` is borrowed for `'1` +... +LL | *node_ref = None; + | ^^^^^^^^^ `*node_ref` is assigned to here but it was already borrowed + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0506`. diff --git a/tests/ui/nll/polonius/iterating-updating-cursor-issue-63908.rs b/tests/ui/nll/polonius/iterating-updating-cursor-issue-63908.rs index 00e48b65fed1..6682926c5237 100644 --- a/tests/ui/nll/polonius/iterating-updating-cursor-issue-63908.rs +++ b/tests/ui/nll/polonius/iterating-updating-cursor-issue-63908.rs @@ -1,11 +1,12 @@ #![crate_type = "lib"] -// An example from #63908 of the linked-list cursor-like pattern of #46859/#48001. +// An example from #63908 of the linked-list cursor-like pattern of #46859/#48001, where the +// polonius alpha analysis shows the same imprecision as NLLs, unlike the datalog implementation. //@ ignore-compare-mode-polonius (explicit revisions) //@ revisions: nll polonius legacy //@ [nll] known-bug: #63908 -//@ [polonius] check-pass +//@ [polonius] known-bug: #63908 //@ [polonius] compile-flags: -Z polonius=next //@ [legacy] check-pass //@ [legacy] compile-flags: -Z polonius=legacy @@ -27,7 +28,7 @@ fn remove_last_node_recursive(node_ref: &mut List) { } } -// NLLs fail here +// NLLs and polonius alpha fail here fn remove_last_node_iterative(mut node_ref: &mut List) { loop { let next_ref = &mut node_ref.as_mut().unwrap().next; From dbc6f5836c798ceea7518696a1cb72b8f602df76 Mon Sep 17 00:00:00 2001 From: Deadbeef Date: Fri, 8 Aug 2025 23:11:08 +0800 Subject: [PATCH 069/195] rustc_metadata: remove unused private trait impls --- .../src/rmeta/decoder/cstore_impl.rs | 18 ------ .../rustc_metadata/src/rmeta/parameterized.rs | 7 +-- compiler/rustc_metadata/src/rmeta/table.rs | 63 ------------------- 3 files changed, 1 insertion(+), 87 deletions(-) diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs index 0f3896fd9be8..df95ed602cd3 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs @@ -74,24 +74,6 @@ impl<'a, 'tcx, T: Copy + Decodable>> ProcessQueryValue<' } } -impl<'a, 'tcx, T: Copy + Decodable>> - ProcessQueryValue<'tcx, ty::EarlyBinder<'tcx, &'tcx [T]>> - for Option> -{ - #[inline(always)] - fn process_decoded( - self, - tcx: TyCtxt<'tcx>, - err: impl Fn() -> !, - ) -> ty::EarlyBinder<'tcx, &'tcx [T]> { - ty::EarlyBinder::bind(if let Some(iter) = self { - tcx.arena.alloc_from_iter(iter) - } else { - err() - }) - } -} - impl<'a, 'tcx, T: Copy + Decodable>> ProcessQueryValue<'tcx, Option<&'tcx [T]>> for Option> { diff --git a/compiler/rustc_metadata/src/rmeta/parameterized.rs b/compiler/rustc_metadata/src/rmeta/parameterized.rs index d632e65104a7..34180001f804 100644 --- a/compiler/rustc_metadata/src/rmeta/parameterized.rs +++ b/compiler/rustc_metadata/src/rmeta/parameterized.rs @@ -6,7 +6,7 @@ use rustc_index::{Idx, IndexVec}; use rustc_middle::ty::{Binder, EarlyBinder}; use rustc_span::Symbol; -use crate::rmeta::{LazyArray, LazyTable, LazyValue}; +use crate::rmeta::{LazyArray, LazyValue}; pub(crate) trait ParameterizedOverTcx: 'static { type Value<'tcx>; @@ -48,10 +48,6 @@ impl ParameterizedOverTcx for LazyArray { type Value<'tcx> = LazyArray>; } -impl ParameterizedOverTcx for LazyTable { - type Value<'tcx> = LazyTable>; -} - macro_rules! trivially_parameterized_over_tcx { ($($ty:ty),+ $(,)?) => { $( @@ -154,7 +150,6 @@ parameterized_over_tcx! { rustc_middle::mir::CoroutineLayout, rustc_middle::mir::interpret::ConstAllocation, rustc_middle::ty::Clause, - rustc_middle::ty::ClauseKind, rustc_middle::ty::Const, rustc_middle::ty::ConstConditions, rustc_middle::ty::FnSig, diff --git a/compiler/rustc_metadata/src/rmeta/table.rs b/compiler/rustc_metadata/src/rmeta/table.rs index 28f406fbc962..0671aa203993 100644 --- a/compiler/rustc_metadata/src/rmeta/table.rs +++ b/compiler/rustc_metadata/src/rmeta/table.rs @@ -66,22 +66,6 @@ pub(super) trait FixedSizeEncoding: IsDefault { fn write_to_bytes(self, b: &mut Self::ByteArray); } -/// This implementation is not used generically, but for reading/writing -/// concrete `u32` fields in `Lazy*` structures, which may be zero. -impl FixedSizeEncoding for u32 { - type ByteArray = [u8; 4]; - - #[inline] - fn from_bytes(b: &[u8; 4]) -> Self { - Self::from_le_bytes(*b) - } - - #[inline] - fn write_to_bytes(self, b: &mut [u8; 4]) { - *b = self.to_le_bytes(); - } -} - impl FixedSizeEncoding for u64 { type ByteArray = [u8; 8]; @@ -174,14 +158,6 @@ fixed_size_enum! { } } -fixed_size_enum! { - ty::ImplPolarity { - ( Positive ) - ( Negative ) - ( Reservation ) - } -} - fixed_size_enum! { hir::Constness { ( NotConst ) @@ -306,45 +282,6 @@ impl FixedSizeEncoding for bool { } } -impl FixedSizeEncoding for Option { - type ByteArray = [u8; 1]; - - #[inline] - fn from_bytes(b: &[u8; 1]) -> Self { - match b[0] { - 0 => Some(false), - 1 => Some(true), - 2 => None, - _ => unreachable!(), - } - } - - #[inline] - fn write_to_bytes(self, b: &mut [u8; 1]) { - debug_assert!(!self.is_default()); - b[0] = match self { - Some(false) => 0, - Some(true) => 1, - None => 2, - }; - } -} - -impl FixedSizeEncoding for UnusedGenericParams { - type ByteArray = [u8; 4]; - - #[inline] - fn from_bytes(b: &[u8; 4]) -> Self { - let x: u32 = u32::from_bytes(b); - UnusedGenericParams::from_bits(x) - } - - #[inline] - fn write_to_bytes(self, b: &mut [u8; 4]) { - self.bits().write_to_bytes(b); - } -} - // NOTE(eddyb) there could be an impl for `usize`, which would enable a more // generic `LazyValue` impl, but in the general case we might not need / want // to fit every `usize` in `u32`. From 57dc64ea32b21f309bd50b9a9ecf8757a6842117 Mon Sep 17 00:00:00 2001 From: Deadbeef Date: Sat, 9 Aug 2025 00:02:09 +0800 Subject: [PATCH 070/195] remove some unused private trait impls --- .../rustc_borrowck/src/polonius/legacy/facts.rs | 16 ---------------- compiler/rustc_symbol_mangling/src/export.rs | 9 +-------- 2 files changed, 1 insertion(+), 24 deletions(-) diff --git a/compiler/rustc_borrowck/src/polonius/legacy/facts.rs b/compiler/rustc_borrowck/src/polonius/legacy/facts.rs index 64389b11a651..1f8177477e68 100644 --- a/compiler/rustc_borrowck/src/polonius/legacy/facts.rs +++ b/compiler/rustc_borrowck/src/polonius/legacy/facts.rs @@ -184,22 +184,6 @@ where } } -impl FactRow for (A, B, C, D) -where - A: FactCell, - B: FactCell, - C: FactCell, - D: FactCell, -{ - fn write( - &self, - out: &mut dyn Write, - location_table: &PoloniusLocationTable, - ) -> Result<(), Box> { - write_row(out, location_table, &[&self.0, &self.1, &self.2, &self.3]) - } -} - fn write_row( out: &mut dyn Write, location_table: &PoloniusLocationTable, diff --git a/compiler/rustc_symbol_mangling/src/export.rs b/compiler/rustc_symbol_mangling/src/export.rs index 956c996326bf..76ac82cf95a1 100644 --- a/compiler/rustc_symbol_mangling/src/export.rs +++ b/compiler/rustc_symbol_mangling/src/export.rs @@ -21,7 +21,7 @@ macro_rules! default_hash_impl { }; } -default_hash_impl! { i8, i16, i32, i64, i128, isize, u8, u16, u32, u64, u128, usize, } +default_hash_impl! { u8, u64, usize, } impl<'tcx> AbiHashStable<'tcx> for bool { #[inline] @@ -37,13 +37,6 @@ impl<'tcx> AbiHashStable<'tcx> for str { } } -impl<'tcx> AbiHashStable<'tcx> for String { - #[inline] - fn abi_hash(&self, tcx: TyCtxt<'tcx>, hasher: &mut StableHasher) { - self[..].abi_hash(tcx, hasher); - } -} - impl<'tcx> AbiHashStable<'tcx> for Symbol { #[inline] fn abi_hash(&self, tcx: TyCtxt<'tcx>, hasher: &mut StableHasher) { From dcc1605fba0ad04b8bc2d96d044f2ca3711b2219 Mon Sep 17 00:00:00 2001 From: Daniel Paoliello Date: Fri, 8 Aug 2025 09:17:35 -0700 Subject: [PATCH 071/195] [win][arm64ec] Partial fix for raw-dylib-link-ordinal on Arm64EC --- tests/run-make/raw-dylib-link-ordinal/exporter.def | 2 +- tests/run-make/raw-dylib-link-ordinal/rmake.rs | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/run-make/raw-dylib-link-ordinal/exporter.def b/tests/run-make/raw-dylib-link-ordinal/exporter.def index 5d87c580a54d..0544e9f18033 100644 --- a/tests/run-make/raw-dylib-link-ordinal/exporter.def +++ b/tests/run-make/raw-dylib-link-ordinal/exporter.def @@ -1,5 +1,5 @@ LIBRARY exporter EXPORTS exported_function @13 NONAME - exported_variable @5 NONAME + exported_variable @5 NONAME DATA print_exported_variable @9 NONAME diff --git a/tests/run-make/raw-dylib-link-ordinal/rmake.rs b/tests/run-make/raw-dylib-link-ordinal/rmake.rs index 43274b9765bb..b9254b167534 100644 --- a/tests/run-make/raw-dylib-link-ordinal/rmake.rs +++ b/tests/run-make/raw-dylib-link-ordinal/rmake.rs @@ -11,7 +11,7 @@ //@ only-windows -use run_make_support::{cc, diff, is_windows_msvc, run, rustc}; +use run_make_support::{cc, diff, extra_c_flags, is_windows_msvc, run, rustc}; // NOTE: build_native_dynamic lib is not used, as the special `def` files // must be passed to the CC compiler. @@ -24,6 +24,7 @@ fn main() { cc().input("exporter.obj") .arg("exporter.def") .args(&["-link", "-dll", "-noimplib", "-out:exporter.dll"]) + .args(extra_c_flags()) .run(); } else { cc().arg("-v").arg("-c").out_exe("exporter.obj").input("exporter.c").run(); From 5e8ebd5ecd8546591a6707ac9e1a3b8a64c72f76 Mon Sep 17 00:00:00 2001 From: binarycat Date: Tue, 10 Jun 2025 13:35:46 -0500 Subject: [PATCH 072/195] rustdoc: IndexItem::{stability -> is_unstable} --- src/librustdoc/formats/cache.rs | 2 +- src/librustdoc/html/render/mod.rs | 10 ++-------- src/librustdoc/html/render/search_index.rs | 4 ++-- 3 files changed, 5 insertions(+), 11 deletions(-) diff --git a/src/librustdoc/formats/cache.rs b/src/librustdoc/formats/cache.rs index c3251ca78062..d0dbc5a74da8 100644 --- a/src/librustdoc/formats/cache.rs +++ b/src/librustdoc/formats/cache.rs @@ -586,7 +586,7 @@ fn add_item_to_search_index(tcx: TyCtxt<'_>, cache: &mut Cache, item: &clean::It search_type, aliases, deprecation, - stability: item.stability(tcx), + is_unstable: item.stability(tcx).map(|x| x.is_unstable()).unwrap_or(false), }; cache.search_index.push(index_item); } diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index 29917bb0fca2..81c05d93f823 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -50,8 +50,7 @@ use std::{fs, str}; use askama::Template; use itertools::Either; use rustc_attr_data_structures::{ - ConstStability, DeprecatedSince, Deprecation, RustcVersion, Stability, StabilityLevel, - StableSince, + ConstStability, DeprecatedSince, Deprecation, RustcVersion, StabilityLevel, StableSince, }; use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; use rustc_hir::Mutability; @@ -141,12 +140,7 @@ pub(crate) struct IndexItem { pub(crate) search_type: Option, pub(crate) aliases: Box<[Symbol]>, pub(crate) deprecation: Option, - pub(crate) stability: Option, -} -impl IndexItem { - fn is_unstable(&self) -> bool { - matches!(&self.stability, Some(Stability { level: StabilityLevel::Unstable { .. }, .. })) - } + pub(crate) is_unstable: bool, } /// A type used for the search index. diff --git a/src/librustdoc/html/render/search_index.rs b/src/librustdoc/html/render/search_index.rs index 6635aa02e97d..a0911bf58736 100644 --- a/src/librustdoc/html/render/search_index.rs +++ b/src/librustdoc/html/render/search_index.rs @@ -93,7 +93,7 @@ pub(crate) fn build_index( ), aliases: item.attrs.get_doc_aliases(), deprecation: item.deprecation(tcx), - stability: item.stability(tcx), + is_unstable: item.stability(tcx).is_some_and(|x| x.is_unstable()), }); } } @@ -700,7 +700,7 @@ pub(crate) fn build_index( // bitmasks always use 1-indexing for items, with 0 as the crate itself deprecated.push(u32::try_from(index + 1).unwrap()); } - if item.is_unstable() { + if item.is_unstable { unstable.push(u32::try_from(index + 1).unwrap()); } } From fdbc8d08a63a3d34b7aebabb2f18a768462a98c4 Mon Sep 17 00:00:00 2001 From: binarycat Date: Fri, 8 Aug 2025 11:55:00 -0500 Subject: [PATCH 073/195] rustdoc search: add performance note about searchIndexUnstable check --- src/librustdoc/html/static/js/search.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js index da15433622af..1ed789f63d5d 100644 --- a/src/librustdoc/html/static/js/search.js +++ b/src/librustdoc/html/static/js/search.js @@ -3291,6 +3291,12 @@ class DocSearch { } // sort unstable items later + // FIXME: there is some doubt if this is the most effecient way to implement this. + // alternative options include: + // * put is_unstable on each item when the index is built. + // increases memory usage but avoids a hashmap lookup. + // * put is_unstable on each item before sorting. + // better worst case performance but worse average case performance. a = Number( // @ts-expect-error this.searchIndexUnstable.get(aaa.item.crate).contains(aaa.item.bitIndex), From b4675dcac87378d1e936ae928fe06f38c4cf6a30 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Fri, 8 Aug 2025 22:44:48 +0530 Subject: [PATCH 074/195] add download_ci_rustc_commit function and invoke from parse_inner --- src/bootstrap/src/core/config/config.rs | 129 +++++++++++++++++++++++- 1 file changed, 128 insertions(+), 1 deletion(-) diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index 2c008f957d98..e7b7ab4f4d4b 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -947,7 +947,14 @@ impl Config { ); } - config.download_rustc_commit = config.download_ci_rustc_commit( + config.download_rustc_commit = download_ci_rustc_commit( + &config.stage0_metadata, + config.path_modification_cache.clone(), + &config.src, + config.is_running_on_ci, + &config.exec_ctx, + &config.rust_info, + &config.host_target, rust_download_rustc, debug_assertions_requested, config.llvm_assertions, @@ -2335,3 +2342,123 @@ pub fn check_stage0_version( )); } } + +pub fn download_ci_rustc_commit( + stage0_metadata: &build_helper::stage0_parser::Stage0, + path_modification_cache: Arc, PathFreshness>>>, + src: &Path, + is_running_on_ci: bool, + exec_ctx: &ExecutionContext, + rust_info: &channel::GitInfo, + host_target: &TargetSelection, + download_rustc: Option, + debug_assertions_requested: bool, + llvm_assertions: bool, +) -> Option { + if !is_download_ci_available(&host_target.triple, llvm_assertions) { + return None; + } + + // If `download-rustc` is not set, default to rebuilding. + let if_unchanged = match download_rustc { + // Globally default `download-rustc` to `false`, because some contributors don't use + // profiles for reasons such as: + // - They need to seamlessly switch between compiler/library work. + // - They don't want to use compiler profile because they need to override too many + // things and it's easier to not use a profile. + None | Some(StringOrBool::Bool(false)) => return None, + Some(StringOrBool::Bool(true)) => false, + Some(StringOrBool::String(s)) if s == "if-unchanged" => { + if !rust_info.is_managed_git_subrepository() { + println!( + "ERROR: `download-rustc=if-unchanged` is only compatible with Git managed sources." + ); + crate::exit!(1); + } + + true + } + Some(StringOrBool::String(other)) => { + panic!("unrecognized option for download-rustc: {other}") + } + }; + + let commit = if rust_info.is_managed_git_subrepository() { + // Look for a version to compare to based on the current commit. + // Only commits merged by bors will have CI artifacts. + let freshness = check_path_modifications_( + stage0_metadata, + src, + path_modification_cache, + RUSTC_IF_UNCHANGED_ALLOWED_PATHS, + ); + exec_ctx.verbose(|| { + eprintln!("rustc freshness: {freshness:?}"); + }); + match freshness { + PathFreshness::LastModifiedUpstream { upstream } => upstream, + PathFreshness::HasLocalModifications { upstream } => { + if if_unchanged { + return None; + } + + if is_running_on_ci { + eprintln!("CI rustc commit matches with HEAD and we are in CI."); + eprintln!( + "`rustc.download-ci` functionality will be skipped as artifacts are not available." + ); + return None; + } + + upstream + } + PathFreshness::MissingUpstream => { + eprintln!("No upstream commit found"); + return None; + } + } + } else { + channel::read_commit_info_file(src) + .map(|info| info.sha.trim().to_owned()) + .expect("git-commit-info is missing in the project root") + }; + + if debug_assertions_requested { + eprintln!( + "WARN: `rust.debug-assertions = true` will prevent downloading CI rustc as alt CI \ + rustc is not currently built with debug assertions." + ); + return None; + } + + Some(commit) +} + +pub fn check_path_modifications_( + stage0_metadata: &build_helper::stage0_parser::Stage0, + src: &Path, + path_modification_cache: Arc, PathFreshness>>>, + paths: &[&'static str], +) -> PathFreshness { + // Checking path modifications through git can be relatively expensive (>100ms). + // We do not assume that the sources would change during bootstrap's execution, + // so we can cache the results here. + // Note that we do not use a static variable for the cache, because it would cause problems + // in tests that create separate `Config` instsances. + path_modification_cache + .lock() + .unwrap() + .entry(paths.to_vec()) + .or_insert_with(|| { + check_path_modifications(src, &git_config(stage0_metadata), paths, CiEnv::current()) + .unwrap() + }) + .clone() +} + +pub fn git_config(stage0_metadata: &build_helper::stage0_parser::Stage0) -> GitConfig<'_> { + GitConfig { + nightly_branch: &stage0_metadata.config.nightly_branch, + git_merge_commit_email: &stage0_metadata.config.git_merge_commit_email, + } +} From 8b95291cd4f0a6e838fec89cb2687d81aff237be Mon Sep 17 00:00:00 2001 From: lcnr Date: Fri, 8 Aug 2025 19:16:03 +0200 Subject: [PATCH 075/195] borrowck: move error tainting earlier --- compiler/rustc_borrowck/src/handle_placeholders.rs | 13 +------------ compiler/rustc_borrowck/src/type_check/mod.rs | 11 +++++++++++ compiler/rustc_borrowck/src/universal_regions.rs | 10 +++++----- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/compiler/rustc_borrowck/src/handle_placeholders.rs b/compiler/rustc_borrowck/src/handle_placeholders.rs index aaaf2f45c869..a0962e9b0151 100644 --- a/compiler/rustc_borrowck/src/handle_placeholders.rs +++ b/compiler/rustc_borrowck/src/handle_placeholders.rs @@ -216,22 +216,11 @@ pub(crate) fn compute_sccs_applying_placeholder_outlives_constraints<'tcx>( placeholder_index_to_region: _, liveness_constraints, mut outlives_constraints, - mut member_constraints, + member_constraints, universe_causes, type_tests, } = constraints; - if let Some(guar) = universal_regions.tainted_by_errors() { - debug!("Universal regions tainted by errors; removing constraints!"); - // Suppress unhelpful extra errors in `infer_opaque_types` by clearing out all - // outlives bounds that we may end up checking. - outlives_constraints = Default::default(); - member_constraints = Default::default(); - - // Also taint the entire scope. - infcx.set_tainted_by_errors(guar); - } - let fr_static = universal_regions.fr_static; let compute_sccs = |constraints: &OutlivesConstraintSet<'tcx>, diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 148d0de3bab2..c3aa205d5aab 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -182,6 +182,17 @@ pub(crate) fn type_check<'tcx>( ) }); + // In case type check encountered an error region, we suppress unhelpful extra + // errors in by clearing out all outlives bounds that we may end up checking. + if let Some(guar) = universal_region_relations.universal_regions.encountered_re_error() { + debug!("encountered an error region; removing constraints!"); + constraints.outlives_constraints = Default::default(); + constraints.member_constraints = Default::default(); + constraints.type_tests = Default::default(); + root_cx.set_tainted_by_errors(guar); + infcx.set_tainted_by_errors(guar); + } + MirTypeckResults { constraints, universal_region_relations, diff --git a/compiler/rustc_borrowck/src/universal_regions.rs b/compiler/rustc_borrowck/src/universal_regions.rs index 240c9a5223be..296a27355333 100644 --- a/compiler/rustc_borrowck/src/universal_regions.rs +++ b/compiler/rustc_borrowck/src/universal_regions.rs @@ -217,7 +217,7 @@ struct UniversalRegionIndices<'tcx> { /// Whether we've encountered an error region. If we have, cancel all /// outlives errors, as they are likely bogus. - pub tainted_by_errors: Cell>, + pub encountered_re_error: Cell>, } #[derive(Debug, PartialEq)] @@ -442,8 +442,8 @@ impl<'tcx> UniversalRegions<'tcx> { self.fr_fn_body } - pub(crate) fn tainted_by_errors(&self) -> Option { - self.indices.tainted_by_errors.get() + pub(crate) fn encountered_re_error(&self) -> Option { + self.indices.encountered_re_error.get() } } @@ -706,7 +706,7 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, 'tcx> { UniversalRegionIndices { indices: global_mapping.chain(arg_mapping).collect(), fr_static, - tainted_by_errors: Cell::new(None), + encountered_re_error: Cell::new(None), } } @@ -916,7 +916,7 @@ impl<'tcx> UniversalRegionIndices<'tcx> { match r.kind() { ty::ReVar(..) => r.as_var(), ty::ReError(guar) => { - self.tainted_by_errors.set(Some(guar)); + self.encountered_re_error.set(Some(guar)); // We use the `'static` `RegionVid` because `ReError` doesn't actually exist in the // `UniversalRegionIndices`. This is fine because 1) it is a fallback only used if // errors are being emitted and 2) it leaves the happy path unaffected. From 4eee55691aeb345cbac0ad558003d5f2ed72b478 Mon Sep 17 00:00:00 2001 From: lcnr Date: Fri, 8 Aug 2025 19:18:17 +0200 Subject: [PATCH 076/195] borrowck: defer opaque type errors --- .../rustc_borrowck/src/diagnostics/mod.rs | 2 +- ...{opaque_suggestions.rs => opaque_types.rs} | 19 +++++ .../src/diagnostics/region_errors.rs | 3 - compiler/rustc_borrowck/src/lib.rs | 8 +- compiler/rustc_borrowck/src/nll.rs | 5 -- .../rustc_borrowck/src/region_infer/mod.rs | 2 +- .../src/region_infer/opaque_types.rs | 78 ++++++++++--------- tests/crashes/135528.rs | 20 ----- .../type-error-post-normalization-test.rs | 24 ++++++ .../type-error-post-normalization-test.stderr | 14 ++++ .../higher-ranked-regions-basic.rs | 2 + .../higher-ranked-regions-basic.stderr | 28 +++++-- .../ui/type-alias-impl-trait/hkl_forbidden.rs | 8 +- .../hkl_forbidden.stderr | 30 +++++-- .../type-alias-impl-trait/param_mismatch2.rs | 4 +- .../param_mismatch2.stderr | 13 +++- .../type-alias-impl-trait/param_mismatch3.rs | 4 +- .../param_mismatch3.stderr | 13 +++- 18 files changed, 193 insertions(+), 84 deletions(-) rename compiler/rustc_borrowck/src/diagnostics/{opaque_suggestions.rs => opaque_types.rs} (93%) delete mode 100644 tests/crashes/135528.rs create mode 100644 tests/ui/impl-trait/issues/type-error-post-normalization-test.rs create mode 100644 tests/ui/impl-trait/issues/type-error-post-normalization-test.stderr diff --git a/compiler/rustc_borrowck/src/diagnostics/mod.rs b/compiler/rustc_borrowck/src/diagnostics/mod.rs index 34bed375cb96..b67dba3af96d 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mod.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mod.rs @@ -51,7 +51,7 @@ mod conflict_errors; mod explain_borrow; mod move_errors; mod mutability_errors; -mod opaque_suggestions; +mod opaque_types; mod region_errors; pub(crate) use bound_region_errors::{ToUniverseInfo, UniverseInfo}; diff --git a/compiler/rustc_borrowck/src/diagnostics/opaque_suggestions.rs b/compiler/rustc_borrowck/src/diagnostics/opaque_types.rs similarity index 93% rename from compiler/rustc_borrowck/src/diagnostics/opaque_suggestions.rs rename to compiler/rustc_borrowck/src/diagnostics/opaque_types.rs index 7192a889adcb..83fe4a9f98f4 100644 --- a/compiler/rustc_borrowck/src/diagnostics/opaque_suggestions.rs +++ b/compiler/rustc_borrowck/src/diagnostics/opaque_types.rs @@ -18,9 +18,28 @@ use rustc_trait_selection::errors::impl_trait_overcapture_suggestion; use crate::MirBorrowckCtxt; use crate::borrow_set::BorrowData; use crate::consumers::RegionInferenceContext; +use crate::region_infer::opaque_types::DeferredOpaqueTypeError; use crate::type_check::Locations; impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { + pub(crate) fn report_opaque_type_errors(&mut self, errors: Vec>) { + if errors.is_empty() { + return; + } + let mut guar = None; + for error in errors { + guar = Some(match error { + DeferredOpaqueTypeError::InvalidOpaqueTypeArgs(err) => err.report(self.infcx), + DeferredOpaqueTypeError::LifetimeMismatchOpaqueParam(err) => { + self.infcx.dcx().emit_err(err) + } + }); + } + let guar = guar.unwrap(); + self.root_cx.set_tainted_by_errors(guar); + self.infcx.set_tainted_by_errors(guar); + } + /// Try to note when an opaque is involved in a borrowck error and that /// opaque captures lifetimes due to edition 2024. // FIXME: This code is otherwise somewhat general, and could easily be adapted diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs index b130cf8ed276..2b74f1a48f73 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs @@ -92,9 +92,6 @@ impl<'tcx> RegionErrors<'tcx> { ) -> impl Iterator, ErrorGuaranteed)> { self.0.into_iter() } - pub(crate) fn has_errors(&self) -> Option { - self.0.get(0).map(|x| x.1) - } } impl std::fmt::Debug for RegionErrors<'_> { diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index 752ff8e6f586..d76d6a04e6e0 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -375,7 +375,7 @@ fn do_mir_borrowck<'tcx>( polonius_context, ); - regioncx.infer_opaque_types(root_cx, &infcx, opaque_type_values); + let opaque_type_errors = regioncx.infer_opaque_types(root_cx, &infcx, opaque_type_values); // Dump MIR results into a file, if that is enabled. This lets us // write unit-tests, as well as helping with debugging. @@ -471,7 +471,11 @@ fn do_mir_borrowck<'tcx>( }; // Compute and report region errors, if any. - mbcx.report_region_errors(nll_errors); + if nll_errors.is_empty() { + mbcx.report_opaque_type_errors(opaque_type_errors); + } else { + mbcx.report_region_errors(nll_errors); + } let (mut flow_analysis, flow_entry_states) = get_flow_results(tcx, body, &move_data, &borrow_set, ®ioncx); diff --git a/compiler/rustc_borrowck/src/nll.rs b/compiler/rustc_borrowck/src/nll.rs index ca6092e70d25..8608a8a3a66f 100644 --- a/compiler/rustc_borrowck/src/nll.rs +++ b/compiler/rustc_borrowck/src/nll.rs @@ -148,11 +148,6 @@ pub(crate) fn compute_regions<'tcx>( let (closure_region_requirements, nll_errors) = regioncx.solve(infcx, body, polonius_output.clone()); - if let Some(guar) = nll_errors.has_errors() { - // Suppress unhelpful extra errors in `infer_opaque_types`. - infcx.set_tainted_by_errors(guar); - } - NllOutput { regioncx, polonius_input: polonius_facts.map(Box::new), diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs index f13200485332..f4dc9627b359 100644 --- a/compiler/rustc_borrowck/src/region_infer/mod.rs +++ b/compiler/rustc_borrowck/src/region_infer/mod.rs @@ -44,7 +44,7 @@ use crate::{ mod dump_mir; mod graphviz; -mod opaque_types; +pub(crate) mod opaque_types; mod reverse_sccs; pub(crate) mod values; diff --git a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs index 6270e6d9a60e..23c4554aa155 100644 --- a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs +++ b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs @@ -6,7 +6,9 @@ use rustc_middle::ty::{ TypeVisitableExt, fold_regions, }; use rustc_span::Span; -use rustc_trait_selection::opaque_types::check_opaque_type_parameter_valid; +use rustc_trait_selection::opaque_types::{ + InvalidOpaqueTypeArgs, check_opaque_type_parameter_valid, +}; use tracing::{debug, instrument}; use super::RegionInferenceContext; @@ -14,6 +16,11 @@ use crate::BorrowCheckRootCtxt; use crate::session_diagnostics::LifetimeMismatchOpaqueParam; use crate::universal_regions::RegionClassification; +pub(crate) enum DeferredOpaqueTypeError<'tcx> { + InvalidOpaqueTypeArgs(InvalidOpaqueTypeArgs<'tcx>), + LifetimeMismatchOpaqueParam(LifetimeMismatchOpaqueParam<'tcx>), +} + impl<'tcx> RegionInferenceContext<'tcx> { /// Resolve any opaque types that were encountered while borrow checking /// this item. This is then used to get the type in the `type_of` query. @@ -58,13 +65,14 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// /// [rustc-dev-guide chapter]: /// https://rustc-dev-guide.rust-lang.org/opaque-types-region-infer-restrictions.html - #[instrument(level = "debug", skip(self, root_cx, infcx), ret)] + #[instrument(level = "debug", skip(self, root_cx, infcx))] pub(crate) fn infer_opaque_types( &self, root_cx: &mut BorrowCheckRootCtxt<'tcx>, infcx: &InferCtxt<'tcx>, opaque_ty_decls: FxIndexMap, OpaqueHiddenType<'tcx>>, - ) { + ) -> Vec> { + let mut errors = Vec::new(); let mut decls_modulo_regions: FxIndexMap, (OpaqueTypeKey<'tcx>, Span)> = FxIndexMap::default(); @@ -124,8 +132,15 @@ impl<'tcx> RegionInferenceContext<'tcx> { }); debug!(?concrete_type); - let ty = - infcx.infer_opaque_definition_from_instantiation(opaque_type_key, concrete_type); + let ty = match infcx + .infer_opaque_definition_from_instantiation(opaque_type_key, concrete_type) + { + Ok(ty) => ty, + Err(err) => { + errors.push(DeferredOpaqueTypeError::InvalidOpaqueTypeArgs(err)); + continue; + } + }; // Sometimes, when the hidden type is an inference variable, it can happen that // the hidden type becomes the opaque type itself. In this case, this was an opaque @@ -149,25 +164,27 @@ impl<'tcx> RegionInferenceContext<'tcx> { // non-region parameters. This is necessary because within the new solver we perform // various query operations modulo regions, and thus could unsoundly select some impls // that don't hold. - if !ty.references_error() - && let Some((prev_decl_key, prev_span)) = decls_modulo_regions.insert( - infcx.tcx.erase_regions(opaque_type_key), - (opaque_type_key, concrete_type.span), - ) - && let Some((arg1, arg2)) = std::iter::zip( - prev_decl_key.iter_captured_args(infcx.tcx).map(|(_, arg)| arg), - opaque_type_key.iter_captured_args(infcx.tcx).map(|(_, arg)| arg), - ) - .find(|(arg1, arg2)| arg1 != arg2) + if let Some((prev_decl_key, prev_span)) = decls_modulo_regions.insert( + infcx.tcx.erase_regions(opaque_type_key), + (opaque_type_key, concrete_type.span), + ) && let Some((arg1, arg2)) = std::iter::zip( + prev_decl_key.iter_captured_args(infcx.tcx).map(|(_, arg)| arg), + opaque_type_key.iter_captured_args(infcx.tcx).map(|(_, arg)| arg), + ) + .find(|(arg1, arg2)| arg1 != arg2) { - infcx.dcx().emit_err(LifetimeMismatchOpaqueParam { - arg: arg1, - prev: arg2, - span: prev_span, - prev_span: concrete_type.span, - }); + errors.push(DeferredOpaqueTypeError::LifetimeMismatchOpaqueParam( + LifetimeMismatchOpaqueParam { + arg: arg1, + prev: arg2, + span: prev_span, + prev_span: concrete_type.span, + }, + )); } } + + errors } /// Map the regions in the type to named regions. This is similar to what @@ -260,19 +277,13 @@ impl<'tcx> InferCtxt<'tcx> { &self, opaque_type_key: OpaqueTypeKey<'tcx>, instantiated_ty: OpaqueHiddenType<'tcx>, - ) -> Ty<'tcx> { - if let Some(e) = self.tainted_by_errors() { - return Ty::new_error(self.tcx, e); - } - - if let Err(err) = check_opaque_type_parameter_valid( + ) -> Result, InvalidOpaqueTypeArgs<'tcx>> { + check_opaque_type_parameter_valid( self, opaque_type_key, instantiated_ty.span, DefiningScopeKind::MirBorrowck, - ) { - return Ty::new_error(self.tcx, err.report(self)); - } + )?; let definition_ty = instantiated_ty .remap_generic_params_to_declaration_params( @@ -282,10 +293,7 @@ impl<'tcx> InferCtxt<'tcx> { ) .ty; - if let Err(e) = definition_ty.error_reported() { - return Ty::new_error(self.tcx, e); - } - - definition_ty + definition_ty.error_reported()?; + Ok(definition_ty) } } diff --git a/tests/crashes/135528.rs b/tests/crashes/135528.rs deleted file mode 100644 index 171550e209e5..000000000000 --- a/tests/crashes/135528.rs +++ /dev/null @@ -1,20 +0,0 @@ -//@ known-bug: #135528 -//@ compile-flags: -Zvalidate-mir -Zinline-mir=yes -#![feature(type_alias_impl_trait)] -type Tait = impl Copy; - -fn set(x: &isize) -> isize { - *x -} - -#[define_opaque(Tait)] -fn d(x: Tait) { - set(x); -} - -#[define_opaque(Tait)] -fn other_define() -> Tait { - () -} - -fn main() {} diff --git a/tests/ui/impl-trait/issues/type-error-post-normalization-test.rs b/tests/ui/impl-trait/issues/type-error-post-normalization-test.rs new file mode 100644 index 000000000000..0108bb236113 --- /dev/null +++ b/tests/ui/impl-trait/issues/type-error-post-normalization-test.rs @@ -0,0 +1,24 @@ +//@ compile-flags: -Zvalidate-mir -Zinline-mir=yes + +// This previously introduced a `{type_error}`` in the MIR body +// during the `PostAnalysisNormalize` pass. While the underlying issue +// #135528 did not get fixed, this reproducer no longer ICEs. + +#![feature(type_alias_impl_trait)] +type Tait = impl Copy; + +fn set(x: &isize) -> isize { + *x +} + +#[define_opaque(Tait)] +fn d(x: Tait) { + set(x); +} + +#[define_opaque(Tait)] +fn other_define() -> Tait { + () //~^ ERROR concrete type differs from previous defining opaque type use +} + +fn main() {} diff --git a/tests/ui/impl-trait/issues/type-error-post-normalization-test.stderr b/tests/ui/impl-trait/issues/type-error-post-normalization-test.stderr new file mode 100644 index 000000000000..7d63c1cfbd6a --- /dev/null +++ b/tests/ui/impl-trait/issues/type-error-post-normalization-test.stderr @@ -0,0 +1,14 @@ +error: concrete type differs from previous defining opaque type use + --> $DIR/type-error-post-normalization-test.rs:20:22 + | +LL | fn other_define() -> Tait { + | ^^^^ expected `&isize`, got `()` + | +note: previous use here + --> $DIR/type-error-post-normalization-test.rs:16:9 + | +LL | set(x); + | ^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/rfcs/type-alias-impl-trait/higher-ranked-regions-basic.rs b/tests/ui/rfcs/type-alias-impl-trait/higher-ranked-regions-basic.rs index 4cf2d1ac4a6b..80a4ab35527a 100644 --- a/tests/ui/rfcs/type-alias-impl-trait/higher-ranked-regions-basic.rs +++ b/tests/ui/rfcs/type-alias-impl-trait/higher-ranked-regions-basic.rs @@ -41,6 +41,7 @@ mod capture_tait_complex_pass { #[define_opaque(Opq2)] fn test() -> Opq2 {} //~^ ERROR: expected generic lifetime parameter, found `'a` + //~| ERROR: expected generic lifetime parameter, found `'b` } // Same as the above, but make sure that different placeholder regions are not equal. @@ -80,6 +81,7 @@ mod constrain_pass { #[define_opaque(Opq2)] fn test() -> Opq2 {} //~^ ERROR: expected generic lifetime parameter, found `'a` + //~| ERROR: expected generic lifetime parameter, found `'a` } fn main() {} diff --git a/tests/ui/rfcs/type-alias-impl-trait/higher-ranked-regions-basic.stderr b/tests/ui/rfcs/type-alias-impl-trait/higher-ranked-regions-basic.stderr index 3614fc8f45c7..665b9a916965 100644 --- a/tests/ui/rfcs/type-alias-impl-trait/higher-ranked-regions-basic.stderr +++ b/tests/ui/rfcs/type-alias-impl-trait/higher-ranked-regions-basic.stderr @@ -36,8 +36,17 @@ LL | type Opq1<'a> = impl for<'b> Trait<'b, Ty = Opq0<'b>>; // <- Note 'b LL | fn test() -> Opq2 {} | ^^ +error[E0792]: expected generic lifetime parameter, found `'b` + --> $DIR/higher-ranked-regions-basic.rs:42:23 + | +LL | type Opq0<'a> = impl Sized; + | -- this generic parameter must be used with a generic lifetime parameter +... +LL | fn test() -> Opq2 {} + | ^^ + error[E0700]: hidden type for `capture_tait_complex_fail::Opq0<'a>` captures lifetime that does not appear in bounds - --> $DIR/higher-ranked-regions-basic.rs:53:23 + --> $DIR/higher-ranked-regions-basic.rs:54:23 | LL | type Opq0<'a> = impl Sized; | ---------- opaque type defined here @@ -48,7 +57,7 @@ LL | fn test() -> Opq2 {} | ^^ error[E0792]: expected generic lifetime parameter, found `'a` - --> $DIR/higher-ranked-regions-basic.rs:62:65 + --> $DIR/higher-ranked-regions-basic.rs:63:65 | LL | type Opq0<'a, 'b> = impl Sized; | -- this generic parameter must be used with a generic lifetime parameter @@ -57,7 +66,7 @@ LL | fn test() -> impl for<'a> Trait<'a, Ty = Opq0<'a, 'static>> {} | ^^ error[E0792]: expected generic lifetime parameter, found `'a` - --> $DIR/higher-ranked-regions-basic.rs:71:60 + --> $DIR/higher-ranked-regions-basic.rs:72:60 | LL | type Opq0<'a, 'b> = impl Sized; | -- this generic parameter must be used with a generic lifetime parameter @@ -66,7 +75,7 @@ LL | fn test() -> impl for<'a> Trait<'a, Ty = Opq0<'a, 'a>> {} | ^^ error[E0792]: expected generic lifetime parameter, found `'a` - --> $DIR/higher-ranked-regions-basic.rs:81:23 + --> $DIR/higher-ranked-regions-basic.rs:82:23 | LL | type Opq1<'a> = impl for<'b> Trait<'b, Ty = Opq0<'a, 'b>>; | -- this generic parameter must be used with a generic lifetime parameter @@ -74,7 +83,16 @@ LL | type Opq1<'a> = impl for<'b> Trait<'b, Ty = Opq0<'a, 'b>>; LL | fn test() -> Opq2 {} | ^^ -error: aborting due to 8 previous errors +error[E0792]: expected generic lifetime parameter, found `'a` + --> $DIR/higher-ranked-regions-basic.rs:82:23 + | +LL | type Opq0<'a, 'b> = impl Sized; + | -- this generic parameter must be used with a generic lifetime parameter +... +LL | fn test() -> Opq2 {} + | ^^ + +error: aborting due to 10 previous errors Some errors have detailed explanations: E0700, E0792. For more information about an error, try `rustc --explain E0700`. diff --git a/tests/ui/type-alias-impl-trait/hkl_forbidden.rs b/tests/ui/type-alias-impl-trait/hkl_forbidden.rs index 994adc476e24..72da2af96b8d 100644 --- a/tests/ui/type-alias-impl-trait/hkl_forbidden.rs +++ b/tests/ui/type-alias-impl-trait/hkl_forbidden.rs @@ -8,7 +8,9 @@ type Opaque<'a> = impl Sized + 'a; #[define_opaque(Opaque)] fn test(s: &str) -> (impl Fn(&str) -> Opaque<'_>, impl Fn(&str) -> Opaque<'_>) { - (id, id) //~ ERROR expected generic lifetime parameter, found `'_` + (id, id) + //~^ ERROR expected generic lifetime parameter, found `'_` + //~| ERROR expected generic lifetime parameter, found `'_` } fn id2<'a, 'b>(s: (&'a str, &'b str)) -> (&'a str, &'b str) { @@ -19,7 +21,9 @@ type Opaque2<'a> = impl Sized + 'a; #[define_opaque(Opaque2)] fn test2() -> impl for<'a, 'b> Fn((&'a str, &'b str)) -> (Opaque2<'a>, Opaque2<'b>) { - id2 //~ ERROR expected generic lifetime parameter, found `'a` + id2 + //~^ ERROR expected generic lifetime parameter, found `'a` + //~| ERROR expected generic lifetime parameter, found `'b` } type Opaque3<'a> = impl Sized + 'a; diff --git a/tests/ui/type-alias-impl-trait/hkl_forbidden.stderr b/tests/ui/type-alias-impl-trait/hkl_forbidden.stderr index d404d60f31ed..315bdfb2272f 100644 --- a/tests/ui/type-alias-impl-trait/hkl_forbidden.stderr +++ b/tests/ui/type-alias-impl-trait/hkl_forbidden.stderr @@ -7,8 +7,28 @@ LL | type Opaque<'a> = impl Sized + 'a; LL | (id, id) | ^^^^^^^^ +error[E0792]: expected generic lifetime parameter, found `'_` + --> $DIR/hkl_forbidden.rs:11:5 + | +LL | type Opaque<'a> = impl Sized + 'a; + | -- this generic parameter must be used with a generic lifetime parameter +... +LL | (id, id) + | ^^^^^^^^ + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + error[E0792]: expected generic lifetime parameter, found `'a` - --> $DIR/hkl_forbidden.rs:22:5 + --> $DIR/hkl_forbidden.rs:24:5 + | +LL | type Opaque2<'a> = impl Sized + 'a; + | -- this generic parameter must be used with a generic lifetime parameter +... +LL | id2 + | ^^^ + +error[E0792]: expected generic lifetime parameter, found `'b` + --> $DIR/hkl_forbidden.rs:24:5 | LL | type Opaque2<'a> = impl Sized + 'a; | -- this generic parameter must be used with a generic lifetime parameter @@ -17,7 +37,7 @@ LL | id2 | ^^^ error[E0792]: expected generic lifetime parameter, found `'_` - --> $DIR/hkl_forbidden.rs:29:5 + --> $DIR/hkl_forbidden.rs:33:5 | LL | type Opaque3<'a> = impl Sized + 'a; | -- this generic parameter must be used with a generic lifetime parameter @@ -26,7 +46,7 @@ LL | (id, s) | ^^^^^^^ error[E0792]: expected generic lifetime parameter, found `'_` - --> $DIR/hkl_forbidden.rs:35:5 + --> $DIR/hkl_forbidden.rs:39:5 | LL | type Opaque4<'a> = impl Sized + 'a; | -- this generic parameter must be used with a generic lifetime parameter @@ -35,7 +55,7 @@ LL | (s, id) | ^^^^^^^ error[E0792]: expected generic lifetime parameter, found `'a` - --> $DIR/hkl_forbidden.rs:41:5 + --> $DIR/hkl_forbidden.rs:45:5 | LL | type Inner<'a> = impl Sized; | -- this generic parameter must be used with a generic lifetime parameter @@ -43,6 +63,6 @@ LL | type Inner<'a> = impl Sized; LL | |x| x | ^^^^^ -error: aborting due to 5 previous errors +error: aborting due to 7 previous errors For more information about this error, try `rustc --explain E0792`. diff --git a/tests/ui/type-alias-impl-trait/param_mismatch2.rs b/tests/ui/type-alias-impl-trait/param_mismatch2.rs index f6a997114110..94c495cb3307 100644 --- a/tests/ui/type-alias-impl-trait/param_mismatch2.rs +++ b/tests/ui/type-alias-impl-trait/param_mismatch2.rs @@ -11,7 +11,9 @@ type Opaque<'a> = impl Sized + 'a; #[define_opaque(Opaque)] fn test(s: &str) -> (impl Fn(&str) -> Opaque<'_>, impl Fn(&str) -> Opaque<'_>) { - (id, id) //~ ERROR: expected generic lifetime parameter, found `'_` + (id, id) + //~^ ERROR: expected generic lifetime parameter, found `'_` + //~| ERROR: expected generic lifetime parameter, found `'_` } fn main() {} diff --git a/tests/ui/type-alias-impl-trait/param_mismatch2.stderr b/tests/ui/type-alias-impl-trait/param_mismatch2.stderr index f5ecade2f02e..5c8a8af6501e 100644 --- a/tests/ui/type-alias-impl-trait/param_mismatch2.stderr +++ b/tests/ui/type-alias-impl-trait/param_mismatch2.stderr @@ -7,6 +7,17 @@ LL | type Opaque<'a> = impl Sized + 'a; LL | (id, id) | ^^^^^^^^ -error: aborting due to 1 previous error +error[E0792]: expected generic lifetime parameter, found `'_` + --> $DIR/param_mismatch2.rs:14:5 + | +LL | type Opaque<'a> = impl Sized + 'a; + | -- this generic parameter must be used with a generic lifetime parameter +... +LL | (id, id) + | ^^^^^^^^ + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0792`. diff --git a/tests/ui/type-alias-impl-trait/param_mismatch3.rs b/tests/ui/type-alias-impl-trait/param_mismatch3.rs index 17b6f8bce2a8..1514dd66db93 100644 --- a/tests/ui/type-alias-impl-trait/param_mismatch3.rs +++ b/tests/ui/type-alias-impl-trait/param_mismatch3.rs @@ -11,7 +11,9 @@ type Opaque<'a> = impl Sized + 'a; #[define_opaque(Opaque)] fn test() -> impl for<'a, 'b> Fn((&'a str, &'b str)) -> (Opaque<'a>, Opaque<'b>) { - id2 //~ ERROR expected generic lifetime parameter, found `'a` + id2 + //~^ ERROR expected generic lifetime parameter, found `'a` + //~| ERROR expected generic lifetime parameter, found `'b` } fn id(s: &str) -> &str { diff --git a/tests/ui/type-alias-impl-trait/param_mismatch3.stderr b/tests/ui/type-alias-impl-trait/param_mismatch3.stderr index 7565bbfc6ff0..76be07ce38f5 100644 --- a/tests/ui/type-alias-impl-trait/param_mismatch3.stderr +++ b/tests/ui/type-alias-impl-trait/param_mismatch3.stderr @@ -7,8 +7,17 @@ LL | type Opaque<'a> = impl Sized + 'a; LL | id2 | ^^^ +error[E0792]: expected generic lifetime parameter, found `'b` + --> $DIR/param_mismatch3.rs:14:5 + | +LL | type Opaque<'a> = impl Sized + 'a; + | -- this generic parameter must be used with a generic lifetime parameter +... +LL | id2 + | ^^^ + error[E0792]: expected generic lifetime parameter, found `'_` - --> $DIR/param_mismatch3.rs:25:5 + --> $DIR/param_mismatch3.rs:27:5 | LL | type Opaque2<'a> = impl Sized + 'a; | -- this generic parameter must be used with a generic lifetime parameter @@ -16,6 +25,6 @@ LL | type Opaque2<'a> = impl Sized + 'a; LL | (id, s) | ^^^^^^^ -error: aborting due to 2 previous errors +error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0792`. From c44fe70d0384d3d83d8f49f4e3045ed3e7e209b9 Mon Sep 17 00:00:00 2001 From: Makai Date: Sat, 9 Aug 2025 01:19:42 +0800 Subject: [PATCH 077/195] fix missing parenthesis in pretty discriminant --- compiler/rustc_public/src/mir/pretty.rs | 2 +- tests/ui/rustc_public-ir-print/async-closure.stdout | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_public/src/mir/pretty.rs b/compiler/rustc_public/src/mir/pretty.rs index 3183c020772a..9dd1ce4de0ea 100644 --- a/compiler/rustc_public/src/mir/pretty.rs +++ b/compiler/rustc_public/src/mir/pretty.rs @@ -100,7 +100,7 @@ fn pretty_statement(writer: &mut W, statement: &StatementKind) -> io:: writeln!(writer, "{INDENT}FakeRead({cause:?}, {place:?});") } StatementKind::SetDiscriminant { place, variant_index } => { - writeln!(writer, "{INDENT}discriminant({place:?} = {};", variant_index.to_index()) + writeln!(writer, "{INDENT}discriminant({place:?}) = {};", variant_index.to_index()) } StatementKind::Deinit(place) => writeln!(writer, "Deinit({place:?};"), StatementKind::StorageLive(local) => { diff --git a/tests/ui/rustc_public-ir-print/async-closure.stdout b/tests/ui/rustc_public-ir-print/async-closure.stdout index 4afb15af7a93..73e9b8fc097a 100644 --- a/tests/ui/rustc_public-ir-print/async-closure.stdout +++ b/tests/ui/rustc_public-ir-print/async-closure.stdout @@ -63,7 +63,7 @@ fn foo::{closure#0}::{closure#0}(_1: Pin<&mut {async closure body@$DIR/async-clo StorageDead(_3); _0 = std::task::Poll::Ready(move _5); _10 = CopyForDeref((_1.0: &mut {async closure body@$DIR/async-closure.rs:9:22: 11:6})); - discriminant((*_10) = 1; + discriminant((*_10)) = 1; return; } bb2: { @@ -101,7 +101,7 @@ fn foo::{closure#0}::{synthetic#0}(_1: Pin<&mut {async closure body@$DIR/async-c StorageDead(_3); _0 = std::task::Poll::Ready(move _5); _10 = CopyForDeref((_1.0: &mut {async closure body@$DIR/async-closure.rs:9:22: 11:6})); - discriminant((*_10) = 1; + discriminant((*_10)) = 1; return; } bb2: { From 111a0e8f2347c7e5996fe1e63d9ade86fba81e01 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Fri, 8 Aug 2025 23:21:57 +0530 Subject: [PATCH 078/195] add parse_download_ci_llvm function and invoke from parse_inner --- src/bootstrap/src/core/build_steps/llvm.rs | 9 +- src/bootstrap/src/core/config/config.rs | 246 ++++++++++++++++++++- 2 files changed, 248 insertions(+), 7 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/llvm.rs b/src/bootstrap/src/core/build_steps/llvm.rs index 721ba6ca459e..513d7260a3ec 100644 --- a/src/bootstrap/src/core/build_steps/llvm.rs +++ b/src/bootstrap/src/core/build_steps/llvm.rs @@ -196,7 +196,10 @@ pub(crate) fn detect_llvm_freshness(config: &Config, is_git: bool) -> PathFreshn /// /// This checks the build triple platform to confirm we're usable at all, and if LLVM /// with/without assertions is available. -pub(crate) fn is_ci_llvm_available_for_target(config: &Config, asserts: bool) -> bool { +pub(crate) fn is_ci_llvm_available_for_target( + host_target: &TargetSelection, + asserts: bool, +) -> bool { // This is currently all tier 1 targets and tier 2 targets with host tools // (since others may not have CI artifacts) // https://doc.rust-lang.org/rustc/platform-support.html#tier-1 @@ -235,8 +238,8 @@ pub(crate) fn is_ci_llvm_available_for_target(config: &Config, asserts: bool) -> ("x86_64-unknown-netbsd", false), ]; - if !supported_platforms.contains(&(&*config.host_target.triple, asserts)) - && (asserts || !supported_platforms.contains(&(&*config.host_target.triple, true))) + if !supported_platforms.contains(&(&*host_target.triple, asserts)) + && (asserts || !supported_platforms.contains(&(&*host_target.triple, true))) { return false; } diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index e7b7ab4f4d4b..bfee43148c0e 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -1200,8 +1200,19 @@ impl Config { config.llvm_enable_warnings = llvm_enable_warnings.unwrap_or(false); config.llvm_build_config = llvm_build_config.clone().unwrap_or(Default::default()); - config.llvm_from_ci = - config.parse_download_ci_llvm(llvm_download_ci_llvm, config.llvm_assertions); + config.llvm_from_ci = parse_download_ci_llvm( + &config.exec_ctx, + &config.submodules, + &config.stage0_metadata, + &config.src, + config.path_modification_cache.clone(), + &config.host_target, + &config.download_rustc_commit, + &config.rust_info, + config.is_running_on_ci, + llvm_download_ci_llvm, + config.llvm_assertions, + ); if config.llvm_from_ci { let warn = |option: &str| { @@ -1902,7 +1913,11 @@ impl Config { let has_changes = self.has_changes_from_upstream(LLVM_INVALIDATION_PATHS); // Return false if there are untracked changes, otherwise check if CI LLVM is available. - if has_changes { false } else { llvm::is_ci_llvm_available_for_target(self, asserts) } + if has_changes { + false + } else { + llvm::is_ci_llvm_available_for_target(&self.host_target, asserts) + } }; match download_ci_llvm { @@ -1921,7 +1936,7 @@ impl Config { } // If download-ci-llvm=true we also want to check that CI llvm is available - b && llvm::is_ci_llvm_available_for_target(self, asserts) + b && llvm::is_ci_llvm_available_for_target(&self.host_target, asserts) } StringOrBool::String(s) if s == "if-unchanged" => if_unchanged(), StringOrBool::String(other) => { @@ -2462,3 +2477,226 @@ pub fn git_config(stage0_metadata: &build_helper::stage0_parser::Stage0) -> GitC git_merge_commit_email: &stage0_metadata.config.git_merge_commit_email, } } + +pub fn parse_download_ci_llvm( + exec_ctx: &ExecutionContext, + submodules: &Option, + stage0_metadata: &build_helper::stage0_parser::Stage0, + src: &Path, + path_modification_cache: Arc, PathFreshness>>>, + host_target: &TargetSelection, + download_rustc_commit: &Option, + rust_info: &channel::GitInfo, + is_running_on_ci: bool, + download_ci_llvm: Option, + asserts: bool, +) -> bool { + // We don't ever want to use `true` on CI, as we should not + // download upstream artifacts if there are any local modifications. + let default = if is_running_on_ci { + StringOrBool::String("if-unchanged".to_string()) + } else { + StringOrBool::Bool(true) + }; + let download_ci_llvm = download_ci_llvm.unwrap_or(default); + + let if_unchanged = || { + if rust_info.is_from_tarball() { + // Git is needed for running "if-unchanged" logic. + println!("ERROR: 'if-unchanged' is only compatible with Git managed sources."); + crate::exit!(1); + } + + // Fetching the LLVM submodule is unnecessary for self-tests. + #[cfg(not(test))] + update_submodule(submodules, exec_ctx, src, rust_info, "src/llvm-project"); + + // Check for untracked changes in `src/llvm-project` and other important places. + let has_changes = has_changes_from_upstream( + stage0_metadata, + src, + path_modification_cache, + LLVM_INVALIDATION_PATHS, + ); + + // Return false if there are untracked changes, otherwise check if CI LLVM is available. + if has_changes { + false + } else { + llvm::is_ci_llvm_available_for_target(host_target, asserts) + } + }; + + match download_ci_llvm { + StringOrBool::Bool(b) => { + if !b && download_rustc_commit.is_some() { + panic!( + "`llvm.download-ci-llvm` cannot be set to `false` if `rust.download-rustc` is set to `true` or `if-unchanged`." + ); + } + + if b && is_running_on_ci { + // On CI, we must always rebuild LLVM if there were any modifications to it + panic!( + "`llvm.download-ci-llvm` cannot be set to `true` on CI. Use `if-unchanged` instead." + ); + } + + // If download-ci-llvm=true we also want to check that CI llvm is available + b && llvm::is_ci_llvm_available_for_target(host_target, asserts) + } + StringOrBool::String(s) if s == "if-unchanged" => if_unchanged(), + StringOrBool::String(other) => { + panic!("unrecognized option for download-ci-llvm: {other:?}") + } + } +} + +pub fn has_changes_from_upstream( + stage0_metadata: &build_helper::stage0_parser::Stage0, + src: &Path, + path_modification_cache: Arc, PathFreshness>>>, + paths: &[&'static str], +) -> bool { + match check_path_modifications_(stage0_metadata, src, path_modification_cache, paths) { + PathFreshness::LastModifiedUpstream { .. } => false, + PathFreshness::HasLocalModifications { .. } | PathFreshness::MissingUpstream => true, + } +} + +#[cfg_attr( + feature = "tracing", + instrument( + level = "trace", + name = "Config::update_submodule", + skip_all, + fields(relative_path = ?relative_path), + ), +)] +pub(crate) fn update_submodule( + submodules: &Option, + exec_ctx: &ExecutionContext, + src: &Path, + rust_info: &channel::GitInfo, + relative_path: &str, +) { + if rust_info.is_from_tarball() || !submodules_(submodules, rust_info) { + return; + } + + let absolute_path = src.join(relative_path); + + // NOTE: This check is required because `jj git clone` doesn't create directories for + // submodules, they are completely ignored. The code below assumes this directory exists, + // so create it here. + if !absolute_path.exists() { + t!(fs::create_dir_all(&absolute_path)); + } + + // NOTE: The check for the empty directory is here because when running x.py the first time, + // the submodule won't be checked out. Check it out now so we can build it. + if !git_info(exec_ctx, false, &absolute_path).is_managed_git_subrepository() + && !helpers::dir_is_empty(&absolute_path) + { + return; + } + + // Submodule updating actually happens during in the dry run mode. We need to make sure that + // all the git commands below are actually executed, because some follow-up code + // in bootstrap might depend on the submodules being checked out. Furthermore, not all + // the command executions below work with an empty output (produced during dry run). + // Therefore, all commands below are marked with `run_in_dry_run()`, so that they also run in + // dry run mode. + let submodule_git = || { + let mut cmd = helpers::git(Some(&absolute_path)); + cmd.run_in_dry_run(); + cmd + }; + + // Determine commit checked out in submodule. + let checked_out_hash = + submodule_git().args(["rev-parse", "HEAD"]).run_capture_stdout(exec_ctx).stdout(); + let checked_out_hash = checked_out_hash.trim_end(); + // Determine commit that the submodule *should* have. + let recorded = helpers::git(Some(src)) + .run_in_dry_run() + .args(["ls-tree", "HEAD"]) + .arg(relative_path) + .run_capture_stdout(exec_ctx) + .stdout(); + + let actual_hash = recorded + .split_whitespace() + .nth(2) + .unwrap_or_else(|| panic!("unexpected output `{recorded}`")); + + if actual_hash == checked_out_hash { + // already checked out + return; + } + + println!("Updating submodule {relative_path}"); + + helpers::git(Some(src)) + .allow_failure() + .run_in_dry_run() + .args(["submodule", "-q", "sync"]) + .arg(relative_path) + .run(exec_ctx); + + // Try passing `--progress` to start, then run git again without if that fails. + let update = |progress: bool| { + // Git is buggy and will try to fetch submodules from the tracking branch for *this* repository, + // even though that has no relation to the upstream for the submodule. + let current_branch = helpers::git(Some(src)) + .allow_failure() + .run_in_dry_run() + .args(["symbolic-ref", "--short", "HEAD"]) + .run_capture(exec_ctx); + + let mut git = helpers::git(Some(&src)).allow_failure(); + git.run_in_dry_run(); + if current_branch.is_success() { + // If there is a tag named after the current branch, git will try to disambiguate by prepending `heads/` to the branch name. + // This syntax isn't accepted by `branch.{branch}`. Strip it. + let branch = current_branch.stdout(); + let branch = branch.trim(); + let branch = branch.strip_prefix("heads/").unwrap_or(branch); + git.arg("-c").arg(format!("branch.{branch}.remote=origin")); + } + git.args(["submodule", "update", "--init", "--recursive", "--depth=1"]); + if progress { + git.arg("--progress"); + } + git.arg(relative_path); + git + }; + if !update(true).allow_failure().run(exec_ctx) { + update(false).allow_failure().run(exec_ctx); + } + + // Save any local changes, but avoid running `git stash pop` if there are none (since it will exit with an error). + // diff-index reports the modifications through the exit status + let has_local_modifications = + !submodule_git().allow_failure().args(["diff-index", "--quiet", "HEAD"]).run(exec_ctx); + if has_local_modifications { + submodule_git().allow_failure().args(["stash", "push"]).run(exec_ctx); + } + + submodule_git().allow_failure().args(["reset", "-q", "--hard"]).run(exec_ctx); + submodule_git().allow_failure().args(["clean", "-qdfx"]).run(exec_ctx); + + if has_local_modifications { + submodule_git().allow_failure().args(["stash", "pop"]).run(exec_ctx); + } +} + +pub fn git_info(exec_ctx: &ExecutionContext, omit_git_hash: bool, dir: &Path) -> GitInfo { + GitInfo::new(omit_git_hash, dir, exec_ctx) +} + +pub fn submodules_(submodules: &Option, rust_info: &channel::GitInfo) -> bool { + // If not specified in config, the default is to only manage + // submodules if we're currently inside a git repository. + submodules.unwrap_or(rust_info.is_managed_git_subrepository()) +} From b2d524c43d113bba72e0a23b4a97cbbeb6eb39bb Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Fri, 8 Aug 2025 18:46:02 +0000 Subject: [PATCH 079/195] Recover for PAT = EXPR {} --- compiler/rustc_parse/messages.ftl | 2 +- compiler/rustc_parse/src/errors.rs | 11 ++++---- compiler/rustc_parse/src/parser/expr.rs | 2 ++ .../ui/suggestions/for-loop-missing-in.fixed | 7 +++-- tests/ui/suggestions/for-loop-missing-in.rs | 7 +++-- .../ui/suggestions/for-loop-missing-in.stderr | 26 ++++++++++++++----- 6 files changed, 33 insertions(+), 22 deletions(-) diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl index 7059ffbf375f..aaf1b6c05bf4 100644 --- a/compiler/rustc_parse/messages.ftl +++ b/compiler/rustc_parse/messages.ftl @@ -642,7 +642,7 @@ parse_missing_for_in_trait_impl = missing `for` in a trait impl .suggestion = add `for` here parse_missing_in_in_for_loop = missing `in` in `for` loop - .use_in_not_of = try using `in` here instead + .use_in = try using `in` here instead .add_in = try adding `in` here parse_missing_let_before_mut = missing keyword diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index 48ff0394d463..ddb2c545c787 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -585,14 +585,13 @@ pub(crate) struct MissingInInForLoop { #[derive(Subdiagnostic)] pub(crate) enum MissingInInForLoopSub { + // User wrote `for pat of expr {}` // Has been misleading, at least in the past (closed Issue #48492), thus maybe-incorrect - #[suggestion( - parse_use_in_not_of, - style = "verbose", - applicability = "maybe-incorrect", - code = "in" - )] + #[suggestion(parse_use_in, style = "verbose", applicability = "maybe-incorrect", code = "in")] InNotOf(#[primary_span] Span), + // User wrote `for pat = expr {}` + #[suggestion(parse_use_in, style = "verbose", applicability = "maybe-incorrect", code = "in")] + InNotEq(#[primary_span] Span), #[suggestion(parse_add_in, style = "verbose", applicability = "maybe-incorrect", code = " in ")] AddIn(#[primary_span] Span), } diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 35b987cf50fa..cbf6b78431cb 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -3028,6 +3028,8 @@ impl<'a> Parser<'a> { let span = self.token.span; self.bump(); (span, errors::MissingInInForLoopSub::InNotOf) + } else if self.eat(exp!(Eq)) { + (self.prev_token.span, errors::MissingInInForLoopSub::InNotEq) } else { (self.prev_token.span.between(self.token.span), errors::MissingInInForLoopSub::AddIn) }; diff --git a/tests/ui/suggestions/for-loop-missing-in.fixed b/tests/ui/suggestions/for-loop-missing-in.fixed index ff376b5a52c5..396c3ff87fff 100644 --- a/tests/ui/suggestions/for-loop-missing-in.fixed +++ b/tests/ui/suggestions/for-loop-missing-in.fixed @@ -1,8 +1,7 @@ //@ run-rustfix fn main() { - for _i in 0..2 { //~ ERROR missing `in` - } - for _i in 0..2 { //~ ERROR missing `in` - } + for _i in 0..2 {} //~ ERROR missing `in` + for _i in 0..2 {} //~ ERROR missing `in` + for _i in 0..2 {} //~ ERROR missing `in` } diff --git a/tests/ui/suggestions/for-loop-missing-in.rs b/tests/ui/suggestions/for-loop-missing-in.rs index 28c12aec665a..6c2092106cb2 100644 --- a/tests/ui/suggestions/for-loop-missing-in.rs +++ b/tests/ui/suggestions/for-loop-missing-in.rs @@ -1,8 +1,7 @@ //@ run-rustfix fn main() { - for _i 0..2 { //~ ERROR missing `in` - } - for _i of 0..2 { //~ ERROR missing `in` - } + for _i 0..2 {} //~ ERROR missing `in` + for _i of 0..2 {} //~ ERROR missing `in` + for _i = 0..2 {} //~ ERROR missing `in` } diff --git a/tests/ui/suggestions/for-loop-missing-in.stderr b/tests/ui/suggestions/for-loop-missing-in.stderr index 4e0cb229d50a..b9c8c3934068 100644 --- a/tests/ui/suggestions/for-loop-missing-in.stderr +++ b/tests/ui/suggestions/for-loop-missing-in.stderr @@ -1,25 +1,37 @@ error: missing `in` in `for` loop --> $DIR/for-loop-missing-in.rs:4:11 | -LL | for _i 0..2 { +LL | for _i 0..2 {} | ^ | help: try adding `in` here | -LL | for _i in 0..2 { +LL | for _i in 0..2 {} | ++ error: missing `in` in `for` loop - --> $DIR/for-loop-missing-in.rs:6:12 + --> $DIR/for-loop-missing-in.rs:5:12 | -LL | for _i of 0..2 { +LL | for _i of 0..2 {} | ^^ | help: try using `in` here instead | -LL - for _i of 0..2 { -LL + for _i in 0..2 { +LL - for _i of 0..2 {} +LL + for _i in 0..2 {} | -error: aborting due to 2 previous errors +error: missing `in` in `for` loop + --> $DIR/for-loop-missing-in.rs:6:12 + | +LL | for _i = 0..2 {} + | ^ + | +help: try using `in` here instead + | +LL - for _i = 0..2 {} +LL + for _i in 0..2 {} + | + +error: aborting due to 3 previous errors From d47150111d8f76b900d23e314c1b63093361e740 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Fri, 18 Jul 2025 19:48:53 +0000 Subject: [PATCH 080/195] Check coroutine upvars and in dtorck constraint --- .../src/traits/query/dropck_outlives.rs | 74 +++++++++++++------ tests/ui/async-await/drop-live-upvar.rs | 23 ++++++ tests/ui/async-await/drop-live-upvar.stderr | 22 ++++++ 3 files changed, 95 insertions(+), 24 deletions(-) create mode 100644 tests/ui/async-await/drop-live-upvar.rs create mode 100644 tests/ui/async-await/drop-live-upvar.stderr diff --git a/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs b/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs index b1b331d1b61e..de4045328997 100644 --- a/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs +++ b/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs @@ -319,39 +319,65 @@ pub fn dtorck_constraint_for_ty_inner<'tcx>( } ty::Coroutine(def_id, args) => { - // rust-lang/rust#49918: types can be constructed, stored - // in the interior, and sit idle when coroutine yields - // (and is subsequently dropped). + // rust-lang/rust#49918: Locals can be stored across await points in the coroutine, + // called interior/witness types. Since we do not compute these witnesses until after + // building MIR, we consider all coroutines to unconditionally require a drop during + // MIR building. However, considering the coroutine to unconditionally require a drop + // here may unnecessarily require its upvars' regions to be live when they don't need + // to be, leading to borrowck errors: . // - // It would be nice to descend into interior of a - // coroutine to determine what effects dropping it might - // have (by looking at any drop effects associated with - // its interior). + // Here, we implement a more precise approximation for the coroutine's dtorck constraint + // by considering whether any of the interior types needs drop. Note that this is still + // an approximation because the coroutine interior has its regions erased, so we must add + // *all* of the upvars to live types set if we find that *any* interior type needs drop. + // This is because any of the regions captured in the upvars may be stored in the interior, + // which then has its regions replaced by a binder (conceptually erasing the regions), + // so there's no way to enforce that the precise region in the interior type is live + // since we've lost that information by this point. // - // However, the interior's representation uses things like - // CoroutineWitness that explicitly assume they are not - // traversed in such a manner. So instead, we will - // simplify things for now by treating all coroutines as - // if they were like trait objects, where its upvars must - // all be alive for the coroutine's (potential) - // destructor. + // Note also that this check requires that the coroutine's upvars are use-live, since + // a region from a type that does not have a destructor that was captured in an upvar + // may flow into an interior type with a destructor. This is stronger than requiring + // the upvars are drop-live. // - // In particular, skipping over `_interior` is safe - // because any side-effects from dropping `_interior` can - // only take place through references with lifetimes - // derived from lifetimes attached to the upvars and resume - // argument, and we *do* incorporate those here. + // For example, if we capture two upvar references `&'1 (), &'2 ()` and have some type + // in the interior, `for<'r> { NeedsDrop<'r> }`, we have no way to tell whether the + // region `'r` came from the `'1` or `'2` region, so we require both are live. This + // could even be unnecessary if `'r` was actually a `'static` region or some region + // local to the coroutine! That's why it's an approximation. let args = args.as_coroutine(); - // While we conservatively assume that all coroutines require drop - // to avoid query cycles during MIR building, we can check the actual - // witness during borrowck to avoid unnecessary liveness constraints. + // Note that we don't care about whether the resume type has any drops since this is + // redundant; there is no storage for the resume type, so if it is actually stored + // in the interior, we'll already detect the need for a drop by checking the interior. let typing_env = tcx.erase_regions(typing_env); - if tcx.mir_coroutine_witnesses(def_id).is_some_and(|witness| { + let needs_drop = tcx.mir_coroutine_witnesses(def_id).is_some_and(|witness| { witness.field_tys.iter().any(|field| field.ty.needs_drop(tcx, typing_env)) - }) { + }); + if needs_drop { + // Pushing types directly to `constraints.outlives` is equivalent + // to requiring them to be use-live, since if we were instead to + // recurse on them like we do below, we only end up collecting the + // types that are relevant for drop-liveness. constraints.outlives.extend(args.upvar_tys().iter().map(ty::GenericArg::from)); constraints.outlives.push(args.resume_ty().into()); + } else { + // Even if a witness type doesn't need a drop, we still require that + // the upvars are drop-live. This is only needed if we aren't already + // counting *all* of the upvars as use-live above, since use-liveness + // is a *stronger requirement* than drop-liveness. Recursing here + // unconditionally would just be collecting duplicated types for no + // reason. + for ty in args.upvar_tys() { + dtorck_constraint_for_ty_inner( + tcx, + typing_env, + span, + depth + 1, + ty, + constraints, + ); + } } } diff --git a/tests/ui/async-await/drop-live-upvar.rs b/tests/ui/async-await/drop-live-upvar.rs new file mode 100644 index 000000000000..8e881f729b91 --- /dev/null +++ b/tests/ui/async-await/drop-live-upvar.rs @@ -0,0 +1,23 @@ +//@ edition: 2018 +// Regression test for . + +struct NeedsDrop<'a>(&'a Vec); + +async fn await_point() {} + +impl Drop for NeedsDrop<'_> { + fn drop(&mut self) {} +} + +fn foo() { + let v = vec![1, 2, 3]; + let x = NeedsDrop(&v); + let c = async { + std::future::ready(()).await; + drop(x); + }; + drop(v); + //~^ ERROR cannot move out of `v` because it is borrowed +} + +fn main() {} diff --git a/tests/ui/async-await/drop-live-upvar.stderr b/tests/ui/async-await/drop-live-upvar.stderr new file mode 100644 index 000000000000..f804484536ba --- /dev/null +++ b/tests/ui/async-await/drop-live-upvar.stderr @@ -0,0 +1,22 @@ +error[E0505]: cannot move out of `v` because it is borrowed + --> $DIR/drop-live-upvar.rs:19:10 + | +LL | let v = vec![1, 2, 3]; + | - binding `v` declared here +LL | let x = NeedsDrop(&v); + | -- borrow of `v` occurs here +... +LL | drop(v); + | ^ move out of `v` occurs here +LL | +LL | } + | - borrow might be used here, when `c` is dropped and runs the destructor for coroutine + | +help: consider cloning the value if the performance cost is acceptable + | +LL | let x = NeedsDrop(&v.clone()); + | ++++++++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0505`. From 560e5dcbcf776e06302ecb2fa81f77313d514306 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Mon, 21 Jul 2025 18:35:34 +0000 Subject: [PATCH 081/195] Add test for upvar breakage --- .../drop-live-upvar-2.may_not_dangle.stderr | 18 +++++++++ tests/ui/async-await/drop-live-upvar-2.rs | 37 +++++++++++++++++++ 2 files changed, 55 insertions(+) create mode 100644 tests/ui/async-await/drop-live-upvar-2.may_not_dangle.stderr create mode 100644 tests/ui/async-await/drop-live-upvar-2.rs diff --git a/tests/ui/async-await/drop-live-upvar-2.may_not_dangle.stderr b/tests/ui/async-await/drop-live-upvar-2.may_not_dangle.stderr new file mode 100644 index 000000000000..34f6ba79246c --- /dev/null +++ b/tests/ui/async-await/drop-live-upvar-2.may_not_dangle.stderr @@ -0,0 +1,18 @@ +error[E0597]: `y` does not live long enough + --> $DIR/drop-live-upvar-2.rs:31:26 + | +LL | let y = (); + | - binding `y` declared here +LL | drop_me = Droppy(&y); + | ^^ borrowed value does not live long enough +... +LL | } + | - `y` dropped here while still borrowed +LL | } + | - borrow might be used here, when `fut` is dropped and runs the destructor for coroutine + | + = note: values in a scope are dropped in the opposite order they are defined + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0597`. diff --git a/tests/ui/async-await/drop-live-upvar-2.rs b/tests/ui/async-await/drop-live-upvar-2.rs new file mode 100644 index 000000000000..605db4c8f765 --- /dev/null +++ b/tests/ui/async-await/drop-live-upvar-2.rs @@ -0,0 +1,37 @@ +//@ revisions: may_dangle may_not_dangle +//@[may_dangle] check-pass +//@ edition: 2018 + +// Ensure that if a coroutine's interior has no drop types then we don't require the upvars to +// be *use-live*, but instead require them to be *drop-live*. In this case, `Droppy<&'?0 ()>` +// does not require that `'?0` is live for drops since the parameter is `#[may_dangle]` in +// the may_dangle revision, but not in the may_not_dangle revision. + +#![feature(dropck_eyepatch)] + +struct Droppy(T); + +#[cfg(may_dangle)] +unsafe impl<#[may_dangle] T> Drop for Droppy { + fn drop(&mut self) { + // This does not use `T` of course. + } +} + +#[cfg(may_not_dangle)] +impl Drop for Droppy { + fn drop(&mut self) {} +} + +fn main() { + let drop_me; + let fut; + { + let y = (); + drop_me = Droppy(&y); + //[may_not_dangle]~^ ERROR `y` does not live long enough + fut = async { + std::mem::drop(drop_me); + }; + } +} From cba591871db4a76b17ba330d800affb316df1cdb Mon Sep 17 00:00:00 2001 From: Aleksey Kliger Date: Fri, 8 Aug 2025 12:57:02 -0400 Subject: [PATCH 082/195] bootstrap: `x.py dist rustc-src` should keep LLVM's siphash --- src/bootstrap/src/core/build_steps/dist.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/bootstrap/src/core/build_steps/dist.rs b/src/bootstrap/src/core/build_steps/dist.rs index cbbfb6b6a110..6b3897c5c6ff 100644 --- a/src/bootstrap/src/core/build_steps/dist.rs +++ b/src/bootstrap/src/core/build_steps/dist.rs @@ -925,6 +925,8 @@ fn copy_src_dirs( "llvm-project\\cmake", "llvm-project/runtimes", "llvm-project\\runtimes", + "llvm-project/third-party", + "llvm-project\\third-party", ]; if spath.contains("llvm-project") && !spath.ends_with("llvm-project") @@ -933,6 +935,18 @@ fn copy_src_dirs( return false; } + // Keep only these third party libraries + const LLVM_THIRD_PARTY: &[&str] = + &["llvm-project/third-party/siphash", "llvm-project\\third-party\\siphash"]; + if (spath.starts_with("llvm-project/third-party") + || spath.starts_with("llvm-project\\third-party")) + && !(spath.ends_with("llvm-project/third-party") + || spath.ends_with("llvm-project\\third-party")) + && !LLVM_THIRD_PARTY.iter().any(|path| spath.contains(path)) + { + return false; + } + const LLVM_TEST: &[&str] = &["llvm-project/llvm/test", "llvm-project\\llvm\\test"]; if LLVM_TEST.iter().any(|path| spath.contains(path)) && (spath.ends_with(".ll") || spath.ends_with(".td") || spath.ends_with(".s")) From 87a09b2ad577fb6f9725dbc2e1adc5e75f783638 Mon Sep 17 00:00:00 2001 From: Daniel Paoliello Date: Fri, 8 Aug 2025 12:21:57 -0700 Subject: [PATCH 083/195] [win][arm64ec] Add '/machine:arm64ec' when linking LLVM as Arm64EC --- src/bootstrap/src/core/build_steps/llvm.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/bootstrap/src/core/build_steps/llvm.rs b/src/bootstrap/src/core/build_steps/llvm.rs index 721ba6ca459e..79772330a6d9 100644 --- a/src/bootstrap/src/core/build_steps/llvm.rs +++ b/src/bootstrap/src/core/build_steps/llvm.rs @@ -421,6 +421,13 @@ impl Step for Llvm { ldflags.shared.push(" -latomic"); } + if target.starts_with("arm64ec") { + // MSVC linker requires the -machine:arm64ec flag to be passed to + // know it's linking as Arm64EC (vs Arm64X). + ldflags.exe.push(" -machine:arm64ec"); + ldflags.shared.push(" -machine:arm64ec"); + } + if target.is_msvc() { cfg.define("CMAKE_MSVC_RUNTIME_LIBRARY", "MultiThreaded"); cfg.static_crt(true); From 7670cdeb709b33cb7d57a7e2fdb790f50cbe6858 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Thu, 7 Aug 2025 14:10:59 -0500 Subject: [PATCH 084/195] Refactor map_unit_fn lint --- compiler/rustc_lint/src/lints.rs | 3 +- compiler/rustc_lint/src/map_unit_fn.rs | 103 +++++++++---------------- compiler/rustc_span/src/symbol.rs | 1 + 3 files changed, 39 insertions(+), 68 deletions(-) diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index ac6147b16312..73e69a1791a4 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -902,9 +902,8 @@ pub(crate) struct MappingToUnit { pub argument_label: Span, #[label(lint_map_label)] pub map_label: Span, - #[suggestion(style = "verbose", code = "{replace}", applicability = "maybe-incorrect")] + #[suggestion(style = "verbose", code = "for_each", applicability = "maybe-incorrect")] pub suggestion: Span, - pub replace: String, } // internal.rs diff --git a/compiler/rustc_lint/src/map_unit_fn.rs b/compiler/rustc_lint/src/map_unit_fn.rs index 34210137bde1..18a947dc1ee0 100644 --- a/compiler/rustc_lint/src/map_unit_fn.rs +++ b/compiler/rustc_lint/src/map_unit_fn.rs @@ -1,6 +1,7 @@ -use rustc_hir::{Expr, ExprKind, HirId, Stmt, StmtKind}; -use rustc_middle::ty::{self, Ty}; +use rustc_hir::{Expr, ExprKind, Stmt, StmtKind}; +use rustc_middle::ty::{self}; use rustc_session::{declare_lint, declare_lint_pass}; +use rustc_span::sym; use crate::lints::MappingToUnit; use crate::{LateContext, LateLintPass, LintContext}; @@ -39,58 +40,43 @@ declare_lint_pass!(MapUnitFn => [MAP_UNIT_FN]); impl<'tcx> LateLintPass<'tcx> for MapUnitFn { fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &Stmt<'_>) { - if stmt.span.from_expansion() { + let StmtKind::Semi(expr) = stmt.kind else { + return; + }; + let ExprKind::MethodCall(path, receiver, [arg], span) = expr.kind else { + return; + }; + if path.ident.name != sym::map + || stmt.span.from_expansion() + || receiver.span.from_expansion() + || arg.span.from_expansion() + || !is_impl_slice(cx, receiver) + || !cx + .typeck_results() + .type_dependent_def_id(expr.hir_id) + .is_some_and(|id| cx.tcx.is_diagnostic_item(sym::IteratorMap, id)) + { return; } - - if let StmtKind::Semi(expr) = stmt.kind - && let ExprKind::MethodCall(path, receiver, args, span) = expr.kind - { - if path.ident.name.as_str() == "map" { - if receiver.span.from_expansion() - || args.iter().any(|e| e.span.from_expansion()) - || !is_impl_slice(cx, receiver) - || !is_diagnostic_name(cx, expr.hir_id, "IteratorMap") - { - return; - } - let arg_ty = cx.typeck_results().expr_ty(&args[0]); - let default_span = args[0].span; - if let ty::FnDef(id, _) = arg_ty.kind() { - let fn_ty = cx.tcx.fn_sig(id).skip_binder(); - let ret_ty = fn_ty.output().skip_binder(); - if is_unit_type(ret_ty) { - cx.emit_span_lint( - MAP_UNIT_FN, - span, - MappingToUnit { - function_label: cx.tcx.span_of_impl(*id).unwrap_or(default_span), - argument_label: args[0].span, - map_label: span, - suggestion: path.ident.span, - replace: "for_each".to_string(), - }, - ) - } - } else if let ty::Closure(id, subs) = arg_ty.kind() { - let cl_ty = subs.as_closure().sig(); - let ret_ty = cl_ty.output().skip_binder(); - if is_unit_type(ret_ty) { - cx.emit_span_lint( - MAP_UNIT_FN, - span, - MappingToUnit { - function_label: cx.tcx.span_of_impl(*id).unwrap_or(default_span), - argument_label: args[0].span, - map_label: span, - suggestion: path.ident.span, - replace: "for_each".to_string(), - }, - ) - } - } - } + let (id, sig) = match *cx.typeck_results().expr_ty(arg).kind() { + ty::Closure(id, subs) => (id, subs.as_closure().sig()), + ty::FnDef(id, _) => (id, cx.tcx.fn_sig(id).skip_binder()), + _ => return, + }; + let ret_ty = sig.output().skip_binder(); + if !(ret_ty.is_unit() || ret_ty.is_never()) { + return; } + cx.emit_span_lint( + MAP_UNIT_FN, + span, + MappingToUnit { + function_label: cx.tcx.span_of_impl(id).unwrap_or(arg.span), + argument_label: arg.span, + map_label: span, + suggestion: path.ident.span, + }, + ); } } @@ -102,18 +88,3 @@ fn is_impl_slice(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { } false } - -fn is_unit_type(ty: Ty<'_>) -> bool { - ty.is_unit() || ty.is_never() -} - -fn is_diagnostic_name(cx: &LateContext<'_>, id: HirId, name: &str) -> bool { - if let Some(def_id) = cx.typeck_results().type_dependent_def_id(id) - && let Some(item) = cx.tcx.get_diagnostic_name(def_id) - { - if item.as_str() == name { - return true; - } - } - false -} diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 36197950221e..bb3a2e1526a9 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -280,6 +280,7 @@ symbols! { IterPeekable, Iterator, IteratorItem, + IteratorMap, Layout, Left, LinkedList, From eec8585f656e853901ff6c1106d1f15456bfac41 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Fri, 8 Aug 2025 17:01:57 -0500 Subject: [PATCH 085/195] Reduce indirect assoc parent queries --- compiler/rustc_hir_analysis/src/collect/predicates_of.rs | 3 +-- compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs | 2 +- compiler/rustc_lint/src/default_could_be_derived.rs | 3 +-- .../src/error_reporting/infer/note_and_explain.rs | 3 +-- src/librustdoc/clean/mod.rs | 2 +- 5 files changed, 5 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs index 522409a5ee52..8dd13da4fa7b 100644 --- a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs @@ -517,8 +517,7 @@ pub(super) fn explicit_predicates_of<'tcx>( projection.args == trait_identity_args // FIXME(return_type_notation): This check should be more robust && !tcx.is_impl_trait_in_trait(projection.def_id) - && tcx.associated_item(projection.def_id).container_id(tcx) - == def_id.to_def_id() + && tcx.parent(projection.def_id) == def_id.to_def_id() } else { false } diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs index 3e446b7c656f..93b82acf6212 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs @@ -755,7 +755,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let limit = if candidates.len() == 5 { 5 } else { 4 }; for (index, &item) in candidates.iter().take(limit).enumerate() { - let impl_ = tcx.impl_of_assoc(item).unwrap(); + let impl_ = tcx.parent(item); let note_span = if item.is_local() { Some(tcx.def_span(item)) diff --git a/compiler/rustc_lint/src/default_could_be_derived.rs b/compiler/rustc_lint/src/default_could_be_derived.rs index 7c39d8917cef..1d92cfbc0391 100644 --- a/compiler/rustc_lint/src/default_could_be_derived.rs +++ b/compiler/rustc_lint/src/default_could_be_derived.rs @@ -62,8 +62,7 @@ impl<'tcx> LateLintPass<'tcx> for DefaultCouldBeDerived { // Look for manual implementations of `Default`. let Some(default_def_id) = cx.tcx.get_diagnostic_item(sym::Default) else { return }; let hir::ImplItemKind::Fn(_sig, body_id) = impl_item.kind else { return }; - let assoc = cx.tcx.associated_item(impl_item.owner_id); - let parent = assoc.container_id(cx.tcx); + let parent = cx.tcx.parent(impl_item.owner_id.to_def_id()); if find_attr!(cx.tcx.get_all_attrs(parent), AttributeKind::AutomaticallyDerived(..)) { // We don't care about what `#[derive(Default)]` produces in this lint. return; diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs index 129d0963a75a..8f0f6d0bf26a 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs @@ -653,7 +653,6 @@ impl Trait for X { ) ); let impl_comparison = matches!(cause_code, ObligationCauseCode::CompareImplItem { .. }); - let assoc = tcx.associated_item(proj_ty.def_id); if impl_comparison { // We do not want to suggest calling functions when the reason of the // type error is a comparison of an `impl` with its `trait`. @@ -661,7 +660,7 @@ impl Trait for X { let point_at_assoc_fn = if callable_scope && self.point_at_methods_that_satisfy_associated_type( diag, - assoc.container_id(tcx), + tcx.parent(proj_ty.def_id), current_method_ident, proj_ty.def_id, values.expected, diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 743ed2b50453..7194c2fceded 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -1662,7 +1662,7 @@ fn clean_qpath<'tcx>(hir_ty: &hir::Ty<'tcx>, cx: &mut DocContext<'tcx>) -> Type } let trait_segments = &p.segments[..p.segments.len() - 1]; - let trait_def = cx.tcx.associated_item(p.res.def_id()).container_id(cx.tcx); + let trait_def = cx.tcx.parent(p.res.def_id()); let trait_ = self::Path { res: Res::Def(DefKind::Trait, trait_def), segments: trait_segments.iter().map(|x| clean_path_segment(x, cx)).collect(), From b39357bbf3fc4688793a6a4671b509efde8834ce Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Fri, 8 Aug 2025 14:04:47 +0200 Subject: [PATCH 086/195] Rank doc aliases lower than equivalently matched items --- src/librustdoc/html/static/js/search.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js index 2caf214ff73d..505652c0f4a7 100644 --- a/src/librustdoc/html/static/js/search.js +++ b/src/librustdoc/html/static/js/search.js @@ -3340,6 +3340,13 @@ class DocSearch { return a - b; } + // sort doc alias items later + a = Number(aaa.item.is_alias === true); + b = Number(bbb.item.is_alias === true); + if (a !== b) { + return a - b; + } + // sort by item name (lexicographically larger goes later) let aw = aaa.word; let bw = bbb.word; From a34bd2baf586b4a6284b11852fede24ad557f787 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Fri, 8 Aug 2025 14:05:19 +0200 Subject: [PATCH 087/195] Add regression test for doc alias matching vs equivalently matched items --- .../rustdoc-js/doc-alias-after-other-items.js | 38 +++++++++++++++++++ .../rustdoc-js/doc-alias-after-other-items.rs | 9 +++++ 2 files changed, 47 insertions(+) create mode 100644 tests/rustdoc-js/doc-alias-after-other-items.js create mode 100644 tests/rustdoc-js/doc-alias-after-other-items.rs diff --git a/tests/rustdoc-js/doc-alias-after-other-items.js b/tests/rustdoc-js/doc-alias-after-other-items.js new file mode 100644 index 000000000000..5b22ef676982 --- /dev/null +++ b/tests/rustdoc-js/doc-alias-after-other-items.js @@ -0,0 +1,38 @@ +// exact-check + +// Checking that doc aliases are always listed after items with equivalent matching. + +const EXPECTED = [ + { + 'query': 'coo', + 'others': [ + { + 'path': 'doc_alias_after_other_items', + 'name': 'Foo', + 'href': '../doc_alias_after_other_items/struct.Foo.html', + }, + { + 'path': 'doc_alias_after_other_items', + 'name': 'bar', + 'alias': 'Boo', + 'is_alias': true + }, + ], + }, + { + 'query': '"confiture"', + 'others': [ + { + 'path': 'doc_alias_after_other_items', + 'name': 'Confiture', + 'href': '../doc_alias_after_other_items/struct.Confiture.html', + }, + { + 'path': 'doc_alias_after_other_items', + 'name': 'this_is_a_long_name', + 'alias': 'Confiture', + 'is_alias': true + }, + ], + }, +]; diff --git a/tests/rustdoc-js/doc-alias-after-other-items.rs b/tests/rustdoc-js/doc-alias-after-other-items.rs new file mode 100644 index 000000000000..2ed555c8e2f3 --- /dev/null +++ b/tests/rustdoc-js/doc-alias-after-other-items.rs @@ -0,0 +1,9 @@ +pub struct Foo; + +#[doc(alias = "Boo")] +pub fn bar() {} + +pub struct Confiture; + +#[doc(alias = "Confiture")] +pub fn this_is_a_long_name() {} From 339be84d9dbe887ce704a5171e467b4fb363fbf4 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Thu, 31 Jul 2025 17:14:39 +0200 Subject: [PATCH 088/195] Use new public libtest `ERROR_EXIT_CODE` constant in rustdoc --- src/librustdoc/doctest.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index 35ace6566381..73ce62cdcde6 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -409,9 +409,7 @@ pub(crate) fn run_tests( // We ensure temp dir destructor is called. std::mem::drop(temp_dir); times.display_times(); - // FIXME(GuillaumeGomez): Uncomment the next line once #144297 has been merged. - // std::process::exit(test::ERROR_EXIT_CODE); - std::process::exit(101); + std::process::exit(test::ERROR_EXIT_CODE); } } From 7eb8f6001e528e811018824e71e36547ea9b7e50 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Sat, 9 Aug 2025 07:23:05 +0530 Subject: [PATCH 089/195] add is_system_llvm function and invoke from parse_inner --- src/bootstrap/src/core/config/config.rs | 34 ++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index bfee43148c0e..56c549297e27 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -1315,7 +1315,14 @@ impl Config { ); } - if config.lld_enabled && config.is_system_llvm(config.host_target) { + if config.lld_enabled + && is_system_llvm( + &config.host_target, + config.llvm_from_ci, + &config.target_config, + config.host_target, + ) + { panic!("Cannot enable LLD with `rust.lld = true` when using external llvm-config."); } @@ -2700,3 +2707,28 @@ pub fn submodules_(submodules: &Option, rust_info: &channel::GitInfo) -> b // submodules if we're currently inside a git repository. submodules.unwrap_or(rust_info.is_managed_git_subrepository()) } + +/// Returns `true` if this is an external version of LLVM not managed by bootstrap. +/// In particular, we expect llvm sources to be available when this is false. +/// +/// NOTE: this is not the same as `!is_rust_llvm` when `llvm_has_patches` is set. +pub fn is_system_llvm( + host_target: &TargetSelection, + llvm_from_ci: bool, + target_config: &HashMap, + target: TargetSelection, +) -> bool { + match target_config.get(&target) { + Some(Target { llvm_config: Some(_), .. }) => { + let ci_llvm = llvm_from_ci && is_host_target(host_target, &target); + !ci_llvm + } + // We're building from the in-tree src/llvm-project sources. + Some(Target { llvm_config: None, .. }) => false, + None => false, + } +} + +pub fn is_host_target(host_target: &TargetSelection, target: &TargetSelection) -> bool { + host_target == target +} From 134fcc8c66808bb781ae67e786e46d63fc48b4b7 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Sat, 9 Aug 2025 07:31:55 +0530 Subject: [PATCH 090/195] add git_info function and invoke from parse_inner --- src/bootstrap/src/core/config/config.rs | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index 56c549297e27..e5bee0e7ce56 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -893,21 +893,25 @@ impl Config { let default = config.channel == "dev"; config.omit_git_hash = rust_omit_git_hash.unwrap_or(default); - config.rust_info = config.git_info(config.omit_git_hash, &config.src); + config.rust_info = git_info(&config.exec_ctx, config.omit_git_hash, &config.src); config.cargo_info = - config.git_info(config.omit_git_hash, &config.src.join("src/tools/cargo")); - config.rust_analyzer_info = - config.git_info(config.omit_git_hash, &config.src.join("src/tools/rust-analyzer")); + git_info(&config.exec_ctx, config.omit_git_hash, &config.src.join("src/tools/cargo")); + config.rust_analyzer_info = git_info( + &config.exec_ctx, + config.omit_git_hash, + &config.src.join("src/tools/rust-analyzer"), + ); config.clippy_info = - config.git_info(config.omit_git_hash, &config.src.join("src/tools/clippy")); + git_info(&config.exec_ctx, config.omit_git_hash, &config.src.join("src/tools/clippy")); config.miri_info = - config.git_info(config.omit_git_hash, &config.src.join("src/tools/miri")); + git_info(&config.exec_ctx, config.omit_git_hash, &config.src.join("src/tools/miri")); config.rustfmt_info = - config.git_info(config.omit_git_hash, &config.src.join("src/tools/rustfmt")); + git_info(&config.exec_ctx, config.omit_git_hash, &config.src.join("src/tools/rustfmt")); config.enzyme_info = - config.git_info(config.omit_git_hash, &config.src.join("src/tools/enzyme")); - config.in_tree_llvm_info = config.git_info(false, &config.src.join("src/llvm-project")); - config.in_tree_gcc_info = config.git_info(false, &config.src.join("src/gcc")); + git_info(&config.exec_ctx, config.omit_git_hash, &config.src.join("src/tools/enzyme")); + config.in_tree_llvm_info = + git_info(&config.exec_ctx, false, &config.src.join("src/llvm-project")); + config.in_tree_gcc_info = git_info(&config.exec_ctx, false, &config.src.join("src/gcc")); config.vendor = build_vendor.unwrap_or( config.rust_info.is_from_tarball() From 2213b7a8f9c3e778a32cddfc19ec58efefcb902c Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Sat, 9 Aug 2025 07:47:26 +0530 Subject: [PATCH 091/195] add ci_llvm_root function and invoke from parse_inner --- src/bootstrap/src/core/config/config.rs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index e5bee0e7ce56..ef8d541500f9 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -1278,7 +1278,8 @@ impl Config { if config.llvm_from_ci { let triple = &config.host_target.triple; - let ci_llvm_bin = config.ci_llvm_root().join("bin"); + let ci_llvm_bin = + ci_llvm_root(config.llvm_from_ci, &config.out, &config.host_target).join("bin"); let build_target = config .target_config .entry(config.host_target) @@ -2736,3 +2737,12 @@ pub fn is_system_llvm( pub fn is_host_target(host_target: &TargetSelection, target: &TargetSelection) -> bool { host_target == target } + +pub(crate) fn ci_llvm_root( + llvm_from_ci: bool, + out: &Path, + host_target: &TargetSelection, +) -> PathBuf { + assert!(llvm_from_ci); + out.join(host_target).join("ci-llvm") +} From 621af4a580436013e7af848f5abea9bc21b21c9b Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Sat, 9 Aug 2025 07:53:56 +0530 Subject: [PATCH 092/195] add read_file_by_commit function and invoke from parse_inner --- src/bootstrap/src/core/config/config.rs | 29 +++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index ef8d541500f9..af6edb1242cc 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -1168,8 +1168,15 @@ impl Config { "WARNING: `rust.download-rustc` is enabled. The `rust.channel` option will be overridden by the CI rustc's channel." ); - let channel = - config.read_file_by_commit(Path::new("src/ci/channel"), commit).trim().to_owned(); + let channel = read_file_by_commit( + &config.exec_ctx, + &config.src, + &config.rust_info, + Path::new("src/ci/channel"), + commit, + ) + .trim() + .to_owned(); config.channel = channel; } @@ -2746,3 +2753,21 @@ pub(crate) fn ci_llvm_root( assert!(llvm_from_ci); out.join(host_target).join("ci-llvm") } + +/// Returns the content of the given file at a specific commit. +pub(crate) fn read_file_by_commit( + exec_ctx: &ExecutionContext, + src: &Path, + rust_info: &channel::GitInfo, + file: &Path, + commit: &str, +) -> String { + assert!( + rust_info.is_managed_git_subrepository(), + "`Config::read_file_by_commit` is not supported in non-git sources." + ); + + let mut git = helpers::git(Some(src)); + git.arg("show").arg(format!("{commit}:{}", file.to_str().unwrap())); + git.run_capture_stdout(exec_ctx).stdout() +} From aad54a8ff4d25d4a20b25ccfcfbae01c9a2310ba Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Sat, 9 Aug 2025 08:41:16 +0530 Subject: [PATCH 093/195] invoke functions from methods --- src/bootstrap/src/core/config/config.rs | 232 +++--------------------- 1 file changed, 28 insertions(+), 204 deletions(-) diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index af6edb1242cc..2912b3f30c30 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -1469,14 +1469,7 @@ impl Config { /// Returns the content of the given file at a specific commit. pub(crate) fn read_file_by_commit(&self, file: &Path, commit: &str) -> String { - assert!( - self.rust_info.is_managed_git_subrepository(), - "`Config::read_file_by_commit` is not supported in non-git sources." - ); - - let mut git = helpers::git(Some(&self.src)); - git.arg("show").arg(format!("{commit}:{}", file.to_str().unwrap())); - git.run_capture_stdout(self).stdout() + read_file_by_commit(&self.exec_ctx, &self.src, &self.rust_info, file, commit) } /// Bootstrap embeds a version number into the name of shared libraries it uploads in CI. @@ -1547,8 +1540,7 @@ impl Config { /// The absolute path to the downloaded LLVM artifacts. pub(crate) fn ci_llvm_root(&self) -> PathBuf { - assert!(self.llvm_from_ci); - self.out.join(self.host_target).join("ci-llvm") + ci_llvm_root(self.llvm_from_ci, &self.out, &self.host_target) } /// Directory where the extracted `rustc-dev` component is stored. @@ -1711,115 +1703,13 @@ impl Config { ), )] pub(crate) fn update_submodule(&self, relative_path: &str) { - if self.rust_info.is_from_tarball() || !self.submodules() { - return; - } - - let absolute_path = self.src.join(relative_path); - - // NOTE: This check is required because `jj git clone` doesn't create directories for - // submodules, they are completely ignored. The code below assumes this directory exists, - // so create it here. - if !absolute_path.exists() { - t!(fs::create_dir_all(&absolute_path)); - } - - // NOTE: The check for the empty directory is here because when running x.py the first time, - // the submodule won't be checked out. Check it out now so we can build it. - if !self.git_info(false, &absolute_path).is_managed_git_subrepository() - && !helpers::dir_is_empty(&absolute_path) - { - return; - } - - // Submodule updating actually happens during in the dry run mode. We need to make sure that - // all the git commands below are actually executed, because some follow-up code - // in bootstrap might depend on the submodules being checked out. Furthermore, not all - // the command executions below work with an empty output (produced during dry run). - // Therefore, all commands below are marked with `run_in_dry_run()`, so that they also run in - // dry run mode. - let submodule_git = || { - let mut cmd = helpers::git(Some(&absolute_path)); - cmd.run_in_dry_run(); - cmd - }; - - // Determine commit checked out in submodule. - let checked_out_hash = - submodule_git().args(["rev-parse", "HEAD"]).run_capture_stdout(self).stdout(); - let checked_out_hash = checked_out_hash.trim_end(); - // Determine commit that the submodule *should* have. - let recorded = helpers::git(Some(&self.src)) - .run_in_dry_run() - .args(["ls-tree", "HEAD"]) - .arg(relative_path) - .run_capture_stdout(self) - .stdout(); - - let actual_hash = recorded - .split_whitespace() - .nth(2) - .unwrap_or_else(|| panic!("unexpected output `{recorded}`")); - - if actual_hash == checked_out_hash { - // already checked out - return; - } - - println!("Updating submodule {relative_path}"); - - helpers::git(Some(&self.src)) - .allow_failure() - .run_in_dry_run() - .args(["submodule", "-q", "sync"]) - .arg(relative_path) - .run(self); - - // Try passing `--progress` to start, then run git again without if that fails. - let update = |progress: bool| { - // Git is buggy and will try to fetch submodules from the tracking branch for *this* repository, - // even though that has no relation to the upstream for the submodule. - let current_branch = helpers::git(Some(&self.src)) - .allow_failure() - .run_in_dry_run() - .args(["symbolic-ref", "--short", "HEAD"]) - .run_capture(self); - - let mut git = helpers::git(Some(&self.src)).allow_failure(); - git.run_in_dry_run(); - if current_branch.is_success() { - // If there is a tag named after the current branch, git will try to disambiguate by prepending `heads/` to the branch name. - // This syntax isn't accepted by `branch.{branch}`. Strip it. - let branch = current_branch.stdout(); - let branch = branch.trim(); - let branch = branch.strip_prefix("heads/").unwrap_or(branch); - git.arg("-c").arg(format!("branch.{branch}.remote=origin")); - } - git.args(["submodule", "update", "--init", "--recursive", "--depth=1"]); - if progress { - git.arg("--progress"); - } - git.arg(relative_path); - git - }; - if !update(true).allow_failure().run(self) { - update(false).allow_failure().run(self); - } - - // Save any local changes, but avoid running `git stash pop` if there are none (since it will exit with an error). - // diff-index reports the modifications through the exit status - let has_local_modifications = - !submodule_git().allow_failure().args(["diff-index", "--quiet", "HEAD"]).run(self); - if has_local_modifications { - submodule_git().allow_failure().args(["stash", "push"]).run(self); - } - - submodule_git().allow_failure().args(["reset", "-q", "--hard"]).run(self); - submodule_git().allow_failure().args(["clean", "-qdfx"]).run(self); - - if has_local_modifications { - submodule_git().allow_failure().args(["stash", "pop"]).run(self); - } + update_submodule( + &self.submodules, + &self.exec_ctx, + &self.src, + &self.rust_info, + relative_path, + ); } /// Returns the commit to download, or `None` if we shouldn't download CI artifacts. @@ -1829,78 +1719,18 @@ impl Config { debug_assertions_requested: bool, llvm_assertions: bool, ) -> Option { - if !is_download_ci_available(&self.host_target.triple, llvm_assertions) { - return None; - } - - // If `download-rustc` is not set, default to rebuilding. - let if_unchanged = match download_rustc { - // Globally default `download-rustc` to `false`, because some contributors don't use - // profiles for reasons such as: - // - They need to seamlessly switch between compiler/library work. - // - They don't want to use compiler profile because they need to override too many - // things and it's easier to not use a profile. - None | Some(StringOrBool::Bool(false)) => return None, - Some(StringOrBool::Bool(true)) => false, - Some(StringOrBool::String(s)) if s == "if-unchanged" => { - if !self.rust_info.is_managed_git_subrepository() { - println!( - "ERROR: `download-rustc=if-unchanged` is only compatible with Git managed sources." - ); - crate::exit!(1); - } - - true - } - Some(StringOrBool::String(other)) => { - panic!("unrecognized option for download-rustc: {other}") - } - }; - - let commit = if self.rust_info.is_managed_git_subrepository() { - // Look for a version to compare to based on the current commit. - // Only commits merged by bors will have CI artifacts. - let freshness = self.check_path_modifications(RUSTC_IF_UNCHANGED_ALLOWED_PATHS); - self.verbose(|| { - eprintln!("rustc freshness: {freshness:?}"); - }); - match freshness { - PathFreshness::LastModifiedUpstream { upstream } => upstream, - PathFreshness::HasLocalModifications { upstream } => { - if if_unchanged { - return None; - } - - if self.is_running_on_ci { - eprintln!("CI rustc commit matches with HEAD and we are in CI."); - eprintln!( - "`rustc.download-ci` functionality will be skipped as artifacts are not available." - ); - return None; - } - - upstream - } - PathFreshness::MissingUpstream => { - eprintln!("No upstream commit found"); - return None; - } - } - } else { - channel::read_commit_info_file(&self.src) - .map(|info| info.sha.trim().to_owned()) - .expect("git-commit-info is missing in the project root") - }; - - if debug_assertions_requested { - eprintln!( - "WARN: `rust.debug-assertions = true` will prevent downloading CI rustc as alt CI \ - rustc is not currently built with debug assertions." - ); - return None; - } - - Some(commit) + download_ci_rustc_commit( + &self.stage0_metadata, + self.path_modification_cache.clone(), + &self.src, + self.is_running_on_ci, + &self.exec_ctx, + &self.rust_info, + &self.host_target, + download_rustc, + debug_assertions_requested, + llvm_assertions, + ) } pub fn parse_download_ci_llvm( @@ -1966,10 +1796,12 @@ impl Config { /// Returns true if any of the `paths` have been modified locally. pub fn has_changes_from_upstream(&self, paths: &[&'static str]) -> bool { - match self.check_path_modifications(paths) { - PathFreshness::LastModifiedUpstream { .. } => false, - PathFreshness::HasLocalModifications { .. } | PathFreshness::MissingUpstream => true, - } + has_changes_from_upstream( + &self.stage0_metadata, + &self.src, + self.path_modification_cache.clone(), + paths, + ) } /// Checks whether any of the given paths have been modified w.r.t. upstream. @@ -2077,15 +1909,7 @@ impl Config { /// /// NOTE: this is not the same as `!is_rust_llvm` when `llvm_has_patches` is set. pub fn is_system_llvm(&self, target: TargetSelection) -> bool { - match self.target_config.get(&target) { - Some(Target { llvm_config: Some(_), .. }) => { - let ci_llvm = self.llvm_from_ci && self.is_host_target(target); - !ci_llvm - } - // We're building from the in-tree src/llvm-project sources. - Some(Target { llvm_config: None, .. }) => false, - None => false, - } + is_system_llvm(&self.host_target, self.llvm_from_ci, &self.target_config, target) } /// Returns `true` if this is our custom, patched, version of LLVM. From 8612db2f3e5e85713e9aacfc794b328810e651f8 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Sat, 9 Aug 2025 09:52:13 +0530 Subject: [PATCH 094/195] extend download context and change functions to directly use that --- src/bootstrap/src/core/config/config.rs | 320 ++++++++---------------- src/bootstrap/src/core/download.rs | 37 ++- 2 files changed, 130 insertions(+), 227 deletions(-) diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index 2912b3f30c30..73a20df9b4e3 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -951,14 +951,9 @@ impl Config { ); } + let dwn_ctx = DownloadContext::from(&config); config.download_rustc_commit = download_ci_rustc_commit( - &config.stage0_metadata, - config.path_modification_cache.clone(), - &config.src, - config.is_running_on_ci, - &config.exec_ctx, - &config.rust_info, - &config.host_target, + dwn_ctx, rust_download_rustc, debug_assertions_requested, config.llvm_assertions, @@ -1168,15 +1163,9 @@ impl Config { "WARNING: `rust.download-rustc` is enabled. The `rust.channel` option will be overridden by the CI rustc's channel." ); - let channel = read_file_by_commit( - &config.exec_ctx, - &config.src, - &config.rust_info, - Path::new("src/ci/channel"), - commit, - ) - .trim() - .to_owned(); + let dwn_ctx = DownloadContext::from(&config); + let channel = + read_file_by_commit(dwn_ctx, Path::new("src/ci/channel"), commit).trim().to_owned(); config.channel = channel; } @@ -1211,19 +1200,9 @@ impl Config { config.llvm_enable_warnings = llvm_enable_warnings.unwrap_or(false); config.llvm_build_config = llvm_build_config.clone().unwrap_or(Default::default()); - config.llvm_from_ci = parse_download_ci_llvm( - &config.exec_ctx, - &config.submodules, - &config.stage0_metadata, - &config.src, - config.path_modification_cache.clone(), - &config.host_target, - &config.download_rustc_commit, - &config.rust_info, - config.is_running_on_ci, - llvm_download_ci_llvm, - config.llvm_assertions, - ); + let dwn_ctx = DownloadContext::from(&config); + config.llvm_from_ci = + parse_download_ci_llvm(dwn_ctx, llvm_download_ci_llvm, config.llvm_assertions); if config.llvm_from_ci { let warn = |option: &str| { @@ -1285,8 +1264,8 @@ impl Config { if config.llvm_from_ci { let triple = &config.host_target.triple; - let ci_llvm_bin = - ci_llvm_root(config.llvm_from_ci, &config.out, &config.host_target).join("bin"); + let dwn_ctx = DownloadContext::from(&config); + let ci_llvm_bin = ci_llvm_root(dwn_ctx).join("bin"); let build_target = config .target_config .entry(config.host_target) @@ -1327,14 +1306,8 @@ impl Config { ); } - if config.lld_enabled - && is_system_llvm( - &config.host_target, - config.llvm_from_ci, - &config.target_config, - config.host_target, - ) - { + let dwn_ctx = DownloadContext::from(&config); + if config.lld_enabled && is_system_llvm(dwn_ctx, config.host_target) { panic!("Cannot enable LLD with `rust.lld = true` when using external llvm-config."); } @@ -1469,7 +1442,8 @@ impl Config { /// Returns the content of the given file at a specific commit. pub(crate) fn read_file_by_commit(&self, file: &Path, commit: &str) -> String { - read_file_by_commit(&self.exec_ctx, &self.src, &self.rust_info, file, commit) + let dwn_ctx = DownloadContext::from(self); + read_file_by_commit(dwn_ctx, file, commit) } /// Bootstrap embeds a version number into the name of shared libraries it uploads in CI. @@ -1540,7 +1514,8 @@ impl Config { /// The absolute path to the downloaded LLVM artifacts. pub(crate) fn ci_llvm_root(&self) -> PathBuf { - ci_llvm_root(self.llvm_from_ci, &self.out, &self.host_target) + let dwn_ctx = DownloadContext::from(self); + ci_llvm_root(dwn_ctx) } /// Directory where the extracted `rustc-dev` component is stored. @@ -1703,13 +1678,8 @@ impl Config { ), )] pub(crate) fn update_submodule(&self, relative_path: &str) { - update_submodule( - &self.submodules, - &self.exec_ctx, - &self.src, - &self.rust_info, - relative_path, - ); + let dwn_ctx = DownloadContext::from(self); + update_submodule(dwn_ctx, relative_path); } /// Returns the commit to download, or `None` if we shouldn't download CI artifacts. @@ -1719,14 +1689,9 @@ impl Config { debug_assertions_requested: bool, llvm_assertions: bool, ) -> Option { + let dwn_ctx = DownloadContext::from(self); download_ci_rustc_commit( - &self.stage0_metadata, - self.path_modification_cache.clone(), - &self.src, - self.is_running_on_ci, - &self.exec_ctx, - &self.rust_info, - &self.host_target, + dwn_ctx, download_rustc, debug_assertions_requested, llvm_assertions, @@ -1738,70 +1703,14 @@ impl Config { download_ci_llvm: Option, asserts: bool, ) -> bool { - // We don't ever want to use `true` on CI, as we should not - // download upstream artifacts if there are any local modifications. - let default = if self.is_running_on_ci { - StringOrBool::String("if-unchanged".to_string()) - } else { - StringOrBool::Bool(true) - }; - let download_ci_llvm = download_ci_llvm.unwrap_or(default); - - let if_unchanged = || { - if self.rust_info.is_from_tarball() { - // Git is needed for running "if-unchanged" logic. - println!("ERROR: 'if-unchanged' is only compatible with Git managed sources."); - crate::exit!(1); - } - - // Fetching the LLVM submodule is unnecessary for self-tests. - #[cfg(not(test))] - self.update_submodule("src/llvm-project"); - - // Check for untracked changes in `src/llvm-project` and other important places. - let has_changes = self.has_changes_from_upstream(LLVM_INVALIDATION_PATHS); - - // Return false if there are untracked changes, otherwise check if CI LLVM is available. - if has_changes { - false - } else { - llvm::is_ci_llvm_available_for_target(&self.host_target, asserts) - } - }; - - match download_ci_llvm { - StringOrBool::Bool(b) => { - if !b && self.download_rustc_commit.is_some() { - panic!( - "`llvm.download-ci-llvm` cannot be set to `false` if `rust.download-rustc` is set to `true` or `if-unchanged`." - ); - } - - if b && self.is_running_on_ci { - // On CI, we must always rebuild LLVM if there were any modifications to it - panic!( - "`llvm.download-ci-llvm` cannot be set to `true` on CI. Use `if-unchanged` instead." - ); - } - - // If download-ci-llvm=true we also want to check that CI llvm is available - b && llvm::is_ci_llvm_available_for_target(&self.host_target, asserts) - } - StringOrBool::String(s) if s == "if-unchanged" => if_unchanged(), - StringOrBool::String(other) => { - panic!("unrecognized option for download-ci-llvm: {other:?}") - } - } + let dwn_ctx = DownloadContext::from(self); + parse_download_ci_llvm(dwn_ctx, download_ci_llvm, asserts) } /// Returns true if any of the `paths` have been modified locally. pub fn has_changes_from_upstream(&self, paths: &[&'static str]) -> bool { - has_changes_from_upstream( - &self.stage0_metadata, - &self.src, - self.path_modification_cache.clone(), - paths, - ) + let dwn_ctx = DownloadContext::from(self); + has_changes_from_upstream(dwn_ctx, paths) } /// Checks whether any of the given paths have been modified w.r.t. upstream. @@ -1909,7 +1818,8 @@ impl Config { /// /// NOTE: this is not the same as `!is_rust_llvm` when `llvm_has_patches` is set. pub fn is_system_llvm(&self, target: TargetSelection) -> bool { - is_system_llvm(&self.host_target, self.llvm_from_ci, &self.target_config, target) + let dwn_ctx = DownloadContext::from(self); + is_system_llvm(dwn_ctx, target) } /// Returns `true` if this is our custom, patched, version of LLVM. @@ -2201,19 +2111,15 @@ pub fn check_stage0_version( } } -pub fn download_ci_rustc_commit( - stage0_metadata: &build_helper::stage0_parser::Stage0, - path_modification_cache: Arc, PathFreshness>>>, - src: &Path, - is_running_on_ci: bool, - exec_ctx: &ExecutionContext, - rust_info: &channel::GitInfo, - host_target: &TargetSelection, +pub fn download_ci_rustc_commit<'a>( + dwn_ctx: impl AsRef>, download_rustc: Option, debug_assertions_requested: bool, llvm_assertions: bool, ) -> Option { - if !is_download_ci_available(&host_target.triple, llvm_assertions) { + let dwn_ctx = dwn_ctx.as_ref(); + + if !is_download_ci_available(&dwn_ctx.host_target.triple, llvm_assertions) { return None; } @@ -2227,7 +2133,7 @@ pub fn download_ci_rustc_commit( None | Some(StringOrBool::Bool(false)) => return None, Some(StringOrBool::Bool(true)) => false, Some(StringOrBool::String(s)) if s == "if-unchanged" => { - if !rust_info.is_managed_git_subrepository() { + if !dwn_ctx.rust_info.is_managed_git_subrepository() { println!( "ERROR: `download-rustc=if-unchanged` is only compatible with Git managed sources." ); @@ -2241,16 +2147,11 @@ pub fn download_ci_rustc_commit( } }; - let commit = if rust_info.is_managed_git_subrepository() { + let commit = if dwn_ctx.rust_info.is_managed_git_subrepository() { // Look for a version to compare to based on the current commit. // Only commits merged by bors will have CI artifacts. - let freshness = check_path_modifications_( - stage0_metadata, - src, - path_modification_cache, - RUSTC_IF_UNCHANGED_ALLOWED_PATHS, - ); - exec_ctx.verbose(|| { + let freshness = check_path_modifications_(dwn_ctx, RUSTC_IF_UNCHANGED_ALLOWED_PATHS); + dwn_ctx.exec_ctx.verbose(|| { eprintln!("rustc freshness: {freshness:?}"); }); match freshness { @@ -2260,7 +2161,7 @@ pub fn download_ci_rustc_commit( return None; } - if is_running_on_ci { + if dwn_ctx.is_running_on_ci { eprintln!("CI rustc commit matches with HEAD and we are in CI."); eprintln!( "`rustc.download-ci` functionality will be skipped as artifacts are not available." @@ -2276,7 +2177,7 @@ pub fn download_ci_rustc_commit( } } } else { - channel::read_commit_info_file(src) + channel::read_commit_info_file(dwn_ctx.src) .map(|info| info.sha.trim().to_owned()) .expect("git-commit-info is missing in the project root") }; @@ -2292,24 +2193,29 @@ pub fn download_ci_rustc_commit( Some(commit) } -pub fn check_path_modifications_( - stage0_metadata: &build_helper::stage0_parser::Stage0, - src: &Path, - path_modification_cache: Arc, PathFreshness>>>, +pub fn check_path_modifications_<'a>( + dwn_ctx: impl AsRef>, paths: &[&'static str], ) -> PathFreshness { + let dwn_ctx = dwn_ctx.as_ref(); // Checking path modifications through git can be relatively expensive (>100ms). // We do not assume that the sources would change during bootstrap's execution, // so we can cache the results here. // Note that we do not use a static variable for the cache, because it would cause problems // in tests that create separate `Config` instsances. - path_modification_cache + dwn_ctx + .path_modification_cache .lock() .unwrap() .entry(paths.to_vec()) .or_insert_with(|| { - check_path_modifications(src, &git_config(stage0_metadata), paths, CiEnv::current()) - .unwrap() + check_path_modifications( + dwn_ctx.src, + &git_config(dwn_ctx.stage0_metadata), + paths, + CiEnv::current(), + ) + .unwrap() }) .clone() } @@ -2321,22 +2227,16 @@ pub fn git_config(stage0_metadata: &build_helper::stage0_parser::Stage0) -> GitC } } -pub fn parse_download_ci_llvm( - exec_ctx: &ExecutionContext, - submodules: &Option, - stage0_metadata: &build_helper::stage0_parser::Stage0, - src: &Path, - path_modification_cache: Arc, PathFreshness>>>, - host_target: &TargetSelection, - download_rustc_commit: &Option, - rust_info: &channel::GitInfo, - is_running_on_ci: bool, +pub fn parse_download_ci_llvm<'a>( + dwn_ctx: impl AsRef>, download_ci_llvm: Option, asserts: bool, ) -> bool { + let dwn_ctx = dwn_ctx.as_ref(); + // We don't ever want to use `true` on CI, as we should not // download upstream artifacts if there are any local modifications. - let default = if is_running_on_ci { + let default = if dwn_ctx.is_running_on_ci { StringOrBool::String("if-unchanged".to_string()) } else { StringOrBool::Bool(true) @@ -2344,7 +2244,7 @@ pub fn parse_download_ci_llvm( let download_ci_llvm = download_ci_llvm.unwrap_or(default); let if_unchanged = || { - if rust_info.is_from_tarball() { + if dwn_ctx.rust_info.is_from_tarball() { // Git is needed for running "if-unchanged" logic. println!("ERROR: 'if-unchanged' is only compatible with Git managed sources."); crate::exit!(1); @@ -2352,33 +2252,28 @@ pub fn parse_download_ci_llvm( // Fetching the LLVM submodule is unnecessary for self-tests. #[cfg(not(test))] - update_submodule(submodules, exec_ctx, src, rust_info, "src/llvm-project"); + update_submodule(dwn_ctx, "src/llvm-project"); // Check for untracked changes in `src/llvm-project` and other important places. - let has_changes = has_changes_from_upstream( - stage0_metadata, - src, - path_modification_cache, - LLVM_INVALIDATION_PATHS, - ); + let has_changes = has_changes_from_upstream(dwn_ctx, LLVM_INVALIDATION_PATHS); // Return false if there are untracked changes, otherwise check if CI LLVM is available. if has_changes { false } else { - llvm::is_ci_llvm_available_for_target(host_target, asserts) + llvm::is_ci_llvm_available_for_target(&dwn_ctx.host_target, asserts) } }; match download_ci_llvm { StringOrBool::Bool(b) => { - if !b && download_rustc_commit.is_some() { + if !b && dwn_ctx.download_rustc_commit.is_some() { panic!( "`llvm.download-ci-llvm` cannot be set to `false` if `rust.download-rustc` is set to `true` or `if-unchanged`." ); } - if b && is_running_on_ci { + if b && dwn_ctx.is_running_on_ci { // On CI, we must always rebuild LLVM if there were any modifications to it panic!( "`llvm.download-ci-llvm` cannot be set to `true` on CI. Use `if-unchanged` instead." @@ -2386,7 +2281,7 @@ pub fn parse_download_ci_llvm( } // If download-ci-llvm=true we also want to check that CI llvm is available - b && llvm::is_ci_llvm_available_for_target(host_target, asserts) + b && llvm::is_ci_llvm_available_for_target(&dwn_ctx.host_target, asserts) } StringOrBool::String(s) if s == "if-unchanged" => if_unchanged(), StringOrBool::String(other) => { @@ -2395,13 +2290,12 @@ pub fn parse_download_ci_llvm( } } -pub fn has_changes_from_upstream( - stage0_metadata: &build_helper::stage0_parser::Stage0, - src: &Path, - path_modification_cache: Arc, PathFreshness>>>, +pub fn has_changes_from_upstream<'a>( + dwn_ctx: impl AsRef>, paths: &[&'static str], ) -> bool { - match check_path_modifications_(stage0_metadata, src, path_modification_cache, paths) { + let dwn_ctx = dwn_ctx.as_ref(); + match check_path_modifications_(dwn_ctx, paths) { PathFreshness::LastModifiedUpstream { .. } => false, PathFreshness::HasLocalModifications { .. } | PathFreshness::MissingUpstream => true, } @@ -2416,18 +2310,13 @@ pub fn has_changes_from_upstream( fields(relative_path = ?relative_path), ), )] -pub(crate) fn update_submodule( - submodules: &Option, - exec_ctx: &ExecutionContext, - src: &Path, - rust_info: &channel::GitInfo, - relative_path: &str, -) { - if rust_info.is_from_tarball() || !submodules_(submodules, rust_info) { +pub(crate) fn update_submodule<'a>(dwn_ctx: impl AsRef>, relative_path: &str) { + let dwn_ctx = dwn_ctx.as_ref(); + if dwn_ctx.rust_info.is_from_tarball() || !submodules_(dwn_ctx.submodules, dwn_ctx.rust_info) { return; } - let absolute_path = src.join(relative_path); + let absolute_path = dwn_ctx.src.join(relative_path); // NOTE: This check is required because `jj git clone` doesn't create directories for // submodules, they are completely ignored. The code below assumes this directory exists, @@ -2438,7 +2327,7 @@ pub(crate) fn update_submodule( // NOTE: The check for the empty directory is here because when running x.py the first time, // the submodule won't be checked out. Check it out now so we can build it. - if !git_info(exec_ctx, false, &absolute_path).is_managed_git_subrepository() + if !git_info(dwn_ctx.exec_ctx, false, &absolute_path).is_managed_git_subrepository() && !helpers::dir_is_empty(&absolute_path) { return; @@ -2458,14 +2347,14 @@ pub(crate) fn update_submodule( // Determine commit checked out in submodule. let checked_out_hash = - submodule_git().args(["rev-parse", "HEAD"]).run_capture_stdout(exec_ctx).stdout(); + submodule_git().args(["rev-parse", "HEAD"]).run_capture_stdout(dwn_ctx.exec_ctx).stdout(); let checked_out_hash = checked_out_hash.trim_end(); // Determine commit that the submodule *should* have. - let recorded = helpers::git(Some(src)) + let recorded = helpers::git(Some(dwn_ctx.src)) .run_in_dry_run() .args(["ls-tree", "HEAD"]) .arg(relative_path) - .run_capture_stdout(exec_ctx) + .run_capture_stdout(dwn_ctx.exec_ctx) .stdout(); let actual_hash = recorded @@ -2480,24 +2369,24 @@ pub(crate) fn update_submodule( println!("Updating submodule {relative_path}"); - helpers::git(Some(src)) + helpers::git(Some(dwn_ctx.src)) .allow_failure() .run_in_dry_run() .args(["submodule", "-q", "sync"]) .arg(relative_path) - .run(exec_ctx); + .run(dwn_ctx.exec_ctx); // Try passing `--progress` to start, then run git again without if that fails. let update = |progress: bool| { // Git is buggy and will try to fetch submodules from the tracking branch for *this* repository, // even though that has no relation to the upstream for the submodule. - let current_branch = helpers::git(Some(src)) + let current_branch = helpers::git(Some(dwn_ctx.src)) .allow_failure() .run_in_dry_run() .args(["symbolic-ref", "--short", "HEAD"]) - .run_capture(exec_ctx); + .run_capture(dwn_ctx.exec_ctx); - let mut git = helpers::git(Some(&src)).allow_failure(); + let mut git = helpers::git(Some(dwn_ctx.src)).allow_failure(); git.run_in_dry_run(); if current_branch.is_success() { // If there is a tag named after the current branch, git will try to disambiguate by prepending `heads/` to the branch name. @@ -2514,23 +2403,25 @@ pub(crate) fn update_submodule( git.arg(relative_path); git }; - if !update(true).allow_failure().run(exec_ctx) { - update(false).allow_failure().run(exec_ctx); + if !update(true).allow_failure().run(dwn_ctx.exec_ctx) { + update(false).allow_failure().run(dwn_ctx.exec_ctx); } // Save any local changes, but avoid running `git stash pop` if there are none (since it will exit with an error). // diff-index reports the modifications through the exit status - let has_local_modifications = - !submodule_git().allow_failure().args(["diff-index", "--quiet", "HEAD"]).run(exec_ctx); + let has_local_modifications = !submodule_git() + .allow_failure() + .args(["diff-index", "--quiet", "HEAD"]) + .run(dwn_ctx.exec_ctx); if has_local_modifications { - submodule_git().allow_failure().args(["stash", "push"]).run(exec_ctx); + submodule_git().allow_failure().args(["stash", "push"]).run(dwn_ctx.exec_ctx); } - submodule_git().allow_failure().args(["reset", "-q", "--hard"]).run(exec_ctx); - submodule_git().allow_failure().args(["clean", "-qdfx"]).run(exec_ctx); + submodule_git().allow_failure().args(["reset", "-q", "--hard"]).run(dwn_ctx.exec_ctx); + submodule_git().allow_failure().args(["clean", "-qdfx"]).run(dwn_ctx.exec_ctx); if has_local_modifications { - submodule_git().allow_failure().args(["stash", "pop"]).run(exec_ctx); + submodule_git().allow_failure().args(["stash", "pop"]).run(dwn_ctx.exec_ctx); } } @@ -2548,15 +2439,14 @@ pub fn submodules_(submodules: &Option, rust_info: &channel::GitInfo) -> b /// In particular, we expect llvm sources to be available when this is false. /// /// NOTE: this is not the same as `!is_rust_llvm` when `llvm_has_patches` is set. -pub fn is_system_llvm( - host_target: &TargetSelection, - llvm_from_ci: bool, - target_config: &HashMap, +pub fn is_system_llvm<'a>( + dwn_ctx: impl AsRef>, target: TargetSelection, ) -> bool { - match target_config.get(&target) { + let dwn_ctx = dwn_ctx.as_ref(); + match dwn_ctx.target_config.get(&target) { Some(Target { llvm_config: Some(_), .. }) => { - let ci_llvm = llvm_from_ci && is_host_target(host_target, &target); + let ci_llvm = dwn_ctx.llvm_from_ci && is_host_target(&dwn_ctx.host_target, &target); !ci_llvm } // We're building from the in-tree src/llvm-project sources. @@ -2569,29 +2459,25 @@ pub fn is_host_target(host_target: &TargetSelection, target: &TargetSelection) - host_target == target } -pub(crate) fn ci_llvm_root( - llvm_from_ci: bool, - out: &Path, - host_target: &TargetSelection, -) -> PathBuf { - assert!(llvm_from_ci); - out.join(host_target).join("ci-llvm") +pub(crate) fn ci_llvm_root<'a>(dwn_ctx: impl AsRef>) -> PathBuf { + let dwn_ctx = dwn_ctx.as_ref(); + assert!(dwn_ctx.llvm_from_ci); + dwn_ctx.out.join(dwn_ctx.host_target).join("ci-llvm") } /// Returns the content of the given file at a specific commit. -pub(crate) fn read_file_by_commit( - exec_ctx: &ExecutionContext, - src: &Path, - rust_info: &channel::GitInfo, +pub(crate) fn read_file_by_commit<'a>( + dwn_ctx: impl AsRef>, file: &Path, commit: &str, ) -> String { + let dwn_ctx = dwn_ctx.as_ref(); assert!( - rust_info.is_managed_git_subrepository(), + dwn_ctx.rust_info.is_managed_git_subrepository(), "`Config::read_file_by_commit` is not supported in non-git sources." ); - let mut git = helpers::git(Some(src)); + let mut git = helpers::git(Some(dwn_ctx.src)); git.arg("show").arg(format!("{commit}:{}", file.to_str().unwrap())); - git.run_capture_stdout(exec_ctx).stdout() + git.run_capture_stdout(dwn_ctx.exec_ctx).stdout() } diff --git a/src/bootstrap/src/core/download.rs b/src/bootstrap/src/core/download.rs index 7ec6c62a07d0..5ded44cef144 100644 --- a/src/bootstrap/src/core/download.rs +++ b/src/bootstrap/src/core/download.rs @@ -1,14 +1,17 @@ +use std::collections::HashMap; use std::env; use std::ffi::OsString; use std::fs::{self, File}; use std::io::{BufRead, BufReader, BufWriter, ErrorKind, Write}; use std::path::{Path, PathBuf}; -use std::sync::OnceLock; +use std::sync::{Arc, Mutex, OnceLock}; +use build_helper::git::PathFreshness; use xz2::bufread::XzDecoder; -use crate::core::config::{BUILDER_CONFIG_FILENAME, TargetSelection}; +use crate::core::config::{BUILDER_CONFIG_FILENAME, Target, TargetSelection}; use crate::utils::build_stamp::BuildStamp; +use crate::utils::channel; use crate::utils::exec::{ExecutionContext, command}; use crate::utils::helpers::{exe, hex_encode, move_file}; use crate::{Config, t}; @@ -398,14 +401,21 @@ impl Config { /// Only should be used for pre config initialization downloads. pub(crate) struct DownloadContext<'a> { - host_target: TargetSelection, - out: &'a Path, - patch_binaries_for_nix: Option, - exec_ctx: &'a ExecutionContext, - stage0_metadata: &'a build_helper::stage0_parser::Stage0, - llvm_assertions: bool, - bootstrap_cache_path: &'a Option, - is_running_on_ci: bool, + pub path_modification_cache: Arc, PathFreshness>>>, + pub src: &'a Path, + pub rust_info: &'a channel::GitInfo, + pub submodules: &'a Option, + pub download_rustc_commit: &'a Option, + pub host_target: TargetSelection, + pub llvm_from_ci: bool, + pub target_config: &'a HashMap, + pub out: &'a Path, + pub patch_binaries_for_nix: Option, + pub exec_ctx: &'a ExecutionContext, + pub stage0_metadata: &'a build_helper::stage0_parser::Stage0, + pub llvm_assertions: bool, + pub bootstrap_cache_path: &'a Option, + pub is_running_on_ci: bool, } impl<'a> AsRef> for DownloadContext<'a> { @@ -417,7 +427,14 @@ impl<'a> AsRef> for DownloadContext<'a> { impl<'a> From<&'a Config> for DownloadContext<'a> { fn from(value: &'a Config) -> Self { DownloadContext { + path_modification_cache: value.path_modification_cache.clone(), + src: &value.src, host_target: value.host_target, + rust_info: &value.rust_info, + download_rustc_commit: &value.download_rustc_commit, + submodules: &value.submodules, + llvm_from_ci: value.llvm_from_ci, + target_config: &value.target_config, out: &value.out, patch_binaries_for_nix: value.patch_binaries_for_nix, exec_ctx: &value.exec_ctx, From 8831c5b9946561a37dc7ee6fd826ef1470e8f380 Mon Sep 17 00:00:00 2001 From: Scott McMurray Date: Fri, 8 Aug 2025 21:59:28 -0700 Subject: [PATCH 095/195] Stop using uadd.with.overflow --- compiler/rustc_codegen_llvm/src/builder.rs | 26 ++++++++++++++------ tests/assembly-llvm/x86_64-bigint-helpers.rs | 23 ++++++++--------- tests/codegen-llvm/bigint-helpers.rs | 15 ++++++++--- 3 files changed, 41 insertions(+), 23 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index 32cdef075e76..917d07e3c61b 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -557,13 +557,25 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { let (size, signed) = ty.int_size_and_signed(self.tcx); let width = size.bits(); - if oop == OverflowOp::Sub && !signed { - // Emit sub and icmp instead of llvm.usub.with.overflow. LLVM considers these - // to be the canonical form. It will attempt to reform llvm.usub.with.overflow - // in the backend if profitable. - let sub = self.sub(lhs, rhs); - let cmp = self.icmp(IntPredicate::IntULT, lhs, rhs); - return (sub, cmp); + if !signed { + match oop { + OverflowOp::Sub => { + // Emit sub and icmp instead of llvm.usub.with.overflow. LLVM considers these + // to be the canonical form. It will attempt to reform llvm.usub.with.overflow + // in the backend if profitable. + let sub = self.sub(lhs, rhs); + let cmp = self.icmp(IntPredicate::IntULT, lhs, rhs); + return (sub, cmp); + } + OverflowOp::Add => { + // Like with sub above, using icmp is the preferred form. See + // + let add = self.add(lhs, rhs); + let cmp = self.icmp(IntPredicate::IntULT, add, lhs); + return (add, cmp); + } + OverflowOp::Mul => {} + } } let oop_str = match oop { diff --git a/tests/assembly-llvm/x86_64-bigint-helpers.rs b/tests/assembly-llvm/x86_64-bigint-helpers.rs index 58785932bc2f..c5efda58fd66 100644 --- a/tests/assembly-llvm/x86_64-bigint-helpers.rs +++ b/tests/assembly-llvm/x86_64-bigint-helpers.rs @@ -2,9 +2,7 @@ //@ assembly-output: emit-asm //@ compile-flags: --crate-type=lib -Copt-level=3 -C target-cpu=x86-64-v4 //@ compile-flags: -C llvm-args=-x86-asm-syntax=intel -//@ revisions: llvm-pre-20 llvm-20 -//@ [llvm-20] min-llvm-version: 20 -//@ [llvm-pre-20] max-llvm-major-version: 19 +//@ min-llvm-version: 20 #![no_std] #![feature(bigint_helper_methods)] @@ -23,16 +21,15 @@ pub unsafe extern "sysv64" fn bigint_chain_carrying_add( n: usize, mut carry: bool, ) -> bool { - // llvm-pre-20: mov [[TEMP:r..]], qword ptr [rsi + 8*[[IND:r..]] + 8] - // llvm-pre-20: adc [[TEMP]], qword ptr [rdx + 8*[[IND]] + 8] - // llvm-pre-20: mov qword ptr [rdi + 8*[[IND]] + 8], [[TEMP]] - // llvm-pre-20: mov [[TEMP]], qword ptr [rsi + 8*[[IND]] + 16] - // llvm-pre-20: adc [[TEMP]], qword ptr [rdx + 8*[[IND]] + 16] - // llvm-pre-20: mov qword ptr [rdi + 8*[[IND]] + 16], [[TEMP]] - // llvm-20: adc [[TEMP:r..]], qword ptr [rdx + 8*[[IND:r..]]] - // llvm-20: mov qword ptr [rdi + 8*[[IND]]], [[TEMP]] - // llvm-20: mov [[TEMP]], qword ptr [rsi + 8*[[IND]] + 8] - // llvm-20: adc [[TEMP]], qword ptr [rdx + 8*[[IND]] + 8] + // Even if we emit A+B, LLVM will sometimes reorder that to B+A, so this + // test doesn't actually check which register is mov vs which is adc. + + // CHECK: mov [[TEMP1:.+]], qword ptr [{{rdx|rsi}} + 8*[[IND:.+]] + 8] + // CHECK: adc [[TEMP1]], qword ptr [{{rdx|rsi}} + 8*[[IND]] + 8] + // CHECK: mov qword ptr [rdi + 8*[[IND]] + 8], [[TEMP1]] + // CHECK: mov [[TEMP2:.+]], qword ptr [{{rdx|rsi}} + 8*[[IND]] + 16] + // CHECK: adc [[TEMP2]], qword ptr [{{rdx|rsi}} + 8*[[IND]] + 16] + // CHECK: mov qword ptr [rdi + 8*[[IND]] + 16], [[TEMP2]] for i in 0..n { (*dest.add(i), carry) = u64::carrying_add(*src1.add(i), *src2.add(i), carry); } diff --git a/tests/codegen-llvm/bigint-helpers.rs b/tests/codegen-llvm/bigint-helpers.rs index 355cccb81504..ec70a3eabedb 100644 --- a/tests/codegen-llvm/bigint-helpers.rs +++ b/tests/codegen-llvm/bigint-helpers.rs @@ -3,11 +3,20 @@ #![crate_type = "lib"] #![feature(bigint_helper_methods)] +// Note that there's also an assembly test for this, which is what checks for +// the `ADC` (Add with Carry) instruction on x86 now that the IR we emit uses +// the preferred instruction phrasing instead of the intrinsic. + // CHECK-LABEL: @u32_carrying_add #[no_mangle] pub fn u32_carrying_add(a: u32, b: u32, c: bool) -> (u32, bool) { - // CHECK: @llvm.uadd.with.overflow.i32 - // CHECK: @llvm.uadd.with.overflow.i32 - // CHECK: or disjoint i1 + // CHECK: %[[AB:.+]] = add i32 {{%a, %b|%b, %a}} + // CHECK: %[[O1:.+]] = icmp ult i32 %[[AB]], %a + // CHECK: %[[CEXT:.+]] = zext i1 %c to i32 + // CHECK: %[[ABC:.+]] = add i32 %[[AB]], %[[CEXT]] + // CHECK: %[[O2:.+]] = icmp ult i32 %[[ABC]], %[[AB]] + // CHECK: %[[O:.+]] = or disjoint i1 %[[O1]], %[[O2]] + // CHECK: insertvalue {{.+}}, i32 %[[ABC]], 0 + // CHECK: insertvalue {{.+}}, i1 %[[O]], 1 u32::carrying_add(a, b, c) } From 89b366955ecab610dd6392e183e83600bc02ba11 Mon Sep 17 00:00:00 2001 From: Chris Denton Date: Sat, 9 Aug 2025 07:02:01 +0000 Subject: [PATCH 096/195] Replace unsafe function with safe alternative The `security_attributes` function is marked as safe despite taking a raw pointer which will later be used. Fortunately this function is only used internally and only in one place that has been basically the same for a decade now. However, we only ever set one bool so it's easy enough to replace with something that's actually safe. --- library/std/src/sys/fs/windows.rs | 15 ++++++++++----- library/std/src/sys/process/windows.rs | 8 +------- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/library/std/src/sys/fs/windows.rs b/library/std/src/sys/fs/windows.rs index 9b674a251652..09feddd0be9a 100644 --- a/library/std/src/sys/fs/windows.rs +++ b/library/std/src/sys/fs/windows.rs @@ -80,7 +80,7 @@ pub struct OpenOptions { attributes: u32, share_mode: u32, security_qos_flags: u32, - security_attributes: *mut c::SECURITY_ATTRIBUTES, + inherit_handle: bool, } #[derive(Clone, PartialEq, Eq, Debug)] @@ -203,7 +203,7 @@ impl OpenOptions { share_mode: c::FILE_SHARE_READ | c::FILE_SHARE_WRITE | c::FILE_SHARE_DELETE, attributes: 0, security_qos_flags: 0, - security_attributes: ptr::null_mut(), + inherit_handle: false, } } @@ -243,8 +243,8 @@ impl OpenOptions { // receive is `SECURITY_ANONYMOUS = 0x0`, which we can't check for later on. self.security_qos_flags = flags | c::SECURITY_SQOS_PRESENT; } - pub fn security_attributes(&mut self, attrs: *mut c::SECURITY_ATTRIBUTES) { - self.security_attributes = attrs; + pub fn inherit_handle(&mut self, inherit: bool) { + self.inherit_handle = inherit; } fn get_access_mode(&self) -> io::Result { @@ -307,12 +307,17 @@ impl File { fn open_native(path: &WCStr, opts: &OpenOptions) -> io::Result { let creation = opts.get_creation_mode()?; + let sa = c::SECURITY_ATTRIBUTES { + nLength: size_of::() as u32, + lpSecurityDescriptor: ptr::null_mut(), + bInheritHandle: opts.inherit_handle as c::BOOL, + }; let handle = unsafe { c::CreateFileW( path.as_ptr(), opts.get_access_mode()?, opts.share_mode, - opts.security_attributes, + if opts.inherit_handle { &sa } else { ptr::null() }, creation, opts.get_flags_and_attributes(), ptr::null_mut(), diff --git a/library/std/src/sys/process/windows.rs b/library/std/src/sys/process/windows.rs index 1ee3fbd285f5..f9e15b824757 100644 --- a/library/std/src/sys/process/windows.rs +++ b/library/std/src/sys/process/windows.rs @@ -623,16 +623,10 @@ impl Stdio { // permissions as well as the ability to be inherited to child // processes (as this is about to be inherited). Stdio::Null => { - let size = size_of::(); - let mut sa = c::SECURITY_ATTRIBUTES { - nLength: size as u32, - lpSecurityDescriptor: ptr::null_mut(), - bInheritHandle: 1, - }; let mut opts = OpenOptions::new(); opts.read(stdio_id == c::STD_INPUT_HANDLE); opts.write(stdio_id != c::STD_INPUT_HANDLE); - opts.security_attributes(&mut sa); + opts.inherit_handle(true); File::open(Path::new(r"\\.\NUL"), &opts).map(|file| file.into_inner()) } } From ad1113f87ef828b300ebb8fca397efd358580da3 Mon Sep 17 00:00:00 2001 From: Deadbeef Date: Sat, 9 Aug 2025 13:24:06 +0800 Subject: [PATCH 097/195] remove `P` --- compiler/rustc_ast/src/ast.rs | 331 +++++++++--------- compiler/rustc_ast/src/ast_traits.rs | 11 +- compiler/rustc_ast/src/attr/mod.rs | 5 +- .../rustc_ast/src/expand/autodiff_attrs.rs | 3 +- compiler/rustc_ast/src/format.rs | 3 +- compiler/rustc_ast/src/lib.rs | 1 - compiler/rustc_ast/src/mut_visit.rs | 19 +- compiler/rustc_ast/src/ptr.rs | 11 - compiler/rustc_ast/src/visit.rs | 29 +- compiler/rustc_ast_lowering/src/expr.rs | 17 +- compiler/rustc_ast_lowering/src/item.rs | 5 +- compiler/rustc_ast_lowering/src/lib.rs | 2 +- compiler/rustc_ast_lowering/src/pat.rs | 5 +- compiler/rustc_ast_lowering/src/path.rs | 2 +- .../rustc_ast_passes/src/ast_validation.rs | 3 +- compiler/rustc_ast_pretty/src/pprust/state.rs | 3 +- .../rustc_ast_pretty/src/pprust/state/expr.rs | 13 +- .../rustc_ast_pretty/src/pprust/state/item.rs | 5 +- .../src/alloc_error_handler.rs | 3 +- compiler/rustc_builtin_macros/src/asm.rs | 13 +- compiler/rustc_builtin_macros/src/assert.rs | 15 +- .../src/assert/context.rs | 32 +- compiler/rustc_builtin_macros/src/autodiff.rs | 55 +-- compiler/rustc_builtin_macros/src/cfg_eval.rs | 15 +- .../rustc_builtin_macros/src/concat_bytes.rs | 3 +- .../src/deriving/cmp/partial_eq.rs | 9 +- .../src/deriving/debug.rs | 2 +- .../src/deriving/default.rs | 2 +- .../src/deriving/generic/mod.rs | 78 ++--- .../src/deriving/generic/ty.rs | 7 +- .../rustc_builtin_macros/src/deriving/mod.rs | 13 +- .../rustc_builtin_macros/src/edition_panic.rs | 5 +- compiler/rustc_builtin_macros/src/format.rs | 5 +- .../src/format_foreign.rs | 12 +- .../src/global_allocator.rs | 13 +- compiler/rustc_builtin_macros/src/iter.rs | 3 +- .../rustc_builtin_macros/src/pattern_type.rs | 16 +- .../src/proc_macro_harness.rs | 3 +- .../rustc_builtin_macros/src/source_util.rs | 5 +- compiler/rustc_builtin_macros/src/test.rs | 15 +- .../rustc_builtin_macros/src/test_harness.rs | 7 +- compiler/rustc_builtin_macros/src/util.rs | 11 +- compiler/rustc_expand/src/base.rs | 95 +++-- compiler/rustc_expand/src/build.rs | 218 ++++++------ compiler/rustc_expand/src/expand.rs | 122 ++++--- compiler/rustc_expand/src/module.rs | 3 +- compiler/rustc_expand/src/placeholders.rs | 68 ++-- compiler/rustc_expand/src/proc_macro.rs | 3 +- compiler/rustc_expand/src/stats.rs | 3 +- compiler/rustc_hir_typeck/src/pat.rs | 2 +- compiler/rustc_interface/src/passes.rs | 2 +- compiler/rustc_lint/src/early.rs | 3 +- compiler/rustc_metadata/src/rmeta/decoder.rs | 2 +- compiler/rustc_parse/src/parser/asm.rs | 3 +- .../rustc_parse/src/parser/diagnostics.rs | 89 ++--- compiler/rustc_parse/src/parser/expr.rs | 206 +++++------ compiler/rustc_parse/src/parser/generics.rs | 6 +- compiler/rustc_parse/src/parser/item.rs | 41 +-- compiler/rustc_parse/src/parser/mod.rs | 31 +- .../rustc_parse/src/parser/nonterminal.rs | 16 +- compiler/rustc_parse/src/parser/pat.rs | 39 +-- compiler/rustc_parse/src/parser/path.rs | 9 +- compiler/rustc_parse/src/parser/stmt.rs | 27 +- compiler/rustc_parse/src/parser/tests.rs | 9 +- compiler/rustc_parse/src/parser/ty.rs | 51 +-- compiler/rustc_passes/src/input_stats.rs | 4 +- compiler/rustc_resolve/src/diagnostics.rs | 3 +- compiler/rustc_resolve/src/late.rs | 19 +- .../rustc_resolve/src/late/diagnostics.rs | 9 +- .../src/error_reporting/traits/suggestions.rs | 2 +- .../src/single_component_path_imports.rs | 3 +- .../clippy_lints/src/unnested_or_patterns.rs | 45 +-- .../clippy/clippy_utils/src/ast_utils/mod.rs | 9 +- .../crates/syntax-bridge/src/lib.rs | 8 +- src/tools/rustfmt/src/chains.rs | 6 +- src/tools/rustfmt/src/closures.rs | 4 +- src/tools/rustfmt/src/expr.rs | 6 +- src/tools/rustfmt/src/items.rs | 10 +- src/tools/rustfmt/src/macros.rs | 10 +- src/tools/rustfmt/src/matches.rs | 6 +- src/tools/rustfmt/src/modules.rs | 8 +- src/tools/rustfmt/src/overflow.rs | 4 +- .../rustfmt/src/parse/macros/lazy_static.rs | 3 +- src/tools/rustfmt/src/parse/macros/mod.rs | 14 +- src/tools/rustfmt/src/parse/parser.rs | 4 +- src/tools/rustfmt/src/patterns.rs | 13 +- src/tools/rustfmt/src/rewrite.rs | 3 +- src/tools/rustfmt/src/spanned.rs | 4 +- src/tools/rustfmt/src/types.rs | 5 +- src/tools/rustfmt/src/utils.rs | 6 +- src/tools/rustfmt/src/visitor.rs | 2 +- tests/ui-fulldeps/auxiliary/parser.rs | 3 +- tests/ui-fulldeps/pprust-expr-roundtrip.rs | 28 +- 93 files changed, 1043 insertions(+), 1056 deletions(-) delete mode 100644 compiler/rustc_ast/src/ptr.rs diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index fdff18ffd471..8160ed3cc46f 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -35,7 +35,6 @@ use rustc_span::{ByteSymbol, DUMMY_SP, ErrorGuaranteed, Ident, Span, Symbol, kw, use thin_vec::{ThinVec, thin_vec}; pub use crate::format::*; -use crate::ptr::P; use crate::token::{self, CommentKind, Delimiter}; use crate::tokenstream::{DelimSpan, LazyAttrTokenStream, TokenStream}; use crate::util::parser::{ExprPrecedence, Fixity}; @@ -225,7 +224,7 @@ pub struct PathSegment { /// `Some` means that parameter list is supplied (`Path`) /// but it can be empty (`Path<>`). /// `P` is used as a size optimization for the common case with no parameters. - pub args: Option>, + pub args: Option>, } // Succeeds if the path segment is arg-free and matches the given symbol. @@ -286,7 +285,7 @@ pub enum GenericArg { /// `'a` in `Foo<'a>`. Lifetime(#[visitable(extra = LifetimeCtxt::GenericArg)] Lifetime), /// `Bar` in `Foo`. - Type(P), + Type(Box), /// `1` in `Foo<1>`. Const(AnonConst), } @@ -328,15 +327,15 @@ impl AngleBracketedArg { } } -impl From for P { +impl From for Box { fn from(val: AngleBracketedArgs) -> Self { - P(GenericArgs::AngleBracketed(val)) + Box::new(GenericArgs::AngleBracketed(val)) } } -impl From for P { +impl From for Box { fn from(val: ParenthesizedArgs) -> Self { - P(GenericArgs::Parenthesized(val)) + Box::new(GenericArgs::Parenthesized(val)) } } @@ -350,7 +349,7 @@ pub struct ParenthesizedArgs { pub span: Span, /// `(A, B)` - pub inputs: ThinVec>, + pub inputs: ThinVec>, /// ```text /// Foo(A, B) -> C @@ -435,10 +434,10 @@ pub enum GenericParamKind { /// A lifetime definition (e.g., `'a: 'b + 'c + 'd`). Lifetime, Type { - default: Option>, + default: Option>, }, Const { - ty: P, + ty: Box, /// Span of the whole parameter definition, including default. span: Span, /// Optional default value for the const generic param. @@ -526,7 +525,7 @@ pub struct WhereBoundPredicate { /// Any generics from a `for` binding. pub bound_generic_params: ThinVec, /// The type being bounded. - pub bounded_ty: P, + pub bounded_ty: Box, /// Trait and lifetime bounds (`Clone + Send + 'static`). #[visitable(extra = BoundKind::Bound)] pub bounds: GenericBounds, @@ -548,8 +547,8 @@ pub struct WhereRegionPredicate { /// E.g., `T = int`. #[derive(Clone, Encodable, Decodable, Debug, Walkable)] pub struct WhereEqPredicate { - pub lhs_ty: P, - pub rhs_ty: P, + pub lhs_ty: Box, + pub rhs_ty: Box, } #[derive(Clone, Encodable, Decodable, Debug, Walkable)] @@ -558,7 +557,7 @@ pub struct Crate { /// expansion placeholders or an unassigned value (`DUMMY_NODE_ID`) before that. pub id: NodeId, pub attrs: AttrVec, - pub items: ThinVec>, + pub items: ThinVec>, pub spans: ModSpans, pub is_placeholder: bool, } @@ -638,7 +637,7 @@ pub struct Pat { impl Pat { /// Attempt reparsing the pattern as a type. /// This is intended for use by diagnostics. - pub fn to_ty(&self) -> Option> { + pub fn to_ty(&self) -> Option> { let kind = match &self.kind { PatKind::Missing => unreachable!(), // In a type expression `_` is an inference variable. @@ -671,7 +670,7 @@ impl Pat { _ => return None, }; - Some(P(Ty { kind, id: self.id, span: self.span, tokens: None })) + Some(Box::new(Ty { kind, id: self.id, span: self.span, tokens: None })) } /// Walk top-down and call `it` in each place where a pattern occurs @@ -764,8 +763,8 @@ impl Pat { } } -impl From> for Pat { - fn from(value: P) -> Self { +impl From> for Pat { + fn from(value: Box) -> Self { *value } } @@ -780,7 +779,7 @@ pub struct PatField { /// The identifier for the field. pub ident: Ident, /// The pattern the field is destructured to. - pub pat: P, + pub pat: Box, pub is_shorthand: bool, pub attrs: AttrVec, pub id: NodeId, @@ -865,44 +864,44 @@ pub enum PatKind { /// or a unit struct/variant pattern, or a const pattern (in the last two cases the third /// field must be `None`). Disambiguation cannot be done with parser alone, so it happens /// during name resolution. - Ident(BindingMode, Ident, Option>), + Ident(BindingMode, Ident, Option>), /// A struct or struct variant pattern (e.g., `Variant {x, y, ..}`). - Struct(Option>, Path, ThinVec, PatFieldsRest), + Struct(Option>, Path, ThinVec, PatFieldsRest), /// A tuple struct/variant pattern (`Variant(x, y, .., z)`). - TupleStruct(Option>, Path, ThinVec>), + TupleStruct(Option>, Path, ThinVec>), /// An or-pattern `A | B | C`. /// Invariant: `pats.len() >= 2`. - Or(ThinVec>), + Or(ThinVec>), /// A possibly qualified path pattern. /// Unqualified path patterns `A::B::C` can legally refer to variants, structs, constants /// or associated constants. Qualified path patterns `::B::C`/`::B::C` can /// only legally refer to associated constants. - Path(Option>, Path), + Path(Option>, Path), /// A tuple pattern (`(a, b)`). - Tuple(ThinVec>), + Tuple(ThinVec>), /// A `box` pattern. - Box(P), + Box(Box), /// A `deref` pattern (currently `deref!()` macro-based syntax). - Deref(P), + Deref(Box), /// A reference pattern (e.g., `&mut (a, b)`). - Ref(P, Mutability), + Ref(Box, Mutability), /// A literal, const block or path. - Expr(P), + Expr(Box), /// A range pattern (e.g., `1...2`, `1..2`, `1..`, `..2`, `1..=2`, `..=2`). - Range(Option>, Option>, Spanned), + Range(Option>, Option>, Spanned), /// A slice pattern `[a, b, c]`. - Slice(ThinVec>), + Slice(ThinVec>), /// A rest pattern `..`. /// @@ -922,13 +921,13 @@ pub enum PatKind { Never, /// A guard pattern (e.g., `x if guard(x)`). - Guard(P, P), + Guard(Box, Box), /// Parentheses in patterns used for grouping (i.e., `(PAT)`). - Paren(P), + Paren(Box), /// A macro pattern; pre-expansion. - MacCall(P), + MacCall(Box), /// Placeholder for a pattern that wasn't syntactically well formed in some way. Err(ErrorGuaranteed), @@ -1223,22 +1222,22 @@ impl Stmt { #[derive(Clone, Encodable, Decodable, Debug, Walkable)] pub enum StmtKind { /// A local (let) binding. - Let(P), + Let(Box), /// An item definition. - Item(P), + Item(Box), /// Expr without trailing semi-colon. - Expr(P), + Expr(Box), /// Expr with a trailing semi-colon. - Semi(P), + Semi(Box), /// Just a trailing semi-colon. Empty, /// Macro. - MacCall(P), + MacCall(Box), } #[derive(Clone, Encodable, Decodable, Debug, Walkable)] pub struct MacCallStmt { - pub mac: P, + pub mac: Box, pub style: MacStmtStyle, pub attrs: AttrVec, pub tokens: Option, @@ -1262,8 +1261,8 @@ pub enum MacStmtStyle { pub struct Local { pub id: NodeId, pub super_: Option, - pub pat: P, - pub ty: Option>, + pub pat: Box, + pub ty: Option>, pub kind: LocalKind, pub span: Span, pub colon_sp: Option, @@ -1278,10 +1277,10 @@ pub enum LocalKind { Decl, /// Local declaration with an initializer. /// Example: `let x = y;` - Init(P), + Init(Box), /// Local declaration with an initializer and an `else` clause. /// Example: `let Some(x) = y else { return };` - InitElse(P, P), + InitElse(Box, Box), } impl LocalKind { @@ -1315,11 +1314,11 @@ impl LocalKind { pub struct Arm { pub attrs: AttrVec, /// Match arm pattern, e.g. `10` in `match foo { 10 => {}, _ => {} }`. - pub pat: P, + pub pat: Box, /// Match arm guard, e.g. `n > 10` in `match foo { n if n > 10 => {}, _ => {} }`. - pub guard: Option>, + pub guard: Option>, /// Match arm body. Omitted if the pattern is a never pattern. - pub body: Option>, + pub body: Option>, pub span: Span, pub id: NodeId, pub is_placeholder: bool, @@ -1332,7 +1331,7 @@ pub struct ExprField { pub id: NodeId, pub span: Span, pub ident: Ident, - pub expr: P, + pub expr: Box, pub is_shorthand: bool, pub is_placeholder: bool, } @@ -1357,7 +1356,7 @@ pub enum UnsafeSource { #[derive(Clone, Encodable, Decodable, Debug, Walkable)] pub struct AnonConst { pub id: NodeId, - pub value: P, + pub value: Box, } /// An expression. @@ -1469,7 +1468,7 @@ impl Expr { } /// Attempts to reparse as `Ty` (for diagnostic purposes). - pub fn to_ty(&self) -> Option> { + pub fn to_ty(&self) -> Option> { let kind = match &self.kind { // Trivial conversions. ExprKind::Path(qself, path) => TyKind::Path(qself.clone(), path.clone()), @@ -1511,7 +1510,7 @@ impl Expr { _ => return None, }; - Some(P(Ty { kind, id: self.id, span: self.span, tokens: None })) + Some(Box::new(Ty { kind, id: self.id, span: self.span, tokens: None })) } pub fn precedence(&self) -> ExprPrecedence { @@ -1632,8 +1631,8 @@ impl Expr { } } -impl From> for Expr { - fn from(value: P) -> Self { +impl From> for Expr { + fn from(value: Box) -> Self { *value } } @@ -1645,8 +1644,8 @@ pub struct Closure { pub constness: Const, pub coroutine_kind: Option, pub movability: Movability, - pub fn_decl: P, - pub body: P, + pub fn_decl: Box, + pub body: Box, /// The span of the declaration block: 'move |...| -> ...' pub fn_decl_span: Span, /// The span of the argument block `|...|` @@ -1677,9 +1676,9 @@ pub struct MethodCall { /// The method name and its generic arguments, e.g. `foo::`. pub seg: PathSegment, /// The receiver, e.g. `x`. - pub receiver: P, + pub receiver: Box, /// The arguments, e.g. `a, b, c`. - pub args: ThinVec>, + pub args: ThinVec>, /// The span of the function, without the dot and receiver e.g. `foo::(a, b, c)`. pub span: Span, @@ -1688,7 +1687,7 @@ pub struct MethodCall { #[derive(Clone, Encodable, Decodable, Debug, Walkable)] pub enum StructRest { /// `..x`. - Base(P), + Base(Box), /// `..`. Rest(Span), /// No trailing `..` or expression. @@ -1697,7 +1696,7 @@ pub enum StructRest { #[derive(Clone, Encodable, Decodable, Debug, Walkable)] pub struct StructExpr { - pub qself: Option>, + pub qself: Option>, pub path: Path, pub fields: ThinVec, pub rest: StructRest, @@ -1707,7 +1706,7 @@ pub struct StructExpr { #[derive(Clone, Encodable, Decodable, Debug)] pub enum ExprKind { /// An array (e.g, `[a, b, c, d]`). - Array(ThinVec>), + Array(ThinVec>), /// Allow anonymous constants from an inline `const` block. ConstBlock(AnonConst), /// A function call. @@ -1716,90 +1715,90 @@ pub enum ExprKind { /// and the second field is the list of arguments. /// This also represents calling the constructor of /// tuple-like ADTs such as tuple structs and enum variants. - Call(P, ThinVec>), + Call(Box, ThinVec>), /// A method call (e.g., `x.foo::(a, b, c)`). MethodCall(Box), /// A tuple (e.g., `(a, b, c, d)`). - Tup(ThinVec>), + Tup(ThinVec>), /// A binary operation (e.g., `a + b`, `a * b`). - Binary(BinOp, P, P), + Binary(BinOp, Box, Box), /// A unary operation (e.g., `!x`, `*x`). - Unary(UnOp, P), + Unary(UnOp, Box), /// A literal (e.g., `1`, `"foo"`). Lit(token::Lit), /// A cast (e.g., `foo as f64`). - Cast(P, P), + Cast(Box, Box), /// A type ascription (e.g., `builtin # type_ascribe(42, usize)`). /// /// Usually not written directly in user code but /// indirectly via the macro `type_ascribe!(...)`. - Type(P, P), + Type(Box, Box), /// A `let pat = expr` expression that is only semantically allowed in the condition /// of `if` / `while` expressions. (e.g., `if let 0 = x { .. }`). /// /// `Span` represents the whole `let pat = expr` statement. - Let(P, P, Span, Recovered), + Let(Box, Box, Span, Recovered), /// An `if` block, with an optional `else` block. /// /// `if expr { block } else { expr }` /// /// If present, the "else" expr is always `ExprKind::Block` (for `else`) or /// `ExprKind::If` (for `else if`). - If(P, P, Option>), + If(Box, Box, Option>), /// A while loop, with an optional label. /// /// `'label: while expr { block }` - While(P, P, Option

(index) } } + + fn metadata(&self) -> Option { + let mut metadata = StepMetadata::doc(&format!("{} (book)", self.name), self.target); + if let Some(compiler) = self.build_compiler { + metadata = metadata.built_by(compiler); + } + + Some(metadata) + } } #[derive(Debug, Clone, Hash, PartialEq, Eq)] pub struct TheBook { - compiler: Compiler, + /// Compiler whose rustdoc will be used to generated documentation. + build_compiler: Compiler, target: TargetSelection, } @@ -212,7 +223,7 @@ impl Step for TheBook { fn make_run(run: RunConfig<'_>) { run.builder.ensure(TheBook { - compiler: run.builder.compiler(run.builder.top_stage, run.builder.config.host_target), + build_compiler: prepare_doc_compiler(run.builder, run.target, run.builder.top_stage), target: run.target, }); } @@ -229,7 +240,7 @@ impl Step for TheBook { fn run(self, builder: &Builder<'_>) { builder.require_submodule("src/doc/book", None); - let compiler = self.compiler; + let compiler = self.build_compiler; let target = self.target; let absolute_path = builder.src.join("src/doc/book"); @@ -242,7 +253,7 @@ impl Step for TheBook { src: absolute_path.clone(), parent: Some(self), languages: vec![], - rustdoc_compiler: None, + build_compiler: None, }); // building older edition redirects @@ -255,7 +266,7 @@ impl Step for TheBook { // treat the other editions as not having a parent. parent: Option::::None, languages: vec![], - rustdoc_compiler: None, + build_compiler: None, }); } @@ -1271,15 +1282,18 @@ impl Step for RustcBook { src: out_base, parent: Some(self), languages: vec![], - rustdoc_compiler: None, + build_compiler: None, }); } } +/// Documents the reference. +/// It is always done using a stage 1+ compiler, because it references in-tree compiler/stdlib +/// concepts. #[derive(Ord, PartialOrd, Debug, Clone, Hash, PartialEq, Eq)] pub struct Reference { - pub compiler: Compiler, - pub target: TargetSelection, + build_compiler: Compiler, + target: TargetSelection, } impl Step for Reference { @@ -1292,8 +1306,19 @@ impl Step for Reference { } fn make_run(run: RunConfig<'_>) { + // Bump the stage to 2, because the reference requires an in-tree compiler. + // At the same time, since this step is enabled by default, we don't want `x doc` to fail + // in stage 1. + // FIXME: create a shared method on builder for auto-bumping, and print some warning when + // it happens. + let stage = if run.builder.config.is_explicit_stage() || run.builder.top_stage >= 2 { + run.builder.top_stage + } else { + 2 + }; + run.builder.ensure(Reference { - compiler: run.builder.compiler(run.builder.top_stage, run.builder.config.host_target), + build_compiler: prepare_doc_compiler(run.builder, run.target, stage), target: run.target, }); } @@ -1304,14 +1329,14 @@ impl Step for Reference { // This is needed for generating links to the standard library using // the mdbook-spec plugin. - builder.std(self.compiler, builder.config.host_target); + builder.std(self.build_compiler, builder.config.host_target); // Run rustbook/mdbook to generate the HTML pages. builder.ensure(RustbookSrc { target: self.target, name: "reference".to_owned(), src: builder.src.join("src/doc/reference"), - rustdoc_compiler: Some(self.compiler), + build_compiler: Some(self.build_compiler), parent: Some(self), languages: vec![], }); diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs index e45a5b8fba9b..4fc2f56da888 100644 --- a/src/bootstrap/src/core/builder/tests.rs +++ b/src/bootstrap/src/core/builder/tests.rs @@ -1870,6 +1870,22 @@ mod snapshot { [doc] Compiletest "); } + + // Reference should be auto-bumped to stage 2. + #[test] + fn doc_reference() { + let ctx = TestCtx::new(); + insta::assert_snapshot!( + ctx.config("doc") + .path("reference") + .render_steps(), @r" + [build] llvm + [build] rustc 0 -> rustc 1 + [build] rustc 1 -> std 1 + [build] rustc 0 -> Rustbook 1 + [doc] rustc 1 -> reference (book) 2 + "); + } } struct ExecutedSteps { From 5217eddf04421249a602e79d06d562d49d6c8195 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Wed, 6 Aug 2025 16:45:24 +0200 Subject: [PATCH 119/195] Update `RustcBook` doc step --- src/bootstrap/src/core/build_steps/doc.rs | 33 ++++++++++++++++------ src/bootstrap/src/core/build_steps/test.rs | 7 ++--- 2 files changed, 26 insertions(+), 14 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/doc.rs b/src/bootstrap/src/core/build_steps/doc.rs index 24dadcd83131..3034ebdf22bd 100644 --- a/src/bootstrap/src/core/build_steps/doc.rs +++ b/src/bootstrap/src/core/build_steps/doc.rs @@ -1200,9 +1200,15 @@ fn symlink_dir_force(config: &Config, original: &Path, link: &Path) { #[derive(Ord, PartialOrd, Debug, Clone, Hash, PartialEq, Eq)] pub struct RustcBook { - pub compiler: Compiler, - pub target: TargetSelection, - pub validate: bool, + build_compiler: Compiler, + target: TargetSelection, + validate: bool, +} + +impl RustcBook { + pub fn validate(build_compiler: Compiler, target: TargetSelection) -> Self { + Self { build_compiler, target, validate: true } + } } impl Step for RustcBook { @@ -1216,8 +1222,17 @@ impl Step for RustcBook { } fn make_run(run: RunConfig<'_>) { + // Bump the stage to 2, because the rustc book requires an in-tree compiler. + // At the same time, since this step is enabled by default, we don't want `x doc` to fail + // in stage 1. + let stage = if run.builder.config.is_explicit_stage() || run.builder.top_stage >= 2 { + run.builder.top_stage + } else { + 2 + }; + run.builder.ensure(RustcBook { - compiler: run.builder.compiler(run.builder.top_stage, run.builder.config.host_target), + build_compiler: prepare_doc_compiler(run.builder, run.target, stage), target: run.target, validate: false, }); @@ -1235,10 +1250,10 @@ impl Step for RustcBook { builder.cp_link_r(&builder.src.join("src/doc/rustc"), &out_base); builder.info(&format!("Generating lint docs ({})", self.target)); - let rustc = builder.rustc(self.compiler); + let rustc = builder.rustc(self.build_compiler); // The tool runs `rustc` for extracting output examples, so it needs a // functional sysroot. - builder.std(self.compiler, self.target); + builder.std(self.build_compiler, self.target); let mut cmd = builder.tool_cmd(Tool::LintDocs); cmd.arg("--src"); cmd.arg(builder.src.join("compiler")); @@ -1264,12 +1279,12 @@ impl Step for RustcBook { // If the lib directories are in an unusual location (changed in // bootstrap.toml), then this needs to explicitly update the dylib search // path. - builder.add_rustc_lib_path(self.compiler, &mut cmd); + builder.add_rustc_lib_path(self.build_compiler, &mut cmd); let doc_generator_guard = builder.msg( Kind::Run, - self.compiler.stage, + self.build_compiler.stage, "lint-docs", - self.compiler.host, + self.build_compiler.host, self.target, ); cmd.run(builder); diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index 5cce07019e9d..df7016005e9d 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -3310,11 +3310,8 @@ impl Step for LintDocs { /// Tests that the lint examples in the rustc book generate the correct /// lints and have the expected format. fn run(self, builder: &Builder<'_>) { - builder.ensure(crate::core::build_steps::doc::RustcBook { - compiler: self.compiler, - target: self.target, - validate: true, - }); + builder + .ensure(crate::core::build_steps::doc::RustcBook::validate(self.compiler, self.target)); } } From c35e847a400e4bb9c25d58e539cfb47e6bb58062 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Wed, 6 Aug 2025 17:17:02 +0200 Subject: [PATCH 120/195] Update `Standalone` and `Releases` doc steps --- src/bootstrap/src/core/build_steps/doc.rs | 45 +++++++++++++++-------- 1 file changed, 29 insertions(+), 16 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/doc.rs b/src/bootstrap/src/core/build_steps/doc.rs index 3034ebdf22bd..9968baf83144 100644 --- a/src/bootstrap/src/core/build_steps/doc.rs +++ b/src/bootstrap/src/core/build_steps/doc.rs @@ -328,7 +328,7 @@ fn invoke_rustdoc( #[derive(Debug, Clone, Hash, PartialEq, Eq)] pub struct Standalone { - compiler: Compiler, + build_compiler: Compiler, target: TargetSelection, } @@ -343,7 +343,11 @@ impl Step for Standalone { fn make_run(run: RunConfig<'_>) { run.builder.ensure(Standalone { - compiler: run.builder.compiler(run.builder.top_stage, run.builder.config.host_target), + build_compiler: prepare_doc_compiler( + run.builder, + run.builder.host_target, + run.builder.top_stage, + ), target: run.target, }); } @@ -358,8 +362,8 @@ impl Step for Standalone { /// In the end, this is just a glorified wrapper around rustdoc! fn run(self, builder: &Builder<'_>) { let target = self.target; - let compiler = self.compiler; - let _guard = builder.msg_doc(compiler, "standalone", target); + let build_compiler = self.build_compiler; + let _guard = builder.msg_doc(build_compiler, "standalone", target); let out = builder.doc_out(target); t!(fs::create_dir_all(&out)); @@ -378,7 +382,7 @@ impl Step for Standalone { } let html = out.join(filename).with_extension("html"); - let rustdoc = builder.rustdoc_for_compiler(compiler); + let rustdoc = builder.rustdoc_for_compiler(build_compiler); if up_to_date(&path, &html) && up_to_date(&footer, &html) && up_to_date(&favicon, &html) @@ -389,7 +393,7 @@ impl Step for Standalone { continue; } - let mut cmd = builder.rustdoc_cmd(compiler); + let mut cmd = builder.rustdoc_cmd(build_compiler); cmd.arg("--html-after-content") .arg(&footer) @@ -426,11 +430,15 @@ impl Step for Standalone { builder.open_in_browser(index); } } + + fn metadata(&self) -> Option { + Some(StepMetadata::doc("standalone", self.target).built_by(self.build_compiler)) + } } #[derive(Debug, Clone, Hash, PartialEq, Eq)] pub struct Releases { - compiler: Compiler, + build_compiler: Compiler, target: TargetSelection, } @@ -445,7 +453,11 @@ impl Step for Releases { fn make_run(run: RunConfig<'_>) { run.builder.ensure(Releases { - compiler: run.builder.compiler(run.builder.top_stage, run.builder.config.host_target), + build_compiler: prepare_doc_compiler( + run.builder, + run.builder.host_target, + run.builder.top_stage, + ), target: run.target, }); } @@ -457,15 +469,12 @@ impl Step for Releases { /// the headline added. In the end, the conversion is done by Rustdoc. fn run(self, builder: &Builder<'_>) { let target = self.target; - let compiler = self.compiler; - let _guard = builder.msg_doc(compiler, "releases", target); + let build_compiler = self.build_compiler; + let _guard = builder.msg_doc(build_compiler, "releases", target); let out = builder.doc_out(target); t!(fs::create_dir_all(&out)); - builder.ensure(Standalone { - compiler: builder.compiler(builder.top_stage, builder.config.host_target), - target, - }); + builder.ensure(Standalone { build_compiler, target }); let version_info = builder.ensure(SharedAssets { target: self.target }).version_info; @@ -476,7 +485,7 @@ impl Step for Releases { let html = out.join("releases.html"); let tmppath = out.join("releases.md"); let inpath = builder.src.join("RELEASES.md"); - let rustdoc = builder.rustdoc_for_compiler(compiler); + let rustdoc = builder.rustdoc_for_compiler(build_compiler); if !up_to_date(&inpath, &html) || !up_to_date(&footer, &html) || !up_to_date(&favicon, &html) @@ -489,7 +498,7 @@ impl Step for Releases { t!(tmpfile.write_all(b"% Rust Release Notes\n\n")); t!(io::copy(&mut t!(fs::File::open(&inpath)), &mut tmpfile)); mem::drop(tmpfile); - let mut cmd = builder.rustdoc_cmd(compiler); + let mut cmd = builder.rustdoc_cmd(build_compiler); cmd.arg("--html-after-content") .arg(&footer) @@ -522,6 +531,10 @@ impl Step for Releases { builder.open_in_browser(&html); } } + + fn metadata(&self) -> Option { + Some(StepMetadata::doc("releases", self.target).built_by(self.build_compiler)) + } } #[derive(Debug, Clone)] From bec8e1dc38526c8a2856deb0119131946ec10c69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Wed, 6 Aug 2025 18:03:17 +0200 Subject: [PATCH 121/195] Fix documentation of tools --- src/bootstrap/src/core/build_steps/doc.rs | 86 ++++++++++++---------- src/bootstrap/src/core/build_steps/test.rs | 2 +- src/bootstrap/src/core/builder/cargo.rs | 4 +- src/bootstrap/src/core/builder/tests.rs | 16 +--- 4 files changed, 54 insertions(+), 54 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/doc.rs b/src/bootstrap/src/core/build_steps/doc.rs index 9968baf83144..5b536072b018 100644 --- a/src/bootstrap/src/core/build_steps/doc.rs +++ b/src/bootstrap/src/core/build_steps/doc.rs @@ -16,8 +16,7 @@ use crate::core::build_steps::tool::{ self, RustcPrivateCompilers, SourceType, Tool, prepare_tool_cargo, }; use crate::core::builder::{ - self, Alias, Builder, Compiler, Kind, RunConfig, ShouldRun, Step, StepMetadata, - crate_description, + self, Builder, Compiler, Kind, RunConfig, ShouldRun, Step, StepMetadata, crate_description, }; use crate::core::config::{Config, TargetSelection}; use crate::helpers::{submodule_path_of, symlink_dir, t, up_to_date}; @@ -26,7 +25,7 @@ use crate::{FileType, Mode}; macro_rules! book { ($($name:ident, $path:expr, $book_name:expr, $lang:expr ;)+) => { $( - #[derive(Debug, Clone, Hash, PartialEq, Eq)] + #[derive(Debug, Clone, Hash, PartialEq, Eq)] pub struct $name { target: TargetSelection, } @@ -797,13 +796,21 @@ pub struct Rustc { impl Rustc { /// Document `stage` compiler for the given `target`. - pub(crate) fn for_stage(builder: &Builder<'_>, target: TargetSelection, stage: u32) -> Self { + pub(crate) fn for_stage(builder: &Builder<'_>, stage: u32, target: TargetSelection) -> Self { + let build_compiler = prepare_doc_compiler(builder, target, stage); + Self::from_build_compiler(builder, build_compiler, target) + } + + fn from_build_compiler( + builder: &Builder<'_>, + build_compiler: Compiler, + target: TargetSelection, + ) -> Self { let crates = builder .in_tree_crates("rustc-main", Some(target)) .into_iter() .map(|krate| krate.name.to_string()) .collect(); - let build_compiler = prepare_doc_compiler(builder, target, stage); Self { build_compiler, target, crates } } } @@ -821,7 +828,7 @@ impl Step for Rustc { } fn make_run(run: RunConfig<'_>) { - run.builder.ensure(Rustc::for_stage(run.builder, run.target, run.builder.top_stage)); + run.builder.ensure(Rustc::for_stage(run.builder, run.builder.top_stage, run.target)); } /// Generates compiler documentation. @@ -942,12 +949,14 @@ macro_rules! tool_doc { ( $tool: ident, $path: literal, - $(rustc_tool = $rustc_tool:literal, )? + $(rustc_private_tool = $rustc_private_tool:literal, )? $(is_library = $is_library:expr,)? $(crates = $crates:expr)? ) => { #[derive(Debug, Clone, Hash, PartialEq, Eq)] pub struct $tool { + build_compiler: Compiler, + mode: Mode, target: TargetSelection, } @@ -962,15 +971,26 @@ macro_rules! tool_doc { } fn make_run(run: RunConfig<'_>) { - run.builder.ensure($tool { target: run.target }); + let target = run.target; + let (build_compiler, mode) = if true $(&& $rustc_private_tool)? { + // Rustdoc needs the rustc sysroot available to build. + let compilers = RustcPrivateCompilers::new(run.builder, run.builder.top_stage, target); + + // Build rustc docs so that we generate relative links. + run.builder.ensure(Rustc::from_build_compiler(run.builder, compilers.build_compiler(), target)); + + (compilers.build_compiler(), Mode::ToolRustc) + } else { + // bootstrap/host tools have to be documented with the stage 0 compiler + (prepare_doc_compiler(run.builder, target, 1), Mode::ToolBootstrap) + }; + + run.builder.ensure($tool { build_compiler, mode, target }); } - /// Generates compiler documentation. + /// Generates documentation for a tool. /// - /// This will generate all documentation for compiler and dependencies. - /// Compiler documentation is distributed separately, so we make sure - /// we do not merge it with the other documentation from std, test and - /// proc_macros. This is largely just a wrapper around `cargo doc`. + /// This is largely just a wrapper around `cargo doc`. fn run(self, builder: &Builder<'_>) { let mut source_type = SourceType::InTree; @@ -979,31 +999,17 @@ macro_rules! tool_doc { builder.require_submodule(&submodule_path, None); } - let stage = builder.top_stage; - let target = self.target; + let $tool { build_compiler, mode, target } = self; // This is the intended out directory for compiler documentation. let out = builder.compiler_doc_out(target); t!(fs::create_dir_all(&out)); - let compiler = builder.compiler(stage, builder.config.host_target); - builder.std(compiler, target); - - if true $(&& $rustc_tool)? { - // Build rustc docs so that we generate relative links. - builder.ensure(Rustc::new(stage, target, builder)); - - // Rustdoc needs the rustc sysroot available to build. - // FIXME: is there a way to only ensure `check::Rustc` here? Last time I tried it failed - // with strange errors, but only on a full bors test ... - builder.ensure(compile::Rustc::new(compiler, target)); - } - // Build cargo command. let mut cargo = prepare_tool_cargo( builder, - compiler, - Mode::ToolRustc, + build_compiler, + mode, target, Kind::Doc, $path, @@ -1030,7 +1036,7 @@ macro_rules! tool_doc { cargo.rustdocflag("--show-type-layout"); cargo.rustdocflag("--generate-link-to-definition"); - let out_dir = builder.stage_out(compiler, Mode::ToolRustc).join(target).join("doc"); + let out_dir = builder.stage_out(build_compiler, mode).join(target).join("doc"); $(for krate in $crates { let dir_name = krate.replace("-", "_"); t!(fs::create_dir_all(out_dir.join(&*dir_name))); @@ -1038,10 +1044,10 @@ macro_rules! tool_doc { // Symlink compiler docs to the output directory of rustdoc documentation. symlink_dir_force(&builder.config, &out, &out_dir); - let proc_macro_out_dir = builder.stage_out(compiler, Mode::ToolRustc).join("doc"); + let proc_macro_out_dir = builder.stage_out(build_compiler, mode).join("doc"); symlink_dir_force(&builder.config, &out, &proc_macro_out_dir); - let _guard = builder.msg_doc(compiler, stringify!($tool).to_lowercase(), target); + let _guard = builder.msg_doc(build_compiler, stringify!($tool).to_lowercase(), target); cargo.into_cmd().run(builder); if !builder.config.dry_run() { @@ -1055,7 +1061,7 @@ macro_rules! tool_doc { } fn metadata(&self) -> Option { - Some(StepMetadata::doc(stringify!($tool), self.target)) + Some(StepMetadata::doc(stringify!($tool), self.target).built_by(self.build_compiler)) } } } @@ -1065,7 +1071,7 @@ macro_rules! tool_doc { tool_doc!( BuildHelper, "src/build_helper", - rustc_tool = false, + rustc_private_tool = false, is_library = true, crates = ["build_helper"] ); @@ -1076,7 +1082,7 @@ tool_doc!(Miri, "src/tools/miri", crates = ["miri"]); tool_doc!( Cargo, "src/tools/cargo", - rustc_tool = false, + rustc_private_tool = false, crates = [ "cargo", "cargo-credential", @@ -1090,25 +1096,25 @@ tool_doc!( "rustfix", ] ); -tool_doc!(Tidy, "src/tools/tidy", rustc_tool = false, crates = ["tidy"]); +tool_doc!(Tidy, "src/tools/tidy", rustc_private_tool = false, crates = ["tidy"]); tool_doc!( Bootstrap, "src/bootstrap", - rustc_tool = false, + rustc_private_tool = false, is_library = true, crates = ["bootstrap"] ); tool_doc!( RunMakeSupport, "src/tools/run-make-support", - rustc_tool = false, + rustc_private_tool = false, is_library = true, crates = ["run_make_support"] ); tool_doc!( Compiletest, "src/tools/compiletest", - rustc_tool = false, + rustc_private_tool = false, is_library = true, crates = ["compiletest"] ); diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index df7016005e9d..d6d7f7be1dfb 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -214,8 +214,8 @@ impl Step for HtmlCheck { builder.default_doc(&[]); builder.ensure(crate::core::build_steps::doc::Rustc::for_stage( builder, - self.target, builder.top_stage, + self.target, )); builder diff --git a/src/bootstrap/src/core/builder/cargo.rs b/src/bootstrap/src/core/builder/cargo.rs index e10af2b55f9e..6910cc7c5794 100644 --- a/src/bootstrap/src/core/builder/cargo.rs +++ b/src/bootstrap/src/core/builder/cargo.rs @@ -493,7 +493,9 @@ impl Builder<'_> { if cmd_kind == Kind::Doc { let my_out = match mode { // This is the intended out directory for compiler documentation. - Mode::Rustc | Mode::ToolRustc => self.compiler_doc_out(target), + Mode::Rustc | Mode::ToolRustc | Mode::ToolBootstrap => { + self.compiler_doc_out(target) + } Mode::Std => { if self.config.cmd.json() { out_dir.join(target).join("json-doc") diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs index 4fc2f56da888..8a22633b362d 100644 --- a/src/bootstrap/src/core/builder/tests.rs +++ b/src/bootstrap/src/core/builder/tests.rs @@ -1845,11 +1845,8 @@ mod snapshot { .path("src/tools/compiletest") .stage(1) .render_steps(), @r" - [build] llvm - [build] rustc 0 -> rustc 1 - [build] rustc 1 -> std 1 - [build] rustdoc 1 - [doc] Compiletest + [build] rustdoc 0 + [doc] rustc 0 -> Compiletest 1 "); } @@ -1861,13 +1858,8 @@ mod snapshot { .path("src/tools/compiletest") .stage(2) .render_steps(), @r" - [build] llvm - [build] rustc 0 -> rustc 1 - [build] rustc 1 -> std 1 - [build] rustc 1 -> rustc 2 - [build] rustc 2 -> std 2 - [build] rustdoc 2 - [doc] Compiletest + [build] rustdoc 0 + [doc] rustc 0 -> Compiletest 1 "); } From 8f3f060c02d826d4b91bf1c936fce2ffea883bdd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Wed, 6 Aug 2025 18:29:57 +0200 Subject: [PATCH 122/195] Update `Std` doc step --- src/bootstrap/src/core/build_steps/dist.rs | 22 +++++++----- src/bootstrap/src/core/build_steps/doc.rs | 41 +++++++++++++--------- src/bootstrap/src/core/build_steps/test.rs | 4 +-- 3 files changed, 40 insertions(+), 27 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/dist.rs b/src/bootstrap/src/core/build_steps/dist.rs index 3fbc8cdbcd5a..f8393d870147 100644 --- a/src/bootstrap/src/core/build_steps/dist.rs +++ b/src/bootstrap/src/core/build_steps/dist.rs @@ -92,7 +92,8 @@ impl Step for Docs { #[derive(Debug, PartialOrd, Ord, Clone, Hash, PartialEq, Eq)] pub struct JsonDocs { - pub host: TargetSelection, + build_compiler: Compiler, + target: TargetSelection, } impl Step for JsonDocs { @@ -105,24 +106,27 @@ impl Step for JsonDocs { } fn make_run(run: RunConfig<'_>) { - run.builder.ensure(JsonDocs { host: run.target }); + run.builder.ensure(JsonDocs { + build_compiler: run.builder.compiler(run.builder.top_stage, run.builder.host_target), + target: run.target, + }); } /// Builds the `rust-docs-json` installer component. fn run(self, builder: &Builder<'_>) -> Option { - let host = self.host; - builder.ensure(crate::core::build_steps::doc::Std::new( - builder.top_stage, - host, + let target = self.target; + builder.ensure(crate::core::build_steps::doc::Std::from_build_compiler( + self.build_compiler, + target, DocumentationFormat::Json, )); let dest = "share/doc/rust/json"; - let mut tarball = Tarball::new(builder, "rust-docs-json", &host.triple); + let mut tarball = Tarball::new(builder, "rust-docs-json", &target.triple); tarball.set_product_name("Rust Documentation In JSON Format"); tarball.is_preview(true); - tarball.add_bulk_dir(builder.json_doc_out(host), dest); + tarball.add_bulk_dir(builder.json_doc_out(target), dest); Some(tarball.generate()) } } @@ -1583,7 +1587,7 @@ impl Step for Extended { } add_component!("rust-docs" => Docs { host: target }); - add_component!("rust-json-docs" => JsonDocs { host: target }); + add_component!("rust-json-docs" => JsonDocs { build_compiler: compiler, target }); add_component!("cargo" => Cargo { build_compiler: compiler, target }); add_component!("rustfmt" => Rustfmt { build_compiler: compiler, target }); add_component!("rust-analyzer" => RustAnalyzer { build_compiler: compiler, target }); diff --git a/src/bootstrap/src/core/build_steps/doc.rs b/src/bootstrap/src/core/build_steps/doc.rs index 5b536072b018..7ab50febad4a 100644 --- a/src/bootstrap/src/core/build_steps/doc.rs +++ b/src/bootstrap/src/core/build_steps/doc.rs @@ -579,17 +579,22 @@ impl Step for SharedAssets { } } +/// Document the standard library using `build_compiler`. #[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] pub struct Std { - pub stage: u32, - pub target: TargetSelection, - pub format: DocumentationFormat, + build_compiler: Compiler, + target: TargetSelection, + format: DocumentationFormat, crates: Vec, } impl Std { - pub(crate) fn new(stage: u32, target: TargetSelection, format: DocumentationFormat) -> Self { - Std { stage, target, format, crates: vec![] } + pub(crate) fn from_build_compiler( + build_compiler: Compiler, + target: TargetSelection, + format: DocumentationFormat, + ) -> Self { + Std { build_compiler, target, format, crates: vec![] } } } @@ -609,7 +614,7 @@ impl Step for Std { return; } run.builder.ensure(Std { - stage: run.builder.top_stage, + build_compiler: run.builder.compiler(run.builder.top_stage, run.builder.host_target), target: run.target, format: if run.builder.config.cmd.json() { DocumentationFormat::Json @@ -625,7 +630,6 @@ impl Step for Std { /// This will generate all documentation for the standard library and its /// dependencies. This is largely just a wrapper around `cargo doc`. fn run(self, builder: &Builder<'_>) { - let stage = self.stage; let target = self.target; let crates = if self.crates.is_empty() { builder @@ -667,7 +671,7 @@ impl Step for Std { // For `--index-page` and `--output-format=json`. extra_args.push("-Zunstable-options"); - doc_std(builder, self.format, stage, target, &out, &extra_args, &crates); + doc_std(builder, self.format, self.build_compiler, target, &out, &extra_args, &crates); // Don't open if the format is json if let DocumentationFormat::Json = self.format { @@ -692,7 +696,7 @@ impl Step for Std { fn metadata(&self) -> Option { Some( StepMetadata::doc("std", self.target) - .stage(self.stage) + .built_by(self.build_compiler) .with_metadata(format!("crates=[{}]", self.crates.join(","))), ) } @@ -728,24 +732,29 @@ impl DocumentationFormat { fn doc_std( builder: &Builder<'_>, format: DocumentationFormat, - stage: u32, + build_compiler: Compiler, target: TargetSelection, out: &Path, extra_args: &[&str], requested_crates: &[String], ) { - let compiler = builder.compiler(stage, builder.config.host_target); - let target_doc_dir_name = if format == DocumentationFormat::Json { "json-doc" } else { "doc" }; - let target_dir = builder.stage_out(compiler, Mode::Std).join(target).join(target_doc_dir_name); + let target_dir = + builder.stage_out(build_compiler, Mode::Std).join(target).join(target_doc_dir_name); // This is directory where the compiler will place the output of the command. // We will then copy the files from this directory into the final `out` directory, the specified // as a function parameter. let out_dir = target_dir.join(target).join("doc"); - let mut cargo = - builder::Cargo::new(builder, compiler, Mode::Std, SourceType::InTree, target, Kind::Doc); + let mut cargo = builder::Cargo::new( + builder, + build_compiler, + Mode::Std, + SourceType::InTree, + target, + Kind::Doc, + ); compile::std_cargo(builder, target, &mut cargo); cargo @@ -773,7 +782,7 @@ fn doc_std( let description = format!("library{} in {} format", crate_description(requested_crates), format.as_str()); - let _guard = builder.msg_doc(compiler, description, target); + let _guard = builder.msg_doc(build_compiler, description, target); cargo.into_cmd().run(builder); builder.cp_link_r(&out_dir, out); diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index d6d7f7be1dfb..2f933baa4a4c 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -983,8 +983,8 @@ impl Step for RustdocJSStd { command.arg("--test-file").arg(path); } } - builder.ensure(crate::core::build_steps::doc::Std::new( - builder.top_stage, + builder.ensure(crate::core::build_steps::doc::Std::from_build_compiler( + builder.compiler(builder.top_stage, builder.host_target), self.target, DocumentationFormat::Html, )); From fe01f580e28c3864747c8115b0c3b402ebb38fdb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Wed, 6 Aug 2025 18:32:13 +0200 Subject: [PATCH 123/195] Update `doc` CI steps stage 2 As they were previously. --- src/ci/docker/host-x86_64/pr-check-2/Dockerfile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ci/docker/host-x86_64/pr-check-2/Dockerfile b/src/ci/docker/host-x86_64/pr-check-2/Dockerfile index e1d706ee8170..1a2191255936 100644 --- a/src/ci/docker/host-x86_64/pr-check-2/Dockerfile +++ b/src/ci/docker/host-x86_64/pr-check-2/Dockerfile @@ -33,11 +33,11 @@ ENV SCRIPT \ python3 ../x.py test --stage 1 src/tools/compiletest && \ python3 ../x.py doc bootstrap && \ # Build both public and internal documentation. - RUSTDOCFLAGS=\"--document-private-items --document-hidden-items\" python3 ../x.py doc compiler && \ - RUSTDOCFLAGS=\"--document-private-items --document-hidden-items\" python3 ../x.py doc library && \ + RUSTDOCFLAGS=\"--document-private-items --document-hidden-items\" python3 ../x.py doc compiler --stage 2 && \ + RUSTDOCFLAGS=\"--document-private-items --document-hidden-items\" python3 ../x.py doc library --stage 2 && \ mkdir -p /checkout/obj/staging/doc && \ cp -r build/x86_64-unknown-linux-gnu/doc /checkout/obj/staging && \ - RUSTDOCFLAGS=\"--document-private-items --document-hidden-items\" python3 ../x.py doc library/test && \ + RUSTDOCFLAGS=\"--document-private-items --document-hidden-items\" python3 ../x.py doc library/test --stage 2 && \ # The BOOTSTRAP_TRACING flag is added to verify whether the # bootstrap process compiles successfully with this flag enabled. BOOTSTRAP_TRACING=1 python3 ../x.py --help From 6443dc0964101bdd73c8b4fd25868392f34f5955 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Fri, 8 Aug 2025 08:16:32 +0200 Subject: [PATCH 124/195] Update tests --- src/bootstrap/src/core/builder/tests.rs | 245 ++++++++++++++++++++---- 1 file changed, 208 insertions(+), 37 deletions(-) diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs index 8a22633b362d..cfbd2a0f484b 100644 --- a/src/bootstrap/src/core/builder/tests.rs +++ b/src/bootstrap/src/core/builder/tests.rs @@ -954,7 +954,7 @@ mod snapshot { [build] llvm [build] rustc 0 -> rustc 1 [build] rustdoc 1 - [doc] std 1 crates=[alloc,compiler_builtins,core,panic_abort,panic_unwind,proc_macro,rustc-std-workspace-core,std,std_detect,sysroot,test,unwind] + [doc] rustc 1 -> std 1 crates=[alloc,compiler_builtins,core,panic_abort,panic_unwind,proc_macro,rustc-std-workspace-core,std,std_detect,sysroot,test,unwind] "); } @@ -1023,19 +1023,36 @@ mod snapshot { .render_steps(), @r" [build] rustc 0 -> UnstableBookGen 1 [build] rustc 0 -> Rustbook 1 + [doc] unstable-book (book) [build] llvm [build] rustc 0 -> rustc 1 [build] rustc 1 -> std 1 + [doc] book (book) + [doc] book/first-edition (book) + [doc] book/second-edition (book) + [doc] book/2018-edition (book) + [build] rustdoc 1 + [doc] rustc 1 -> standalone 2 [build] rustc 1 -> rustc 2 [build] rustdoc 2 - [doc] std 2 crates=[alloc,compiler_builtins,core,panic_abort,panic_unwind,proc_macro,rustc-std-workspace-core,std,std_detect,sysroot,test,unwind] + [doc] rustc 2 -> std 2 crates=[alloc,compiler_builtins,core,panic_abort,panic_unwind,proc_macro,rustc-std-workspace-core,std,std_detect,sysroot,test,unwind] [build] rustc 1 -> error-index 2 [doc] rustc 1 -> error-index 2 - [build] rustc 2 -> std 2 + [doc] nomicon (book) + [doc] rustc 1 -> reference (book) 2 + [doc] rustdoc (book) + [doc] rust-by-example (book) [build] rustc 0 -> LintDocs 1 + [doc] rustc (book) + [doc] cargo (book) + [doc] clippy (book) + [doc] embedded-book (book) + [doc] edition-guide (book) + [doc] style-guide (book) + [doc] rustc 1 -> releases 2 [build] rustc 0 -> RustInstaller 1 [dist] docs - [doc] std 2 crates=[] + [doc] rustc 2 -> std 2 crates=[] [dist] mingw [build] rustc 0 -> GenerateCopyright 1 [dist] rustc @@ -1061,25 +1078,42 @@ mod snapshot { .render_steps(), @r" [build] rustc 0 -> UnstableBookGen 1 [build] rustc 0 -> Rustbook 1 + [doc] unstable-book (book) [build] llvm [build] rustc 0 -> rustc 1 [build] rustc 0 -> LldWrapper 1 [build] rustc 0 -> WasmComponentLd 1 [build] rustc 0 -> LlvmBitcodeLinker 1 [build] rustc 1 -> std 1 + [doc] book (book) + [doc] book/first-edition (book) + [doc] book/second-edition (book) + [doc] book/2018-edition (book) + [build] rustdoc 1 + [doc] rustc 1 -> standalone 2 [build] rustc 1 -> rustc 2 [build] rustc 1 -> LldWrapper 2 [build] rustc 1 -> WasmComponentLd 2 [build] rustc 1 -> LlvmBitcodeLinker 2 [build] rustdoc 2 - [doc] std 2 crates=[alloc,compiler_builtins,core,panic_abort,panic_unwind,proc_macro,rustc-std-workspace-core,std,std_detect,sysroot,test,unwind] + [doc] rustc 2 -> std 2 crates=[alloc,compiler_builtins,core,panic_abort,panic_unwind,proc_macro,rustc-std-workspace-core,std,std_detect,sysroot,test,unwind] [build] rustc 1 -> error-index 2 [doc] rustc 1 -> error-index 2 - [build] rustc 2 -> std 2 + [doc] nomicon (book) + [doc] rustc 1 -> reference (book) 2 + [doc] rustdoc (book) + [doc] rust-by-example (book) [build] rustc 0 -> LintDocs 1 + [doc] rustc (book) + [doc] cargo (book) + [doc] clippy (book) + [doc] embedded-book (book) + [doc] edition-guide (book) + [doc] style-guide (book) + [doc] rustc 1 -> releases 2 [build] rustc 0 -> RustInstaller 1 [dist] docs - [doc] std 2 crates=[] + [doc] rustc 2 -> std 2 crates=[] [dist] mingw [build] rustc 1 -> rust-analyzer-proc-macro-srv 2 [build] rustc 0 -> GenerateCopyright 1 @@ -1094,6 +1128,7 @@ mod snapshot { [build] rustc 1 -> cargo-clippy 2 [build] rustc 1 -> miri 2 [build] rustc 1 -> cargo-miri 2 + [doc] rustc 1 -> std 1 crates=[] "); } @@ -1108,22 +1143,56 @@ mod snapshot { .render_steps(), @r" [build] rustc 0 -> UnstableBookGen 1 [build] rustc 0 -> Rustbook 1 + [doc] unstable-book (book) + [doc] unstable-book (book) [build] llvm [build] rustc 0 -> rustc 1 [build] rustc 1 -> std 1 + [doc] book (book) + [doc] book/first-edition (book) + [doc] book/second-edition (book) + [doc] book/2018-edition (book) + [build] rustdoc 1 + [build] rustc 1 -> std 1 + [doc] book (book) + [doc] book/first-edition (book) + [doc] book/second-edition (book) + [doc] book/2018-edition (book) + [doc] rustc 1 -> standalone 2 + [doc] rustc 1 -> standalone 2 [build] rustc 1 -> rustc 2 [build] rustdoc 2 - [doc] std 2 crates=[alloc,compiler_builtins,core,panic_abort,panic_unwind,proc_macro,rustc-std-workspace-core,std,std_detect,sysroot,test,unwind] - [doc] std 2 crates=[alloc,compiler_builtins,core,panic_abort,panic_unwind,proc_macro,rustc-std-workspace-core,std,std_detect,sysroot,test,unwind] + [doc] rustc 2 -> std 2 crates=[alloc,compiler_builtins,core,panic_abort,panic_unwind,proc_macro,rustc-std-workspace-core,std,std_detect,sysroot,test,unwind] + [doc] rustc 2 -> std 2 crates=[alloc,compiler_builtins,core,panic_abort,panic_unwind,proc_macro,rustc-std-workspace-core,std,std_detect,sysroot,test,unwind] [build] rustc 1 -> error-index 2 [doc] rustc 1 -> error-index 2 - [build] rustc 2 -> std 2 + [doc] nomicon (book) + [doc] nomicon (book) + [doc] rustc 1 -> reference (book) 2 + [doc] rustc 1 -> reference (book) 2 + [doc] rustdoc (book) + [doc] rustdoc (book) + [doc] rust-by-example (book) + [doc] rust-by-example (book) [build] rustc 0 -> LintDocs 1 + [doc] rustc (book) + [doc] cargo (book) + [doc] cargo (book) + [doc] clippy (book) + [doc] clippy (book) + [doc] embedded-book (book) + [doc] embedded-book (book) + [doc] edition-guide (book) + [doc] edition-guide (book) + [doc] style-guide (book) + [doc] style-guide (book) + [doc] rustc 1 -> releases 2 + [doc] rustc 1 -> releases 2 [build] rustc 0 -> RustInstaller 1 [dist] docs [dist] docs - [doc] std 2 crates=[] - [doc] std 2 crates=[] + [doc] rustc 2 -> std 2 crates=[] + [doc] rustc 2 -> std 2 crates=[] [dist] mingw [dist] mingw [build] rustc 0 -> GenerateCopyright 1 @@ -1147,12 +1216,19 @@ mod snapshot { .render_steps(), @r" [build] rustc 0 -> UnstableBookGen 1 [build] rustc 0 -> Rustbook 1 + [doc] unstable-book (book) [build] llvm [build] rustc 0 -> rustc 1 [build] rustc 1 -> std 1 + [doc] book (book) + [doc] book/first-edition (book) + [doc] book/second-edition (book) + [doc] book/2018-edition (book) + [build] rustdoc 1 + [doc] rustc 1 -> standalone 2 [build] rustc 1 -> rustc 2 [build] rustdoc 2 - [doc] std 2 crates=[alloc,compiler_builtins,core,panic_abort,panic_unwind,proc_macro,rustc-std-workspace-core,std,std_detect,sysroot,test,unwind] + [doc] rustc 2 -> std 2 crates=[alloc,compiler_builtins,core,panic_abort,panic_unwind,proc_macro,rustc-std-workspace-core,std,std_detect,sysroot,test,unwind] [build] rustc 1 -> error-index 2 [doc] rustc 1 -> error-index 2 [build] llvm @@ -1160,12 +1236,22 @@ mod snapshot { [build] rustc 1 -> rustc 2 [build] rustc 1 -> error-index 2 [doc] rustc 1 -> error-index 2 - [build] rustc 2 -> std 2 + [doc] nomicon (book) + [doc] rustc 1 -> reference (book) 2 + [doc] rustdoc (book) + [doc] rust-by-example (book) [build] rustc 0 -> LintDocs 1 - [build] rustc 2 -> std 2 + [doc] rustc (book) + [doc] rustc (book) + [doc] cargo (book) + [doc] clippy (book) + [doc] embedded-book (book) + [doc] edition-guide (book) + [doc] style-guide (book) + [doc] rustc 1 -> releases 2 [build] rustc 0 -> RustInstaller 1 [dist] docs - [doc] std 2 crates=[] + [doc] rustc 2 -> std 2 crates=[] [dist] mingw [build] rustc 0 -> GenerateCopyright 1 [dist] rustc @@ -1188,28 +1274,61 @@ mod snapshot { .render_steps(), @r" [build] rustc 0 -> UnstableBookGen 1 [build] rustc 0 -> Rustbook 1 + [doc] unstable-book (book) + [doc] unstable-book (book) [build] llvm [build] rustc 0 -> rustc 1 [build] rustc 1 -> std 1 + [doc] book (book) + [doc] book/first-edition (book) + [doc] book/second-edition (book) + [doc] book/2018-edition (book) + [build] rustdoc 1 + [build] rustc 1 -> std 1 + [doc] book (book) + [doc] book/first-edition (book) + [doc] book/second-edition (book) + [doc] book/2018-edition (book) + [doc] rustc 1 -> standalone 2 + [doc] rustc 1 -> standalone 2 [build] rustc 1 -> rustc 2 [build] rustdoc 2 - [doc] std 2 crates=[alloc,compiler_builtins,core,panic_abort,panic_unwind,proc_macro,rustc-std-workspace-core,std,std_detect,sysroot,test,unwind] - [doc] std 2 crates=[alloc,compiler_builtins,core,panic_abort,panic_unwind,proc_macro,rustc-std-workspace-core,std,std_detect,sysroot,test,unwind] + [doc] rustc 2 -> std 2 crates=[alloc,compiler_builtins,core,panic_abort,panic_unwind,proc_macro,rustc-std-workspace-core,std,std_detect,sysroot,test,unwind] + [doc] rustc 2 -> std 2 crates=[alloc,compiler_builtins,core,panic_abort,panic_unwind,proc_macro,rustc-std-workspace-core,std,std_detect,sysroot,test,unwind] [build] rustc 1 -> error-index 2 [doc] rustc 1 -> error-index 2 [build] llvm - [build] rustc 1 -> std 1 [build] rustc 1 -> rustc 2 [build] rustc 1 -> error-index 2 [doc] rustc 1 -> error-index 2 - [build] rustc 2 -> std 2 + [doc] nomicon (book) + [doc] nomicon (book) + [doc] rustc 1 -> reference (book) 2 + [doc] rustc 1 -> reference (book) 2 + [doc] rustdoc (book) + [doc] rustdoc (book) + [doc] rust-by-example (book) + [doc] rust-by-example (book) [build] rustc 0 -> LintDocs 1 - [build] rustc 2 -> std 2 + [doc] rustc (book) + [doc] rustc (book) + [doc] cargo (book) + [doc] cargo (book) + [doc] clippy (book) + [doc] clippy (book) + [doc] embedded-book (book) + [doc] embedded-book (book) + [doc] edition-guide (book) + [doc] edition-guide (book) + [doc] style-guide (book) + [doc] style-guide (book) + [doc] rustc 1 -> releases 2 + [doc] rustc 1 -> releases 2 [build] rustc 0 -> RustInstaller 1 [dist] docs [dist] docs - [doc] std 2 crates=[] - [doc] std 2 crates=[] + [doc] rustc 2 -> std 2 crates=[] + [doc] rustc 2 -> std 2 crates=[] [dist] mingw [dist] mingw [build] rustc 0 -> GenerateCopyright 1 @@ -1234,16 +1353,33 @@ mod snapshot { .render_steps(), @r" [build] rustc 0 -> UnstableBookGen 1 [build] rustc 0 -> Rustbook 1 + [doc] unstable-book (book) [build] llvm [build] rustc 0 -> rustc 1 + [build] rustc 1 -> std 1 + [doc] book (book) + [doc] book/first-edition (book) + [doc] book/second-edition (book) + [doc] book/2018-edition (book) + [build] rustdoc 1 [build] rustc 1 -> std 1 + [doc] rustc 1 -> standalone 2 [build] rustc 1 -> rustc 2 [build] rustdoc 2 - [doc] std 2 crates=[alloc,compiler_builtins,core,panic_abort,panic_unwind,proc_macro,rustc-std-workspace-core,std,std_detect,sysroot,test,unwind] - [build] rustc 2 -> std 2 + [doc] rustc 2 -> std 2 crates=[alloc,compiler_builtins,core,panic_abort,panic_unwind,proc_macro,rustc-std-workspace-core,std,std_detect,sysroot,test,unwind] + [doc] nomicon (book) + [doc] rustc 1 -> reference (book) 2 + [doc] rustdoc (book) + [doc] rust-by-example (book) + [doc] cargo (book) + [doc] clippy (book) + [doc] embedded-book (book) + [doc] edition-guide (book) + [doc] style-guide (book) + [doc] rustc 1 -> releases 2 [build] rustc 0 -> RustInstaller 1 [dist] docs - [doc] std 2 crates=[] + [doc] rustc 2 -> std 2 crates=[] [dist] mingw [build] rustc 2 -> std 2 [dist] rustc 2 -> std 2 @@ -1264,26 +1400,42 @@ mod snapshot { .render_steps(), @r" [build] rustc 0 -> UnstableBookGen 1 [build] rustc 0 -> Rustbook 1 + [doc] unstable-book (book) [build] llvm [build] rustc 0 -> rustc 1 [build] rustc 0 -> WasmComponentLd 1 + [build] rustc 1 -> std 1 + [doc] book (book) + [doc] book/first-edition (book) + [doc] book/second-edition (book) + [doc] book/2018-edition (book) + [build] rustdoc 1 [build] rustc 1 -> std 1 + [doc] rustc 1 -> standalone 2 [build] rustc 1 -> rustc 2 [build] rustc 1 -> WasmComponentLd 2 [build] rustdoc 2 - [doc] std 2 crates=[alloc,compiler_builtins,core,panic_abort,panic_unwind,proc_macro,rustc-std-workspace-core,std,std_detect,sysroot,test,unwind] + [doc] rustc 2 -> std 2 crates=[alloc,compiler_builtins,core,panic_abort,panic_unwind,proc_macro,rustc-std-workspace-core,std,std_detect,sysroot,test,unwind] [build] llvm - [build] rustc 1 -> std 1 [build] rustc 1 -> rustc 2 [build] rustc 1 -> WasmComponentLd 2 [build] rustc 1 -> error-index 2 [doc] rustc 1 -> error-index 2 - [build] rustc 2 -> std 2 - [build] rustc 2 -> std 2 + [doc] nomicon (book) + [doc] rustc 1 -> reference (book) 2 + [doc] rustdoc (book) + [doc] rust-by-example (book) [build] rustc 0 -> LintDocs 1 + [doc] rustc (book) + [doc] cargo (book) + [doc] clippy (book) + [doc] embedded-book (book) + [doc] edition-guide (book) + [doc] style-guide (book) + [doc] rustc 1 -> releases 2 [build] rustc 0 -> RustInstaller 1 [dist] docs - [doc] std 2 crates=[] + [doc] rustc 2 -> std 2 crates=[] [dist] mingw [build] rustdoc 2 [build] rustc 1 -> rust-analyzer-proc-macro-srv 2 @@ -1300,6 +1452,7 @@ mod snapshot { [build] rustc 1 -> miri 2 [build] rustc 1 -> cargo-miri 2 [build] rustc 1 -> LlvmBitcodeLinker 2 + [doc] rustc 1 -> std 1 crates=[] "); } @@ -1708,14 +1861,32 @@ mod snapshot { .render_steps(), @r" [build] rustc 0 -> UnstableBookGen 1 [build] rustc 0 -> Rustbook 1 + [doc] unstable-book (book) + [doc] book (book) + [doc] book/first-edition (book) + [doc] book/second-edition (book) + [doc] book/2018-edition (book) + [build] rustdoc 0 + [doc] rustc 0 -> standalone 1 [build] llvm [build] rustc 0 -> rustc 1 [build] rustdoc 1 - [doc] std 1 crates=[alloc,compiler_builtins,core,panic_abort,panic_unwind,proc_macro,rustc-std-workspace-core,std,std_detect,sysroot,test,unwind] + [doc] rustc 1 -> std 1 crates=[alloc,compiler_builtins,core,panic_abort,panic_unwind,proc_macro,rustc-std-workspace-core,std,std_detect,sysroot,test,unwind] [build] rustc 0 -> error-index 1 [doc] rustc 0 -> error-index 1 + [doc] nomicon (book) [build] rustc 1 -> std 1 + [doc] rustc 1 -> reference (book) 2 + [doc] rustdoc (book) + [doc] rust-by-example (book) [build] rustc 0 -> LintDocs 1 + [doc] rustc (book) + [doc] cargo (book) + [doc] clippy (book) + [doc] embedded-book (book) + [doc] edition-guide (book) + [doc] style-guide (book) + [doc] rustc 0 -> releases 1 "); } @@ -1729,7 +1900,7 @@ mod snapshot { [build] llvm [build] rustc 0 -> rustc 1 [build] rustdoc 1 - [doc] std 1 crates=[alloc,compiler_builtins,core,panic_abort,panic_unwind,proc_macro,rustc-std-workspace-core,std,std_detect,sysroot,test,unwind] + [doc] rustc 1 -> std 1 crates=[alloc,compiler_builtins,core,panic_abort,panic_unwind,proc_macro,rustc-std-workspace-core,std,std_detect,sysroot,test,unwind] "); } @@ -1743,7 +1914,7 @@ mod snapshot { [build] llvm [build] rustc 0 -> rustc 1 [build] rustdoc 1 - [doc] std 1 crates=[core] + [doc] rustc 1 -> std 1 crates=[core] "); } @@ -1758,7 +1929,7 @@ mod snapshot { [build] llvm [build] rustc 0 -> rustc 1 [build] rustdoc 1 - [doc] std 1 crates=[core] + [doc] rustc 1 -> std 1 crates=[core] "); } @@ -1773,7 +1944,7 @@ mod snapshot { [build] llvm [build] rustc 0 -> rustc 1 [build] rustdoc 1 - [doc] std 1 crates=[alloc,core] + [doc] rustc 1 -> std 1 crates=[alloc,core] "); } @@ -1789,7 +1960,7 @@ mod snapshot { [build] llvm [build] rustc 0 -> rustc 1 [build] rustdoc 1 - [doc] std 1 crates=[alloc,core] + [doc] rustc 1 -> std 1 crates=[alloc,core] "); } From c843e1d28f564d623c90b4cb3ee009ba6bebc1d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Fri, 8 Aug 2025 08:18:48 +0200 Subject: [PATCH 125/195] Add change tracker entry --- src/bootstrap/src/utils/change_tracker.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/bootstrap/src/utils/change_tracker.rs b/src/bootstrap/src/utils/change_tracker.rs index d3331b81587e..091956e7e5f3 100644 --- a/src/bootstrap/src/utils/change_tracker.rs +++ b/src/bootstrap/src/utils/change_tracker.rs @@ -491,4 +491,9 @@ pub const CONFIG_CHANGE_HISTORY: &[ChangeInfo] = &[ severity: ChangeSeverity::Warning, summary: "Added `build.compiletest-allow-stage0` flag instead of `COMPILETEST_FORCE_STAGE0` env var, and reject running `compiletest` self tests against stage 0 rustc unless explicitly allowed.", }, + ChangeInfo { + change_id: 145011, + severity: ChangeSeverity::Warning, + summary: "It is no longer possible to `x doc` with stage 0. All doc commands have to be on stage 1+.", + }, ]; From 89a27d26dcc36c3286cc1e88de704bc83f5f9859 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Sun, 10 Aug 2025 11:29:44 +0200 Subject: [PATCH 126/195] Review remarks --- src/bootstrap/src/core/build_steps/doc.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/doc.rs b/src/bootstrap/src/core/build_steps/doc.rs index 7ab50febad4a..28a764dc8ba0 100644 --- a/src/bootstrap/src/core/build_steps/doc.rs +++ b/src/bootstrap/src/core/build_steps/doc.rs @@ -790,6 +790,7 @@ fn doc_std( /// Prepare a compiler that will be able to document something for `target` at `stage`. fn prepare_doc_compiler(builder: &Builder<'_>, target: TargetSelection, stage: u32) -> Compiler { + assert!(stage > 0, "Cannot document anything in stage 0"); let build_compiler = builder.compiler(stage - 1, builder.host_target); builder.std(build_compiler, target); build_compiler @@ -1226,10 +1227,13 @@ fn symlink_dir_force(config: &Config, original: &Path, link: &Path) { ); } +/// Builds the Rust compiler book. #[derive(Ord, PartialOrd, Debug, Clone, Hash, PartialEq, Eq)] pub struct RustcBook { build_compiler: Compiler, target: TargetSelection, + /// Test that the examples of lints in the book produce the correct lints in the expected + /// format. validate: bool, } @@ -1331,8 +1335,8 @@ impl Step for RustcBook { } /// Documents the reference. -/// It is always done using a stage 1+ compiler, because it references in-tree compiler/stdlib -/// concepts. +/// It has to always be done using a stage 1+ compiler, because it references in-tree +/// compiler/stdlib concepts. #[derive(Ord, PartialOrd, Debug, Clone, Hash, PartialEq, Eq)] pub struct Reference { build_compiler: Compiler, From 8d53418bd4648e738a0d9b03570d0f66fe24415c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Sun, 10 Aug 2025 11:40:43 +0200 Subject: [PATCH 127/195] Bless tests --- src/bootstrap/src/core/builder/tests.rs | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs index cfbd2a0f484b..b60ca5f428fd 100644 --- a/src/bootstrap/src/core/builder/tests.rs +++ b/src/bootstrap/src/core/builder/tests.rs @@ -1468,21 +1468,38 @@ mod snapshot { .render_steps(), @r" [build] rustc 0 -> UnstableBookGen 1 [build] rustc 0 -> Rustbook 1 + [doc] unstable-book (book) [build] llvm [build] rustc 0 -> rustc 1 [build] rustc 0 -> rustc_codegen_cranelift 1 [build] rustc 1 -> std 1 + [doc] book (book) + [doc] book/first-edition (book) + [doc] book/second-edition (book) + [doc] book/2018-edition (book) + [build] rustdoc 1 + [doc] rustc 1 -> standalone 2 [build] rustc 1 -> rustc 2 [build] rustc 1 -> rustc_codegen_cranelift 2 [build] rustdoc 2 - [doc] std 2 crates=[alloc,compiler_builtins,core,panic_abort,panic_unwind,proc_macro,rustc-std-workspace-core,std,std_detect,sysroot,test,unwind] + [doc] rustc 2 -> std 2 crates=[alloc,compiler_builtins,core,panic_abort,panic_unwind,proc_macro,rustc-std-workspace-core,std,std_detect,sysroot,test,unwind] [build] rustc 1 -> error-index 2 [doc] rustc 1 -> error-index 2 - [build] rustc 2 -> std 2 + [doc] nomicon (book) + [doc] rustc 1 -> reference (book) 2 + [doc] rustdoc (book) + [doc] rust-by-example (book) [build] rustc 0 -> LintDocs 1 + [doc] rustc (book) + [doc] cargo (book) + [doc] clippy (book) + [doc] embedded-book (book) + [doc] edition-guide (book) + [doc] style-guide (book) + [doc] rustc 1 -> releases 2 [build] rustc 0 -> RustInstaller 1 [dist] docs - [doc] std 2 crates=[] + [doc] rustc 2 -> std 2 crates=[] [dist] mingw [build] rustc 0 -> GenerateCopyright 1 [dist] rustc From ba350ff9110de2c75dab1e9530b63898bdc10154 Mon Sep 17 00:00:00 2001 From: Lee ByeongJun Date: Sun, 10 Aug 2025 22:50:14 +0900 Subject: [PATCH 128/195] fix: re-enable self-assignment --- compiler/rustc_passes/src/dead.rs | 6 ++- tests/ui/lint/dead-code/self-assign.rs | 17 ++++----- tests/ui/lint/dead-code/self-assign.stderr | 44 ++++++++++++++++++++++ 3 files changed, 55 insertions(+), 12 deletions(-) create mode 100644 tests/ui/lint/dead-code/self-assign.stderr diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs index fa9d0c7b1b7d..d5d7cc5dc2e1 100644 --- a/compiler/rustc_passes/src/dead.rs +++ b/compiler/rustc_passes/src/dead.rs @@ -172,7 +172,6 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { } } - #[allow(dead_code)] // FIXME(81658): should be used + lint reinstated after #83171 relands. fn handle_assign(&mut self, expr: &'tcx hir::Expr<'tcx>) { if self .typeck_results() @@ -189,7 +188,6 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { } } - #[allow(dead_code)] // FIXME(81658): should be used + lint reinstated after #83171 relands. fn check_for_self_assign(&mut self, assign: &'tcx hir::Expr<'tcx>) { fn check_for_self_assign_helper<'tcx>( typeck_results: &'tcx ty::TypeckResults<'tcx>, @@ -576,6 +574,10 @@ impl<'tcx> Visitor<'tcx> for MarkSymbolVisitor<'tcx> { hir::ExprKind::OffsetOf(..) => { self.handle_offset_of(expr); } + hir::ExprKind::Assign(ref lhs, ..) => { + self.handle_assign(lhs); + self.check_for_self_assign(expr); + } _ => (), } diff --git a/tests/ui/lint/dead-code/self-assign.rs b/tests/ui/lint/dead-code/self-assign.rs index 357846baf221..ce91f53cbf1e 100644 --- a/tests/ui/lint/dead-code/self-assign.rs +++ b/tests/ui/lint/dead-code/self-assign.rs @@ -7,23 +7,20 @@ //! - `dead_code` lint expansion for self-assignments was implemented in #87129. //! - Unfortunately implementation components of #87129 had to be disabled as part of reverts //! #86212, #83171 (to revert #81473) to address regressions #81626 and #81658. -//! - Consequently, none of the following warnings are emitted. +//! - Re-enabled in current version to properly detect self-assignments. //@ check-pass -// Implementation of self-assignment `dead_code` lint expansions disabled due to reverts. -//@ known-bug: #75356 - #![allow(unused_assignments)] #![warn(dead_code)] fn main() { let mut x = 0; x = x; - // FIXME ~^ WARNING: useless assignment of variable of type `i32` to itself + //~^ WARNING: useless assignment of variable of type `i32` to itself x = (x); - // FIXME ~^ WARNING: useless assignment of variable of type `i32` to itself + //~^ WARNING: useless assignment of variable of type `i32` to itself x = {x}; // block expressions don't count as self-assignments @@ -32,10 +29,10 @@ fn main() { struct S<'a> { f: &'a str } let mut s = S { f: "abc" }; s = s; - // FIXME ~^ WARNING: useless assignment of variable of type `S` to itself + //~^ WARNING: useless assignment of variable of type `S<'_>` to itself s.f = s.f; - // FIXME ~^ WARNING: useless assignment of field of type `&str` to itself + //~^ WARNING: useless assignment of field of type `&str` to itself struct N0 { x: Box } @@ -44,11 +41,11 @@ fn main() { struct N3 { n: N2 }; let mut n3 = N3 { n: N2(N1 { n: N0 { x: Box::new(42) } }) }; n3.n.0.n.x = n3.n.0.n.x; - // FIXME ~^ WARNING: useless assignment of field of type `Box` to itself + //~^ WARNING: useless assignment of field of type `Box` to itself let mut t = (1, ((2, 3, (4, 5)),)); t.1.0.2.1 = t.1.0.2.1; - // FIXME ~^ WARNING: useless assignment of field of type `i32` to itself + //~^ WARNING: useless assignment of field of type `i32` to itself let mut y = 0; diff --git a/tests/ui/lint/dead-code/self-assign.stderr b/tests/ui/lint/dead-code/self-assign.stderr new file mode 100644 index 000000000000..408ebdb658b5 --- /dev/null +++ b/tests/ui/lint/dead-code/self-assign.stderr @@ -0,0 +1,44 @@ +warning: useless assignment of variable of type `i32` to itself + --> $DIR/self-assign.rs:19:5 + | +LL | x = x; + | ^^^^^ + | +note: the lint level is defined here + --> $DIR/self-assign.rs:15:9 + | +LL | #![warn(dead_code)] + | ^^^^^^^^^ + +warning: useless assignment of variable of type `i32` to itself + --> $DIR/self-assign.rs:22:5 + | +LL | x = (x); + | ^^^^^^^ + +warning: useless assignment of variable of type `S<'_>` to itself + --> $DIR/self-assign.rs:31:5 + | +LL | s = s; + | ^^^^^ + +warning: useless assignment of field of type `&str` to itself + --> $DIR/self-assign.rs:34:5 + | +LL | s.f = s.f; + | ^^^^^^^^^ + +warning: useless assignment of field of type `Box` to itself + --> $DIR/self-assign.rs:43:5 + | +LL | n3.n.0.n.x = n3.n.0.n.x; + | ^^^^^^^^^^^^^^^^^^^^^^^ + +warning: useless assignment of field of type `i32` to itself + --> $DIR/self-assign.rs:47:5 + | +LL | t.1.0.2.1 = t.1.0.2.1; + | ^^^^^^^^^^^^^^^^^^^^^ + +warning: 6 warnings emitted + From 8b4d9411bacf46fcdd47c8613dee9b4ebe3a1cdf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Sun, 10 Aug 2025 16:32:40 +0200 Subject: [PATCH 129/195] Explicitly pass path to built stdlib JSON docs and use the correct compiler for it --- src/bootstrap/src/core/build_steps/dist.rs | 10 +++--- src/bootstrap/src/core/build_steps/doc.rs | 36 ++++++++++++---------- src/bootstrap/src/core/builder/tests.rs | 3 +- 3 files changed, 26 insertions(+), 23 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/dist.rs b/src/bootstrap/src/core/build_steps/dist.rs index f8393d870147..c2f46e66c3f5 100644 --- a/src/bootstrap/src/core/build_steps/dist.rs +++ b/src/bootstrap/src/core/build_steps/dist.rs @@ -115,7 +115,7 @@ impl Step for JsonDocs { /// Builds the `rust-docs-json` installer component. fn run(self, builder: &Builder<'_>) -> Option { let target = self.target; - builder.ensure(crate::core::build_steps::doc::Std::from_build_compiler( + let directory = builder.ensure(crate::core::build_steps::doc::Std::from_build_compiler( self.build_compiler, target, DocumentationFormat::Json, @@ -126,7 +126,7 @@ impl Step for JsonDocs { let mut tarball = Tarball::new(builder, "rust-docs-json", &target.triple); tarball.set_product_name("Rust Documentation In JSON Format"); tarball.is_preview(true); - tarball.add_bulk_dir(builder.json_doc_out(target), dest); + tarball.add_bulk_dir(directory, dest); Some(tarball.generate()) } } @@ -1575,11 +1575,12 @@ impl Step for Extended { }; } + let target_compiler = builder.compiler(stage, target); // When rust-std package split from rustc, we needed to ensure that during // upgrades rustc was upgraded before rust-std. To avoid rustc clobbering // the std files during uninstall. To do this ensure that rustc comes // before rust-std in the list below. - tarballs.push(builder.ensure(Rustc { compiler: builder.compiler(stage, target) })); + tarballs.push(builder.ensure(Rustc { compiler: target_compiler })); tarballs.push(builder.ensure(Std { compiler, target }).expect("missing std")); if target.is_windows_gnu() { @@ -1587,7 +1588,8 @@ impl Step for Extended { } add_component!("rust-docs" => Docs { host: target }); - add_component!("rust-json-docs" => JsonDocs { build_compiler: compiler, target }); + // Std stage N is documented with compiler stage N + add_component!("rust-json-docs" => JsonDocs { build_compiler: target_compiler, target }); add_component!("cargo" => Cargo { build_compiler: compiler, target }); add_component!("rustfmt" => Rustfmt { build_compiler: compiler, target }); add_component!("rust-analyzer" => RustAnalyzer { build_compiler: compiler, target }); diff --git a/src/bootstrap/src/core/build_steps/doc.rs b/src/bootstrap/src/core/build_steps/doc.rs index 28a764dc8ba0..ca7a6dc8e07e 100644 --- a/src/bootstrap/src/core/build_steps/doc.rs +++ b/src/bootstrap/src/core/build_steps/doc.rs @@ -599,7 +599,9 @@ impl Std { } impl Step for Std { - type Output = (); + /// Path to a directory with the built documentation. + type Output = PathBuf; + const DEFAULT: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { @@ -629,7 +631,7 @@ impl Step for Std { /// /// This will generate all documentation for the standard library and its /// dependencies. This is largely just a wrapper around `cargo doc`. - fn run(self, builder: &Builder<'_>) { + fn run(self, builder: &Builder<'_>) -> Self::Output { let target = self.target; let crates = if self.crates.is_empty() { builder @@ -673,24 +675,24 @@ impl Step for Std { doc_std(builder, self.format, self.build_compiler, target, &out, &extra_args, &crates); - // Don't open if the format is json - if let DocumentationFormat::Json = self.format { - return; - } - - if builder.paths.iter().any(|path| path.ends_with("library")) { - // For `x.py doc library --open`, open `std` by default. - let index = out.join("std").join("index.html"); - builder.open_in_browser(index); - } else { - for requested_crate in crates { - if STD_PUBLIC_CRATES.iter().any(|&k| k == requested_crate) { - let index = out.join(requested_crate).join("index.html"); - builder.open_in_browser(index); - break; + // Open if the format is HTML + if let DocumentationFormat::Html = self.format { + if builder.paths.iter().any(|path| path.ends_with("library")) { + // For `x.py doc library --open`, open `std` by default. + let index = out.join("std").join("index.html"); + builder.open_in_browser(index); + } else { + for requested_crate in crates { + if STD_PUBLIC_CRATES.iter().any(|&k| k == requested_crate) { + let index = out.join(requested_crate).join("index.html"); + builder.open_in_browser(index); + break; + } } } } + + out } fn metadata(&self) -> Option { diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs index b60ca5f428fd..32d191c4265d 100644 --- a/src/bootstrap/src/core/builder/tests.rs +++ b/src/bootstrap/src/core/builder/tests.rs @@ -1128,7 +1128,6 @@ mod snapshot { [build] rustc 1 -> cargo-clippy 2 [build] rustc 1 -> miri 2 [build] rustc 1 -> cargo-miri 2 - [doc] rustc 1 -> std 1 crates=[] "); } @@ -1452,7 +1451,7 @@ mod snapshot { [build] rustc 1 -> miri 2 [build] rustc 1 -> cargo-miri 2 [build] rustc 1 -> LlvmBitcodeLinker 2 - [doc] rustc 1 -> std 1 crates=[] + [doc] rustc 2 -> std 2 crates=[] "); } From de13718c66965852ba8970781908fa1eda9a54b9 Mon Sep 17 00:00:00 2001 From: Eval EXEC Date: Sun, 10 Aug 2025 23:02:53 +0800 Subject: [PATCH 130/195] test: Add rustdoc test for enum negative overflow --- tests/rustdoc/enum/enum-variant-value.rs | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/tests/rustdoc/enum/enum-variant-value.rs b/tests/rustdoc/enum/enum-variant-value.rs index 1670de8a24f1..9cc85dfe10dc 100644 --- a/tests/rustdoc/enum/enum-variant-value.rs +++ b/tests/rustdoc/enum/enum-variant-value.rs @@ -189,3 +189,25 @@ pub use bar::P; //@ has - '//*[@id="variant.A"]/h3' 'A(u32)' //@ matches - '//*[@id="variant.B"]/h3' '^B$' pub use bar::Q; + +// Ensure signed implicit discriminants are rendered correctly after a negative explicit value. +//@ has 'foo/enum.R.html' +//@ has - '//*[@class="rust item-decl"]/code' 'A = -2,' +//@ has - '//*[@class="rust item-decl"]/code' 'B = -1,' +//@ matches - '//*[@id="variant.A"]/h3' '^A = -2$' +//@ matches - '//*[@id="variant.B"]/h3' '^B = -1$' +pub enum R { + A = -2, + B, +} + +// Also check that incrementing -1 yields 0 for the next implicit variant. +//@ has 'foo/enum.S.html' +//@ has - '//*[@class="rust item-decl"]/code' 'A = -1,' +//@ has - '//*[@class="rust item-decl"]/code' 'B = 0,' +//@ matches - '//*[@id="variant.A"]/h3' '^A = -1$' +//@ matches - '//*[@id="variant.B"]/h3' '^B = 0$' +pub enum S { + A = -1, + B, +} From 20a134f5f0fb5a68025143d0cd24cf4e4783819b Mon Sep 17 00:00:00 2001 From: Eval EXEC Date: Sun, 10 Aug 2025 23:33:23 +0800 Subject: [PATCH 131/195] rustdoc: Use `discr`s `Display` impl to render the value with the correct signedness --- src/librustdoc/html/render/print_item.rs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs index 02ee34aaac68..759f53974f57 100644 --- a/src/librustdoc/html/render/print_item.rs +++ b/src/librustdoc/html/render/print_item.rs @@ -1656,11 +1656,9 @@ fn display_c_like_variant( } else if should_show_enum_discriminant { let adt_def = cx.tcx().adt_def(enum_def_id); let discr = adt_def.discriminant_for_variant(cx.tcx(), index); - if discr.ty.is_signed() { - write!(w, "{} = {}", name.as_str(), discr.val as i128)?; - } else { - write!(w, "{} = {}", name.as_str(), discr.val)?; - } + // Use `discr`'s `Display` impl to render the value with the correct + // signedness, including proper sign-extension for signed types. + write!(w, "{} = {}", name.as_str(), discr)?; } else { write!(w, "{name}")?; } From aa543963c68061d9ca46037071d66a028626ff3f Mon Sep 17 00:00:00 2001 From: Oneirical Date: Sun, 13 Jul 2025 16:39:45 -0400 Subject: [PATCH 132/195] Rehome tests/ui/issues/ tests [4/?] --- .../unioned-keys-with-associated-type-23442.rs} | 1 + .../auto-deref-on-cow-regression-91489.rs} | 1 + .../struct-destructuring-repeated-bindings-9725.rs} | 1 + ...ruct-destructuring-repeated-bindings-9725.stderr} | 4 ++-- .../constant-expression-cast-9942.rs} | 1 + .../closure-type-inference-in-context-9129.rs} | 1 + .../trait-object-coercion-distribution-9951.rs} | 1 + .../trait-object-coercion-distribution-9951.stderr} | 2 +- .../try-from-with-const-genericsrs-98299.rs} | 1 + .../try-from-with-const-genericsrs-98299.stderr} | 12 ++++++------ .../auxiliary/aux-9155.rs} | 0 .../auxiliary/aux-9906.rs} | 0 .../generic-newtypes-cross-crate-usage-9155.rs | 11 +++++++++++ .../reexported-structs-impls-link-error-9906.rs | 10 ++++++++++ .../dereferenceable-type-behavior-22992.rs} | 2 +- .../multiple-drop-safe-code-25549.rs} | 1 + .../static-variable-with-drop-trait-9243.rs} | 1 + .../edition-specific-identifier-shadowing-53333.rs} | 1 + .../enum-discriminant-const-eval-truncation-9837.rs} | 1 + .../single-variant-enum-deref-error-9814.rs} | 1 + .../single-variant-enum-deref-error-9814.stderr} | 2 +- .../zero-variant-enum-pattern-matching-3037.rs} | 1 + .../issue-9968.rs => imports/auxiliary/aux-9968.rs} | 0 tests/ui/imports/pub-use-link-errors-9968.rs | 12 ++++++++++++ tests/ui/issues/issue-9155.rs | 11 ----------- tests/ui/issues/issue-9906.rs | 10 ---------- tests/ui/issues/issue-9968.rs | 12 ------------ .../for-loop-over-mut-iterator-21655.rs} | 1 + .../struct-with-lifetime-parameters-9259.rs} | 1 + ...-results-lint-triggered-by-derive-debug-29710.rs} | 1 + .../issue-9047.rs => loops/loop-with-label-9047.rs} | 1 + .../macro-expansion-module-structure-9110.rs} | 1 + .../macro-invocation-with-variable-in-scope-9737.rs} | 1 + .../private-struct-member-macro-access-25386.rs} | 1 + .../private-struct-member-macro-access-25386.stderr} | 2 +- ...wlines-in-diagnostic-fix-suggestions-92741.fixed} | 1 + .../newlines-in-diagnostic-fix-suggestions-92741.rs} | 1 + ...lines-in-diagnostic-fix-suggestions-92741.stderr} | 6 +++--- .../misaligned-reference-drop-field-99838.rs} | 1 + .../unit-variant-pattern-matching-29383.rs} | 1 + .../unit-variant-pattern-matching-29383.stderr} | 4 ++-- .../static-string-slice-9249.rs} | 1 + .../struct-field-access-errors-24365.rs} | 1 + .../struct-field-access-errors-24365.stderr} | 6 +++--- .../thread-local-with-attributes-30756.rs} | 1 + .../unused-type-parameter-span-30236.rs} | 1 + .../unused-type-parameter-span-30236.stderr} | 2 +- .../uninhabited-type-layout-computation-88150.rs} | 1 + 48 files changed, 83 insertions(+), 54 deletions(-) rename tests/ui/{issues/issue-23442.rs => associated-types/unioned-keys-with-associated-type-23442.rs} (88%) rename tests/ui/{issues/issue-91489.rs => autoref-autoderef/auto-deref-on-cow-regression-91489.rs} (93%) rename tests/ui/{issues/issue-9725.rs => binding/struct-destructuring-repeated-bindings-9725.rs} (80%) rename tests/ui/{issues/issue-9725.stderr => binding/struct-destructuring-repeated-bindings-9725.stderr} (81%) rename tests/ui/{issues/issue-9942.rs => cast/constant-expression-cast-9942.rs} (61%) rename tests/ui/{issues/issue-9129.rs => closures/closure-type-inference-in-context-9129.rs} (92%) rename tests/ui/{issues/issue-9951.rs => coercion/trait-object-coercion-distribution-9951.rs} (90%) rename tests/ui/{issues/issue-9951.stderr => coercion/trait-object-coercion-distribution-9951.stderr} (78%) rename tests/ui/{issues/issue-98299.rs => const-generics/try-from-with-const-genericsrs-98299.rs} (90%) rename tests/ui/{issues/issue-98299.stderr => const-generics/try-from-with-const-genericsrs-98299.stderr} (86%) rename tests/ui/{issues/auxiliary/issue-9155.rs => cross-crate/auxiliary/aux-9155.rs} (100%) rename tests/ui/{issues/auxiliary/issue-9906.rs => cross-crate/auxiliary/aux-9906.rs} (100%) create mode 100644 tests/ui/cross-crate/generic-newtypes-cross-crate-usage-9155.rs create mode 100644 tests/ui/cross-crate/reexported-structs-impls-link-error-9906.rs rename tests/ui/{issues/issue-22992.rs => deref/dereferenceable-type-behavior-22992.rs} (98%) rename tests/ui/{issues/issue-25549-multiple-drop.rs => drop/multiple-drop-safe-code-25549.rs} (92%) rename tests/ui/{issues/issue-9243.rs => drop/static-variable-with-drop-trait-9243.rs} (83%) rename tests/ui/{issues/issue-53333.rs => editions/edition-specific-identifier-shadowing-53333.rs} (72%) rename tests/ui/{issues/issue-9837.rs => enum-discriminant/enum-discriminant-const-eval-truncation-9837.rs} (76%) rename tests/ui/{issues/issue-9814.rs => enum/single-variant-enum-deref-error-9814.rs} (80%) rename tests/ui/{issues/issue-9814.stderr => enum/single-variant-enum-deref-error-9814.stderr} (81%) rename tests/ui/{issues/issue-3037.rs => enum/zero-variant-enum-pattern-matching-3037.rs} (76%) rename tests/ui/{issues/auxiliary/issue-9968.rs => imports/auxiliary/aux-9968.rs} (100%) create mode 100644 tests/ui/imports/pub-use-link-errors-9968.rs delete mode 100644 tests/ui/issues/issue-9155.rs delete mode 100644 tests/ui/issues/issue-9906.rs delete mode 100644 tests/ui/issues/issue-9968.rs rename tests/ui/{issues/issue-21655.rs => iterators/for-loop-over-mut-iterator-21655.rs} (77%) rename tests/ui/{issues/issue-9259.rs => lifetimes/struct-with-lifetime-parameters-9259.rs} (85%) rename tests/ui/{issues/issue-29710.rs => lint/unused-results-lint-triggered-by-derive-debug-29710.rs} (75%) rename tests/ui/{issues/issue-9047.rs => loops/loop-with-label-9047.rs} (82%) rename tests/ui/{issues/issue-9110.rs => macros/macro-expansion-module-structure-9110.rs} (83%) rename tests/ui/{issues/issue-9737.rs => macros/macro-invocation-with-variable-in-scope-9737.rs} (75%) rename tests/ui/{issues/issue-25386.rs => macros/private-struct-member-macro-access-25386.rs} (91%) rename tests/ui/{issues/issue-25386.stderr => macros/private-struct-member-macro-access-25386.stderr} (89%) rename tests/ui/{issues/issue-92741.fixed => mismatched_types/newlines-in-diagnostic-fix-suggestions-92741.fixed} (80%) rename tests/ui/{issues/issue-92741.rs => mismatched_types/newlines-in-diagnostic-fix-suggestions-92741.rs} (87%) rename tests/ui/{issues/issue-92741.stderr => mismatched_types/newlines-in-diagnostic-fix-suggestions-92741.stderr} (86%) rename tests/ui/{issues/issue-99838.rs => packed/misaligned-reference-drop-field-99838.rs} (94%) rename tests/ui/{issues/issue-pr29383.rs => pattern/unit-variant-pattern-matching-29383.rs} (86%) rename tests/ui/{issues/issue-pr29383.stderr => pattern/unit-variant-pattern-matching-29383.stderr} (83%) rename tests/ui/{issues/issue-9249.rs => static/static-string-slice-9249.rs} (68%) rename tests/ui/{issues/issue-24365.rs => structs/struct-field-access-errors-24365.rs} (89%) rename tests/ui/{issues/issue-24365.stderr => structs/struct-field-access-errors-24365.stderr} (76%) rename tests/ui/{issues/issue-30756.rs => thread-local/thread-local-with-attributes-30756.rs} (63%) rename tests/ui/{issues/issue-30236.rs => typeck/unused-type-parameter-span-30236.rs} (65%) rename tests/ui/{issues/issue-30236.stderr => typeck/unused-type-parameter-span-30236.stderr} (89%) rename tests/ui/{issues/issue-88150.rs => uninhabited/uninhabited-type-layout-computation-88150.rs} (86%) diff --git a/tests/ui/issues/issue-23442.rs b/tests/ui/associated-types/unioned-keys-with-associated-type-23442.rs similarity index 88% rename from tests/ui/issues/issue-23442.rs rename to tests/ui/associated-types/unioned-keys-with-associated-type-23442.rs index 883c5bb511ae..89a2d0177a3f 100644 --- a/tests/ui/issues/issue-23442.rs +++ b/tests/ui/associated-types/unioned-keys-with-associated-type-23442.rs @@ -1,3 +1,4 @@ +// https://github.com/rust-lang/rust/issues/23442 //@ check-pass #![allow(dead_code)] use std::marker::PhantomData; diff --git a/tests/ui/issues/issue-91489.rs b/tests/ui/autoref-autoderef/auto-deref-on-cow-regression-91489.rs similarity index 93% rename from tests/ui/issues/issue-91489.rs rename to tests/ui/autoref-autoderef/auto-deref-on-cow-regression-91489.rs index 0566302c4811..929e98ad719f 100644 --- a/tests/ui/issues/issue-91489.rs +++ b/tests/ui/autoref-autoderef/auto-deref-on-cow-regression-91489.rs @@ -1,3 +1,4 @@ +// https://github.com/rust-lang/rust/issues/91489 //@ check-pass // regression test for #91489 diff --git a/tests/ui/issues/issue-9725.rs b/tests/ui/binding/struct-destructuring-repeated-bindings-9725.rs similarity index 80% rename from tests/ui/issues/issue-9725.rs rename to tests/ui/binding/struct-destructuring-repeated-bindings-9725.rs index 360effbd1194..6b0b8e37b8c5 100644 --- a/tests/ui/issues/issue-9725.rs +++ b/tests/ui/binding/struct-destructuring-repeated-bindings-9725.rs @@ -1,3 +1,4 @@ +// https://github.com/rust-lang/rust/issues/9725 struct A { foo: isize } fn main() { diff --git a/tests/ui/issues/issue-9725.stderr b/tests/ui/binding/struct-destructuring-repeated-bindings-9725.stderr similarity index 81% rename from tests/ui/issues/issue-9725.stderr rename to tests/ui/binding/struct-destructuring-repeated-bindings-9725.stderr index 687e0cc0f3ef..f4d19bed4195 100644 --- a/tests/ui/issues/issue-9725.stderr +++ b/tests/ui/binding/struct-destructuring-repeated-bindings-9725.stderr @@ -1,11 +1,11 @@ error[E0416]: identifier `foo` is bound more than once in the same pattern - --> $DIR/issue-9725.rs:4:18 + --> $DIR/struct-destructuring-repeated-bindings-9725.rs:5:18 | LL | let A { foo, foo } = A { foo: 3 }; | ^^^ used in a pattern more than once error[E0025]: field `foo` bound multiple times in the pattern - --> $DIR/issue-9725.rs:4:18 + --> $DIR/struct-destructuring-repeated-bindings-9725.rs:5:18 | LL | let A { foo, foo } = A { foo: 3 }; | --- ^^^ multiple uses of `foo` in pattern diff --git a/tests/ui/issues/issue-9942.rs b/tests/ui/cast/constant-expression-cast-9942.rs similarity index 61% rename from tests/ui/issues/issue-9942.rs rename to tests/ui/cast/constant-expression-cast-9942.rs index 6332d9b3e080..d0a6f27b7e33 100644 --- a/tests/ui/issues/issue-9942.rs +++ b/tests/ui/cast/constant-expression-cast-9942.rs @@ -1,3 +1,4 @@ +// https://github.com/rust-lang/rust/issues/9942 //@ run-pass pub fn main() { diff --git a/tests/ui/issues/issue-9129.rs b/tests/ui/closures/closure-type-inference-in-context-9129.rs similarity index 92% rename from tests/ui/issues/issue-9129.rs rename to tests/ui/closures/closure-type-inference-in-context-9129.rs index 3856cd133e85..53ee8faab853 100644 --- a/tests/ui/issues/issue-9129.rs +++ b/tests/ui/closures/closure-type-inference-in-context-9129.rs @@ -1,3 +1,4 @@ +// https://github.com/rust-lang/rust/issues/9129 //@ run-pass #![allow(dead_code)] #![allow(non_camel_case_types)] diff --git a/tests/ui/issues/issue-9951.rs b/tests/ui/coercion/trait-object-coercion-distribution-9951.rs similarity index 90% rename from tests/ui/issues/issue-9951.rs rename to tests/ui/coercion/trait-object-coercion-distribution-9951.rs index 2cd7cd4f4302..526d65615101 100644 --- a/tests/ui/issues/issue-9951.rs +++ b/tests/ui/coercion/trait-object-coercion-distribution-9951.rs @@ -1,3 +1,4 @@ +// https://github.com/rust-lang/rust/issues/9951 //@ run-pass #![allow(unused_variables)] diff --git a/tests/ui/issues/issue-9951.stderr b/tests/ui/coercion/trait-object-coercion-distribution-9951.stderr similarity index 78% rename from tests/ui/issues/issue-9951.stderr rename to tests/ui/coercion/trait-object-coercion-distribution-9951.stderr index 62ed9f3e0cc0..0c672aa9b332 100644 --- a/tests/ui/issues/issue-9951.stderr +++ b/tests/ui/coercion/trait-object-coercion-distribution-9951.stderr @@ -1,5 +1,5 @@ warning: method `noop` is never used - --> $DIR/issue-9951.rs:6:6 + --> $DIR/trait-object-coercion-distribution-9951.rs:7:6 | LL | trait Bar { | --- method in this trait diff --git a/tests/ui/issues/issue-98299.rs b/tests/ui/const-generics/try-from-with-const-genericsrs-98299.rs similarity index 90% rename from tests/ui/issues/issue-98299.rs rename to tests/ui/const-generics/try-from-with-const-genericsrs-98299.rs index ba63d963475a..49c88856bc96 100644 --- a/tests/ui/issues/issue-98299.rs +++ b/tests/ui/const-generics/try-from-with-const-genericsrs-98299.rs @@ -1,3 +1,4 @@ +// https://github.com/rust-lang/rust/issues/98299 use std::convert::TryFrom; pub fn test_usage(p: ()) { diff --git a/tests/ui/issues/issue-98299.stderr b/tests/ui/const-generics/try-from-with-const-genericsrs-98299.stderr similarity index 86% rename from tests/ui/issues/issue-98299.stderr rename to tests/ui/const-generics/try-from-with-const-genericsrs-98299.stderr index b645267e3b91..1557b83b00ec 100644 --- a/tests/ui/issues/issue-98299.stderr +++ b/tests/ui/const-generics/try-from-with-const-genericsrs-98299.stderr @@ -1,5 +1,5 @@ error[E0284]: type annotations needed for `SmallCString<_>` - --> $DIR/issue-98299.rs:4:36 + --> $DIR/try-from-with-const-genericsrs-98299.rs:5:36 | LL | SmallCString::try_from(p).map(|cstr| cstr); | ------------ ^^^^ @@ -7,7 +7,7 @@ LL | SmallCString::try_from(p).map(|cstr| cstr); | type must be known at this point | note: required by a const generic parameter in `SmallCString` - --> $DIR/issue-98299.rs:10:25 + --> $DIR/try-from-with-const-genericsrs-98299.rs:11:25 | LL | pub struct SmallCString {} | ^^^^^^^^^^^^^^ required by this const generic parameter in `SmallCString` @@ -17,7 +17,7 @@ LL | SmallCString::try_from(p).map(|cstr: SmallCString| cstr); | +++++++++++++++++ error[E0284]: type annotations needed for `SmallCString<_>` - --> $DIR/issue-98299.rs:4:36 + --> $DIR/try-from-with-const-genericsrs-98299.rs:5:36 | LL | SmallCString::try_from(p).map(|cstr| cstr); | ------------ ^^^^ @@ -25,7 +25,7 @@ LL | SmallCString::try_from(p).map(|cstr| cstr); | type must be known at this point | note: required for `SmallCString<_>` to implement `TryFrom<()>` - --> $DIR/issue-98299.rs:12:22 + --> $DIR/try-from-with-const-genericsrs-98299.rs:13:22 | LL | impl TryFrom<()> for SmallCString { | -------------- ^^^^^^^^^^^ ^^^^^^^^^^^^^^^ @@ -37,7 +37,7 @@ LL | SmallCString::try_from(p).map(|cstr: SmallCString| cstr); | +++++++++++++++++ error[E0284]: type annotations needed for `SmallCString<_>` - --> $DIR/issue-98299.rs:4:36 + --> $DIR/try-from-with-const-genericsrs-98299.rs:5:36 | LL | SmallCString::try_from(p).map(|cstr| cstr); | ------------------------- ^^^^ @@ -45,7 +45,7 @@ LL | SmallCString::try_from(p).map(|cstr| cstr); | type must be known at this point | note: required for `SmallCString<_>` to implement `TryFrom<()>` - --> $DIR/issue-98299.rs:12:22 + --> $DIR/try-from-with-const-genericsrs-98299.rs:13:22 | LL | impl TryFrom<()> for SmallCString { | -------------- ^^^^^^^^^^^ ^^^^^^^^^^^^^^^ diff --git a/tests/ui/issues/auxiliary/issue-9155.rs b/tests/ui/cross-crate/auxiliary/aux-9155.rs similarity index 100% rename from tests/ui/issues/auxiliary/issue-9155.rs rename to tests/ui/cross-crate/auxiliary/aux-9155.rs diff --git a/tests/ui/issues/auxiliary/issue-9906.rs b/tests/ui/cross-crate/auxiliary/aux-9906.rs similarity index 100% rename from tests/ui/issues/auxiliary/issue-9906.rs rename to tests/ui/cross-crate/auxiliary/aux-9906.rs diff --git a/tests/ui/cross-crate/generic-newtypes-cross-crate-usage-9155.rs b/tests/ui/cross-crate/generic-newtypes-cross-crate-usage-9155.rs new file mode 100644 index 000000000000..35286615980f --- /dev/null +++ b/tests/ui/cross-crate/generic-newtypes-cross-crate-usage-9155.rs @@ -0,0 +1,11 @@ +// https://github.com/rust-lang/rust/issues/9155 +//@ run-pass +//@ aux-build:aux-9155.rs + +extern crate aux_9155; + +struct Baz; + +pub fn main() { + aux_9155::Foo::new(Baz); +} diff --git a/tests/ui/cross-crate/reexported-structs-impls-link-error-9906.rs b/tests/ui/cross-crate/reexported-structs-impls-link-error-9906.rs new file mode 100644 index 000000000000..b49951bd1f98 --- /dev/null +++ b/tests/ui/cross-crate/reexported-structs-impls-link-error-9906.rs @@ -0,0 +1,10 @@ +// https://github.com/rust-lang/rust/issues/9906 +//@ run-pass +//@ aux-build:aux-9906.rs + +extern crate aux_9906 as testmod; + +pub fn main() { + testmod::foo(); + testmod::FooBar::new(1); +} diff --git a/tests/ui/issues/issue-22992.rs b/tests/ui/deref/dereferenceable-type-behavior-22992.rs similarity index 98% rename from tests/ui/issues/issue-22992.rs rename to tests/ui/deref/dereferenceable-type-behavior-22992.rs index 3bc15cc948ad..19fc2e7eb0b6 100644 --- a/tests/ui/issues/issue-22992.rs +++ b/tests/ui/deref/dereferenceable-type-behavior-22992.rs @@ -1,3 +1,4 @@ +// https://github.com/rust-lang/rust/issues/22992 //@ run-pass struct X { val: i32 } @@ -6,7 +7,6 @@ impl std::ops::Deref for X { fn deref(&self) -> &i32 { &self.val } } - trait M { fn m(self); } impl M for i32 { fn m(self) { println!("i32::m()"); } } impl M for X { fn m(self) { println!("X::m()"); } } diff --git a/tests/ui/issues/issue-25549-multiple-drop.rs b/tests/ui/drop/multiple-drop-safe-code-25549.rs similarity index 92% rename from tests/ui/issues/issue-25549-multiple-drop.rs rename to tests/ui/drop/multiple-drop-safe-code-25549.rs index 1eec15a4aa28..dcf7a3fc79c4 100644 --- a/tests/ui/issues/issue-25549-multiple-drop.rs +++ b/tests/ui/drop/multiple-drop-safe-code-25549.rs @@ -1,3 +1,4 @@ +// https://github.com/rust-lang/rust/issues/25549 //@ run-pass #![allow(unused_variables)] struct Foo<'r>(&'r mut i32); diff --git a/tests/ui/issues/issue-9243.rs b/tests/ui/drop/static-variable-with-drop-trait-9243.rs similarity index 83% rename from tests/ui/issues/issue-9243.rs rename to tests/ui/drop/static-variable-with-drop-trait-9243.rs index 34ae944d1d86..0ae32c983e9e 100644 --- a/tests/ui/issues/issue-9243.rs +++ b/tests/ui/drop/static-variable-with-drop-trait-9243.rs @@ -1,3 +1,4 @@ +// https://github.com/rust-lang/rust/issues/9243 //@ build-pass #![allow(dead_code)] // Regression test for issue 9243 diff --git a/tests/ui/issues/issue-53333.rs b/tests/ui/editions/edition-specific-identifier-shadowing-53333.rs similarity index 72% rename from tests/ui/issues/issue-53333.rs rename to tests/ui/editions/edition-specific-identifier-shadowing-53333.rs index 468b7d8075fe..dd973bb8439d 100644 --- a/tests/ui/issues/issue-53333.rs +++ b/tests/ui/editions/edition-specific-identifier-shadowing-53333.rs @@ -1,3 +1,4 @@ +// https://github.com/rust-lang/rust/issues/53333 //@ run-pass #![allow(unused_imports)] //@ edition:2018 diff --git a/tests/ui/issues/issue-9837.rs b/tests/ui/enum-discriminant/enum-discriminant-const-eval-truncation-9837.rs similarity index 76% rename from tests/ui/issues/issue-9837.rs rename to tests/ui/enum-discriminant/enum-discriminant-const-eval-truncation-9837.rs index 33152a5d077f..8768d81b93fa 100644 --- a/tests/ui/issues/issue-9837.rs +++ b/tests/ui/enum-discriminant/enum-discriminant-const-eval-truncation-9837.rs @@ -1,3 +1,4 @@ +// https://github.com/rust-lang/rust/issues/9837 //@ run-pass const C1: i32 = 0x12345678; const C2: isize = C1 as i16 as isize; diff --git a/tests/ui/issues/issue-9814.rs b/tests/ui/enum/single-variant-enum-deref-error-9814.rs similarity index 80% rename from tests/ui/issues/issue-9814.rs rename to tests/ui/enum/single-variant-enum-deref-error-9814.rs index a87478e221b0..f10d665299cf 100644 --- a/tests/ui/issues/issue-9814.rs +++ b/tests/ui/enum/single-variant-enum-deref-error-9814.rs @@ -1,3 +1,4 @@ +// https://github.com/rust-lang/rust/issues/9814 // Verify that single-variant enums can't be de-referenced // Regression test for issue #9814 diff --git a/tests/ui/issues/issue-9814.stderr b/tests/ui/enum/single-variant-enum-deref-error-9814.stderr similarity index 81% rename from tests/ui/issues/issue-9814.stderr rename to tests/ui/enum/single-variant-enum-deref-error-9814.stderr index fa23fb7c1762..5e069f4c21de 100644 --- a/tests/ui/issues/issue-9814.stderr +++ b/tests/ui/enum/single-variant-enum-deref-error-9814.stderr @@ -1,5 +1,5 @@ error[E0614]: type `Foo` cannot be dereferenced - --> $DIR/issue-9814.rs:7:13 + --> $DIR/single-variant-enum-deref-error-9814.rs:8:13 | LL | let _ = *Foo::Bar(2); | ^^^^^^^^^^^^ can't be dereferenced diff --git a/tests/ui/issues/issue-3037.rs b/tests/ui/enum/zero-variant-enum-pattern-matching-3037.rs similarity index 76% rename from tests/ui/issues/issue-3037.rs rename to tests/ui/enum/zero-variant-enum-pattern-matching-3037.rs index 933b450ac8ea..7a7abb1c67c7 100644 --- a/tests/ui/issues/issue-3037.rs +++ b/tests/ui/enum/zero-variant-enum-pattern-matching-3037.rs @@ -1,3 +1,4 @@ +// https://github.com/rust-lang/rust/issues/3037 //@ run-pass #![allow(dead_code)] #![allow(non_camel_case_types)] diff --git a/tests/ui/issues/auxiliary/issue-9968.rs b/tests/ui/imports/auxiliary/aux-9968.rs similarity index 100% rename from tests/ui/issues/auxiliary/issue-9968.rs rename to tests/ui/imports/auxiliary/aux-9968.rs diff --git a/tests/ui/imports/pub-use-link-errors-9968.rs b/tests/ui/imports/pub-use-link-errors-9968.rs new file mode 100644 index 000000000000..517a0049ce0b --- /dev/null +++ b/tests/ui/imports/pub-use-link-errors-9968.rs @@ -0,0 +1,12 @@ +// https://github.com/rust-lang/rust/issues/9968 +//@ run-pass +//@ aux-build:aux-9968.rs + +extern crate aux_9968 as lib; + +use lib::{Trait, Struct}; + +pub fn main() +{ + Struct::init().test(); +} diff --git a/tests/ui/issues/issue-9155.rs b/tests/ui/issues/issue-9155.rs deleted file mode 100644 index dfd9dea20090..000000000000 --- a/tests/ui/issues/issue-9155.rs +++ /dev/null @@ -1,11 +0,0 @@ -//@ run-pass -//@ aux-build:issue-9155.rs - - -extern crate issue_9155; - -struct Baz; - -pub fn main() { - issue_9155::Foo::new(Baz); -} diff --git a/tests/ui/issues/issue-9906.rs b/tests/ui/issues/issue-9906.rs deleted file mode 100644 index 50417d3e4561..000000000000 --- a/tests/ui/issues/issue-9906.rs +++ /dev/null @@ -1,10 +0,0 @@ -//@ run-pass -//@ aux-build:issue-9906.rs - - -extern crate issue_9906 as testmod; - -pub fn main() { - testmod::foo(); - testmod::FooBar::new(1); -} diff --git a/tests/ui/issues/issue-9968.rs b/tests/ui/issues/issue-9968.rs deleted file mode 100644 index 89e60ba5ac7f..000000000000 --- a/tests/ui/issues/issue-9968.rs +++ /dev/null @@ -1,12 +0,0 @@ -//@ run-pass -//@ aux-build:issue-9968.rs - - -extern crate issue_9968 as lib; - -use lib::{Trait, Struct}; - -pub fn main() -{ - Struct::init().test(); -} diff --git a/tests/ui/issues/issue-21655.rs b/tests/ui/iterators/for-loop-over-mut-iterator-21655.rs similarity index 77% rename from tests/ui/issues/issue-21655.rs rename to tests/ui/iterators/for-loop-over-mut-iterator-21655.rs index 1068b28b3383..b5c9826bd454 100644 --- a/tests/ui/issues/issue-21655.rs +++ b/tests/ui/iterators/for-loop-over-mut-iterator-21655.rs @@ -1,3 +1,4 @@ +// https://github.com/rust-lang/rust/issues/21655 //@ run-pass fn test(it: &mut dyn Iterator) { diff --git a/tests/ui/issues/issue-9259.rs b/tests/ui/lifetimes/struct-with-lifetime-parameters-9259.rs similarity index 85% rename from tests/ui/issues/issue-9259.rs rename to tests/ui/lifetimes/struct-with-lifetime-parameters-9259.rs index c45288f7d65e..7e39fdc3f10b 100644 --- a/tests/ui/issues/issue-9259.rs +++ b/tests/ui/lifetimes/struct-with-lifetime-parameters-9259.rs @@ -1,3 +1,4 @@ +// https://github.com/rust-lang/rust/issues/9259 //@ run-pass #![allow(dead_code)] diff --git a/tests/ui/issues/issue-29710.rs b/tests/ui/lint/unused-results-lint-triggered-by-derive-debug-29710.rs similarity index 75% rename from tests/ui/issues/issue-29710.rs rename to tests/ui/lint/unused-results-lint-triggered-by-derive-debug-29710.rs index 906ffe9e77b6..51702f69c203 100644 --- a/tests/ui/issues/issue-29710.rs +++ b/tests/ui/lint/unused-results-lint-triggered-by-derive-debug-29710.rs @@ -1,3 +1,4 @@ +// https://github.com/rust-lang/rust/issues/29710 //@ check-pass #![deny(unused_results)] #![allow(dead_code)] diff --git a/tests/ui/issues/issue-9047.rs b/tests/ui/loops/loop-with-label-9047.rs similarity index 82% rename from tests/ui/issues/issue-9047.rs rename to tests/ui/loops/loop-with-label-9047.rs index 97733588d514..29e6dba0b9e4 100644 --- a/tests/ui/issues/issue-9047.rs +++ b/tests/ui/loops/loop-with-label-9047.rs @@ -1,3 +1,4 @@ +// https://github.com/rust-lang/rust/issues/9047 //@ run-pass #![allow(unused_mut)] #![allow(unused_variables)] diff --git a/tests/ui/issues/issue-9110.rs b/tests/ui/macros/macro-expansion-module-structure-9110.rs similarity index 83% rename from tests/ui/issues/issue-9110.rs rename to tests/ui/macros/macro-expansion-module-structure-9110.rs index 47533dc43b59..b6241a7c18e4 100644 --- a/tests/ui/issues/issue-9110.rs +++ b/tests/ui/macros/macro-expansion-module-structure-9110.rs @@ -1,3 +1,4 @@ +// https://github.com/rust-lang/rust/issues/9110 //@ check-pass #![allow(dead_code)] #![allow(non_snake_case)] diff --git a/tests/ui/issues/issue-9737.rs b/tests/ui/macros/macro-invocation-with-variable-in-scope-9737.rs similarity index 75% rename from tests/ui/issues/issue-9737.rs rename to tests/ui/macros/macro-invocation-with-variable-in-scope-9737.rs index a8a17e58dd6f..957c2e3f1033 100644 --- a/tests/ui/issues/issue-9737.rs +++ b/tests/ui/macros/macro-invocation-with-variable-in-scope-9737.rs @@ -1,3 +1,4 @@ +// https://github.com/rust-lang/rust/issues/9737 //@ run-pass #![allow(unused_variables)] macro_rules! f { diff --git a/tests/ui/issues/issue-25386.rs b/tests/ui/macros/private-struct-member-macro-access-25386.rs similarity index 91% rename from tests/ui/issues/issue-25386.rs rename to tests/ui/macros/private-struct-member-macro-access-25386.rs index b26cc77680da..88e5a22a699f 100644 --- a/tests/ui/issues/issue-25386.rs +++ b/tests/ui/macros/private-struct-member-macro-access-25386.rs @@ -1,3 +1,4 @@ +// https://github.com/rust-lang/rust/issues/25386 mod stuff { pub struct Item { c_object: Box, diff --git a/tests/ui/issues/issue-25386.stderr b/tests/ui/macros/private-struct-member-macro-access-25386.stderr similarity index 89% rename from tests/ui/issues/issue-25386.stderr rename to tests/ui/macros/private-struct-member-macro-access-25386.stderr index 720b77866a58..d02a41848f47 100644 --- a/tests/ui/issues/issue-25386.stderr +++ b/tests/ui/macros/private-struct-member-macro-access-25386.stderr @@ -1,5 +1,5 @@ error[E0616]: field `c_object` of struct `Item` is private - --> $DIR/issue-25386.rs:19:16 + --> $DIR/private-struct-member-macro-access-25386.rs:20:16 | LL | (*$var.c_object).$member.is_some() | ^^^^^^^^ private field diff --git a/tests/ui/issues/issue-92741.fixed b/tests/ui/mismatched_types/newlines-in-diagnostic-fix-suggestions-92741.fixed similarity index 80% rename from tests/ui/issues/issue-92741.fixed rename to tests/ui/mismatched_types/newlines-in-diagnostic-fix-suggestions-92741.fixed index cb37d25273f4..c165779e402f 100644 --- a/tests/ui/issues/issue-92741.fixed +++ b/tests/ui/mismatched_types/newlines-in-diagnostic-fix-suggestions-92741.fixed @@ -1,3 +1,4 @@ +// https://github.com/rust-lang/rust/issues/92741 //@ run-rustfix fn main() {} fn _foo() -> bool { diff --git a/tests/ui/issues/issue-92741.rs b/tests/ui/mismatched_types/newlines-in-diagnostic-fix-suggestions-92741.rs similarity index 87% rename from tests/ui/issues/issue-92741.rs rename to tests/ui/mismatched_types/newlines-in-diagnostic-fix-suggestions-92741.rs index 1c5d5810a57e..b3fa5f77308c 100644 --- a/tests/ui/issues/issue-92741.rs +++ b/tests/ui/mismatched_types/newlines-in-diagnostic-fix-suggestions-92741.rs @@ -1,3 +1,4 @@ +// https://github.com/rust-lang/rust/issues/92741 //@ run-rustfix fn main() {} fn _foo() -> bool { diff --git a/tests/ui/issues/issue-92741.stderr b/tests/ui/mismatched_types/newlines-in-diagnostic-fix-suggestions-92741.stderr similarity index 86% rename from tests/ui/issues/issue-92741.stderr rename to tests/ui/mismatched_types/newlines-in-diagnostic-fix-suggestions-92741.stderr index 49315e7a8bf6..60917d9a63e4 100644 --- a/tests/ui/issues/issue-92741.stderr +++ b/tests/ui/mismatched_types/newlines-in-diagnostic-fix-suggestions-92741.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/issue-92741.rs:4:5 + --> $DIR/newlines-in-diagnostic-fix-suggestions-92741.rs:5:5 | LL | fn _foo() -> bool { | ---- expected `bool` because of return type @@ -15,7 +15,7 @@ LL - mut | error[E0308]: mismatched types - --> $DIR/issue-92741.rs:10:5 + --> $DIR/newlines-in-diagnostic-fix-suggestions-92741.rs:11:5 | LL | fn _bar() -> bool { | ---- expected `bool` because of return type @@ -31,7 +31,7 @@ LL + if true { true } else { false } | error[E0308]: mismatched types - --> $DIR/issue-92741.rs:15:5 + --> $DIR/newlines-in-diagnostic-fix-suggestions-92741.rs:16:5 | LL | fn _baz() -> bool { | ---- expected `bool` because of return type diff --git a/tests/ui/issues/issue-99838.rs b/tests/ui/packed/misaligned-reference-drop-field-99838.rs similarity index 94% rename from tests/ui/issues/issue-99838.rs rename to tests/ui/packed/misaligned-reference-drop-field-99838.rs index 687b47fbe71a..58e168162cbb 100644 --- a/tests/ui/issues/issue-99838.rs +++ b/tests/ui/packed/misaligned-reference-drop-field-99838.rs @@ -1,3 +1,4 @@ +// https://github.com/rust-lang/rust/issues/99838 //@ run-pass use std::hint; diff --git a/tests/ui/issues/issue-pr29383.rs b/tests/ui/pattern/unit-variant-pattern-matching-29383.rs similarity index 86% rename from tests/ui/issues/issue-pr29383.rs rename to tests/ui/pattern/unit-variant-pattern-matching-29383.rs index 2bcc0aa2782d..e339dc01f46e 100644 --- a/tests/ui/issues/issue-pr29383.rs +++ b/tests/ui/pattern/unit-variant-pattern-matching-29383.rs @@ -1,3 +1,4 @@ +// https://github.com/rust-lang/rust/issues/29383 enum E { A, B, diff --git a/tests/ui/issues/issue-pr29383.stderr b/tests/ui/pattern/unit-variant-pattern-matching-29383.stderr similarity index 83% rename from tests/ui/issues/issue-pr29383.stderr rename to tests/ui/pattern/unit-variant-pattern-matching-29383.stderr index 57783d75ba18..e30837568a5f 100644 --- a/tests/ui/issues/issue-pr29383.stderr +++ b/tests/ui/pattern/unit-variant-pattern-matching-29383.stderr @@ -1,5 +1,5 @@ error[E0532]: expected tuple struct or tuple variant, found unit variant `E::A` - --> $DIR/issue-pr29383.rs:9:14 + --> $DIR/unit-variant-pattern-matching-29383.rs:10:14 | LL | A, | - `E::A` defined here @@ -8,7 +8,7 @@ LL | Some(E::A(..)) => {} | ^^^^^^^^ help: use this syntax instead: `E::A` error[E0532]: expected tuple struct or tuple variant, found unit variant `E::B` - --> $DIR/issue-pr29383.rs:11:14 + --> $DIR/unit-variant-pattern-matching-29383.rs:12:14 | LL | B, | - `E::B` defined here diff --git a/tests/ui/issues/issue-9249.rs b/tests/ui/static/static-string-slice-9249.rs similarity index 68% rename from tests/ui/issues/issue-9249.rs rename to tests/ui/static/static-string-slice-9249.rs index b98ba050521a..da099117bd4e 100644 --- a/tests/ui/issues/issue-9249.rs +++ b/tests/ui/static/static-string-slice-9249.rs @@ -1,3 +1,4 @@ +// https://github.com/rust-lang/rust/issues/9249 //@ check-pass #![allow(dead_code)] diff --git a/tests/ui/issues/issue-24365.rs b/tests/ui/structs/struct-field-access-errors-24365.rs similarity index 89% rename from tests/ui/issues/issue-24365.rs rename to tests/ui/structs/struct-field-access-errors-24365.rs index da1951160478..13a95cd1ccac 100644 --- a/tests/ui/issues/issue-24365.rs +++ b/tests/ui/structs/struct-field-access-errors-24365.rs @@ -1,3 +1,4 @@ +// https://github.com/rust-lang/rust/issues/24365 pub enum Attribute { Code {attr_name_idx: u16}, } diff --git a/tests/ui/issues/issue-24365.stderr b/tests/ui/structs/struct-field-access-errors-24365.stderr similarity index 76% rename from tests/ui/issues/issue-24365.stderr rename to tests/ui/structs/struct-field-access-errors-24365.stderr index 3f6ed0231d8b..65275af4701f 100644 --- a/tests/ui/issues/issue-24365.stderr +++ b/tests/ui/structs/struct-field-access-errors-24365.stderr @@ -1,17 +1,17 @@ error[E0609]: no field `b` on type `Foo` - --> $DIR/issue-24365.rs:10:22 + --> $DIR/struct-field-access-errors-24365.rs:11:22 | LL | println!("{}", a.b); | ^ unknown field error[E0609]: no field `attr_name_idx` on type `&Attribute` - --> $DIR/issue-24365.rs:17:18 + --> $DIR/struct-field-access-errors-24365.rs:18:18 | LL | let z = (&x).attr_name_idx; | ^^^^^^^^^^^^^ unknown field error[E0609]: no field `attr_name_idx` on type `Attribute` - --> $DIR/issue-24365.rs:18:15 + --> $DIR/struct-field-access-errors-24365.rs:19:15 | LL | let y = x.attr_name_idx; | ^^^^^^^^^^^^^ unknown field diff --git a/tests/ui/issues/issue-30756.rs b/tests/ui/thread-local/thread-local-with-attributes-30756.rs similarity index 63% rename from tests/ui/issues/issue-30756.rs rename to tests/ui/thread-local/thread-local-with-attributes-30756.rs index d103776406c5..fcf7bb813c81 100644 --- a/tests/ui/issues/issue-30756.rs +++ b/tests/ui/thread-local/thread-local-with-attributes-30756.rs @@ -1,3 +1,4 @@ +// https://github.com/rust-lang/rust/issues/30756 //@ run-pass #![forbid(unsafe_code)] diff --git a/tests/ui/issues/issue-30236.rs b/tests/ui/typeck/unused-type-parameter-span-30236.rs similarity index 65% rename from tests/ui/issues/issue-30236.rs rename to tests/ui/typeck/unused-type-parameter-span-30236.rs index 08d08a544021..bcdc922a71bb 100644 --- a/tests/ui/issues/issue-30236.rs +++ b/tests/ui/typeck/unused-type-parameter-span-30236.rs @@ -1,3 +1,4 @@ +// https://github.com/rust-lang/rust/issues/30236 type Foo< Unused //~ ERROR type parameter `Unused` is never used > = u8; diff --git a/tests/ui/issues/issue-30236.stderr b/tests/ui/typeck/unused-type-parameter-span-30236.stderr similarity index 89% rename from tests/ui/issues/issue-30236.stderr rename to tests/ui/typeck/unused-type-parameter-span-30236.stderr index bfe374a653f5..038bd1ebd289 100644 --- a/tests/ui/issues/issue-30236.stderr +++ b/tests/ui/typeck/unused-type-parameter-span-30236.stderr @@ -1,5 +1,5 @@ error[E0091]: type parameter `Unused` is never used - --> $DIR/issue-30236.rs:2:5 + --> $DIR/unused-type-parameter-span-30236.rs:3:5 | LL | Unused | ^^^^^^ unused type parameter diff --git a/tests/ui/issues/issue-88150.rs b/tests/ui/uninhabited/uninhabited-type-layout-computation-88150.rs similarity index 86% rename from tests/ui/issues/issue-88150.rs rename to tests/ui/uninhabited/uninhabited-type-layout-computation-88150.rs index 1dadba307c0b..1387c5b6c109 100644 --- a/tests/ui/issues/issue-88150.rs +++ b/tests/ui/uninhabited/uninhabited-type-layout-computation-88150.rs @@ -1,3 +1,4 @@ +// https://github.com/rust-lang/rust/issues/88150 //@ run-pass //@ compile-flags:-C debuginfo=2 //@ edition:2018 From cef5ebc5ffdbb65b85c4312335cb4fa80b9ac0c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Sun, 10 Aug 2025 11:10:31 +0200 Subject: [PATCH 133/195] Ship the correct Cranelift backend in its dist step --- src/bootstrap/src/core/build_steps/compile.rs | 32 +++++++++++------- src/bootstrap/src/core/build_steps/dist.rs | 33 ++++++++----------- 2 files changed, 35 insertions(+), 30 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs index 79174eb281f6..4519731ada7f 100644 --- a/src/bootstrap/src/core/build_steps/compile.rs +++ b/src/bootstrap/src/core/build_steps/compile.rs @@ -1761,20 +1761,30 @@ fn copy_codegen_backends_to_sysroot( } if stamp.path().exists() { - let dylib = t!(fs::read_to_string(stamp.path())); - let file = Path::new(&dylib); - let filename = file.file_name().unwrap().to_str().unwrap(); - // change `librustc_codegen_cranelift-xxxxxx.so` to - // `librustc_codegen_cranelift-release.so` - let target_filename = { - let dash = filename.find('-').unwrap(); - let dot = filename.find('.').unwrap(); - format!("{}-{}{}", &filename[..dash], builder.rust_release(), &filename[dot..]) - }; - builder.copy_link(file, &dst.join(target_filename), FileType::NativeLibrary); + let file = get_codegen_backend_file(&stamp); + builder.copy_link( + &file, + &dst.join(normalize_codegen_backend_name(builder, &file)), + FileType::NativeLibrary, + ); } } +/// Gets the path to a dynamic codegen backend library from its build stamp. +pub fn get_codegen_backend_file(stamp: &BuildStamp) -> PathBuf { + PathBuf::from(t!(fs::read_to_string(stamp.path()))) +} + +/// Normalize the name of a dynamic codegen backend library. +pub fn normalize_codegen_backend_name(builder: &Builder<'_>, path: &Path) -> String { + let filename = path.file_name().unwrap().to_str().unwrap(); + // change e.g. `librustc_codegen_cranelift-xxxxxx.so` to + // `librustc_codegen_cranelift-release.so` + let dash = filename.find('-').unwrap(); + let dot = filename.find('.').unwrap(); + format!("{}-{}{}", &filename[..dash], builder.rust_release(), &filename[dot..]) +} + pub fn compiler_file( builder: &Builder<'_>, compiler: &Path, diff --git a/src/bootstrap/src/core/build_steps/dist.rs b/src/bootstrap/src/core/build_steps/dist.rs index 3fbc8cdbcd5a..8f0de4c87d81 100644 --- a/src/bootstrap/src/core/build_steps/dist.rs +++ b/src/bootstrap/src/core/build_steps/dist.rs @@ -19,6 +19,7 @@ use object::read::archive::ArchiveFile; #[cfg(feature = "tracing")] use tracing::instrument; +use crate::core::build_steps::compile::{get_codegen_backend_file, normalize_codegen_backend_name}; use crate::core::build_steps::doc::DocumentationFormat; use crate::core::build_steps::tool::{self, RustcPrivateCompilers, Tool}; use crate::core::build_steps::vendor::{VENDOR_DIR, Vendor}; @@ -1442,35 +1443,29 @@ impl Step for CraneliftCodegenBackend { tarball.is_preview(true); tarball.add_legal_and_readme_to("share/doc/rustc_codegen_cranelift"); - builder.ensure(compile::CraneliftCodegenBackend { compilers }); + let stamp = builder.ensure(compile::CraneliftCodegenBackend { compilers }); if builder.config.dry_run() { return None; } - let src = builder.sysroot(self.build_compiler); - let backends_src = builder.sysroot_codegen_backends(self.build_compiler); - let backends_rel = backends_src - .strip_prefix(src) + // Get the relative path of where the codegen backend should be stored. + let backends_dst = builder.sysroot_codegen_backends(compilers.target_compiler()); + let backends_rel = backends_dst + .strip_prefix(builder.sysroot(compilers.target_compiler())) .unwrap() - .strip_prefix(builder.sysroot_libdir_relative(self.build_compiler)) + .strip_prefix(builder.sysroot_libdir_relative(compilers.target_compiler())) .unwrap(); // Don't use custom libdir here because ^lib/ will be resolved again with installer let backends_dst = PathBuf::from("lib").join(backends_rel); - let mut found_backend = false; - for backend in fs::read_dir(&backends_src).unwrap() { - let file_name = backend.unwrap().file_name(); - if file_name.to_str().unwrap().contains("rustc_codegen_cranelift") { - tarball.add_file( - backends_src.join(file_name), - &backends_dst, - FileType::NativeLibrary, - ); - found_backend = true; - } - } - assert!(found_backend); + let codegen_backend_dylib = get_codegen_backend_file(&stamp); + tarball.add_renamed_file( + &codegen_backend_dylib, + &backends_dst, + &normalize_codegen_backend_name(builder, &codegen_backend_dylib), + FileType::NativeLibrary, + ); Some(tarball.generate()) } From 01652d37ae86bae3d773ccc2de01993a45ef15be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Sun, 10 Aug 2025 13:55:55 +0200 Subject: [PATCH 134/195] Extract Cranelift component --- src/tools/opt-dist/src/tests.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/tools/opt-dist/src/tests.rs b/src/tools/opt-dist/src/tests.rs index d5121b8c7869..fa55805bbf21 100644 --- a/src/tools/opt-dist/src/tests.rs +++ b/src/tools/opt-dist/src/tests.rs @@ -36,6 +36,16 @@ pub fn run_tests(env: &Environment) -> anyhow::Result<()> { let cargo_dir = extract_dist_dir(&format!("cargo-{version}-{host_triple}"))?.join("cargo"); let extracted_src_dir = extract_dist_dir(&format!("rust-src-{version}"))?.join("rust-src"); + // If we have a Cranelift archive, copy it to the rustc sysroot + if let Ok(_) = find_file_in_dir(&dist_dir, "rustc-codegen-cranelift-", ".tar.xz") { + let extracted_codegen_dir = + extract_dist_dir(&format!("rustc-codegen-cranelift-{version}-{host_triple}"))? + .join("rustc-codegen-cranelift-preview"); + let rel_path = + Utf8Path::new("lib").join("rustlib").join(host_triple).join("codegen-backends"); + copy_directory(&extracted_codegen_dir.join(&rel_path), &rustc_dir.join(&rel_path))?; + } + // We need to manually copy libstd to the extracted rustc sysroot copy_directory( &libstd_dir.join("lib").join("rustlib").join(host_triple).join("lib"), From 6893e691ea71b294f87a2c4a5c8f0a1f8168751d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Sun, 10 Aug 2025 20:46:30 +0200 Subject: [PATCH 135/195] Add a post-dist test for compiling a basic program with Cranelift --- .../dist/cranelift-x86_64-unknown-linux-gnu-dist.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 tests/ui/dist/cranelift-x86_64-unknown-linux-gnu-dist.rs diff --git a/tests/ui/dist/cranelift-x86_64-unknown-linux-gnu-dist.rs b/tests/ui/dist/cranelift-x86_64-unknown-linux-gnu-dist.rs new file mode 100644 index 000000000000..198f8d1bc10c --- /dev/null +++ b/tests/ui/dist/cranelift-x86_64-unknown-linux-gnu-dist.rs @@ -0,0 +1,11 @@ +// Ensure that Cranelift can be used to compile a simple program with `x86_64-unknown-linux-gnu` +// dist artifacts. + +//@ only-dist +//@ only-x86_64-unknown-linux-gnu +//@ compile-flags: -Z codegen-backend=cranelift +//@ run-pass + +fn main() { + println!("Hello world!"); +} From 3fde438cecc2650ca34b510fef992ad595229306 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Sun, 10 Aug 2025 16:22:15 +0200 Subject: [PATCH 136/195] Enable RUST_BACKTRACE=1 on CI We should really see the backtrace if something in bootstrap fails on CI. --- src/ci/run.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/ci/run.sh b/src/ci/run.sh index f58a067041dd..c9d81f1ff510 100755 --- a/src/ci/run.sh +++ b/src/ci/run.sh @@ -205,6 +205,9 @@ if [ "$ENABLE_GCC_CODEGEN" = "1" ]; then RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --enable-new-symbol-mangling" fi +# If bootstrap fails, we want to see its backtrace +export RUST_BACKTRACE=1 + # Print the date from the local machine and the date from an external source to # check for clock drifts. An HTTP URL is used instead of HTTPS since on Azure # Pipelines it happened that the certificates were marked as expired. From 23946b7f902d7cfa774aa38089553a8cc9e6fafb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Sun, 10 Aug 2025 20:58:39 +0200 Subject: [PATCH 137/195] Fix Cargo cross-compilation (take two) --- src/bootstrap/src/core/build_steps/tool.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/bootstrap/src/core/build_steps/tool.rs b/src/bootstrap/src/core/build_steps/tool.rs index aa00cd03c5bb..42f59f00e5a1 100644 --- a/src/bootstrap/src/core/build_steps/tool.rs +++ b/src/bootstrap/src/core/build_steps/tool.rs @@ -857,7 +857,9 @@ impl Step for Cargo { fn run(self, builder: &Builder<'_>) -> ToolBuildResult { builder.build.require_submodule("src/tools/cargo", None); + builder.std(self.build_compiler, builder.host_target); builder.std(self.build_compiler, self.target); + builder.ensure(ToolBuild { build_compiler: self.build_compiler, target: self.target, From 464a6b1b4af28f7b2d1adf051bad3f182e23b88e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Tue, 21 Jan 2025 21:07:20 +0000 Subject: [PATCH 138/195] Detect struct construction with private field in field with default When trying to construct a struct that has a public field of a private type, suggest using `..` if that field has a default value. ``` error[E0603]: struct `Priv1` is private --> $DIR/non-exhaustive-ctor.rs:25:39 | LL | let _ = S { field: (), field1: m::Priv1 {} }; | ------ ^^^^^ private struct | | | while setting this field | note: the struct `Priv1` is defined here --> $DIR/non-exhaustive-ctor.rs:14:4 | LL | struct Priv1 {} | ^^^^^^^^^^^^ help: the field `field1` you're trying to set has a default value, you can use `..` to use it | LL | let _ = S { field: (), .. }; | ~~ ``` --- .../src/rmeta/decoder/cstore_impl.rs | 1 + compiler/rustc_middle/src/query/mod.rs | 7 ++ .../rustc_resolve/src/build_reduced_graph.rs | 14 ++- compiler/rustc_resolve/src/diagnostics.rs | 60 +++++++++- compiler/rustc_resolve/src/ident.rs | 19 +++- compiler/rustc_resolve/src/late.rs | 36 +++--- .../rustc_resolve/src/late/diagnostics.rs | 27 +++-- compiler/rustc_resolve/src/lib.rs | 18 +++ .../auxiliary/struct_field_default.rs | 10 ++ .../non-exhaustive-ctor-2.rs | 29 +++++ .../non-exhaustive-ctor-2.stderr | 105 ++++++++++++++++++ 11 files changed, 290 insertions(+), 36 deletions(-) create mode 100644 tests/ui/structs/default-field-values/non-exhaustive-ctor-2.rs create mode 100644 tests/ui/structs/default-field-values/non-exhaustive-ctor-2.stderr diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs index df95ed602cd3..11fef3be5d09 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs @@ -395,6 +395,7 @@ provide! { tcx, def_id, other, cdata, crate_extern_paths => { cdata.source().paths().cloned().collect() } expn_that_defined => { cdata.get_expn_that_defined(def_id.index, tcx.sess) } + default_field => { cdata.get_default_field(def_id.index) } is_doc_hidden => { cdata.get_attr_flags(def_id.index).contains(AttrFlags::IS_DOC_HIDDEN) } doc_link_resolutions => { tcx.arena.alloc(cdata.get_doc_link_resolutions(def_id.index)) } doc_link_traits_in_scope => { diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index a45895765945..f1f4f1b54655 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -1864,6 +1864,13 @@ rustc_queries! { feedable } + /// Returns whether the impl or associated function has the `default` keyword. + query default_field(def_id: DefId) -> Option { + desc { |tcx| "looking up the `const` corresponding to the default for `{}`", tcx.def_path_str(def_id) } + separate_provide_extern + feedable + } + query check_well_formed(key: LocalDefId) -> Result<(), ErrorGuaranteed> { desc { |tcx| "checking that `{}` is well-formed", tcx.def_path_str(key) } return_result_from_ensure_ok diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs index 2ad8543bf8ca..d18d0fc16a8e 100644 --- a/compiler/rustc_resolve/src/build_reduced_graph.rs +++ b/compiler/rustc_resolve/src/build_reduced_graph.rs @@ -420,14 +420,18 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { // The fields are not expanded yet. return; } - let fields = fields + let field_name = |i, field: &ast::FieldDef| { + field.ident.unwrap_or_else(|| Ident::from_str_and_span(&format!("{i}"), field.span)) + }; + let field_names: Vec<_> = + fields.iter().enumerate().map(|(i, field)| field_name(i, field)).collect(); + let defaults = fields .iter() .enumerate() - .map(|(i, field)| { - field.ident.unwrap_or_else(|| Ident::from_str_and_span(&format!("{i}"), field.span)) - }) + .filter_map(|(i, field)| field.default.as_ref().map(|_| field_name(i, field).name)) .collect(); - self.r.field_names.insert(def_id, fields); + self.r.field_names.insert(def_id, field_names); + self.r.field_defaults.insert(def_id, defaults); } fn insert_field_visibilities_local(&mut self, def_id: DefId, fields: &[ast::FieldDef]) { diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index 66f79585d92f..40ea90bf4847 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -1943,8 +1943,15 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } fn report_privacy_error(&mut self, privacy_error: &PrivacyError<'ra>) { - let PrivacyError { ident, binding, outermost_res, parent_scope, single_nested, dedup_span } = - *privacy_error; + let PrivacyError { + ident, + binding, + outermost_res, + parent_scope, + single_nested, + dedup_span, + ref source, + } = *privacy_error; let res = binding.res(); let ctor_fields_span = self.ctor_fields_span(binding); @@ -1960,6 +1967,55 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { let mut err = self.dcx().create_err(errors::IsPrivate { span: ident.span, ident_descr, ident }); + if let Some(expr) = source + && let ast::ExprKind::Struct(struct_expr) = &expr.kind + && let Some(Res::Def(_, def_id)) = self.partial_res_map + [&struct_expr.path.segments.iter().last().unwrap().id] + .full_res() + && let Some(default_fields) = self.field_defaults(def_id) + && !struct_expr.fields.is_empty() + { + let last_span = struct_expr.fields.iter().last().unwrap().span; + let mut iter = struct_expr.fields.iter().peekable(); + let mut prev: Option = None; + while let Some(field) = iter.next() { + if field.expr.span.overlaps(ident.span) { + err.span_label(field.ident.span, "while setting this field"); + if default_fields.contains(&field.ident.name) { + let sugg = if last_span == field.span { + vec![(field.span, "..".to_string())] + } else { + vec![ + ( + // Account for trailing commas and ensure we remove them. + match (prev, iter.peek()) { + (_, Some(next)) => field.span.with_hi(next.span.lo()), + (Some(prev), _) => field.span.with_lo(prev.hi()), + (None, None) => field.span, + }, + String::new(), + ), + (last_span.shrink_to_hi(), ", ..".to_string()), + ] + }; + err.multipart_suggestion_verbose( + format!( + "the type `{ident}` of field `{}` is private, but you can \ + construct the default value defined for it in `{}` using `..` in \ + the struct initializer expression", + field.ident, + self.tcx.item_name(def_id), + ), + sugg, + Applicability::MachineApplicable, + ); + break; + } + } + prev = Some(field.span); + } + } + let mut not_publicly_reexported = false; if let Some((this_res, outer_ident)) = outermost_res { let import_suggestions = self.lookup_import_candidates( diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs index 092bb6fc4f98..9efcef695b7b 100644 --- a/compiler/rustc_resolve/src/ident.rs +++ b/compiler/rustc_resolve/src/ident.rs @@ -1029,6 +1029,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { binding, dedup_span: path_span, outermost_res: None, + source: None, parent_scope: *parent_scope, single_nested: path_span != root_span, }); @@ -1435,7 +1436,16 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { parent_scope: &ParentScope<'ra>, ignore_import: Option>, ) -> PathResult<'ra> { - self.resolve_path_with_ribs(path, opt_ns, parent_scope, None, None, None, ignore_import) + self.resolve_path_with_ribs( + path, + opt_ns, + parent_scope, + None, + None, + None, + None, + ignore_import, + ) } #[instrument(level = "debug", skip(self))] pub(crate) fn resolve_path<'r>( @@ -1451,6 +1461,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { path, opt_ns, parent_scope, + None, finalize, None, ignore_binding, @@ -1463,6 +1474,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { path: &[Segment], opt_ns: Option, // `None` indicates a module path in import parent_scope: &ParentScope<'ra>, + source: Option>, finalize: Option, ribs: Option<&PerNS>>>, ignore_binding: Option>, @@ -1645,6 +1657,11 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { if finalize.is_some() { for error in &mut self.get_mut().privacy_errors[privacy_errors_len..] { error.outermost_res = Some((res, ident)); + error.source = match source { + Some(PathSource::Struct(Some(expr))) + | Some(PathSource::Expr(Some(expr))) => Some(expr.clone()), + _ => None, + }; } } diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 8f0c08c3998b..953b72fd72b8 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -424,7 +424,7 @@ pub(crate) enum PathSource<'a, 'ast, 'ra> { /// Paths in path patterns `Path`. Pat, /// Paths in struct expressions and patterns `Path { .. }`. - Struct, + Struct(Option<&'a Expr>), /// Paths in tuple struct patterns `Path(..)`. TupleStruct(Span, &'ra [Span]), /// `m::A::B` in `::B::C`. @@ -447,7 +447,7 @@ impl PathSource<'_, '_, '_> { match self { PathSource::Type | PathSource::Trait(_) - | PathSource::Struct + | PathSource::Struct(_) | PathSource::DefineOpaques => TypeNS, PathSource::Expr(..) | PathSource::Pat @@ -464,7 +464,7 @@ impl PathSource<'_, '_, '_> { PathSource::Type | PathSource::Expr(..) | PathSource::Pat - | PathSource::Struct + | PathSource::Struct(_) | PathSource::TupleStruct(..) | PathSource::ReturnTypeNotation => true, PathSource::Trait(_) @@ -481,7 +481,7 @@ impl PathSource<'_, '_, '_> { PathSource::Type => "type", PathSource::Trait(_) => "trait", PathSource::Pat => "unit struct, unit variant or constant", - PathSource::Struct => "struct, variant or union type", + PathSource::Struct(_) => "struct, variant or union type", PathSource::TraitItem(ValueNS, PathSource::TupleStruct(..)) | PathSource::TupleStruct(..) => "tuple struct or tuple variant", PathSource::TraitItem(ns, _) => match ns { @@ -576,7 +576,7 @@ impl PathSource<'_, '_, '_> { || matches!(res, Res::Def(DefKind::Const | DefKind::AssocConst, _)) } PathSource::TupleStruct(..) => res.expected_in_tuple_struct_pat(), - PathSource::Struct => matches!( + PathSource::Struct(_) => matches!( res, Res::Def( DefKind::Struct @@ -616,8 +616,8 @@ impl PathSource<'_, '_, '_> { (PathSource::Trait(_), false) => E0405, (PathSource::Type | PathSource::DefineOpaques, true) => E0573, (PathSource::Type | PathSource::DefineOpaques, false) => E0412, - (PathSource::Struct, true) => E0574, - (PathSource::Struct, false) => E0422, + (PathSource::Struct(_), true) => E0574, + (PathSource::Struct(_), false) => E0422, (PathSource::Expr(..), true) | (PathSource::Delegation, true) => E0423, (PathSource::Expr(..), false) | (PathSource::Delegation, false) => E0425, (PathSource::Pat | PathSource::TupleStruct(..), true) => E0532, @@ -1482,11 +1482,13 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { path: &[Segment], opt_ns: Option, // `None` indicates a module path in import finalize: Option, + source: PathSource<'_, 'ast, 'ra>, ) -> PathResult<'ra> { self.r.cm().resolve_path_with_ribs( path, opt_ns, &self.parent_scope, + Some(source), finalize, Some(&self.ribs), None, @@ -1966,7 +1968,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { &mut self, partial_res: PartialRes, path: &[Segment], - source: PathSource<'_, '_, '_>, + source: PathSource<'_, 'ast, 'ra>, path_span: Span, ) { let proj_start = path.len() - partial_res.unresolved_segments(); @@ -2019,7 +2021,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { | PathSource::ReturnTypeNotation => false, PathSource::Expr(..) | PathSource::Pat - | PathSource::Struct + | PathSource::Struct(_) | PathSource::TupleStruct(..) | PathSource::DefineOpaques | PathSource::Delegation => true, @@ -3866,7 +3868,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { self.smart_resolve_path(pat.id, qself, path, PathSource::Pat); } PatKind::Struct(ref qself, ref path, ref _fields, ref rest) => { - self.smart_resolve_path(pat.id, qself, path, PathSource::Struct); + self.smart_resolve_path(pat.id, qself, path, PathSource::Struct(None)); self.record_patterns_with_skipped_bindings(pat, rest); } PatKind::Or(ref ps) => { @@ -4110,7 +4112,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { id: NodeId, qself: &Option>, path: &Path, - source: PathSource<'_, 'ast, '_>, + source: PathSource<'_, 'ast, 'ra>, ) { self.smart_resolve_path_fragment( qself, @@ -4127,7 +4129,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { &mut self, qself: &Option>, path: &[Segment], - source: PathSource<'_, 'ast, '_>, + source: PathSource<'_, 'ast, 'ra>, finalize: Finalize, record_partial_res: RecordPartialRes, parent_qself: Option<&QSelf>, @@ -4365,7 +4367,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { std_path.push(Segment::from_ident(Ident::with_dummy_span(sym::std))); std_path.extend(path); if let PathResult::Module(_) | PathResult::NonModule(_) = - self.resolve_path(&std_path, Some(ns), None) + self.resolve_path(&std_path, Some(ns), None, source) { // Check if we wrote `str::from_utf8` instead of `std::str::from_utf8` let item_span = @@ -4439,7 +4441,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { span: Span, defer_to_typeck: bool, finalize: Finalize, - source: PathSource<'_, 'ast, '_>, + source: PathSource<'_, 'ast, 'ra>, ) -> Result, Spanned>> { let mut fin_res = None; @@ -4488,7 +4490,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { path: &[Segment], ns: Namespace, finalize: Finalize, - source: PathSource<'_, 'ast, '_>, + source: PathSource<'_, 'ast, 'ra>, ) -> Result, Spanned>> { debug!( "resolve_qpath(qself={:?}, path={:?}, ns={:?}, finalize={:?})", @@ -4551,7 +4553,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { ))); } - let result = match self.resolve_path(path, Some(ns), Some(finalize)) { + let result = match self.resolve_path(path, Some(ns), Some(finalize), source) { PathResult::NonModule(path_res) => path_res, PathResult::Module(ModuleOrUniformRoot::Module(module)) if !module.is_normal() => { PartialRes::new(module.res().unwrap()) @@ -4774,7 +4776,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { } ExprKind::Struct(ref se) => { - self.smart_resolve_path(expr.id, &se.qself, &se.path, PathSource::Struct); + self.smart_resolve_path(expr.id, &se.qself, &se.path, PathSource::Struct(parent)); // This is the same as `visit::walk_expr(self, expr);`, but we want to pass the // parent in for accurate suggestions when encountering `Foo { bar }` that should // have been `Foo { bar: self.bar }`. diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index 0807142a7c35..aca251da71d3 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -174,7 +174,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { &mut self, path: &[Segment], span: Span, - source: PathSource<'_, '_, '_>, + source: PathSource<'_, 'ast, 'ra>, res: Option, ) -> BaseError { // Make the base error. @@ -318,7 +318,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { (String::new(), "the crate root".to_string(), Some(CRATE_DEF_ID.to_def_id()), None) } else { let mod_path = &path[..path.len() - 1]; - let mod_res = self.resolve_path(mod_path, Some(TypeNS), None); + let mod_res = self.resolve_path(mod_path, Some(TypeNS), None, source); let mod_prefix = match mod_res { PathResult::Module(ModuleOrUniformRoot::Module(module)) => module.res(), _ => None, @@ -419,7 +419,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { path: &[Segment], following_seg: Option<&Segment>, span: Span, - source: PathSource<'_, '_, '_>, + source: PathSource<'_, 'ast, 'ra>, res: Option, qself: Option<&QSelf>, ) -> (Diag<'tcx>, Vec) { @@ -1014,7 +1014,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { fn suggest_typo( &mut self, err: &mut Diag<'_>, - source: PathSource<'_, '_, '_>, + source: PathSource<'_, 'ast, 'ra>, path: &[Segment], following_seg: Option<&Segment>, span: Span, @@ -1333,7 +1333,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { fn suggest_swapping_misplaced_self_ty_and_trait( &mut self, err: &mut Diag<'_>, - source: PathSource<'_, '_, '_>, + source: PathSource<'_, 'ast, 'ra>, res: Option, span: Span, ) { @@ -1341,7 +1341,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { self.diag_metadata.currently_processing_impl_trait.clone() && let TyKind::Path(_, self_ty_path) = &self_ty.kind && let PathResult::Module(ModuleOrUniformRoot::Module(module)) = - self.resolve_path(&Segment::from_path(self_ty_path), Some(TypeNS), None) + self.resolve_path(&Segment::from_path(self_ty_path), Some(TypeNS), None, source) && let ModuleKind::Def(DefKind::Trait, ..) = module.kind && trait_ref.path.span == span && let PathSource::Trait(_) = source @@ -1449,13 +1449,13 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { fn get_single_associated_item( &mut self, path: &[Segment], - source: &PathSource<'_, '_, '_>, + source: &PathSource<'_, 'ast, 'ra>, filter_fn: &impl Fn(Res) -> bool, ) -> Option { if let crate::PathSource::TraitItem(_, _) = source { let mod_path = &path[..path.len() - 1]; if let PathResult::Module(ModuleOrUniformRoot::Module(module)) = - self.resolve_path(mod_path, None, None) + self.resolve_path(mod_path, None, None, *source) { let targets: Vec<_> = self .r @@ -1854,7 +1854,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { PathSource::Expr(Some(Expr { kind: ExprKind::Index(..) | ExprKind::Call(..), .. })) - | PathSource::Struct, + | PathSource::Struct(_), ) => { // Don't suggest macro if it's unstable. let suggestable = def_id.is_local() @@ -2502,7 +2502,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { // Search in module. let mod_path = &path[..path.len() - 1]; if let PathResult::Module(ModuleOrUniformRoot::Module(module)) = - self.resolve_path(mod_path, Some(TypeNS), None) + self.resolve_path(mod_path, Some(TypeNS), None, PathSource::Type) { self.r.add_module_candidates(module, &mut names, &filter_fn, None); } @@ -3673,7 +3673,12 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { if let TyKind::Path(None, path) = &ty.kind { // Check if the path being borrowed is likely to be owned. let path: Vec<_> = Segment::from_path(path); - match self.resolve_path(&path, Some(TypeNS), None) { + match self.resolve_path( + &path, + Some(TypeNS), + None, + PathSource::Type, + ) { PathResult::Module(ModuleOrUniformRoot::Module(module)) => { match module.res() { Some(Res::PrimTy(PrimTy::Str)) => { diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 23f44ff16583..b43f71913d9b 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -822,6 +822,7 @@ struct PrivacyError<'ra> { parent_scope: ParentScope<'ra>, /// Is the format `use a::{b,c}`? single_nested: bool, + source: Option, } #[derive(Debug)] @@ -1064,6 +1065,7 @@ pub struct Resolver<'ra, 'tcx> { /// N.B., this is used only for better diagnostics, not name resolution itself. field_names: LocalDefIdMap>, + field_defaults: LocalDefIdMap>, /// Span of the privacy modifier in fields of an item `DefId` accessible with dot syntax. /// Used for hints during error reporting. @@ -1538,6 +1540,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { extern_prelude, field_names: Default::default(), + field_defaults: Default::default(), field_visibility_spans: FxHashMap::default(), determined_imports: Vec::new(), @@ -2318,6 +2321,21 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } } + fn field_defaults(&self, def_id: DefId) -> Option> { + match def_id.as_local() { + Some(def_id) => self.field_defaults.get(&def_id).cloned(), + None => Some( + self.tcx + .associated_item_def_ids(def_id) + .iter() + .filter_map(|&def_id| { + self.tcx.default_field(def_id).map(|_| self.tcx.item_name(def_id)) + }) + .collect(), + ), + } + } + /// Checks if an expression refers to a function marked with /// `#[rustc_legacy_const_generics]` and returns the argument index list /// from the attribute. diff --git a/tests/ui/structs/default-field-values/auxiliary/struct_field_default.rs b/tests/ui/structs/default-field-values/auxiliary/struct_field_default.rs index b315df5dba28..a1c9645a1ae2 100644 --- a/tests/ui/structs/default-field-values/auxiliary/struct_field_default.rs +++ b/tests/ui/structs/default-field-values/auxiliary/struct_field_default.rs @@ -3,3 +3,13 @@ pub struct A { pub a: isize = 42, } + +struct Priv; + +pub struct B { + pub a: Priv = Priv, +} + +pub struct C { + pub a: Priv, +} diff --git a/tests/ui/structs/default-field-values/non-exhaustive-ctor-2.rs b/tests/ui/structs/default-field-values/non-exhaustive-ctor-2.rs new file mode 100644 index 000000000000..047505c3f1ca --- /dev/null +++ b/tests/ui/structs/default-field-values/non-exhaustive-ctor-2.rs @@ -0,0 +1,29 @@ +//@ aux-build:struct_field_default.rs +#![feature(default_field_values)] + +extern crate struct_field_default as xc; + +use m::S; + +mod m { + pub struct S { + pub field: () = (), + pub field1: Priv1 = Priv1 {}, + pub field2: Priv2 = Priv2, + } + struct Priv1 {} + struct Priv2; +} + +fn main() { + let _ = S { field: (), field1: m::Priv1 {} }; + //~^ ERROR missing field `field2` + //~| ERROR struct `Priv1` is private + let _ = S { field: (), field1: m::Priv1 {}, field2: m::Priv2 }; + //~^ ERROR struct `Priv1` is private + //~| ERROR unit struct `Priv2` is private + let _ = xc::B { a: xc::Priv }; + //~^ ERROR unit struct `Priv` is private + let _ = xc::C { a: xc::Priv }; + //~^ ERROR unit struct `Priv` is private +} diff --git a/tests/ui/structs/default-field-values/non-exhaustive-ctor-2.stderr b/tests/ui/structs/default-field-values/non-exhaustive-ctor-2.stderr new file mode 100644 index 000000000000..66de73561db2 --- /dev/null +++ b/tests/ui/structs/default-field-values/non-exhaustive-ctor-2.stderr @@ -0,0 +1,105 @@ +error[E0603]: struct `Priv1` is private + --> $DIR/non-exhaustive-ctor-2.rs:19:39 + | +LL | let _ = S { field: (), field1: m::Priv1 {} }; + | ------ ^^^^^ private struct + | | + | while setting this field + | +note: the struct `Priv1` is defined here + --> $DIR/non-exhaustive-ctor-2.rs:14:4 + | +LL | struct Priv1 {} + | ^^^^^^^^^^^^ +help: the type `Priv1` of field `field1` is private, but you can construct the default value defined for it in `S` using `..` in the struct initializer expression + | +LL - let _ = S { field: (), field1: m::Priv1 {} }; +LL + let _ = S { field: (), .. }; + | + +error[E0603]: struct `Priv1` is private + --> $DIR/non-exhaustive-ctor-2.rs:22:39 + | +LL | let _ = S { field: (), field1: m::Priv1 {}, field2: m::Priv2 }; + | ------ ^^^^^ private struct + | | + | while setting this field + | +note: the struct `Priv1` is defined here + --> $DIR/non-exhaustive-ctor-2.rs:14:4 + | +LL | struct Priv1 {} + | ^^^^^^^^^^^^ +help: the type `Priv1` of field `field1` is private, but you can construct the default value defined for it in `S` using `..` in the struct initializer expression + | +LL - let _ = S { field: (), field1: m::Priv1 {}, field2: m::Priv2 }; +LL + let _ = S { field: (), field2: m::Priv2, .. }; + | + +error[E0603]: unit struct `Priv2` is private + --> $DIR/non-exhaustive-ctor-2.rs:22:60 + | +LL | let _ = S { field: (), field1: m::Priv1 {}, field2: m::Priv2 }; + | ------ ^^^^^ private unit struct + | | + | while setting this field + | +note: the unit struct `Priv2` is defined here + --> $DIR/non-exhaustive-ctor-2.rs:15:4 + | +LL | struct Priv2; + | ^^^^^^^^^^^^^ +help: the type `Priv2` of field `field2` is private, but you can construct the default value defined for it in `S` using `..` in the struct initializer expression + | +LL - let _ = S { field: (), field1: m::Priv1 {}, field2: m::Priv2 }; +LL + let _ = S { field: (), field1: m::Priv1 {}, .. }; + | + +error[E0603]: unit struct `Priv` is private + --> $DIR/non-exhaustive-ctor-2.rs:25:28 + | +LL | let _ = xc::B { a: xc::Priv }; + | - ^^^^ private unit struct + | | + | while setting this field + | +note: the unit struct `Priv` is defined here + --> $DIR/auxiliary/struct_field_default.rs:7:1 + | +LL | struct Priv; + | ^^^^^^^^^^^ +help: the type `Priv` of field `a` is private, but you can construct the default value defined for it in `B` using `..` in the struct initializer expression + | +LL - let _ = xc::B { a: xc::Priv }; +LL + let _ = xc::B { .. }; + | + +error[E0603]: unit struct `Priv` is private + --> $DIR/non-exhaustive-ctor-2.rs:27:28 + | +LL | let _ = xc::C { a: xc::Priv }; + | - ^^^^ private unit struct + | | + | while setting this field + | +note: the unit struct `Priv` is defined here + --> $DIR/auxiliary/struct_field_default.rs:7:1 + | +LL | struct Priv; + | ^^^^^^^^^^^ + +error[E0063]: missing field `field2` in initializer of `S` + --> $DIR/non-exhaustive-ctor-2.rs:19:13 + | +LL | let _ = S { field: (), field1: m::Priv1 {} }; + | ^ missing `field2` + | +help: all remaining fields have default values, you can use those values with `..` + | +LL | let _ = S { field: (), field1: m::Priv1 {}, .. }; + | ++++ + +error: aborting due to 6 previous errors + +Some errors have detailed explanations: E0063, E0603. +For more information about an error, try `rustc --explain E0063`. From 29d26f27a6d6c466ed143bdfa128f76a19226743 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Fri, 27 Jun 2025 22:38:29 +0000 Subject: [PATCH 139/195] review comments --- compiler/rustc_middle/src/query/mod.rs | 3 +- compiler/rustc_resolve/src/diagnostics.rs | 128 ++++++++++++++-------- 2 files changed, 81 insertions(+), 50 deletions(-) diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index f1f4f1b54655..f4d0120a2e7b 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -1864,11 +1864,10 @@ rustc_queries! { feedable } - /// Returns whether the impl or associated function has the `default` keyword. + /// Returns whether the field corresponding to the `DefId` has a default field value. query default_field(def_id: DefId) -> Option { desc { |tcx| "looking up the `const` corresponding to the default for `{}`", tcx.def_path_str(def_id) } separate_provide_extern - feedable } query check_well_formed(key: LocalDefId) -> Result<(), ErrorGuaranteed> { diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index 40ea90bf4847..210ab72678c5 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -1967,54 +1967,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { let mut err = self.dcx().create_err(errors::IsPrivate { span: ident.span, ident_descr, ident }); - if let Some(expr) = source - && let ast::ExprKind::Struct(struct_expr) = &expr.kind - && let Some(Res::Def(_, def_id)) = self.partial_res_map - [&struct_expr.path.segments.iter().last().unwrap().id] - .full_res() - && let Some(default_fields) = self.field_defaults(def_id) - && !struct_expr.fields.is_empty() - { - let last_span = struct_expr.fields.iter().last().unwrap().span; - let mut iter = struct_expr.fields.iter().peekable(); - let mut prev: Option = None; - while let Some(field) = iter.next() { - if field.expr.span.overlaps(ident.span) { - err.span_label(field.ident.span, "while setting this field"); - if default_fields.contains(&field.ident.name) { - let sugg = if last_span == field.span { - vec![(field.span, "..".to_string())] - } else { - vec![ - ( - // Account for trailing commas and ensure we remove them. - match (prev, iter.peek()) { - (_, Some(next)) => field.span.with_hi(next.span.lo()), - (Some(prev), _) => field.span.with_lo(prev.hi()), - (None, None) => field.span, - }, - String::new(), - ), - (last_span.shrink_to_hi(), ", ..".to_string()), - ] - }; - err.multipart_suggestion_verbose( - format!( - "the type `{ident}` of field `{}` is private, but you can \ - construct the default value defined for it in `{}` using `..` in \ - the struct initializer expression", - field.ident, - self.tcx.item_name(def_id), - ), - sugg, - Applicability::MachineApplicable, - ); - break; - } - } - prev = Some(field.span); - } - } + self.mention_default_field_values(source, ident, &mut err); let mut not_publicly_reexported = false; if let Some((this_res, outer_ident)) = outermost_res { @@ -2197,6 +2150,85 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { err.emit(); } + /// When a private field is being set that has a default field value, we suggest using `..` and + /// setting the value of that field implicitly with its default. + /// + /// If we encounter code like + /// ```text + /// struct Priv; + /// pub struct S { + /// pub field: Priv = Priv, + /// } + /// ``` + /// which is used from a place where `Priv` isn't accessible + /// ```text + /// let _ = S { field: m::Priv1 {} }; + /// // ^^^^^ private struct + /// ``` + /// we will suggest instead using the `default_field_values` syntax instead: + /// ```text + /// let _ = S { .. }; + /// ``` + fn mention_default_field_values( + &self, + source: &Option, + ident: Ident, + err: &mut Diag<'_>, + ) { + let Some(expr) = source else { return }; + let ast::ExprKind::Struct(struct_expr) = &expr.kind else { return }; + // We don't have to handle type-relative paths because they're forbidden in ADT + // expressions, but that would change with `#[feature(more_qualified_paths)]`. + let Some(Res::Def(_, def_id)) = + self.partial_res_map[&struct_expr.path.segments.iter().last().unwrap().id].full_res() + else { + return; + }; + let Some(default_fields) = self.field_defaults(def_id) else { return }; + if struct_expr.fields.is_empty() { + return; + } + let last_span = struct_expr.fields.iter().last().unwrap().span; + let mut iter = struct_expr.fields.iter().peekable(); + let mut prev: Option = None; + while let Some(field) = iter.next() { + if field.expr.span.overlaps(ident.span) { + err.span_label(field.ident.span, "while setting this field"); + if default_fields.contains(&field.ident.name) { + let sugg = if last_span == field.span { + vec![(field.span, "..".to_string())] + } else { + vec![ + ( + // Account for trailing commas and ensure we remove them. + match (prev, iter.peek()) { + (_, Some(next)) => field.span.with_hi(next.span.lo()), + (Some(prev), _) => field.span.with_lo(prev.hi()), + (None, None) => field.span, + }, + String::new(), + ), + (last_span.shrink_to_hi(), ", ..".to_string()), + ] + }; + err.multipart_suggestion_verbose( + format!( + "the type `{ident}` of field `{}` is private, but you can construct \ + the default value defined for it in `{}` using `..` in the struct \ + initializer expression", + field.ident, + self.tcx.item_name(def_id), + ), + sugg, + Applicability::MachineApplicable, + ); + break; + } + } + prev = Some(field.span); + } + } + pub(crate) fn find_similarly_named_module_or_crate( &self, ident: Symbol, From a71b024c893abb51335f55023fbfe43787ac9857 Mon Sep 17 00:00:00 2001 From: Daniel Paoliello Date: Sun, 10 Aug 2025 12:22:13 -0700 Subject: [PATCH 140/195] Fix typo with paren rustc_llvm/build.rs --- compiler/rustc_llvm/build.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_llvm/build.rs b/compiler/rustc_llvm/build.rs index 6c740156c4d1..1394edcee6b3 100644 --- a/compiler/rustc_llvm/build.rs +++ b/compiler/rustc_llvm/build.rs @@ -255,7 +255,7 @@ fn main() { println!("cargo:rustc-link-lib=kstat"); } - if (target.starts_with("arm") && !target.contains("freebsd")) && !target.contains("ohos") + if (target.starts_with("arm") && !target.contains("freebsd") && !target.contains("ohos")) || target.starts_with("mips-") || target.starts_with("mipsel-") || target.starts_with("powerpc-") From 1aa5668d20d53976860d141a66aa727e425e1079 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Mon, 28 Jul 2025 02:01:46 +0000 Subject: [PATCH 141/195] Point at the `Fn()` or `FnMut()` bound that coerced a closure, which caused a move error When encountering a move error involving a closure because the captured value isn't `Copy`, and the obligation comes from a bound on a type parameter that requires `Fn` or `FnMut`, we point at it and explain that an `FnOnce` wouldn't cause the move error. ``` error[E0507]: cannot move out of `foo`, a captured variable in an `Fn` closure --> f111.rs:15:25 | 14 | fn do_stuff(foo: Option) { | --- ----------- move occurs because `foo` has type `Option`, which does not implement the `Copy` trait | | | captured outer variable 15 | require_fn_trait(|| async { | -- ^^^^^ `foo` is moved here | | | captured by this `Fn` closure 16 | if foo.map_or(false, |f| f.foo()) { | --- variable moved due to use in coroutine | help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once --> f111.rs:12:53 | 12 | fn require_fn_trait>(_: impl Fn() -> F) {} | ^^^^^^^^^ help: consider cloning the value if the performance cost is acceptable | 16 | if foo.clone().map_or(false, |f| f.foo()) { | ++++++++ ``` --- .../src/diagnostics/move_errors.rs | 40 ++++- compiler/rustc_errors/src/diagnostic.rs | 7 +- tests/ui/borrowck/borrowck-in-static.stderr | 1 + .../borrowck/borrowck-move-by-capture.stderr | 5 + tests/ui/borrowck/issue-103624.stderr | 5 + .../issue-87456-point-to-closure.stderr | 5 + ...ove-upvar-from-non-once-ref-closure.stderr | 5 + tests/ui/issues/issue-4335.stderr | 1 + ...-move-out-of-closure-env-issue-1965.stderr | 5 + ...e-52663-span-decl-captured-variable.stderr | 5 + ...borrowck-call-is-borrow-issue-12224.stderr | 1 + .../dont-suggest-ref/move-into-closure.rs | 21 +++ .../dont-suggest-ref/move-into-closure.stderr | 147 +++++++++++++++--- .../suggestions/option-content-move2.stderr | 11 ++ .../suggestions/option-content-move3.stderr | 12 ++ .../unboxed-closure-illegal-move.stderr | 22 +++ 16 files changed, 268 insertions(+), 25 deletions(-) diff --git a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs index 1067f1e40ef6..b24cb012d2b8 100644 --- a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs @@ -9,7 +9,7 @@ use rustc_middle::bug; use rustc_middle::mir::*; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_mir_dataflow::move_paths::{LookupResult, MovePathIndex}; -use rustc_span::{BytePos, ExpnKind, MacroKind, Span}; +use rustc_span::{BytePos, DUMMY_SP, ExpnKind, MacroKind, Span}; use rustc_trait_selection::error_reporting::traits::FindExprBySpan; use rustc_trait_selection::infer::InferCtxtExt; use tracing::debug; @@ -507,12 +507,50 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { ); let closure_span = tcx.def_span(def_id); + let mut clause_span = DUMMY_SP; + let typck_result = self.infcx.tcx.typeck(self.mir_def_id()); + if let Some(closure_def_id) = def_id.as_local() + && let hir::Node::Expr(expr) = tcx.hir_node_by_def_id(closure_def_id) + && let hir::Node::Expr(parent) = tcx.parent_hir_node(expr.hir_id) + && let hir::ExprKind::Call(callee, _) = parent.kind + && let Some(ty) = typck_result.node_type_opt(callee.hir_id) + && let ty::FnDef(fn_def_id, args) = ty.kind() + && let predicates = tcx.predicates_of(fn_def_id).instantiate(tcx, args) + && let Some((_, span)) = + predicates.predicates.iter().zip(predicates.spans.iter()).find( + |(pred, _)| match pred.as_trait_clause() { + Some(clause) + if let ty::Closure(clause_closure_def_id, _) = + clause.self_ty().skip_binder().kind() + && clause_closure_def_id == def_id + && (tcx.lang_items().fn_mut_trait() + == Some(clause.def_id()) + || tcx.lang_items().fn_trait() + == Some(clause.def_id())) => + { + // Found `` + true + } + _ => false, + }, + ) + { + // We point at the `Fn()` or `FnMut()` bound that coerced the closure, which + // could be changed to `FnOnce()` to avoid the move error. + clause_span = *span; + } + self.cannot_move_out_of(span, &place_description) .with_span_label(upvar_span, "captured outer variable") .with_span_label( closure_span, format!("captured by this `{closure_kind}` closure"), ) + .with_span_help( + clause_span, + "`Fn` and `FnMut` closures require captured values to be able to be \ + consumed multiple times, but an `FnOnce` consume them only once", + ) } _ => { let source = self.borrowed_content_source(deref_base); diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs index 5a5563c7bb2c..98be37fd84b5 100644 --- a/compiler/rustc_errors/src/diagnostic.rs +++ b/compiler/rustc_errors/src/diagnostic.rs @@ -847,17 +847,18 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { self } + with_fn! { with_span_help, /// Prints the span with some help above it. /// This is like [`Diag::help()`], but it gets its own span. #[rustc_lint_diagnostics] - pub fn span_help>( + pub fn span_help( &mut self, - sp: S, + sp: impl Into, msg: impl Into, ) -> &mut Self { self.sub(Level::Help, msg, sp.into()); self - } + } } /// Disallow attaching suggestions to this diagnostic. /// Any suggestions attached e.g. with the `span_suggestion_*` methods diff --git a/tests/ui/borrowck/borrowck-in-static.stderr b/tests/ui/borrowck/borrowck-in-static.stderr index 9bcf64dd62e2..d85f6f5fdd5c 100644 --- a/tests/ui/borrowck/borrowck-in-static.stderr +++ b/tests/ui/borrowck/borrowck-in-static.stderr @@ -10,6 +10,7 @@ LL | Box::new(|| x) | | | captured by this `Fn` closure | + = help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once help: consider cloning the value if the performance cost is acceptable | LL | Box::new(|| x.clone()) diff --git a/tests/ui/borrowck/borrowck-move-by-capture.stderr b/tests/ui/borrowck/borrowck-move-by-capture.stderr index 732af1593d60..e9e054407662 100644 --- a/tests/ui/borrowck/borrowck-move-by-capture.stderr +++ b/tests/ui/borrowck/borrowck-move-by-capture.stderr @@ -12,6 +12,11 @@ LL | let _h = to_fn_once(move || -> isize { *bar }); | | | `bar` is moved here | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/borrowck-move-by-capture.rs:3:37 + | +LL | fn to_fn_mut>(f: F) -> F { f } + | ^^^^^^^^ help: consider cloning the value before moving it into the closure | LL ~ let value = bar.clone(); diff --git a/tests/ui/borrowck/issue-103624.stderr b/tests/ui/borrowck/issue-103624.stderr index af65deb16dcf..ef0228088867 100644 --- a/tests/ui/borrowck/issue-103624.stderr +++ b/tests/ui/borrowck/issue-103624.stderr @@ -13,6 +13,11 @@ LL | LL | self.b; | ^^^^^^ `self.b` is moved here | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/issue-103624.rs:7:36 + | +LL | async fn spawn_blocking(f: impl (Fn() -> T) + Send + Sync + 'static) -> T { + | ^^^^^^^^^^^ note: if `StructB` implemented `Clone`, you could clone the value --> $DIR/issue-103624.rs:23:1 | diff --git a/tests/ui/borrowck/issue-87456-point-to-closure.stderr b/tests/ui/borrowck/issue-87456-point-to-closure.stderr index a0c7cac2addd..043e336cd86d 100644 --- a/tests/ui/borrowck/issue-87456-point-to-closure.stderr +++ b/tests/ui/borrowck/issue-87456-point-to-closure.stderr @@ -10,6 +10,11 @@ LL | LL | let _foo: String = val; | ^^^ move occurs because `val` has type `String`, which does not implement the `Copy` trait | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/issue-87456-point-to-closure.rs:3:24 + | +LL | fn take_mut(_val: impl FnMut()) {} + | ^^^^^^^ help: consider borrowing here | LL | let _foo: String = &val; diff --git a/tests/ui/borrowck/unboxed-closures-move-upvar-from-non-once-ref-closure.stderr b/tests/ui/borrowck/unboxed-closures-move-upvar-from-non-once-ref-closure.stderr index 177e9c8d2487..d33330413103 100644 --- a/tests/ui/borrowck/unboxed-closures-move-upvar-from-non-once-ref-closure.stderr +++ b/tests/ui/borrowck/unboxed-closures-move-upvar-from-non-once-ref-closure.stderr @@ -10,6 +10,11 @@ LL | y.into_iter(); | | | move occurs because `y` has type `Vec`, which does not implement the `Copy` trait | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/unboxed-closures-move-upvar-from-non-once-ref-closure.rs:5:28 + | +LL | fn call(f: F) where F : Fn() { + | ^^^^ note: `into_iter` takes ownership of the receiver `self`, which moves `y` --> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL help: you can `clone` the value and consume it, but this might not be your desired behavior diff --git a/tests/ui/issues/issue-4335.stderr b/tests/ui/issues/issue-4335.stderr index 42ac63225640..b6d8f0861638 100644 --- a/tests/ui/issues/issue-4335.stderr +++ b/tests/ui/issues/issue-4335.stderr @@ -10,6 +10,7 @@ LL | id(Box::new(|| *v)) | | | captured by this `FnMut` closure | + = help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once help: if `T` implemented `Clone`, you could clone the value --> $DIR/issue-4335.rs:5:10 | diff --git a/tests/ui/moves/moves-based-on-type-move-out-of-closure-env-issue-1965.stderr b/tests/ui/moves/moves-based-on-type-move-out-of-closure-env-issue-1965.stderr index 51d0f85c031f..dfc983bf4874 100644 --- a/tests/ui/moves/moves-based-on-type-move-out-of-closure-env-issue-1965.stderr +++ b/tests/ui/moves/moves-based-on-type-move-out-of-closure-env-issue-1965.stderr @@ -10,6 +10,11 @@ LL | let _f = to_fn(|| test(i)); | | | captured by this `Fn` closure | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/moves-based-on-type-move-out-of-closure-env-issue-1965.rs:3:33 + | +LL | fn to_fn>(f: F) -> F { f } + | ^^^^^ help: consider cloning the value if the performance cost is acceptable | LL | let _f = to_fn(|| test(i.clone())); diff --git a/tests/ui/nll/issue-52663-span-decl-captured-variable.stderr b/tests/ui/nll/issue-52663-span-decl-captured-variable.stderr index 575460370065..7f9a8e50dae6 100644 --- a/tests/ui/nll/issue-52663-span-decl-captured-variable.stderr +++ b/tests/ui/nll/issue-52663-span-decl-captured-variable.stderr @@ -10,6 +10,11 @@ LL | expect_fn(|| drop(x.0)); | | | captured by this `Fn` closure | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/issue-52663-span-decl-captured-variable.rs:1:33 + | +LL | fn expect_fn(f: F) where F : Fn() { + | ^^^^ help: consider cloning the value if the performance cost is acceptable | LL | expect_fn(|| drop(x.0.clone())); diff --git a/tests/ui/span/borrowck-call-is-borrow-issue-12224.stderr b/tests/ui/span/borrowck-call-is-borrow-issue-12224.stderr index f37dc320fa31..8081f7b3a8b0 100644 --- a/tests/ui/span/borrowck-call-is-borrow-issue-12224.stderr +++ b/tests/ui/span/borrowck-call-is-borrow-issue-12224.stderr @@ -44,6 +44,7 @@ LL | LL | foo(f); | ^ move occurs because `f` has type `{closure@$DIR/borrowck-call-is-borrow-issue-12224.rs:52:17: 52:58}`, which does not implement the `Copy` trait | + = help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once help: consider cloning the value if the performance cost is acceptable | LL | foo(f.clone()); diff --git a/tests/ui/suggestions/dont-suggest-ref/move-into-closure.rs b/tests/ui/suggestions/dont-suggest-ref/move-into-closure.rs index 44eac3691a3b..0154810f5643 100644 --- a/tests/ui/suggestions/dont-suggest-ref/move-into-closure.rs +++ b/tests/ui/suggestions/dont-suggest-ref/move-into-closure.rs @@ -11,8 +11,29 @@ struct X(Y); struct Y; fn consume_fn(_f: F) { } +//~^ HELP `Fn` and `FnMut` closures +//~| HELP `Fn` and `FnMut` closures +//~| HELP `Fn` and `FnMut` closures +//~| HELP `Fn` and `FnMut` closures +//~| HELP `Fn` and `FnMut` closures +//~| HELP `Fn` and `FnMut` closures +//~| HELP `Fn` and `FnMut` closures +//~| HELP `Fn` and `FnMut` closures +//~| HELP `Fn` and `FnMut` closures +//~| HELP `Fn` and `FnMut` closures fn consume_fnmut(_f: F) { } +//~^ HELP `Fn` and `FnMut` closures +//~| HELP `Fn` and `FnMut` closures +//~| HELP `Fn` and `FnMut` closures +//~| HELP `Fn` and `FnMut` closures +//~| HELP `Fn` and `FnMut` closures +//~| HELP `Fn` and `FnMut` closures +//~| HELP `Fn` and `FnMut` closures +//~| HELP `Fn` and `FnMut` closures +//~| HELP `Fn` and `FnMut` closures +//~| HELP `Fn` and `FnMut` closures +//~| HELP `Fn` and `FnMut` closures pub fn main() { } diff --git a/tests/ui/suggestions/dont-suggest-ref/move-into-closure.stderr b/tests/ui/suggestions/dont-suggest-ref/move-into-closure.stderr index edda2cbc735a..d9ed25983d66 100644 --- a/tests/ui/suggestions/dont-suggest-ref/move-into-closure.stderr +++ b/tests/ui/suggestions/dont-suggest-ref/move-into-closure.stderr @@ -1,5 +1,5 @@ error[E0507]: cannot move out of `x.0`, as `x` is a captured variable in an `Fn` closure - --> $DIR/move-into-closure.rs:28:21 + --> $DIR/move-into-closure.rs:49:21 | LL | let x = X(Y); | - captured outer variable @@ -12,13 +12,18 @@ LL | let X(_t) = x; | data moved here | move occurs because `_t` has type `Y`, which does not implement the `Copy` trait | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/move-into-closure.rs:13:18 + | +LL | fn consume_fn(_f: F) { } + | ^^^^ help: consider borrowing here | LL | let X(_t) = &x; | + error[E0507]: cannot move out of `e.0`, as `e` is a captured variable in an `Fn` closure - --> $DIR/move-into-closure.rs:31:34 + --> $DIR/move-into-closure.rs:52:34 | LL | let e = Either::One(X(Y)); | - captured outer variable @@ -32,13 +37,18 @@ LL | if let Either::One(_t) = e { } | data moved here | move occurs because `_t` has type `X`, which does not implement the `Copy` trait | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/move-into-closure.rs:13:18 + | +LL | fn consume_fn(_f: F) { } + | ^^^^ help: consider borrowing here | LL | if let Either::One(_t) = &e { } | + error[E0507]: cannot move out of `e.0`, as `e` is a captured variable in an `Fn` closure - --> $DIR/move-into-closure.rs:34:37 + --> $DIR/move-into-closure.rs:55:37 | LL | let e = Either::One(X(Y)); | - captured outer variable @@ -52,13 +62,18 @@ LL | while let Either::One(_t) = e { } | data moved here | move occurs because `_t` has type `X`, which does not implement the `Copy` trait | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/move-into-closure.rs:13:18 + | +LL | fn consume_fn(_f: F) { } + | ^^^^ help: consider borrowing here | LL | while let Either::One(_t) = &e { } | + error[E0507]: cannot move out of `e.0`, as `e` is a captured variable in an `Fn` closure - --> $DIR/move-into-closure.rs:37:15 + --> $DIR/move-into-closure.rs:58:15 | LL | let e = Either::One(X(Y)); | - captured outer variable @@ -75,13 +90,18 @@ LL | Either::One(_t) | data moved here | move occurs because `_t` has type `X`, which does not implement the `Copy` trait | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/move-into-closure.rs:13:18 + | +LL | fn consume_fn(_f: F) { } + | ^^^^ help: consider borrowing here | LL | match &e { | + error[E0507]: cannot move out of `e.0`, as `e` is a captured variable in an `Fn` closure - --> $DIR/move-into-closure.rs:43:15 + --> $DIR/move-into-closure.rs:64:15 | LL | let e = Either::One(X(Y)); | - captured outer variable @@ -98,13 +118,18 @@ LL | Either::One(_t) => (), | data moved here | move occurs because `_t` has type `X`, which does not implement the `Copy` trait | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/move-into-closure.rs:13:18 + | +LL | fn consume_fn(_f: F) { } + | ^^^^ help: consider borrowing here | LL | match &e { | + error[E0507]: cannot move out of `x.0`, as `x` is a captured variable in an `Fn` closure - --> $DIR/move-into-closure.rs:51:25 + --> $DIR/move-into-closure.rs:72:25 | LL | let x = X(Y); | - captured outer variable @@ -118,13 +143,18 @@ LL | let X(mut _t) = x; | data moved here | move occurs because `_t` has type `Y`, which does not implement the `Copy` trait | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/move-into-closure.rs:13:18 + | +LL | fn consume_fn(_f: F) { } + | ^^^^ help: consider borrowing here | LL | let X(mut _t) = &x; | + error[E0507]: cannot move out of `em.0`, as `em` is a captured variable in an `Fn` closure - --> $DIR/move-into-closure.rs:54:38 + --> $DIR/move-into-closure.rs:75:38 | LL | let mut em = Either::One(X(Y)); | ------ captured outer variable @@ -138,13 +168,18 @@ LL | if let Either::One(mut _t) = em { } | data moved here | move occurs because `_t` has type `X`, which does not implement the `Copy` trait | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/move-into-closure.rs:13:18 + | +LL | fn consume_fn(_f: F) { } + | ^^^^ help: consider borrowing here | LL | if let Either::One(mut _t) = &em { } | + error[E0507]: cannot move out of `em.0`, as `em` is a captured variable in an `Fn` closure - --> $DIR/move-into-closure.rs:57:41 + --> $DIR/move-into-closure.rs:78:41 | LL | let mut em = Either::One(X(Y)); | ------ captured outer variable @@ -158,13 +193,18 @@ LL | while let Either::One(mut _t) = em { } | data moved here | move occurs because `_t` has type `X`, which does not implement the `Copy` trait | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/move-into-closure.rs:13:18 + | +LL | fn consume_fn(_f: F) { } + | ^^^^ help: consider borrowing here | LL | while let Either::One(mut _t) = &em { } | + error[E0507]: cannot move out of `em.0`, as `em` is a captured variable in an `Fn` closure - --> $DIR/move-into-closure.rs:60:15 + --> $DIR/move-into-closure.rs:81:15 | LL | let mut em = Either::One(X(Y)); | ------ captured outer variable @@ -181,13 +221,18 @@ LL | Either::One(mut _t) | data moved here | move occurs because `_t` has type `X`, which does not implement the `Copy` trait | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/move-into-closure.rs:13:18 + | +LL | fn consume_fn(_f: F) { } + | ^^^^ help: consider borrowing here | LL | match &em { | + error[E0507]: cannot move out of `em.0`, as `em` is a captured variable in an `Fn` closure - --> $DIR/move-into-closure.rs:66:15 + --> $DIR/move-into-closure.rs:87:15 | LL | let mut em = Either::One(X(Y)); | ------ captured outer variable @@ -204,13 +249,18 @@ LL | Either::One(mut _t) => (), | data moved here | move occurs because `_t` has type `X`, which does not implement the `Copy` trait | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/move-into-closure.rs:13:18 + | +LL | fn consume_fn(_f: F) { } + | ^^^^ help: consider borrowing here | LL | match &em { | + error[E0507]: cannot move out of `x.0`, as `x` is a captured variable in an `FnMut` closure - --> $DIR/move-into-closure.rs:85:21 + --> $DIR/move-into-closure.rs:106:21 | LL | let x = X(Y); | - captured outer variable @@ -223,13 +273,18 @@ LL | let X(_t) = x; | data moved here | move occurs because `_t` has type `Y`, which does not implement the `Copy` trait | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/move-into-closure.rs:25:21 + | +LL | fn consume_fnmut(_f: F) { } + | ^^^^^^^ help: consider borrowing here | LL | let X(_t) = &x; | + error[E0507]: cannot move out of `e.0`, as `e` is a captured variable in an `FnMut` closure - --> $DIR/move-into-closure.rs:88:34 + --> $DIR/move-into-closure.rs:109:34 | LL | let e = Either::One(X(Y)); | - captured outer variable @@ -243,13 +298,18 @@ LL | if let Either::One(_t) = e { } | data moved here | move occurs because `_t` has type `X`, which does not implement the `Copy` trait | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/move-into-closure.rs:25:21 + | +LL | fn consume_fnmut(_f: F) { } + | ^^^^^^^ help: consider borrowing here | LL | if let Either::One(_t) = &e { } | + error[E0507]: cannot move out of `e.0`, as `e` is a captured variable in an `FnMut` closure - --> $DIR/move-into-closure.rs:91:37 + --> $DIR/move-into-closure.rs:112:37 | LL | let e = Either::One(X(Y)); | - captured outer variable @@ -263,13 +323,18 @@ LL | while let Either::One(_t) = e { } | data moved here | move occurs because `_t` has type `X`, which does not implement the `Copy` trait | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/move-into-closure.rs:25:21 + | +LL | fn consume_fnmut(_f: F) { } + | ^^^^^^^ help: consider borrowing here | LL | while let Either::One(_t) = &e { } | + error[E0507]: cannot move out of `e.0`, as `e` is a captured variable in an `FnMut` closure - --> $DIR/move-into-closure.rs:94:15 + --> $DIR/move-into-closure.rs:115:15 | LL | let e = Either::One(X(Y)); | - captured outer variable @@ -286,13 +351,18 @@ LL | Either::One(_t) | data moved here | move occurs because `_t` has type `X`, which does not implement the `Copy` trait | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/move-into-closure.rs:25:21 + | +LL | fn consume_fnmut(_f: F) { } + | ^^^^^^^ help: consider borrowing here | LL | match &e { | + error[E0507]: cannot move out of `e.0`, as `e` is a captured variable in an `FnMut` closure - --> $DIR/move-into-closure.rs:100:15 + --> $DIR/move-into-closure.rs:121:15 | LL | let e = Either::One(X(Y)); | - captured outer variable @@ -309,13 +379,18 @@ LL | Either::One(_t) => (), | data moved here | move occurs because `_t` has type `X`, which does not implement the `Copy` trait | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/move-into-closure.rs:25:21 + | +LL | fn consume_fnmut(_f: F) { } + | ^^^^^^^ help: consider borrowing here | LL | match &e { | + error[E0507]: cannot move out of `x.0`, as `x` is a captured variable in an `FnMut` closure - --> $DIR/move-into-closure.rs:108:25 + --> $DIR/move-into-closure.rs:129:25 | LL | let x = X(Y); | - captured outer variable @@ -329,13 +404,18 @@ LL | let X(mut _t) = x; | data moved here | move occurs because `_t` has type `Y`, which does not implement the `Copy` trait | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/move-into-closure.rs:25:21 + | +LL | fn consume_fnmut(_f: F) { } + | ^^^^^^^ help: consider borrowing here | LL | let X(mut _t) = &x; | + error[E0507]: cannot move out of `em.0`, as `em` is a captured variable in an `FnMut` closure - --> $DIR/move-into-closure.rs:111:38 + --> $DIR/move-into-closure.rs:132:38 | LL | let mut em = Either::One(X(Y)); | ------ captured outer variable @@ -349,13 +429,18 @@ LL | if let Either::One(mut _t) = em { } | data moved here | move occurs because `_t` has type `X`, which does not implement the `Copy` trait | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/move-into-closure.rs:25:21 + | +LL | fn consume_fnmut(_f: F) { } + | ^^^^^^^ help: consider borrowing here | LL | if let Either::One(mut _t) = &em { } | + error[E0507]: cannot move out of `em.0`, as `em` is a captured variable in an `FnMut` closure - --> $DIR/move-into-closure.rs:114:41 + --> $DIR/move-into-closure.rs:135:41 | LL | let mut em = Either::One(X(Y)); | ------ captured outer variable @@ -369,13 +454,18 @@ LL | while let Either::One(mut _t) = em { } | data moved here | move occurs because `_t` has type `X`, which does not implement the `Copy` trait | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/move-into-closure.rs:25:21 + | +LL | fn consume_fnmut(_f: F) { } + | ^^^^^^^ help: consider borrowing here | LL | while let Either::One(mut _t) = &em { } | + error[E0507]: cannot move out of `em.0`, as `em` is a captured variable in an `FnMut` closure - --> $DIR/move-into-closure.rs:117:15 + --> $DIR/move-into-closure.rs:138:15 | LL | let mut em = Either::One(X(Y)); | ------ captured outer variable @@ -392,13 +482,18 @@ LL | Either::One(mut _t) | data moved here | move occurs because `_t` has type `X`, which does not implement the `Copy` trait | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/move-into-closure.rs:25:21 + | +LL | fn consume_fnmut(_f: F) { } + | ^^^^^^^ help: consider borrowing here | LL | match &em { | + error[E0507]: cannot move out of `em.0`, as `em` is a captured variable in an `FnMut` closure - --> $DIR/move-into-closure.rs:123:15 + --> $DIR/move-into-closure.rs:144:15 | LL | let mut em = Either::One(X(Y)); | ------ captured outer variable @@ -415,13 +510,18 @@ LL | Either::One(mut _t) => (), | data moved here | move occurs because `_t` has type `X`, which does not implement the `Copy` trait | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/move-into-closure.rs:25:21 + | +LL | fn consume_fnmut(_f: F) { } + | ^^^^^^^ help: consider borrowing here | LL | match &em { | + error[E0507]: cannot move out of `em.0`, as `em` is a captured variable in an `FnMut` closure - --> $DIR/move-into-closure.rs:130:15 + --> $DIR/move-into-closure.rs:151:15 | LL | let mut em = Either::One(X(Y)); | ------ captured outer variable @@ -438,6 +538,11 @@ LL | Either::One(mut _t) => (), | data moved here | move occurs because `_t` has type `X`, which does not implement the `Copy` trait | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/move-into-closure.rs:25:21 + | +LL | fn consume_fnmut(_f: F) { } + | ^^^^^^^ help: consider borrowing here | LL | match &em { diff --git a/tests/ui/suggestions/option-content-move2.stderr b/tests/ui/suggestions/option-content-move2.stderr index c73e874b4036..c8aa6667b583 100644 --- a/tests/ui/suggestions/option-content-move2.stderr +++ b/tests/ui/suggestions/option-content-move2.stderr @@ -14,6 +14,11 @@ LL | LL | var = Some(NotCopyable); | --- variable moved due to use in closure | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/option-content-move2.rs:5:12 + | +LL | fn func H, H: FnMut()>(_: F) {} + | ^^^^^^^^^^^^ note: if `NotCopyable` implemented `Clone`, you could clone the value --> $DIR/option-content-move2.rs:1:1 | @@ -38,6 +43,12 @@ LL | move || { LL | LL | var = Some(NotCopyableButCloneable); | --- variable moved due to use in closure + | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/option-content-move2.rs:5:12 + | +LL | fn func H, H: FnMut()>(_: F) {} + | ^^^^^^^^^^^^ error: aborting due to 2 previous errors diff --git a/tests/ui/suggestions/option-content-move3.stderr b/tests/ui/suggestions/option-content-move3.stderr index 68c52352a651..2c9a86c036be 100644 --- a/tests/ui/suggestions/option-content-move3.stderr +++ b/tests/ui/suggestions/option-content-move3.stderr @@ -9,6 +9,7 @@ LL | move || { LL | let x = var; | ^^^ move occurs because `var` has type `NotCopyable`, which does not implement the `Copy` trait | + = help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once note: if `NotCopyable` implemented `Clone`, you could clone the value --> $DIR/option-content-move3.rs:2:1 | @@ -37,6 +38,11 @@ LL | move || { LL | let x = var; | --- variable moved due to use in closure | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/option-content-move3.rs:6:12 + | +LL | fn func H, H: FnMut()>(_: F) {} + | ^^^^^^^^^^^^ note: if `NotCopyable` implemented `Clone`, you could clone the value --> $DIR/option-content-move3.rs:2:1 | @@ -57,6 +63,7 @@ LL | move || { LL | let x = var; | ^^^ move occurs because `var` has type `NotCopyableButCloneable`, which does not implement the `Copy` trait | + = help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once help: consider borrowing here | LL | let x = &var; @@ -77,6 +84,11 @@ LL | move || { LL | let x = var; | --- variable moved due to use in closure | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/option-content-move3.rs:6:12 + | +LL | fn func H, H: FnMut()>(_: F) {} + | ^^^^^^^^^^^^ help: consider cloning the value before moving it into the closure | LL ~ { diff --git a/tests/ui/unboxed-closures/unboxed-closure-illegal-move.stderr b/tests/ui/unboxed-closures/unboxed-closure-illegal-move.stderr index 8d9a61cb6812..9d87402a15bf 100644 --- a/tests/ui/unboxed-closures/unboxed-closure-illegal-move.stderr +++ b/tests/ui/unboxed-closures/unboxed-closure-illegal-move.stderr @@ -10,6 +10,11 @@ LL | let f = to_fn(|| drop(x)); | | | captured by this `Fn` closure | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/unboxed-closure-illegal-move.rs:7:33 + | +LL | fn to_fn>(f: F) -> F { f } + | ^^^^^ help: consider cloning the value if the performance cost is acceptable | LL | let f = to_fn(|| drop(x.clone())); @@ -27,6 +32,11 @@ LL | let f = to_fn_mut(|| drop(x)); | | | captured by this `FnMut` closure | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/unboxed-closure-illegal-move.rs:8:37 + | +LL | fn to_fn_mut>(f: F) -> F { f } + | ^^^^^^^^ help: consider cloning the value if the performance cost is acceptable | LL | let f = to_fn_mut(|| drop(x.clone())); @@ -43,6 +53,12 @@ LL | let f = to_fn(move || drop(x)); | ------- ^ `x` is moved here | | | captured by this `Fn` closure + | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/unboxed-closure-illegal-move.rs:7:33 + | +LL | fn to_fn>(f: F) -> F { f } + | ^^^^^ error[E0507]: cannot move out of `x`, a captured variable in an `FnMut` closure --> $DIR/unboxed-closure-illegal-move.rs:32:40 @@ -55,6 +71,12 @@ LL | let f = to_fn_mut(move || drop(x)); | ------- ^ `x` is moved here | | | captured by this `FnMut` closure + | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/unboxed-closure-illegal-move.rs:8:37 + | +LL | fn to_fn_mut>(f: F) -> F { f } + | ^^^^^^^^ error: aborting due to 4 previous errors From 2484e189bfbc3decc2aa8480ce249dd84f6d2f9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Thu, 7 Aug 2025 21:09:42 +0000 Subject: [PATCH 142/195] Add support for method calls --- .../src/diagnostics/move_errors.rs | 74 ++- .../dont-suggest-ref/move-into-closure.rs | 140 +++++ .../dont-suggest-ref/move-into-closure.stderr | 566 +++++++++++++++++- 3 files changed, 725 insertions(+), 55 deletions(-) diff --git a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs index b24cb012d2b8..5adbaca7e113 100644 --- a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs @@ -9,6 +9,7 @@ use rustc_middle::bug; use rustc_middle::mir::*; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_mir_dataflow::move_paths::{LookupResult, MovePathIndex}; +use rustc_span::def_id::DefId; use rustc_span::{BytePos, DUMMY_SP, ExpnKind, MacroKind, Span}; use rustc_trait_selection::error_reporting::traits::FindExprBySpan; use rustc_trait_selection::infer::InferCtxtExt; @@ -507,38 +508,6 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { ); let closure_span = tcx.def_span(def_id); - let mut clause_span = DUMMY_SP; - let typck_result = self.infcx.tcx.typeck(self.mir_def_id()); - if let Some(closure_def_id) = def_id.as_local() - && let hir::Node::Expr(expr) = tcx.hir_node_by_def_id(closure_def_id) - && let hir::Node::Expr(parent) = tcx.parent_hir_node(expr.hir_id) - && let hir::ExprKind::Call(callee, _) = parent.kind - && let Some(ty) = typck_result.node_type_opt(callee.hir_id) - && let ty::FnDef(fn_def_id, args) = ty.kind() - && let predicates = tcx.predicates_of(fn_def_id).instantiate(tcx, args) - && let Some((_, span)) = - predicates.predicates.iter().zip(predicates.spans.iter()).find( - |(pred, _)| match pred.as_trait_clause() { - Some(clause) - if let ty::Closure(clause_closure_def_id, _) = - clause.self_ty().skip_binder().kind() - && clause_closure_def_id == def_id - && (tcx.lang_items().fn_mut_trait() - == Some(clause.def_id()) - || tcx.lang_items().fn_trait() - == Some(clause.def_id())) => - { - // Found `` - true - } - _ => false, - }, - ) - { - // We point at the `Fn()` or `FnMut()` bound that coerced the closure, which - // could be changed to `FnOnce()` to avoid the move error. - clause_span = *span; - } self.cannot_move_out_of(span, &place_description) .with_span_label(upvar_span, "captured outer variable") @@ -547,7 +516,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { format!("captured by this `{closure_kind}` closure"), ) .with_span_help( - clause_span, + self.get_closure_bound_clause_span(*def_id), "`Fn` and `FnMut` closures require captured values to be able to be \ consumed multiple times, but an `FnOnce` consume them only once", ) @@ -599,6 +568,45 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { err } + fn get_closure_bound_clause_span(&self, def_id: DefId) -> Span { + let tcx = self.infcx.tcx; + let typeck_result = tcx.typeck(self.mir_def_id()); + let Some(closure_def_id) = def_id.as_local() else { return DUMMY_SP }; + let hir::Node::Expr(expr) = tcx.hir_node_by_def_id(closure_def_id) else { return DUMMY_SP }; + let hir::Node::Expr(parent) = tcx.parent_hir_node(expr.hir_id) else { return DUMMY_SP }; + let predicates = match parent.kind { + hir::ExprKind::Call(callee, _) => { + let Some(ty) = typeck_result.node_type_opt(callee.hir_id) else { return DUMMY_SP }; + let ty::FnDef(fn_def_id, args) = ty.kind() else { return DUMMY_SP }; + tcx.predicates_of(fn_def_id).instantiate(tcx, args) + } + hir::ExprKind::MethodCall(..) => { + let Some((_, method)) = typeck_result.type_dependent_def(parent.hir_id) else { + return DUMMY_SP; + }; + let args = typeck_result.node_args(parent.hir_id); + tcx.predicates_of(method).instantiate(tcx, args) + } + _ => return DUMMY_SP, + }; + for (pred, span) in predicates.predicates.iter().zip(predicates.spans.iter()) { + tracing::info!(?pred); + tracing::info!(?span); + if let Some(clause) = pred.as_trait_clause() + && let ty::Closure(clause_closure_def_id, _) = clause.self_ty().skip_binder().kind() + && *clause_closure_def_id == def_id + && (tcx.lang_items().fn_mut_trait() == Some(clause.def_id()) + || tcx.lang_items().fn_trait() == Some(clause.def_id())) + { + // Found `` + // We point at the `Fn()` or `FnMut()` bound that coerced the closure, which + // could be changed to `FnOnce()` to avoid the move error. + return *span; + } + } + DUMMY_SP + } + fn add_move_hints(&self, error: GroupedMoveError<'tcx>, err: &mut Diag<'_>, span: Span) { match error { GroupedMoveError::MovesFromPlace { mut binds_to, move_from, .. } => { diff --git a/tests/ui/suggestions/dont-suggest-ref/move-into-closure.rs b/tests/ui/suggestions/dont-suggest-ref/move-into-closure.rs index 0154810f5643..34088f219b2b 100644 --- a/tests/ui/suggestions/dont-suggest-ref/move-into-closure.rs +++ b/tests/ui/suggestions/dont-suggest-ref/move-into-closure.rs @@ -35,6 +35,32 @@ fn consume_fnmut(_f: F) { } //~| HELP `Fn` and `FnMut` closures //~| HELP `Fn` and `FnMut` closures +trait T { + fn consume_fn(_f: F) { } + //~^ HELP `Fn` and `FnMut` closures + //~| HELP `Fn` and `FnMut` closures + //~| HELP `Fn` and `FnMut` closures + //~| HELP `Fn` and `FnMut` closures + //~| HELP `Fn` and `FnMut` closures + //~| HELP `Fn` and `FnMut` closures + //~| HELP `Fn` and `FnMut` closures + //~| HELP `Fn` and `FnMut` closures + //~| HELP `Fn` and `FnMut` closures + //~| HELP `Fn` and `FnMut` closures + fn method_consume_fn(&self, _f: F) { } + //~^ HELP `Fn` and `FnMut` closures + //~| HELP `Fn` and `FnMut` closures + //~| HELP `Fn` and `FnMut` closures + //~| HELP `Fn` and `FnMut` closures + //~| HELP `Fn` and `FnMut` closures + //~| HELP `Fn` and `FnMut` closures + //~| HELP `Fn` and `FnMut` closures + //~| HELP `Fn` and `FnMut` closures + //~| HELP `Fn` and `FnMut` closures + //~| HELP `Fn` and `FnMut` closures +} +impl T for () {} + pub fn main() { } fn move_into_fn() { @@ -94,6 +120,120 @@ fn move_into_fn() { }); } +fn move_into_assoc_fn() { + let e = Either::One(X(Y)); + let mut em = Either::One(X(Y)); + + let x = X(Y); + + // move into Fn + + <() as T>::consume_fn(|| { + let X(_t) = x; + //~^ ERROR cannot move + //~| HELP consider borrowing here + if let Either::One(_t) = e { } + //~^ ERROR cannot move + //~| HELP consider borrowing here + while let Either::One(_t) = e { } + //~^ ERROR cannot move + //~| HELP consider borrowing here + match e { + //~^ ERROR cannot move + //~| HELP consider borrowing here + Either::One(_t) + | Either::Two(_t) => (), + } + match e { + //~^ ERROR cannot move + //~| HELP consider borrowing here + Either::One(_t) => (), + Either::Two(ref _t) => (), + // FIXME: should suggest removing `ref` too + } + + let X(mut _t) = x; + //~^ ERROR cannot move + //~| HELP consider borrowing here + if let Either::One(mut _t) = em { } + //~^ ERROR cannot move + //~| HELP consider borrowing here + while let Either::One(mut _t) = em { } + //~^ ERROR cannot move + //~| HELP consider borrowing here + match em { + //~^ ERROR cannot move + //~| HELP consider borrowing here + Either::One(mut _t) + | Either::Two(mut _t) => (), + } + match em { + //~^ ERROR cannot move + //~| HELP consider borrowing here + Either::One(mut _t) => (), + Either::Two(ref _t) => (), + // FIXME: should suggest removing `ref` too + } + }); +} + +fn move_into_method() { + let e = Either::One(X(Y)); + let mut em = Either::One(X(Y)); + + let x = X(Y); + + // move into Fn + + ().method_consume_fn(|| { + let X(_t) = x; + //~^ ERROR cannot move + //~| HELP consider borrowing here + if let Either::One(_t) = e { } + //~^ ERROR cannot move + //~| HELP consider borrowing here + while let Either::One(_t) = e { } + //~^ ERROR cannot move + //~| HELP consider borrowing here + match e { + //~^ ERROR cannot move + //~| HELP consider borrowing here + Either::One(_t) + | Either::Two(_t) => (), + } + match e { + //~^ ERROR cannot move + //~| HELP consider borrowing here + Either::One(_t) => (), + Either::Two(ref _t) => (), + // FIXME: should suggest removing `ref` too + } + + let X(mut _t) = x; + //~^ ERROR cannot move + //~| HELP consider borrowing here + if let Either::One(mut _t) = em { } + //~^ ERROR cannot move + //~| HELP consider borrowing here + while let Either::One(mut _t) = em { } + //~^ ERROR cannot move + //~| HELP consider borrowing here + match em { + //~^ ERROR cannot move + //~| HELP consider borrowing here + Either::One(mut _t) + | Either::Two(mut _t) => (), + } + match em { + //~^ ERROR cannot move + //~| HELP consider borrowing here + Either::One(mut _t) => (), + Either::Two(ref _t) => (), + // FIXME: should suggest removing `ref` too + } + }); +} + fn move_into_fnmut() { let e = Either::One(X(Y)); let mut em = Either::One(X(Y)); diff --git a/tests/ui/suggestions/dont-suggest-ref/move-into-closure.stderr b/tests/ui/suggestions/dont-suggest-ref/move-into-closure.stderr index d9ed25983d66..132a31c8f7ce 100644 --- a/tests/ui/suggestions/dont-suggest-ref/move-into-closure.stderr +++ b/tests/ui/suggestions/dont-suggest-ref/move-into-closure.stderr @@ -1,5 +1,5 @@ error[E0507]: cannot move out of `x.0`, as `x` is a captured variable in an `Fn` closure - --> $DIR/move-into-closure.rs:49:21 + --> $DIR/move-into-closure.rs:75:21 | LL | let x = X(Y); | - captured outer variable @@ -23,7 +23,7 @@ LL | let X(_t) = &x; | + error[E0507]: cannot move out of `e.0`, as `e` is a captured variable in an `Fn` closure - --> $DIR/move-into-closure.rs:52:34 + --> $DIR/move-into-closure.rs:78:34 | LL | let e = Either::One(X(Y)); | - captured outer variable @@ -48,7 +48,7 @@ LL | if let Either::One(_t) = &e { } | + error[E0507]: cannot move out of `e.0`, as `e` is a captured variable in an `Fn` closure - --> $DIR/move-into-closure.rs:55:37 + --> $DIR/move-into-closure.rs:81:37 | LL | let e = Either::One(X(Y)); | - captured outer variable @@ -73,7 +73,7 @@ LL | while let Either::One(_t) = &e { } | + error[E0507]: cannot move out of `e.0`, as `e` is a captured variable in an `Fn` closure - --> $DIR/move-into-closure.rs:58:15 + --> $DIR/move-into-closure.rs:84:15 | LL | let e = Either::One(X(Y)); | - captured outer variable @@ -101,7 +101,7 @@ LL | match &e { | + error[E0507]: cannot move out of `e.0`, as `e` is a captured variable in an `Fn` closure - --> $DIR/move-into-closure.rs:64:15 + --> $DIR/move-into-closure.rs:90:15 | LL | let e = Either::One(X(Y)); | - captured outer variable @@ -129,7 +129,7 @@ LL | match &e { | + error[E0507]: cannot move out of `x.0`, as `x` is a captured variable in an `Fn` closure - --> $DIR/move-into-closure.rs:72:25 + --> $DIR/move-into-closure.rs:98:25 | LL | let x = X(Y); | - captured outer variable @@ -154,7 +154,7 @@ LL | let X(mut _t) = &x; | + error[E0507]: cannot move out of `em.0`, as `em` is a captured variable in an `Fn` closure - --> $DIR/move-into-closure.rs:75:38 + --> $DIR/move-into-closure.rs:101:38 | LL | let mut em = Either::One(X(Y)); | ------ captured outer variable @@ -179,7 +179,7 @@ LL | if let Either::One(mut _t) = &em { } | + error[E0507]: cannot move out of `em.0`, as `em` is a captured variable in an `Fn` closure - --> $DIR/move-into-closure.rs:78:41 + --> $DIR/move-into-closure.rs:104:41 | LL | let mut em = Either::One(X(Y)); | ------ captured outer variable @@ -204,7 +204,7 @@ LL | while let Either::One(mut _t) = &em { } | + error[E0507]: cannot move out of `em.0`, as `em` is a captured variable in an `Fn` closure - --> $DIR/move-into-closure.rs:81:15 + --> $DIR/move-into-closure.rs:107:15 | LL | let mut em = Either::One(X(Y)); | ------ captured outer variable @@ -232,7 +232,7 @@ LL | match &em { | + error[E0507]: cannot move out of `em.0`, as `em` is a captured variable in an `Fn` closure - --> $DIR/move-into-closure.rs:87:15 + --> $DIR/move-into-closure.rs:113:15 | LL | let mut em = Either::One(X(Y)); | ------ captured outer variable @@ -259,8 +259,530 @@ help: consider borrowing here LL | match &em { | + +error[E0507]: cannot move out of `x.0`, as `x` is a captured variable in an `Fn` closure + --> $DIR/move-into-closure.rs:132:21 + | +LL | let x = X(Y); + | - captured outer variable +... +LL | <() as T>::consume_fn(|| { + | -- captured by this `Fn` closure +LL | let X(_t) = x; + | -- ^ + | | + | data moved here + | move occurs because `_t` has type `Y`, which does not implement the `Copy` trait + | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/move-into-closure.rs:39:22 + | +LL | fn consume_fn(_f: F) { } + | ^^^^ +help: consider borrowing here + | +LL | let X(_t) = &x; + | + + +error[E0507]: cannot move out of `e.0`, as `e` is a captured variable in an `Fn` closure + --> $DIR/move-into-closure.rs:135:34 + | +LL | let e = Either::One(X(Y)); + | - captured outer variable +... +LL | <() as T>::consume_fn(|| { + | -- captured by this `Fn` closure +... +LL | if let Either::One(_t) = e { } + | -- ^ + | | + | data moved here + | move occurs because `_t` has type `X`, which does not implement the `Copy` trait + | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/move-into-closure.rs:39:22 + | +LL | fn consume_fn(_f: F) { } + | ^^^^ +help: consider borrowing here + | +LL | if let Either::One(_t) = &e { } + | + + +error[E0507]: cannot move out of `e.0`, as `e` is a captured variable in an `Fn` closure + --> $DIR/move-into-closure.rs:138:37 + | +LL | let e = Either::One(X(Y)); + | - captured outer variable +... +LL | <() as T>::consume_fn(|| { + | -- captured by this `Fn` closure +... +LL | while let Either::One(_t) = e { } + | -- ^ + | | + | data moved here + | move occurs because `_t` has type `X`, which does not implement the `Copy` trait + | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/move-into-closure.rs:39:22 + | +LL | fn consume_fn(_f: F) { } + | ^^^^ +help: consider borrowing here + | +LL | while let Either::One(_t) = &e { } + | + + +error[E0507]: cannot move out of `e.0`, as `e` is a captured variable in an `Fn` closure + --> $DIR/move-into-closure.rs:141:15 + | +LL | let e = Either::One(X(Y)); + | - captured outer variable +... +LL | <() as T>::consume_fn(|| { + | -- captured by this `Fn` closure +... +LL | match e { + | ^ +... +LL | Either::One(_t) + | -- + | | + | data moved here + | move occurs because `_t` has type `X`, which does not implement the `Copy` trait + | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/move-into-closure.rs:39:22 + | +LL | fn consume_fn(_f: F) { } + | ^^^^ +help: consider borrowing here + | +LL | match &e { + | + + +error[E0507]: cannot move out of `e.0`, as `e` is a captured variable in an `Fn` closure + --> $DIR/move-into-closure.rs:147:15 + | +LL | let e = Either::One(X(Y)); + | - captured outer variable +... +LL | <() as T>::consume_fn(|| { + | -- captured by this `Fn` closure +... +LL | match e { + | ^ +... +LL | Either::One(_t) => (), + | -- + | | + | data moved here + | move occurs because `_t` has type `X`, which does not implement the `Copy` trait + | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/move-into-closure.rs:39:22 + | +LL | fn consume_fn(_f: F) { } + | ^^^^ +help: consider borrowing here + | +LL | match &e { + | + + +error[E0507]: cannot move out of `x.0`, as `x` is a captured variable in an `Fn` closure + --> $DIR/move-into-closure.rs:155:25 + | +LL | let x = X(Y); + | - captured outer variable +... +LL | <() as T>::consume_fn(|| { + | -- captured by this `Fn` closure +... +LL | let X(mut _t) = x; + | ------ ^ + | | + | data moved here + | move occurs because `_t` has type `Y`, which does not implement the `Copy` trait + | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/move-into-closure.rs:39:22 + | +LL | fn consume_fn(_f: F) { } + | ^^^^ +help: consider borrowing here + | +LL | let X(mut _t) = &x; + | + + +error[E0507]: cannot move out of `em.0`, as `em` is a captured variable in an `Fn` closure + --> $DIR/move-into-closure.rs:158:38 + | +LL | let mut em = Either::One(X(Y)); + | ------ captured outer variable +... +LL | <() as T>::consume_fn(|| { + | -- captured by this `Fn` closure +... +LL | if let Either::One(mut _t) = em { } + | ------ ^^ + | | + | data moved here + | move occurs because `_t` has type `X`, which does not implement the `Copy` trait + | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/move-into-closure.rs:39:22 + | +LL | fn consume_fn(_f: F) { } + | ^^^^ +help: consider borrowing here + | +LL | if let Either::One(mut _t) = &em { } + | + + +error[E0507]: cannot move out of `em.0`, as `em` is a captured variable in an `Fn` closure + --> $DIR/move-into-closure.rs:161:41 + | +LL | let mut em = Either::One(X(Y)); + | ------ captured outer variable +... +LL | <() as T>::consume_fn(|| { + | -- captured by this `Fn` closure +... +LL | while let Either::One(mut _t) = em { } + | ------ ^^ + | | + | data moved here + | move occurs because `_t` has type `X`, which does not implement the `Copy` trait + | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/move-into-closure.rs:39:22 + | +LL | fn consume_fn(_f: F) { } + | ^^^^ +help: consider borrowing here + | +LL | while let Either::One(mut _t) = &em { } + | + + +error[E0507]: cannot move out of `em.0`, as `em` is a captured variable in an `Fn` closure + --> $DIR/move-into-closure.rs:164:15 + | +LL | let mut em = Either::One(X(Y)); + | ------ captured outer variable +... +LL | <() as T>::consume_fn(|| { + | -- captured by this `Fn` closure +... +LL | match em { + | ^^ +... +LL | Either::One(mut _t) + | ------ + | | + | data moved here + | move occurs because `_t` has type `X`, which does not implement the `Copy` trait + | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/move-into-closure.rs:39:22 + | +LL | fn consume_fn(_f: F) { } + | ^^^^ +help: consider borrowing here + | +LL | match &em { + | + + +error[E0507]: cannot move out of `em.0`, as `em` is a captured variable in an `Fn` closure + --> $DIR/move-into-closure.rs:170:15 + | +LL | let mut em = Either::One(X(Y)); + | ------ captured outer variable +... +LL | <() as T>::consume_fn(|| { + | -- captured by this `Fn` closure +... +LL | match em { + | ^^ +... +LL | Either::One(mut _t) => (), + | ------ + | | + | data moved here + | move occurs because `_t` has type `X`, which does not implement the `Copy` trait + | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/move-into-closure.rs:39:22 + | +LL | fn consume_fn(_f: F) { } + | ^^^^ +help: consider borrowing here + | +LL | match &em { + | + + +error[E0507]: cannot move out of `x.0`, as `x` is a captured variable in an `Fn` closure + --> $DIR/move-into-closure.rs:189:21 + | +LL | let x = X(Y); + | - captured outer variable +... +LL | ().method_consume_fn(|| { + | -- captured by this `Fn` closure +LL | let X(_t) = x; + | -- ^ + | | + | data moved here + | move occurs because `_t` has type `Y`, which does not implement the `Copy` trait + | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/move-into-closure.rs:50:29 + | +LL | fn method_consume_fn(&self, _f: F) { } + | ^^^^ +help: consider borrowing here + | +LL | let X(_t) = &x; + | + + +error[E0507]: cannot move out of `e.0`, as `e` is a captured variable in an `Fn` closure + --> $DIR/move-into-closure.rs:192:34 + | +LL | let e = Either::One(X(Y)); + | - captured outer variable +... +LL | ().method_consume_fn(|| { + | -- captured by this `Fn` closure +... +LL | if let Either::One(_t) = e { } + | -- ^ + | | + | data moved here + | move occurs because `_t` has type `X`, which does not implement the `Copy` trait + | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/move-into-closure.rs:50:29 + | +LL | fn method_consume_fn(&self, _f: F) { } + | ^^^^ +help: consider borrowing here + | +LL | if let Either::One(_t) = &e { } + | + + +error[E0507]: cannot move out of `e.0`, as `e` is a captured variable in an `Fn` closure + --> $DIR/move-into-closure.rs:195:37 + | +LL | let e = Either::One(X(Y)); + | - captured outer variable +... +LL | ().method_consume_fn(|| { + | -- captured by this `Fn` closure +... +LL | while let Either::One(_t) = e { } + | -- ^ + | | + | data moved here + | move occurs because `_t` has type `X`, which does not implement the `Copy` trait + | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/move-into-closure.rs:50:29 + | +LL | fn method_consume_fn(&self, _f: F) { } + | ^^^^ +help: consider borrowing here + | +LL | while let Either::One(_t) = &e { } + | + + +error[E0507]: cannot move out of `e.0`, as `e` is a captured variable in an `Fn` closure + --> $DIR/move-into-closure.rs:198:15 + | +LL | let e = Either::One(X(Y)); + | - captured outer variable +... +LL | ().method_consume_fn(|| { + | -- captured by this `Fn` closure +... +LL | match e { + | ^ +... +LL | Either::One(_t) + | -- + | | + | data moved here + | move occurs because `_t` has type `X`, which does not implement the `Copy` trait + | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/move-into-closure.rs:50:29 + | +LL | fn method_consume_fn(&self, _f: F) { } + | ^^^^ +help: consider borrowing here + | +LL | match &e { + | + + +error[E0507]: cannot move out of `e.0`, as `e` is a captured variable in an `Fn` closure + --> $DIR/move-into-closure.rs:204:15 + | +LL | let e = Either::One(X(Y)); + | - captured outer variable +... +LL | ().method_consume_fn(|| { + | -- captured by this `Fn` closure +... +LL | match e { + | ^ +... +LL | Either::One(_t) => (), + | -- + | | + | data moved here + | move occurs because `_t` has type `X`, which does not implement the `Copy` trait + | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/move-into-closure.rs:50:29 + | +LL | fn method_consume_fn(&self, _f: F) { } + | ^^^^ +help: consider borrowing here + | +LL | match &e { + | + + +error[E0507]: cannot move out of `x.0`, as `x` is a captured variable in an `Fn` closure + --> $DIR/move-into-closure.rs:212:25 + | +LL | let x = X(Y); + | - captured outer variable +... +LL | ().method_consume_fn(|| { + | -- captured by this `Fn` closure +... +LL | let X(mut _t) = x; + | ------ ^ + | | + | data moved here + | move occurs because `_t` has type `Y`, which does not implement the `Copy` trait + | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/move-into-closure.rs:50:29 + | +LL | fn method_consume_fn(&self, _f: F) { } + | ^^^^ +help: consider borrowing here + | +LL | let X(mut _t) = &x; + | + + +error[E0507]: cannot move out of `em.0`, as `em` is a captured variable in an `Fn` closure + --> $DIR/move-into-closure.rs:215:38 + | +LL | let mut em = Either::One(X(Y)); + | ------ captured outer variable +... +LL | ().method_consume_fn(|| { + | -- captured by this `Fn` closure +... +LL | if let Either::One(mut _t) = em { } + | ------ ^^ + | | + | data moved here + | move occurs because `_t` has type `X`, which does not implement the `Copy` trait + | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/move-into-closure.rs:50:29 + | +LL | fn method_consume_fn(&self, _f: F) { } + | ^^^^ +help: consider borrowing here + | +LL | if let Either::One(mut _t) = &em { } + | + + +error[E0507]: cannot move out of `em.0`, as `em` is a captured variable in an `Fn` closure + --> $DIR/move-into-closure.rs:218:41 + | +LL | let mut em = Either::One(X(Y)); + | ------ captured outer variable +... +LL | ().method_consume_fn(|| { + | -- captured by this `Fn` closure +... +LL | while let Either::One(mut _t) = em { } + | ------ ^^ + | | + | data moved here + | move occurs because `_t` has type `X`, which does not implement the `Copy` trait + | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/move-into-closure.rs:50:29 + | +LL | fn method_consume_fn(&self, _f: F) { } + | ^^^^ +help: consider borrowing here + | +LL | while let Either::One(mut _t) = &em { } + | + + +error[E0507]: cannot move out of `em.0`, as `em` is a captured variable in an `Fn` closure + --> $DIR/move-into-closure.rs:221:15 + | +LL | let mut em = Either::One(X(Y)); + | ------ captured outer variable +... +LL | ().method_consume_fn(|| { + | -- captured by this `Fn` closure +... +LL | match em { + | ^^ +... +LL | Either::One(mut _t) + | ------ + | | + | data moved here + | move occurs because `_t` has type `X`, which does not implement the `Copy` trait + | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/move-into-closure.rs:50:29 + | +LL | fn method_consume_fn(&self, _f: F) { } + | ^^^^ +help: consider borrowing here + | +LL | match &em { + | + + +error[E0507]: cannot move out of `em.0`, as `em` is a captured variable in an `Fn` closure + --> $DIR/move-into-closure.rs:227:15 + | +LL | let mut em = Either::One(X(Y)); + | ------ captured outer variable +... +LL | ().method_consume_fn(|| { + | -- captured by this `Fn` closure +... +LL | match em { + | ^^ +... +LL | Either::One(mut _t) => (), + | ------ + | | + | data moved here + | move occurs because `_t` has type `X`, which does not implement the `Copy` trait + | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/move-into-closure.rs:50:29 + | +LL | fn method_consume_fn(&self, _f: F) { } + | ^^^^ +help: consider borrowing here + | +LL | match &em { + | + + error[E0507]: cannot move out of `x.0`, as `x` is a captured variable in an `FnMut` closure - --> $DIR/move-into-closure.rs:106:21 + --> $DIR/move-into-closure.rs:246:21 | LL | let x = X(Y); | - captured outer variable @@ -284,7 +806,7 @@ LL | let X(_t) = &x; | + error[E0507]: cannot move out of `e.0`, as `e` is a captured variable in an `FnMut` closure - --> $DIR/move-into-closure.rs:109:34 + --> $DIR/move-into-closure.rs:249:34 | LL | let e = Either::One(X(Y)); | - captured outer variable @@ -309,7 +831,7 @@ LL | if let Either::One(_t) = &e { } | + error[E0507]: cannot move out of `e.0`, as `e` is a captured variable in an `FnMut` closure - --> $DIR/move-into-closure.rs:112:37 + --> $DIR/move-into-closure.rs:252:37 | LL | let e = Either::One(X(Y)); | - captured outer variable @@ -334,7 +856,7 @@ LL | while let Either::One(_t) = &e { } | + error[E0507]: cannot move out of `e.0`, as `e` is a captured variable in an `FnMut` closure - --> $DIR/move-into-closure.rs:115:15 + --> $DIR/move-into-closure.rs:255:15 | LL | let e = Either::One(X(Y)); | - captured outer variable @@ -362,7 +884,7 @@ LL | match &e { | + error[E0507]: cannot move out of `e.0`, as `e` is a captured variable in an `FnMut` closure - --> $DIR/move-into-closure.rs:121:15 + --> $DIR/move-into-closure.rs:261:15 | LL | let e = Either::One(X(Y)); | - captured outer variable @@ -390,7 +912,7 @@ LL | match &e { | + error[E0507]: cannot move out of `x.0`, as `x` is a captured variable in an `FnMut` closure - --> $DIR/move-into-closure.rs:129:25 + --> $DIR/move-into-closure.rs:269:25 | LL | let x = X(Y); | - captured outer variable @@ -415,7 +937,7 @@ LL | let X(mut _t) = &x; | + error[E0507]: cannot move out of `em.0`, as `em` is a captured variable in an `FnMut` closure - --> $DIR/move-into-closure.rs:132:38 + --> $DIR/move-into-closure.rs:272:38 | LL | let mut em = Either::One(X(Y)); | ------ captured outer variable @@ -440,7 +962,7 @@ LL | if let Either::One(mut _t) = &em { } | + error[E0507]: cannot move out of `em.0`, as `em` is a captured variable in an `FnMut` closure - --> $DIR/move-into-closure.rs:135:41 + --> $DIR/move-into-closure.rs:275:41 | LL | let mut em = Either::One(X(Y)); | ------ captured outer variable @@ -465,7 +987,7 @@ LL | while let Either::One(mut _t) = &em { } | + error[E0507]: cannot move out of `em.0`, as `em` is a captured variable in an `FnMut` closure - --> $DIR/move-into-closure.rs:138:15 + --> $DIR/move-into-closure.rs:278:15 | LL | let mut em = Either::One(X(Y)); | ------ captured outer variable @@ -493,7 +1015,7 @@ LL | match &em { | + error[E0507]: cannot move out of `em.0`, as `em` is a captured variable in an `FnMut` closure - --> $DIR/move-into-closure.rs:144:15 + --> $DIR/move-into-closure.rs:284:15 | LL | let mut em = Either::One(X(Y)); | ------ captured outer variable @@ -521,7 +1043,7 @@ LL | match &em { | + error[E0507]: cannot move out of `em.0`, as `em` is a captured variable in an `FnMut` closure - --> $DIR/move-into-closure.rs:151:15 + --> $DIR/move-into-closure.rs:291:15 | LL | let mut em = Either::One(X(Y)); | ------ captured outer variable @@ -548,6 +1070,6 @@ help: consider borrowing here LL | match &em { | + -error: aborting due to 21 previous errors +error: aborting due to 41 previous errors For more information about this error, try `rustc --explain E0507`. From 74496bc4d9f64ea01ae3714b9680cc22c1535cb2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Sun, 10 Aug 2025 19:26:03 +0000 Subject: [PATCH 143/195] review comments --- .../rustc_borrowck/src/diagnostics/move_errors.rs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs index 5adbaca7e113..af71db694832 100644 --- a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs @@ -571,9 +571,11 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { fn get_closure_bound_clause_span(&self, def_id: DefId) -> Span { let tcx = self.infcx.tcx; let typeck_result = tcx.typeck(self.mir_def_id()); - let Some(closure_def_id) = def_id.as_local() else { return DUMMY_SP }; - let hir::Node::Expr(expr) = tcx.hir_node_by_def_id(closure_def_id) else { return DUMMY_SP }; - let hir::Node::Expr(parent) = tcx.parent_hir_node(expr.hir_id) else { return DUMMY_SP }; + // Check whether the closure is an argument to a call, if so, + // get the instantiated where-bounds of that call. + let closure_hir_id = tcx.local_def_id_to_hir_id(def_id.expect_local()); + let hir::Node::Expr(parent) = tcx.parent_hir_node(closure_hir_id) else { return DUMMY_SP }; + let predicates = match parent.kind { hir::ExprKind::Call(callee, _) => { let Some(ty) = typeck_result.node_type_opt(callee.hir_id) else { return DUMMY_SP }; @@ -589,9 +591,9 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { } _ => return DUMMY_SP, }; + + // Check whether one of the where-bounds requires the closure to impl `Fn[Mut]`. for (pred, span) in predicates.predicates.iter().zip(predicates.spans.iter()) { - tracing::info!(?pred); - tracing::info!(?span); if let Some(clause) = pred.as_trait_clause() && let ty::Closure(clause_closure_def_id, _) = clause.self_ty().skip_binder().kind() && *clause_closure_def_id == def_id From 0885f660a5bc4b31d0f7a869be589d81c08eecda Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Sun, 10 Aug 2025 14:04:07 -0700 Subject: [PATCH 144/195] Fix macro infinite recursion test to not trigger warning about semicolon in expr The test cases for issue 41731 are about infinite macro recursion that incorporates `print!` and `println!`. However, they also included trailing semicolons despite expanding to expressions; that isn't what these particular test cases are designed to test. Eliminate the trailing semicolons, to simplify future work on removing this special case. Every *other* macro that expands to a semicolon in an expression is a test case for that specifically. --- tests/ui/infinite/issue-41731-infinite-macro-print.rs | 2 +- tests/ui/infinite/issue-41731-infinite-macro-print.stderr | 6 +++--- tests/ui/infinite/issue-41731-infinite-macro-println.rs | 2 +- tests/ui/infinite/issue-41731-infinite-macro-println.stderr | 6 +++--- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/ui/infinite/issue-41731-infinite-macro-print.rs b/tests/ui/infinite/issue-41731-infinite-macro-print.rs index 7cd3ff3d629e..aa5555b117af 100644 --- a/tests/ui/infinite/issue-41731-infinite-macro-print.rs +++ b/tests/ui/infinite/issue-41731-infinite-macro-print.rs @@ -5,7 +5,7 @@ fn main() { macro_rules! stack { ($overflow:expr) => { - print!(stack!($overflow)); + print!(stack!($overflow)) //~^ ERROR recursion limit reached while expanding //~| ERROR format argument must be a string literal }; diff --git a/tests/ui/infinite/issue-41731-infinite-macro-print.stderr b/tests/ui/infinite/issue-41731-infinite-macro-print.stderr index 71510816d0be..84436de9aa37 100644 --- a/tests/ui/infinite/issue-41731-infinite-macro-print.stderr +++ b/tests/ui/infinite/issue-41731-infinite-macro-print.stderr @@ -14,11 +14,11 @@ LL | stack!("overflow"); | ^^^^^^^^^^^^^^^^^^ | = note: expanding `stack! { "overflow" }` - = note: to `print! (stack! ("overflow"));` + = note: to `print! (stack! ("overflow"))` = note: expanding `print! { stack! ("overflow") }` = note: to `{ $crate :: io :: _print($crate :: format_args! (stack! ("overflow"))); }` = note: expanding `stack! { "overflow" }` - = note: to `print! (stack! ("overflow"));` + = note: to `print! (stack! ("overflow"))` = note: expanding `print! { stack! ("overflow") }` = note: to `{ $crate :: io :: _print($crate :: format_args! (stack! ("overflow"))); }` @@ -31,7 +31,7 @@ LL | stack!("overflow"); = note: this error originates in the macro `print` which comes from the expansion of the macro `stack` (in Nightly builds, run with -Z macro-backtrace for more info) help: you might be missing a string literal to format with | -LL | print!("{}", stack!($overflow)); +LL | print!("{}", stack!($overflow)) | +++++ error: aborting due to 2 previous errors diff --git a/tests/ui/infinite/issue-41731-infinite-macro-println.rs b/tests/ui/infinite/issue-41731-infinite-macro-println.rs index 491f18dc4c63..cf59afb01940 100644 --- a/tests/ui/infinite/issue-41731-infinite-macro-println.rs +++ b/tests/ui/infinite/issue-41731-infinite-macro-println.rs @@ -5,7 +5,7 @@ fn main() { macro_rules! stack { ($overflow:expr) => { - println!(stack!($overflow)); + println!(stack!($overflow)) //~^ ERROR recursion limit reached while expanding //~| ERROR format argument must be a string literal }; diff --git a/tests/ui/infinite/issue-41731-infinite-macro-println.stderr b/tests/ui/infinite/issue-41731-infinite-macro-println.stderr index 645176d45cb9..6d0432abe4c5 100644 --- a/tests/ui/infinite/issue-41731-infinite-macro-println.stderr +++ b/tests/ui/infinite/issue-41731-infinite-macro-println.stderr @@ -14,11 +14,11 @@ LL | stack!("overflow"); | ^^^^^^^^^^^^^^^^^^ | = note: expanding `stack! { "overflow" }` - = note: to `println! (stack! ("overflow"));` + = note: to `println! (stack! ("overflow"))` = note: expanding `println! { stack! ("overflow") }` = note: to `{ $crate :: io :: _print($crate :: format_args_nl! (stack! ("overflow"))); }` = note: expanding `stack! { "overflow" }` - = note: to `println! (stack! ("overflow"));` + = note: to `println! (stack! ("overflow"))` = note: expanding `println! { stack! ("overflow") }` = note: to `{ $crate :: io :: _print($crate :: format_args_nl! (stack! ("overflow"))); }` @@ -31,7 +31,7 @@ LL | stack!("overflow"); = note: this error originates in the macro `println` which comes from the expansion of the macro `stack` (in Nightly builds, run with -Z macro-backtrace for more info) help: you might be missing a string literal to format with | -LL | println!("{}", stack!($overflow)); +LL | println!("{}", stack!($overflow)) | +++++ error: aborting due to 2 previous errors From e9609abda42e8d88cf1f07946362cd81516792da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Sun, 10 Aug 2025 21:47:52 +0000 Subject: [PATCH 145/195] Account for macros when trying to point at inference cause Do not point at macro invocation which expands to an inference error. Avoid the following: ``` error[E0308]: mismatched types --> $DIR/does-not-have-iter-interpolated.rs:12:5 | LL | quote!($($nonrep)*); | ^^^^^^^^^^^^^^^^^^^ | | | expected `HasIterator`, found `ThereIsNoIteratorInRepetition` | expected due to this | here the type of `has_iter` is inferred to be `ThereIsNoIteratorInRepetition` ``` --- compiler/rustc_hir_typeck/src/demand.rs | 2 +- .../quote/does-not-have-iter-interpolated-dup.stderr | 1 - .../proc-macro/quote/does-not-have-iter-interpolated.stderr | 1 - .../ui/proc-macro/quote/does-not-have-iter-separated.stderr | 5 +---- tests/ui/proc-macro/quote/does-not-have-iter.stderr | 5 +---- 5 files changed, 3 insertions(+), 11 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs index e5684f8cbe66..fb6ebe066a8f 100644 --- a/compiler/rustc_hir_typeck/src/demand.rs +++ b/compiler/rustc_hir_typeck/src/demand.rs @@ -698,7 +698,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) { match (self.tcx.parent_hir_node(expr.hir_id), error) { (hir::Node::LetStmt(hir::LetStmt { ty: Some(ty), init: Some(init), .. }), _) - if init.hir_id == expr.hir_id => + if init.hir_id == expr.hir_id && !ty.span.source_equal(init.span) => { // Point at `let` assignment type. err.span_label(ty.span, "expected due to this"); diff --git a/tests/ui/proc-macro/quote/does-not-have-iter-interpolated-dup.stderr b/tests/ui/proc-macro/quote/does-not-have-iter-interpolated-dup.stderr index ecb12c1df3b6..0bcea9b85f47 100644 --- a/tests/ui/proc-macro/quote/does-not-have-iter-interpolated-dup.stderr +++ b/tests/ui/proc-macro/quote/does-not-have-iter-interpolated-dup.stderr @@ -5,7 +5,6 @@ LL | quote!($($nonrep $nonrep)*); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | | | expected `HasIterator`, found `ThereIsNoIteratorInRepetition` - | expected due to this | here the type of `has_iter` is inferred to be `ThereIsNoIteratorInRepetition` error: aborting due to 1 previous error diff --git a/tests/ui/proc-macro/quote/does-not-have-iter-interpolated.stderr b/tests/ui/proc-macro/quote/does-not-have-iter-interpolated.stderr index 093e2ebc0985..d945ab41a12e 100644 --- a/tests/ui/proc-macro/quote/does-not-have-iter-interpolated.stderr +++ b/tests/ui/proc-macro/quote/does-not-have-iter-interpolated.stderr @@ -5,7 +5,6 @@ LL | quote!($($nonrep)*); | ^^^^^^^^^^^^^^^^^^^ | | | expected `HasIterator`, found `ThereIsNoIteratorInRepetition` - | expected due to this | here the type of `has_iter` is inferred to be `ThereIsNoIteratorInRepetition` error: aborting due to 1 previous error diff --git a/tests/ui/proc-macro/quote/does-not-have-iter-separated.stderr b/tests/ui/proc-macro/quote/does-not-have-iter-separated.stderr index 937209e675ec..2d715f293d43 100644 --- a/tests/ui/proc-macro/quote/does-not-have-iter-separated.stderr +++ b/tests/ui/proc-macro/quote/does-not-have-iter-separated.stderr @@ -2,10 +2,7 @@ error[E0308]: mismatched types --> $DIR/does-not-have-iter-separated.rs:8:5 | LL | quote!($(a b),*); - | ^^^^^^^^^^^^^^^^ - | | - | expected `HasIterator`, found `ThereIsNoIteratorInRepetition` - | expected due to this + | ^^^^^^^^^^^^^^^^ expected `HasIterator`, found `ThereIsNoIteratorInRepetition` error: aborting due to 1 previous error diff --git a/tests/ui/proc-macro/quote/does-not-have-iter.stderr b/tests/ui/proc-macro/quote/does-not-have-iter.stderr index e74ea3348992..ac8e725038a3 100644 --- a/tests/ui/proc-macro/quote/does-not-have-iter.stderr +++ b/tests/ui/proc-macro/quote/does-not-have-iter.stderr @@ -2,10 +2,7 @@ error[E0308]: mismatched types --> $DIR/does-not-have-iter.rs:8:5 | LL | quote!($(a b)*); - | ^^^^^^^^^^^^^^^ - | | - | expected `HasIterator`, found `ThereIsNoIteratorInRepetition` - | expected due to this + | ^^^^^^^^^^^^^^^ expected `HasIterator`, found `ThereIsNoIteratorInRepetition` error: aborting due to 1 previous error From d9bd24d331919299975f36c64ddf6febc4f41fd4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Sun, 10 Aug 2025 21:52:23 +0000 Subject: [PATCH 146/195] Add test showing innecessary inference span --- tests/ui/proc-macro/auxiliary/match-expander.rs | 15 +++++++++++++++ tests/ui/proc-macro/match-expander.rs | 13 +++++++++++++ tests/ui/proc-macro/match-expander.stderr | 14 ++++++++++++++ 3 files changed, 42 insertions(+) create mode 100644 tests/ui/proc-macro/auxiliary/match-expander.rs create mode 100644 tests/ui/proc-macro/match-expander.rs create mode 100644 tests/ui/proc-macro/match-expander.stderr diff --git a/tests/ui/proc-macro/auxiliary/match-expander.rs b/tests/ui/proc-macro/auxiliary/match-expander.rs new file mode 100644 index 000000000000..bf78df2addf3 --- /dev/null +++ b/tests/ui/proc-macro/auxiliary/match-expander.rs @@ -0,0 +1,15 @@ +extern crate proc_macro; + +use proc_macro::TokenStream; + +#[proc_macro] +pub fn matcher(input: TokenStream) -> TokenStream { +" +struct S(()); +let s = S(()); +match s { + true => {} + _ => {} +} +".parse().unwrap() +} diff --git a/tests/ui/proc-macro/match-expander.rs b/tests/ui/proc-macro/match-expander.rs new file mode 100644 index 000000000000..34e9a876153a --- /dev/null +++ b/tests/ui/proc-macro/match-expander.rs @@ -0,0 +1,13 @@ +//@ proc-macro: match-expander.rs +// Ensure that we don't point at macro invocation when providing inference contexts. + +#[macro_use] +extern crate match_expander; + +fn main() { + match_expander::matcher!(); + //~^ ERROR: mismatched types + //~| NOTE: expected `S`, found `bool` + //~| NOTE: this expression has type `S` + //~| NOTE: in this expansion of match_expander::matcher! +} diff --git a/tests/ui/proc-macro/match-expander.stderr b/tests/ui/proc-macro/match-expander.stderr new file mode 100644 index 000000000000..38a0acc4941e --- /dev/null +++ b/tests/ui/proc-macro/match-expander.stderr @@ -0,0 +1,14 @@ +error[E0308]: mismatched types + --> $DIR/match-expander.rs:8:5 + | +LL | match_expander::matcher!(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | expected `S`, found `bool` + | this expression has type `S` + | + = note: this error originates in the macro `match_expander::matcher` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0308`. From 48816e74930cb8769fea5acb8a706878b4d79d5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Sun, 10 Aug 2025 21:55:02 +0000 Subject: [PATCH 147/195] Do not point at macro invocation when providing inference context --- .../rustc_trait_selection/src/error_reporting/infer/mod.rs | 2 +- tests/ui/proc-macro/match-expander.rs | 1 - tests/ui/proc-macro/match-expander.stderr | 5 +---- 3 files changed, 2 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs index 1c890821b1d0..8551780bcd5f 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs @@ -459,7 +459,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { span, format!("this is an iterator with items of type `{}`", args.type_at(0)), ); - } else { + } else if !span.overlaps(cause.span) { let expected_ty = self.tcx.short_string(expected_ty, err.long_ty_path()); err.span_label(span, format!("this expression has type `{expected_ty}`")); } diff --git a/tests/ui/proc-macro/match-expander.rs b/tests/ui/proc-macro/match-expander.rs index 34e9a876153a..23e5746c540f 100644 --- a/tests/ui/proc-macro/match-expander.rs +++ b/tests/ui/proc-macro/match-expander.rs @@ -8,6 +8,5 @@ fn main() { match_expander::matcher!(); //~^ ERROR: mismatched types //~| NOTE: expected `S`, found `bool` - //~| NOTE: this expression has type `S` //~| NOTE: in this expansion of match_expander::matcher! } diff --git a/tests/ui/proc-macro/match-expander.stderr b/tests/ui/proc-macro/match-expander.stderr index 38a0acc4941e..b77468ec60a5 100644 --- a/tests/ui/proc-macro/match-expander.stderr +++ b/tests/ui/proc-macro/match-expander.stderr @@ -2,10 +2,7 @@ error[E0308]: mismatched types --> $DIR/match-expander.rs:8:5 | LL | match_expander::matcher!(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - | | - | expected `S`, found `bool` - | this expression has type `S` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `S`, found `bool` | = note: this error originates in the macro `match_expander::matcher` (in Nightly builds, run with -Z macro-backtrace for more info) From 74c3727b1fbb274d5b5f841302310461ac54ca5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Sun, 10 Aug 2025 21:59:04 +0000 Subject: [PATCH 148/195] Remove unnecessary parentheses in `assert!`s --- library/coretests/tests/tuple.rs | 2 +- library/std/tests/env.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/library/coretests/tests/tuple.rs b/library/coretests/tests/tuple.rs index ea1e281425c8..5d680d104723 100644 --- a/library/coretests/tests/tuple.rs +++ b/library/coretests/tests/tuple.rs @@ -37,7 +37,7 @@ fn test_partial_ord() { assert!(!((1.0f64, 2.0f64) <= (f64::NAN, 3.0))); assert!(!((1.0f64, 2.0f64) > (f64::NAN, 3.0))); assert!(!((1.0f64, 2.0f64) >= (f64::NAN, 3.0))); - assert!(((1.0f64, 2.0f64) < (2.0, f64::NAN))); + assert!((1.0f64, 2.0f64) < (2.0, f64::NAN)); assert!(!((2.0f64, 2.0f64) < (2.0, f64::NAN))); } diff --git a/library/std/tests/env.rs b/library/std/tests/env.rs index e754cf8263b0..b53fd69b7070 100644 --- a/library/std/tests/env.rs +++ b/library/std/tests/env.rs @@ -16,7 +16,7 @@ fn test_self_exe_path() { #[test] fn test() { - assert!((!Path::new("test-path").is_absolute())); + assert!(!Path::new("test-path").is_absolute()); #[cfg(not(target_env = "sgx"))] current_dir().unwrap(); From fadc961d99f221fd720f97dc9eec61c2332cd451 Mon Sep 17 00:00:00 2001 From: gewitternacht <60887951+gewitternacht@users.noreply.github.com> Date: Mon, 11 Aug 2025 00:05:47 +0200 Subject: [PATCH 149/195] mention `Hash` and `Ord`; refine description of `derive` --- library/core/src/clone.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/library/core/src/clone.rs b/library/core/src/clone.rs index 6fcba298a432..b5daa3d41fa0 100644 --- a/library/core/src/clone.rs +++ b/library/core/src/clone.rs @@ -153,10 +153,14 @@ mod uninit; /// Standard library collections such as /// [`HashMap`], [`HashSet`], [`BTreeMap`], [`BTreeSet`] and [`BinaryHeap`] /// rely on their keys respecting this property for correct behavior. +/// Furthermore, these collections require that cloning a key preserves the outcome of the +/// [`Hash`] and [`Ord`] methods. Thankfully, this follows automatically from `x.clone() == x` +/// if `Hash` and `Ord` are correctly implemented according to their own requirements. /// -/// This property is automatically satisfied when deriving both `Clone` and [`PartialEq`] -/// using `#[derive(Clone, PartialEq)]` or when additionally deriving [`Eq`] -/// using `#[derive(Clone, PartialEq, Eq)]`. +/// When deriving both `Clone` and [`PartialEq`] using `#[derive(Clone, PartialEq)]` +/// or when additionally deriving [`Eq`] using `#[derive(Clone, PartialEq, Eq)]`, +/// then this property is automatically upheld – provided that it is satisfied by +/// the underlying types. /// /// Violating this property is a logic error. The behavior resulting from a logic error is not /// specified, but users of the trait must ensure that such logic errors do *not* result in From e1e1385ce04301f34c41e2651fc5eb2f34c89348 Mon Sep 17 00:00:00 2001 From: lcnr Date: Fri, 8 Aug 2025 14:02:42 +0200 Subject: [PATCH 150/195] remove `from_forall` --- .../rustc_borrowck/src/handle_placeholders.rs | 2 +- compiler/rustc_borrowck/src/region_infer/mod.rs | 13 +++++++++---- compiler/rustc_borrowck/src/renumber.rs | 2 +- .../rustc_borrowck/src/type_check/relate_tys.rs | 16 ++++------------ compiler/rustc_infer/src/infer/mod.rs | 11 ----------- 5 files changed, 15 insertions(+), 29 deletions(-) diff --git a/compiler/rustc_borrowck/src/handle_placeholders.rs b/compiler/rustc_borrowck/src/handle_placeholders.rs index e168b819a8d6..34599ac55b85 100644 --- a/compiler/rustc_borrowck/src/handle_placeholders.rs +++ b/compiler/rustc_borrowck/src/handle_placeholders.rs @@ -157,7 +157,7 @@ fn region_definitions<'tcx>( for info in var_infos.iter() { let origin = match info.origin { RegionVariableOrigin::Nll(origin) => origin, - _ => NllRegionVariableOrigin::Existential { from_forall: false, name: None }, + _ => NllRegionVariableOrigin::Existential { name: None }, }; let definition = RegionDefinition { origin, universe: info.universe, external_name: None }; diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs index dbd245214a24..b0c31ac96010 100644 --- a/compiler/rustc_borrowck/src/region_infer/mod.rs +++ b/compiler/rustc_borrowck/src/region_infer/mod.rs @@ -1939,10 +1939,15 @@ impl<'tcx> RegionInferenceContext<'tcx> { // // and here we prefer to blame the source (the y = x statement). let blame_source = match from_region_origin { - NllRegionVariableOrigin::FreeRegion - | NllRegionVariableOrigin::Existential { from_forall: false, name: _ } => true, - NllRegionVariableOrigin::Placeholder(_) - | NllRegionVariableOrigin::Existential { from_forall: true, name: _ } => false, + NllRegionVariableOrigin::FreeRegion => true, + NllRegionVariableOrigin::Placeholder(_) => false, + // `'existential: 'whatever` never results in a region error by itself. + // We may always infer it to `'static` afterall. This means while an error + // path may go through an existential, these existentials are never the + // `from_region`. + NllRegionVariableOrigin::Existential { name: _ } => { + unreachable!("existentials can outlive everything") + } }; // To pick a constraint to blame, we organize constraints by how interesting we expect them diff --git a/compiler/rustc_borrowck/src/renumber.rs b/compiler/rustc_borrowck/src/renumber.rs index 100d30704b9e..fa3064afee81 100644 --- a/compiler/rustc_borrowck/src/renumber.rs +++ b/compiler/rustc_borrowck/src/renumber.rs @@ -66,7 +66,7 @@ impl<'a, 'tcx> RegionRenumberer<'a, 'tcx> { T: TypeFoldable>, F: Fn() -> RegionCtxt, { - let origin = NllRegionVariableOrigin::Existential { from_forall: false, name: None }; + let origin = NllRegionVariableOrigin::Existential { name: None }; fold_regions(self.infcx.tcx, value, |_region, _depth| { self.infcx.next_nll_region_var(origin, || region_ctxt_fn()) }) diff --git a/compiler/rustc_borrowck/src/type_check/relate_tys.rs b/compiler/rustc_borrowck/src/type_check/relate_tys.rs index 393adafea0b0..84ca9bad2c14 100644 --- a/compiler/rustc_borrowck/src/type_check/relate_tys.rs +++ b/compiler/rustc_borrowck/src/type_check/relate_tys.rs @@ -216,7 +216,7 @@ impl<'a, 'b, 'tcx> NllTypeRelating<'a, 'b, 'tcx> { *ex_reg_var } else { let ex_reg_var = - self.next_existential_region_var(true, br.kind.get_name(infcx.infcx.tcx)); + self.next_existential_region_var(br.kind.get_name(infcx.infcx.tcx)); debug!(?ex_reg_var); reg_map.insert(br, ex_reg_var); @@ -244,17 +244,9 @@ impl<'a, 'b, 'tcx> NllTypeRelating<'a, 'b, 'tcx> { } #[instrument(skip(self), level = "debug")] - fn next_existential_region_var( - &mut self, - from_forall: bool, - name: Option, - ) -> ty::Region<'tcx> { - let origin = NllRegionVariableOrigin::Existential { name, from_forall }; - - let reg_var = - self.type_checker.infcx.next_nll_region_var(origin, || RegionCtxt::Existential(name)); - - reg_var + fn next_existential_region_var(&mut self, name: Option) -> ty::Region<'tcx> { + let origin = NllRegionVariableOrigin::Existential { name }; + self.type_checker.infcx.next_nll_region_var(origin, || RegionCtxt::Existential(name)) } #[instrument(skip(self), level = "debug")] diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index a2afdc45fa87..82d4856df39c 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -485,17 +485,6 @@ pub enum NllRegionVariableOrigin { Existential { name: Option, - /// If this is true, then this variable was created to represent a lifetime - /// bound in a `for` binder. For example, it might have been created to - /// represent the lifetime `'a` in a type like `for<'a> fn(&'a u32)`. - /// Such variables are created when we are trying to figure out if there - /// is any valid instantiation of `'a` that could fit into some scenario. - /// - /// This is used to inform error reporting: in the case that we are trying to - /// determine whether there is any valid instantiation of a `'a` variable that meets - /// some constraint C, we want to blame the "source" of that `for` type, - /// rather than blaming the source of the constraint C. - from_forall: bool, }, } From eff5315cb174020c0f34c5c7b2c351728efdbae4 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 11 Aug 2025 14:21:01 +0200 Subject: [PATCH 151/195] Update `sysinfo` version to `0.37.0` --- Cargo.lock | 4 ++-- src/bootstrap/Cargo.lock | 4 ++-- src/bootstrap/Cargo.toml | 2 +- src/tools/opt-dist/Cargo.toml | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index dbb76ada8377..4eb246995b1c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5284,9 +5284,9 @@ dependencies = [ [[package]] name = "sysinfo" -version = "0.36.1" +version = "0.37.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "252800745060e7b9ffb7b2badbd8b31cfa4aa2e61af879d0a3bf2a317c20217d" +checksum = "07cec4dc2d2e357ca1e610cfb07de2fa7a10fc3e9fe89f72545f3d244ea87753" dependencies = [ "libc", "objc2-core-foundation", diff --git a/src/bootstrap/Cargo.lock b/src/bootstrap/Cargo.lock index e091c94eb53a..044d360ac376 100644 --- a/src/bootstrap/Cargo.lock +++ b/src/bootstrap/Cargo.lock @@ -730,9 +730,9 @@ dependencies = [ [[package]] name = "sysinfo" -version = "0.36.0" +version = "0.37.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aab138f5c1bb35231de19049060a87977ad23e04f2303e953bc5c2947ac7dec4" +checksum = "07cec4dc2d2e357ca1e610cfb07de2fa7a10fc3e9fe89f72545f3d244ea87753" dependencies = [ "libc", "memchr", diff --git a/src/bootstrap/Cargo.toml b/src/bootstrap/Cargo.toml index 8dc41d1dec69..60d4976b9344 100644 --- a/src/bootstrap/Cargo.toml +++ b/src/bootstrap/Cargo.toml @@ -58,7 +58,7 @@ walkdir = "2.4" xz2 = "0.1" # Dependencies needed by the build-metrics feature -sysinfo = { version = "0.36.0", default-features = false, optional = true, features = ["system"] } +sysinfo = { version = "0.37.0", default-features = false, optional = true, features = ["system"] } # Dependencies needed by the `tracing` feature tracing = { version = "0.1", optional = true, features = ["attributes"] } diff --git a/src/tools/opt-dist/Cargo.toml b/src/tools/opt-dist/Cargo.toml index 2ed3fbac709e..f4051ae67d7c 100644 --- a/src/tools/opt-dist/Cargo.toml +++ b/src/tools/opt-dist/Cargo.toml @@ -10,7 +10,7 @@ log = "0.4" anyhow = "1" humantime = "2" humansize = "2" -sysinfo = { version = "0.36.0", default-features = false, features = ["disk"] } +sysinfo = { version = "0.37.0", default-features = false, features = ["disk"] } fs_extra = "1" camino = "1" tar = "0.4" From cd4676c40dad98fffb3910854454e58303370f0a Mon Sep 17 00:00:00 2001 From: Stypox Date: Mon, 11 Aug 2025 14:45:46 +0200 Subject: [PATCH 152/195] Turn _span into _trace as trace span name _span could possibly be confused with the Span type in rustc --- .../rustc_const_eval/src/interpret/call.rs | 4 ++-- .../src/interpret/eval_context.rs | 8 +++---- .../rustc_const_eval/src/interpret/operand.rs | 4 ++-- .../rustc_const_eval/src/interpret/place.rs | 2 +- .../rustc_const_eval/src/interpret/step.rs | 4 ++-- .../rustc_const_eval/src/interpret/util.rs | 14 +++++------ .../src/interpret/validity.rs | 2 +- src/tools/miri/src/borrow_tracker/mod.rs | 24 +++++++++---------- 8 files changed, 31 insertions(+), 31 deletions(-) diff --git a/compiler/rustc_const_eval/src/interpret/call.rs b/compiler/rustc_const_eval/src/interpret/call.rs index a0160d1188d0..b1cc0cc2878a 100644 --- a/compiler/rustc_const_eval/src/interpret/call.rs +++ b/compiler/rustc_const_eval/src/interpret/call.rs @@ -346,7 +346,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { destination: &PlaceTy<'tcx, M::Provenance>, mut cont: ReturnContinuation, ) -> InterpResult<'tcx> { - let _span = enter_trace_span!(M, step::init_stack_frame, %instance, tracing_separate_thread = Empty); + let _trace = enter_trace_span!(M, step::init_stack_frame, %instance, tracing_separate_thread = Empty); // Compute callee information. // FIXME: for variadic support, do we have to somehow determine callee's extra_args? @@ -527,7 +527,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { target: Option, unwind: mir::UnwindAction, ) -> InterpResult<'tcx> { - let _span = + let _trace = enter_trace_span!(M, step::init_fn_call, tracing_separate_thread = Empty, ?fn_val) .or_if_tracing_disabled(|| trace!("init_fn_call: {:#?}", fn_val)); diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs index c4b705d7124e..d4f2bb8257db 100644 --- a/compiler/rustc_const_eval/src/interpret/eval_context.rs +++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs @@ -113,7 +113,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { /// See [LayoutOf::layout_of] for the original documentation. #[inline(always)] pub fn layout_of(&self, ty: Ty<'tcx>) -> >::LayoutOfResult { - let _span = enter_trace_span!(M, layouting::layout_of, ty = ?ty.kind()); + let _trace = enter_trace_span!(M, layouting::layout_of, ty = ?ty.kind()); LayoutOf::layout_of(self, ty) } @@ -126,7 +126,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { sig: ty::PolyFnSig<'tcx>, extra_args: &'tcx ty::List>, ) -> >::FnAbiOfResult { - let _span = enter_trace_span!(M, layouting::fn_abi_of_fn_ptr, ?sig, ?extra_args); + let _trace = enter_trace_span!(M, layouting::fn_abi_of_fn_ptr, ?sig, ?extra_args); FnAbiOf::fn_abi_of_fn_ptr(self, sig, extra_args) } @@ -139,7 +139,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { instance: ty::Instance<'tcx>, extra_args: &'tcx ty::List>, ) -> >::FnAbiOfResult { - let _span = enter_trace_span!(M, layouting::fn_abi_of_instance, ?instance, ?extra_args); + let _trace = enter_trace_span!(M, layouting::fn_abi_of_instance, ?instance, ?extra_args); FnAbiOf::fn_abi_of_instance(self, instance, extra_args) } } @@ -322,7 +322,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { frame: &Frame<'tcx, M::Provenance, M::FrameExtra>, value: T, ) -> Result { - let _span = enter_trace_span!( + let _trace = enter_trace_span!( M, "instantiate_from_frame_and_normalize_erasing_regions", "{}", diff --git a/compiler/rustc_const_eval/src/interpret/operand.rs b/compiler/rustc_const_eval/src/interpret/operand.rs index 145418090700..53a440b646b0 100644 --- a/compiler/rustc_const_eval/src/interpret/operand.rs +++ b/compiler/rustc_const_eval/src/interpret/operand.rs @@ -773,7 +773,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { mir_place: mir::Place<'tcx>, layout: Option>, ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { - let _span = enter_trace_span!( + let _trace = enter_trace_span!( M, step::eval_place_to_op, ?mir_place, @@ -823,7 +823,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { mir_op: &mir::Operand<'tcx>, layout: Option>, ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { - let _span = + let _trace = enter_trace_span!(M, step::eval_operand, ?mir_op, tracing_separate_thread = Empty); use rustc_middle::mir::Operand::*; diff --git a/compiler/rustc_const_eval/src/interpret/place.rs b/compiler/rustc_const_eval/src/interpret/place.rs index 45c4edb85037..6ff50dc700fa 100644 --- a/compiler/rustc_const_eval/src/interpret/place.rs +++ b/compiler/rustc_const_eval/src/interpret/place.rs @@ -526,7 +526,7 @@ where &self, mir_place: mir::Place<'tcx>, ) -> InterpResult<'tcx, PlaceTy<'tcx, M::Provenance>> { - let _span = + let _trace = enter_trace_span!(M, step::eval_place, ?mir_place, tracing_separate_thread = Empty); let mut place = self.local_to_place(mir_place.local)?; diff --git a/compiler/rustc_const_eval/src/interpret/step.rs b/compiler/rustc_const_eval/src/interpret/step.rs index 9df49c0f4ccd..76e470b69dce 100644 --- a/compiler/rustc_const_eval/src/interpret/step.rs +++ b/compiler/rustc_const_eval/src/interpret/step.rs @@ -76,7 +76,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { /// /// This does NOT move the statement counter forward, the caller has to do that! pub fn eval_statement(&mut self, stmt: &mir::Statement<'tcx>) -> InterpResult<'tcx> { - let _span = enter_trace_span!( + let _trace = enter_trace_span!( M, step::eval_statement, stmt = ?stmt.kind, @@ -465,7 +465,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { } fn eval_terminator(&mut self, terminator: &mir::Terminator<'tcx>) -> InterpResult<'tcx> { - let _span = enter_trace_span!( + let _trace = enter_trace_span!( M, step::eval_terminator, terminator = ?terminator.kind, diff --git a/compiler/rustc_const_eval/src/interpret/util.rs b/compiler/rustc_const_eval/src/interpret/util.rs index 71800950faab..72bee3454065 100644 --- a/compiler/rustc_const_eval/src/interpret/util.rs +++ b/compiler/rustc_const_eval/src/interpret/util.rs @@ -85,11 +85,11 @@ impl EnteredTraceSpan for tracing::span::EnteredSpan { /// # let my_debug_var = String::new(); /// // logs a span named "hello" with a field named "arg" of value 42 (works only because /// // 42 implements the tracing::Value trait, otherwise use one of the options below) -/// let _span = enter_trace_span!(M, "hello", arg = 42); +/// let _trace = enter_trace_span!(M, "hello", arg = 42); /// // logs a field called "my_display_var" using the Display implementation -/// let _span = enter_trace_span!(M, "hello", %my_display_var); +/// let _trace = enter_trace_span!(M, "hello", %my_display_var); /// // logs a field called "my_debug_var" using the Debug implementation -/// let _span = enter_trace_span!(M, "hello", ?my_debug_var); +/// let _trace = enter_trace_span!(M, "hello", ?my_debug_var); /// ``` /// /// ### `NAME::SUBNAME` syntax @@ -107,8 +107,8 @@ impl EnteredTraceSpan for tracing::span::EnteredSpan { /// # use rustc_const_eval::enter_trace_span; /// # type M = rustc_const_eval::const_eval::CompileTimeMachine<'static>; /// // for example, the first will expand to the second -/// let _span = enter_trace_span!(M, borrow_tracker::on_stack_pop, /* ... */); -/// let _span = enter_trace_span!(M, "borrow_tracker", borrow_tracker = "on_stack_pop", /* ... */); +/// let _trace = enter_trace_span!(M, borrow_tracker::on_stack_pop, /* ... */); +/// let _trace = enter_trace_span!(M, "borrow_tracker", borrow_tracker = "on_stack_pop", /* ... */); /// ``` /// /// ### `tracing_separate_thread` parameter @@ -124,7 +124,7 @@ impl EnteredTraceSpan for tracing::span::EnteredSpan { /// ```rust /// # use rustc_const_eval::enter_trace_span; /// # type M = rustc_const_eval::const_eval::CompileTimeMachine<'static>; -/// let _span = enter_trace_span!(M, step::eval_statement, tracing_separate_thread = tracing::field::Empty); +/// let _trace = enter_trace_span!(M, step::eval_statement, tracing_separate_thread = tracing::field::Empty); /// ``` /// /// ### Executing something else when tracing is disabled @@ -136,7 +136,7 @@ impl EnteredTraceSpan for tracing::span::EnteredSpan { /// # use rustc_const_eval::enter_trace_span; /// # use rustc_const_eval::interpret::EnteredTraceSpan; /// # type M = rustc_const_eval::const_eval::CompileTimeMachine<'static>; -/// let _span = enter_trace_span!(M, step::eval_statement) +/// let _trace = enter_trace_span!(M, step::eval_statement) /// .or_if_tracing_disabled(|| tracing::info!("eval_statement")); /// ``` #[macro_export] diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs index ed48f53c3105..ab0c0665d511 100644 --- a/compiler/rustc_const_eval/src/interpret/validity.rs +++ b/compiler/rustc_const_eval/src/interpret/validity.rs @@ -1415,7 +1415,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { recursive: bool, reset_provenance_and_padding: bool, ) -> InterpResult<'tcx> { - let _span = enter_trace_span!( + let _trace = enter_trace_span!( M, "validate_operand", "recursive={recursive}, reset_provenance_and_padding={reset_provenance_and_padding}, val={val:?}" diff --git a/src/tools/miri/src/borrow_tracker/mod.rs b/src/tools/miri/src/borrow_tracker/mod.rs index ec6c2c60ca9c..89bd93edae12 100644 --- a/src/tools/miri/src/borrow_tracker/mod.rs +++ b/src/tools/miri/src/borrow_tracker/mod.rs @@ -260,7 +260,7 @@ impl GlobalStateInner { kind: MemoryKind, machine: &MiriMachine<'_>, ) -> AllocState { - let _span = enter_trace_span!(borrow_tracker::new_allocation, ?id, ?alloc_size, ?kind); + let _trace = enter_trace_span!(borrow_tracker::new_allocation, ?id, ?alloc_size, ?kind); match self.borrow_tracker_method { BorrowTrackerMethod::StackedBorrows => AllocState::StackedBorrows(Box::new(RefCell::new(Stacks::new_allocation( @@ -281,7 +281,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { kind: RetagKind, val: &ImmTy<'tcx>, ) -> InterpResult<'tcx, ImmTy<'tcx>> { - let _span = enter_trace_span!(borrow_tracker::retag_ptr_value, ?kind, ?val.layout); + let _trace = enter_trace_span!(borrow_tracker::retag_ptr_value, ?kind, ?val.layout); let this = self.eval_context_mut(); let method = this.machine.borrow_tracker.as_ref().unwrap().borrow().borrow_tracker_method; match method { @@ -295,7 +295,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { kind: RetagKind, place: &PlaceTy<'tcx>, ) -> InterpResult<'tcx> { - let _span = enter_trace_span!(borrow_tracker::retag_place_contents, ?kind, ?place); + let _trace = enter_trace_span!(borrow_tracker::retag_place_contents, ?kind, ?place); let this = self.eval_context_mut(); let method = this.machine.borrow_tracker.as_ref().unwrap().borrow().borrow_tracker_method; match method { @@ -305,7 +305,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } fn protect_place(&mut self, place: &MPlaceTy<'tcx>) -> InterpResult<'tcx, MPlaceTy<'tcx>> { - let _span = enter_trace_span!(borrow_tracker::protect_place, ?place); + let _trace = enter_trace_span!(borrow_tracker::protect_place, ?place); let this = self.eval_context_mut(); let method = this.machine.borrow_tracker.as_ref().unwrap().borrow().borrow_tracker_method; match method { @@ -315,7 +315,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } fn expose_tag(&self, alloc_id: AllocId, tag: BorTag) -> InterpResult<'tcx> { - let _span = + let _trace = enter_trace_span!(borrow_tracker::expose_tag, alloc_id = alloc_id.0, tag = tag.0); let this = self.eval_context_ref(); let method = this.machine.borrow_tracker.as_ref().unwrap().borrow().borrow_tracker_method; @@ -360,7 +360,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { &self, frame: &Frame<'tcx, Provenance, FrameExtra<'tcx>>, ) -> InterpResult<'tcx> { - let _span = enter_trace_span!(borrow_tracker::on_stack_pop); + let _trace = enter_trace_span!(borrow_tracker::on_stack_pop); let this = self.eval_context_ref(); let borrow_tracker = this.machine.borrow_tracker.as_ref().unwrap(); // The body of this loop needs `borrow_tracker` immutably @@ -438,7 +438,7 @@ impl AllocState { range: AllocRange, machine: &MiriMachine<'tcx>, ) -> InterpResult<'tcx> { - let _span = enter_trace_span!(borrow_tracker::before_memory_read, alloc_id = alloc_id.0); + let _trace = enter_trace_span!(borrow_tracker::before_memory_read, alloc_id = alloc_id.0); match self { AllocState::StackedBorrows(sb) => sb.borrow_mut().before_memory_read(alloc_id, prov_extra, range, machine), @@ -460,7 +460,7 @@ impl AllocState { range: AllocRange, machine: &MiriMachine<'tcx>, ) -> InterpResult<'tcx> { - let _span = enter_trace_span!(borrow_tracker::before_memory_write, alloc_id = alloc_id.0); + let _trace = enter_trace_span!(borrow_tracker::before_memory_write, alloc_id = alloc_id.0); match self { AllocState::StackedBorrows(sb) => sb.get_mut().before_memory_write(alloc_id, prov_extra, range, machine), @@ -482,7 +482,7 @@ impl AllocState { size: Size, machine: &MiriMachine<'tcx>, ) -> InterpResult<'tcx> { - let _span = + let _trace = enter_trace_span!(borrow_tracker::before_memory_deallocation, alloc_id = alloc_id.0); match self { AllocState::StackedBorrows(sb) => @@ -493,7 +493,7 @@ impl AllocState { } pub fn remove_unreachable_tags(&self, tags: &FxHashSet) { - let _span = enter_trace_span!(borrow_tracker::remove_unreachable_tags); + let _trace = enter_trace_span!(borrow_tracker::remove_unreachable_tags); match self { AllocState::StackedBorrows(sb) => sb.borrow_mut().remove_unreachable_tags(tags), AllocState::TreeBorrows(tb) => tb.borrow_mut().remove_unreachable_tags(tags), @@ -508,7 +508,7 @@ impl AllocState { tag: BorTag, alloc_id: AllocId, // diagnostics ) -> InterpResult<'tcx> { - let _span = enter_trace_span!( + let _trace = enter_trace_span!( borrow_tracker::release_protector, alloc_id = alloc_id.0, tag = tag.0 @@ -523,7 +523,7 @@ impl AllocState { impl VisitProvenance for AllocState { fn visit_provenance(&self, visit: &mut VisitWith<'_>) { - let _span = enter_trace_span!(borrow_tracker::visit_provenance); + let _trace = enter_trace_span!(borrow_tracker::visit_provenance); match self { AllocState::StackedBorrows(sb) => sb.visit_provenance(visit), AllocState::TreeBorrows(tb) => tb.visit_provenance(visit), From 6603fe1caa9aefea78aea19c587828cdcffc883d Mon Sep 17 00:00:00 2001 From: Sasha Pourcelot Date: Sun, 3 Aug 2025 11:54:07 +0200 Subject: [PATCH 153/195] Port `#[allow_internal_unsafe]` to the new attribute system (attempt 2) --- .../src/attributes/macro_attrs.rs | 8 +++ compiler/rustc_attr_parsing/src/context.rs | 5 +- compiler/rustc_expand/src/base.rs | 5 +- .../rustc_hir/src/attrs/data_structures.rs | 3 ++ .../rustc_hir/src/attrs/encode_cross_crate.rs | 1 + compiler/rustc_hir/src/hir.rs | 1 + compiler/rustc_lint/src/builtin.rs | 20 +++++--- compiler/rustc_passes/messages.ftl | 2 +- compiler/rustc_passes/src/check_attr.rs | 49 ++++++++++++++++--- compiler/rustc_passes/src/errors.rs | 4 +- tests/ui/attributes/malformed-attrs.stderr | 15 +++--- 11 files changed, 85 insertions(+), 28 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/attributes/macro_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/macro_attrs.rs index 886f7a889d30..0779248e1a95 100644 --- a/compiler/rustc_attr_parsing/src/attributes/macro_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/macro_attrs.rs @@ -113,3 +113,11 @@ impl AttributeParser for MacroUseParser { Some(AttributeKind::MacroUse { span: self.first_span?, arguments: self.state }) } } + +pub(crate) struct AllowInternalUnsafeParser; + +impl NoArgsAttributeParser for AllowInternalUnsafeParser { + const PATH: &[Symbol] = &[sym::allow_internal_unsafe]; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Ignore; + const CREATE: fn(Span) -> AttributeKind = |span| AttributeKind::AllowInternalUnsafe(span); +} diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 80dfdffdb554..1420753a44ea 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -33,7 +33,9 @@ use crate::attributes::lint_helpers::{ AsPtrParser, AutomaticallyDerivedParser, PassByValueParser, PubTransparentParser, }; use crate::attributes::loop_match::{ConstContinueParser, LoopMatchParser}; -use crate::attributes::macro_attrs::{MacroEscapeParser, MacroUseParser}; +use crate::attributes::macro_attrs::{ + AllowInternalUnsafeParser, MacroEscapeParser, MacroUseParser, +}; use crate::attributes::must_use::MustUseParser; use crate::attributes::no_implicit_prelude::NoImplicitPreludeParser; use crate::attributes::non_exhaustive::NonExhaustiveParser; @@ -178,6 +180,7 @@ attribute_parsers!( Single, Single, Single>, + Single>, Single>, Single>, Single>, diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index c234aa43c09c..7da3bf27eb5c 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -904,10 +904,7 @@ impl SyntaxExtension { find_attr!(attrs, AttributeKind::AllowInternalUnstable(i, _) => i) .map(|i| i.as_slice()) .unwrap_or_default(); - // FIXME(jdonszelman): allow_internal_unsafe isn't yet new-style - // let allow_internal_unsafe = find_attr!(attrs, AttributeKind::AllowInternalUnsafe); - let allow_internal_unsafe = - ast::attr::find_by_name(attrs, sym::allow_internal_unsafe).is_some(); + let allow_internal_unsafe = find_attr!(attrs, AttributeKind::AllowInternalUnsafe(_)); let local_inner_macros = ast::attr::find_by_name(attrs, sym::macro_export) .and_then(|macro_export| macro_export.meta_item_list()) diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index 5f4193154674..e02edf5fe24d 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -249,6 +249,9 @@ pub enum AttributeKind { /// Represents `#[rustc_allow_incoherent_impl]`. AllowIncoherentImpl(Span), + /// Represents `#[allow_internal_unsafe]`. + AllowInternalUnsafe(Span), + /// Represents `#[allow_internal_unstable]`. AllowInternalUnstable(ThinVec<(Symbol, Span)>, Span), diff --git a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs index e3a7f0b97a8f..7ce624dcc550 100644 --- a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs +++ b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs @@ -16,6 +16,7 @@ impl AttributeKind { Align { .. } => No, AllowConstFnUnstable(..) => No, AllowIncoherentImpl(..) => No, + AllowInternalUnsafe(..) => Yes, AllowInternalUnstable(..) => Yes, AsPtr(..) => Yes, AutomaticallyDerived(..) => Yes, diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 34db6f92d928..0f63512b34c3 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -1310,6 +1310,7 @@ impl AttributeExt for Attribute { Attribute::Parsed(AttributeKind::Ignore { span, .. }) => *span, Attribute::Parsed(AttributeKind::ShouldPanic { span, .. }) => *span, Attribute::Parsed(AttributeKind::AutomaticallyDerived(span)) => *span, + Attribute::Parsed(AttributeKind::AllowInternalUnsafe(span)) => *span, a => panic!("can't get the span of an arbitrary parsed attribute: {a:?}"), } } diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index c893b7233755..4ae0ef68534f 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -21,6 +21,7 @@ use rustc_ast::tokenstream::{TokenStream, TokenTree}; use rustc_ast::visit::{FnCtxt, FnKind}; use rustc_ast::{self as ast, *}; use rustc_ast_pretty::pprust::expr_to_string; +use rustc_attr_parsing::AttributeParser; use rustc_errors::{Applicability, LintDiagnostic}; use rustc_feature::GateIssue; use rustc_hir as hir; @@ -248,12 +249,6 @@ impl UnsafeCode { } impl EarlyLintPass for UnsafeCode { - fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &ast::Attribute) { - if attr.has_name(sym::allow_internal_unsafe) { - self.report_unsafe(cx, attr.span, BuiltinUnsafe::AllowInternalUnsafe); - } - } - #[inline] fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) { if let ast::ExprKind::Block(ref blk, _) = e.kind { @@ -312,6 +307,19 @@ impl EarlyLintPass for UnsafeCode { } } + ast::ItemKind::MacroDef(..) => { + if let Some(attr) = AttributeParser::parse_limited( + cx.builder.sess(), + &it.attrs, + sym::allow_internal_unsafe, + it.span, + DUMMY_NODE_ID, + Some(cx.builder.features()), + ) { + self.report_unsafe(cx, attr.span(), BuiltinUnsafe::AllowInternalUnsafe); + } + } + _ => {} } } diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl index 6a28fe2617ed..4a1f01cc5c85 100644 --- a/compiler/rustc_passes/messages.ftl +++ b/compiler/rustc_passes/messages.ftl @@ -29,7 +29,7 @@ passes_allow_incoherent_impl = `rustc_allow_incoherent_impl` attribute should be applied to impl items .label = the only currently supported targets are inherent methods -passes_allow_internal_unstable = +passes_macro_only_attribute = attribute should be applied to a macro .label = not a macro diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 10c532b436aa..d768aa6a8ddc 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -207,6 +207,9 @@ impl<'tcx> CheckAttrVisitor<'tcx> { Attribute::Parsed(AttributeKind::ConstContinue(attr_span)) => { self.check_const_continue(hir_id, *attr_span, target) } + Attribute::Parsed(AttributeKind::AllowInternalUnsafe(attr_span)) => { + self.check_allow_internal_unsafe(hir_id, *attr_span, span, target, attrs) + } Attribute::Parsed(AttributeKind::AllowInternalUnstable(_, first_span)) => { self.check_allow_internal_unstable(hir_id, *first_span, span, target, attrs) } @@ -413,7 +416,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { // internal | sym::prelude_import | sym::panic_handler - | sym::allow_internal_unsafe | sym::lang | sym::needs_allocator | sym::default_lib_allocator @@ -2212,7 +2214,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { /// Outputs an error for `#[allow_internal_unstable]` which can only be applied to macros. /// (Allows proc_macro functions) - // FIXME(jdonszelmann): if possible, move to attr parsing fn check_allow_internal_unstable( &self, hir_id: HirId, @@ -2220,6 +2221,42 @@ impl<'tcx> CheckAttrVisitor<'tcx> { span: Span, target: Target, attrs: &[Attribute], + ) { + self.check_macro_only_attr( + hir_id, + attr_span, + span, + target, + attrs, + "allow_internal_unstable", + ) + } + + /// Outputs an error for `#[allow_internal_unsafe]` which can only be applied to macros. + /// (Allows proc_macro functions) + fn check_allow_internal_unsafe( + &self, + hir_id: HirId, + attr_span: Span, + span: Span, + target: Target, + attrs: &[Attribute], + ) { + self.check_macro_only_attr(hir_id, attr_span, span, target, attrs, "allow_internal_unsafe") + } + + /// Outputs an error for attributes that can only be applied to macros, such as + /// `#[allow_internal_unsafe]` and `#[allow_internal_unstable]`. + /// (Allows proc_macro functions) + // FIXME(jdonszelmann): if possible, move to attr parsing + fn check_macro_only_attr( + &self, + hir_id: HirId, + attr_span: Span, + span: Span, + target: Target, + attrs: &[Attribute], + attr_name: &str, ) { match target { Target::Fn => { @@ -2238,18 +2275,14 @@ impl<'tcx> CheckAttrVisitor<'tcx> { // erroneously allowed it and some crates used it accidentally, to be compatible // with crates depending on them, we can't throw an error here. Target::Field | Target::Arm => { - self.inline_attr_str_error_without_macro_def( - hir_id, - attr_span, - "allow_internal_unstable", - ); + self.inline_attr_str_error_without_macro_def(hir_id, attr_span, attr_name); return; } // otherwise continue out of the match _ => {} } - self.tcx.dcx().emit_err(errors::AllowInternalUnstable { attr_span, span }); + self.tcx.dcx().emit_err(errors::MacroOnlyAttribute { attr_span, span }); } /// Checks if the items on the `#[debugger_visualizer]` attribute are valid. diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index c6ab6b0d6017..10b30fbe8c94 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -643,8 +643,8 @@ pub(crate) struct UsedStatic { } #[derive(Diagnostic)] -#[diag(passes_allow_internal_unstable)] -pub(crate) struct AllowInternalUnstable { +#[diag(passes_macro_only_attribute)] +pub(crate) struct MacroOnlyAttribute { #[primary_span] pub attr_span: Span, #[label] diff --git a/tests/ui/attributes/malformed-attrs.stderr b/tests/ui/attributes/malformed-attrs.stderr index dd9dd3a6ce7f..741101834504 100644 --- a/tests/ui/attributes/malformed-attrs.stderr +++ b/tests/ui/attributes/malformed-attrs.stderr @@ -145,12 +145,6 @@ LL - #[macro_export = 18] LL + #[macro_export] | -error: malformed `allow_internal_unsafe` attribute input - --> $DIR/malformed-attrs.rs:213:1 - | -LL | #[allow_internal_unsafe = 1] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[allow_internal_unsafe]` - error: the `#[proc_macro]` attribute is only usable with crates of the `proc-macro` crate type --> $DIR/malformed-attrs.rs:96:1 | @@ -561,6 +555,15 @@ error: valid forms for the attribute are `#[macro_use(name1, name2, ...)]` and ` LL | #[macro_use = 1] | ^^^^^^^^^^^^^^^^ +error[E0565]: malformed `allow_internal_unsafe` attribute input + --> $DIR/malformed-attrs.rs:213:1 + | +LL | #[allow_internal_unsafe = 1] + | ^^^^^^^^^^^^^^^^^^^^^^^^---^ + | | | + | | didn't expect any arguments here + | help: must be of the form: `#[allow_internal_unsafe]` + error[E0565]: malformed `type_const` attribute input --> $DIR/malformed-attrs.rs:140:5 | From d523b9f3253b935ef3b293971bf9e7f911ff105f Mon Sep 17 00:00:00 2001 From: tiif Date: Mon, 11 Aug 2025 13:09:15 +0000 Subject: [PATCH 154/195] Support using #[unstable_feature_bound] on trait --- compiler/rustc_passes/src/check_attr.rs | 3 +- ...nstable_feature_bound_on_trait.fail.stderr | 18 ++++++++++ .../unstable_feature_bound_on_trait.rs | 33 +++++++++++++++++++ 3 files changed, 52 insertions(+), 2 deletions(-) create mode 100644 tests/ui/unstable-feature-bound/unstable_feature_bound_on_trait.fail.stderr create mode 100644 tests/ui/unstable-feature-bound/unstable_feature_bound_on_trait.rs diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 10c532b436aa..48638bd1a8d7 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -2294,7 +2294,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { match target { // FIXME(staged_api): There's no reason we can't support more targets here. We're just // being conservative to begin with. - Target::Fn | Target::Impl { .. } => {} + Target::Fn | Target::Impl { .. } | Target::Trait => {} Target::ExternCrate | Target::Use | Target::Static @@ -2309,7 +2309,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | Target::Struct | Target::Field | Target::Union - | Target::Trait | Target::TraitAlias | Target::Expression | Target::Statement diff --git a/tests/ui/unstable-feature-bound/unstable_feature_bound_on_trait.fail.stderr b/tests/ui/unstable-feature-bound/unstable_feature_bound_on_trait.fail.stderr new file mode 100644 index 000000000000..69be101a40d2 --- /dev/null +++ b/tests/ui/unstable-feature-bound/unstable_feature_bound_on_trait.fail.stderr @@ -0,0 +1,18 @@ +error: unstable feature `foo` is used without being enabled. + --> $DIR/unstable_feature_bound_on_trait.rs:28:5 + | +LL | Foo::bar(); + | ^^^^^^^^^^ + | + = help: The feature can be enabled by marking the current item with `#[unstable_feature_bound(foo)]` +note: required by a bound in `Bar::bar` + --> $DIR/unstable_feature_bound_on_trait.rs:16:1 + | +LL | #[unstable_feature_bound(foo)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `Bar::bar` +... +LL | fn bar() {} + | --- required by a bound in this associated function + +error: aborting due to 1 previous error + diff --git a/tests/ui/unstable-feature-bound/unstable_feature_bound_on_trait.rs b/tests/ui/unstable-feature-bound/unstable_feature_bound_on_trait.rs new file mode 100644 index 000000000000..0ee00d5e7fb3 --- /dev/null +++ b/tests/ui/unstable-feature-bound/unstable_feature_bound_on_trait.rs @@ -0,0 +1,33 @@ +//@ revisions: pass fail +//@[pass] check-pass + +#![allow(internal_features)] +#![feature(staged_api)] +#![stable(feature = "a", since = "1.1.1" )] + +/// Test the behaviour of marking a trait with #[unstable_feature_bound]. +/// In this testcase, even though the trait method `bar` and the `struct Foo` are +/// both stable, #[unstable_feature_bound] is still needed at the call site of Foo::bar(). + +#[stable(feature = "a", since = "1.1.1" )] +struct Foo; + +#[unstable(feature = "foo", issue = "none" )] +#[unstable_feature_bound(foo)] +trait Bar { + #[stable(feature = "a", since = "1.1.1" )] + fn bar() {} +} + +#[unstable_feature_bound(foo)] +impl Bar for Foo { +} + +#[cfg_attr(pass, unstable_feature_bound(foo))] +fn moo() { + Foo::bar(); + //[fail]~^ ERROR: unstable feature `foo` is used without being enabled. +} + + +fn main() {} From 9a8facbe535e6197f4d2dfee03df11ccb902c73d Mon Sep 17 00:00:00 2001 From: tiif Date: Mon, 11 Aug 2025 13:17:45 +0000 Subject: [PATCH 155/195] Update rustc dev guide --- src/doc/rustc-dev-guide/src/stability.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/doc/rustc-dev-guide/src/stability.md b/src/doc/rustc-dev-guide/src/stability.md index c26d34273d7e..3c4c65fdd5a8 100644 --- a/src/doc/rustc-dev-guide/src/stability.md +++ b/src/doc/rustc-dev-guide/src/stability.md @@ -182,6 +182,11 @@ of the standard library raises it to a warning with `#![warn(deprecated_in_future)]`. ## unstable_feature_bound -The `#[unstable_feature_bound(foo)]` attribute can be used together with `#[unstable]` attribute to mark an `impl` of stable type and stable trait as unstable. In std/core, an item annotated with `#[unstable_feature_bound(foo)]` can only be used by another item that is also annotated with `#[unstable_feature_bound(foo)]`. Outside of std/core, using an item with `#[unstable_feature_bound(foo)]` requires the feature to be enabled with `#![feature(foo)]` attribute on the crate. Currently, only `impl`s and free functions can be annotated with `#[unstable_feature_bound]`. +The `#[unstable_feature_bound(foo)]` attribute can be used together with `#[unstable]` attribute to mark an `impl` of stable type and stable trait as unstable. In std/core, an item annotated with `#[unstable_feature_bound(foo)]` can only be used by another item that is also annotated with `#[unstable_feature_bound(foo)]`. Outside of std/core, using an item with `#[unstable_feature_bound(foo)]` requires the feature to be enabled with `#![feature(foo)]` attribute on the crate. + +Currently, the items that can be annotated with `#[unstable_feature_bound]` are: +- `impl` +- free function +- trait [blog]: https://www.ralfj.de/blog/2018/07/19/const.html From bcf87e4172416b96e24be998e18d81a8af183356 Mon Sep 17 00:00:00 2001 From: tiif Date: Mon, 11 Aug 2025 13:27:46 +0000 Subject: [PATCH 156/195] Update error message --- compiler/rustc_passes/messages.ftl | 4 ++-- .../ui/unstable-feature-bound/unstable_inherent_method.rs | 4 ++-- .../unstable_inherent_method.stderr | 8 ++++---- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl index 6a28fe2617ed..92e8dd32dd29 100644 --- a/compiler/rustc_passes/messages.ftl +++ b/compiler/rustc_passes/messages.ftl @@ -669,8 +669,8 @@ passes_rustc_std_internal_symbol = .label = not a function or static passes_rustc_unstable_feature_bound = - attribute should be applied to `impl` or free function outside of any `impl` or trait - .label = not an `impl` or free function + attribute should be applied to `impl`, trait or free function + .label = not an `impl`, trait or free function passes_should_be_applied_to_fn = attribute should be applied to a function definition diff --git a/tests/ui/unstable-feature-bound/unstable_inherent_method.rs b/tests/ui/unstable-feature-bound/unstable_inherent_method.rs index 5f3095430a80..0d6e4ebb4085 100644 --- a/tests/ui/unstable-feature-bound/unstable_inherent_method.rs +++ b/tests/ui/unstable-feature-bound/unstable_inherent_method.rs @@ -9,14 +9,14 @@ pub trait Trait { #[unstable(feature = "feat", issue = "none" )] #[unstable_feature_bound(foo)] - //~^ ERROR: attribute should be applied to `impl` or free function outside of any `impl` or trait + //~^ ERROR: attribute should be applied to `impl`, trait or free function fn foo(); } #[stable(feature = "a", since = "1.1.1" )] impl Trait for u8 { #[unstable_feature_bound(foo)] - //~^ ERROR: attribute should be applied to `impl` or free function outside of any `impl` or trait + //~^ ERROR: attribute should be applied to `impl`, trait or free function fn foo() {} } diff --git a/tests/ui/unstable-feature-bound/unstable_inherent_method.stderr b/tests/ui/unstable-feature-bound/unstable_inherent_method.stderr index fa1c39db259a..90cbb32df7c2 100644 --- a/tests/ui/unstable-feature-bound/unstable_inherent_method.stderr +++ b/tests/ui/unstable-feature-bound/unstable_inherent_method.stderr @@ -1,20 +1,20 @@ -error: attribute should be applied to `impl` or free function outside of any `impl` or trait +error: attribute should be applied to `impl`, trait or free function --> $DIR/unstable_inherent_method.rs:11:5 | LL | #[unstable_feature_bound(foo)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ LL | LL | fn foo(); - | --------- not an `impl` or free function + | --------- not an `impl`, trait or free function -error: attribute should be applied to `impl` or free function outside of any `impl` or trait +error: attribute should be applied to `impl`, trait or free function --> $DIR/unstable_inherent_method.rs:18:5 | LL | #[unstable_feature_bound(foo)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ LL | LL | fn foo() {} - | ----------- not an `impl` or free function + | ----------- not an `impl`, trait or free function error: aborting due to 2 previous errors From 0ccbe8fc71173bf5d2e348e5b3ed6cb344db913b Mon Sep 17 00:00:00 2001 From: Ayush Singh Date: Mon, 14 Apr 2025 18:55:04 +0530 Subject: [PATCH 157/195] std: sys: pal: uefi: Overhaul Time Use a time representation with 1900-01-01-00:00:00 at timezone -1440 min as anchor. This is the earliest time supported in UEFI. Signed-off-by: Ayush Singh --- library/std/src/sys/pal/uefi/tests.rs | 3 + library/std/src/sys/pal/uefi/time.rs | 125 ++++++++++++++++++++++---- 2 files changed, 110 insertions(+), 18 deletions(-) diff --git a/library/std/src/sys/pal/uefi/tests.rs b/library/std/src/sys/pal/uefi/tests.rs index 49e75a1a70d7..6a15c03184cf 100644 --- a/library/std/src/sys/pal/uefi/tests.rs +++ b/library/std/src/sys/pal/uefi/tests.rs @@ -1,3 +1,6 @@ +//! These tests are not run automatically right now. Please run these tests manually by copying them +//! to a separate project when modifying any related code. + use super::alloc::*; use super::time::*; use crate::io::{IoSlice, IoSliceMut}; diff --git a/library/std/src/sys/pal/uefi/time.rs b/library/std/src/sys/pal/uefi/time.rs index eeb2c35ffbbc..32c3cfd143c3 100644 --- a/library/std/src/sys/pal/uefi/time.rs +++ b/library/std/src/sys/pal/uefi/time.rs @@ -1,16 +1,28 @@ use crate::time::Duration; -const SECS_IN_MINUTE: u64 = 60; -const SECS_IN_HOUR: u64 = SECS_IN_MINUTE * 60; -const SECS_IN_DAY: u64 = SECS_IN_HOUR * 24; - #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)] pub struct Instant(Duration); +/// When a Timezone is specified, the stored Duration is in UTC. If timezone is unspecified, then +/// the timezone is assumed to be in UTC. +/// +/// UEFI SystemTime is stored as Duration from 1900-01-01-00:00:00 #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)] pub struct SystemTime(Duration); -pub const UNIX_EPOCH: SystemTime = SystemTime(Duration::from_secs(0)); +pub const UNIX_EPOCH: SystemTime = SystemTime::from_uefi(r_efi::efi::Time { + year: 1970, + month: 1, + day: 1, + hour: 0, + minute: 0, + second: 0, + nanosecond: 0, + timezone: 0, + daylight: 0, + pad1: 0, + pad2: 0, +}); impl Instant { pub fn now() -> Instant { @@ -40,6 +52,14 @@ impl Instant { } impl SystemTime { + pub(crate) const fn from_uefi(t: r_efi::efi::Time) -> Self { + Self(system_time_internal::from_uefi(&t)) + } + + pub(crate) const fn to_uefi(self, timezone: i16, daylight: u8) -> Option { + system_time_internal::to_uefi(&self.0, timezone, daylight) + } + pub fn now() -> SystemTime { system_time_internal::now() .unwrap_or_else(|| panic!("time not implemented on this platform")) @@ -50,11 +70,17 @@ impl SystemTime { } pub fn checked_add_duration(&self, other: &Duration) -> Option { - Some(SystemTime(self.0.checked_add(*other)?)) + let temp = Self(self.0.checked_add(*other)?); + + // Check if can be represented in UEFI + if temp.to_uefi(0, 0).is_some() { Some(temp) } else { None } } pub fn checked_sub_duration(&self, other: &Duration) -> Option { - Some(SystemTime(self.0.checked_sub(*other)?)) + let temp = Self(self.0.checked_sub(*other)?); + + // Check if can be represented in UEFI + if temp.to_uefi(0, 0).is_some() { Some(temp) } else { None } } } @@ -66,51 +92,114 @@ pub(crate) mod system_time_internal { use crate::mem::MaybeUninit; use crate::ptr::NonNull; + const SECS_IN_MINUTE: u64 = 60; + const SECS_IN_HOUR: u64 = SECS_IN_MINUTE * 60; + const SECS_IN_DAY: u64 = SECS_IN_HOUR * 24; + const TIMEZONE_DELTA: u64 = 1440 * SECS_IN_MINUTE; + pub fn now() -> Option { let runtime_services: NonNull = helpers::runtime_services()?; let mut t: MaybeUninit Foo { .. }` or `impl Trait for Foo { .. }`. - Impl(Box), + Impl(Impl), /// A macro invocation. /// /// E.g., `foo!(..)`. @@ -3880,7 +3884,7 @@ impl ItemKind { | Self::Union(_, generics, _) | Self::Trait(box Trait { generics, .. }) | Self::TraitAlias(_, generics, _) - | Self::Impl(box Impl { generics, .. }) => Some(generics), + | Self::Impl(Impl { generics, .. }) => Some(generics), _ => None, } } @@ -4040,7 +4044,7 @@ mod size_asserts { static_assert_size!(GenericArg, 24); static_assert_size!(GenericBound, 88); static_assert_size!(Generics, 40); - static_assert_size!(Impl, 136); + static_assert_size!(Impl, 64); static_assert_size!(Item, 144); static_assert_size!(ItemKind, 80); static_assert_size!(LitKind, 24); @@ -4053,6 +4057,7 @@ mod size_asserts { static_assert_size!(PathSegment, 24); static_assert_size!(Stmt, 32); static_assert_size!(StmtKind, 16); + static_assert_size!(TraitImplHeader, 80); static_assert_size!(Ty, 64); static_assert_size!(TyKind, 40); // tidy-alphabetical-end diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index 5fdce27db53e..68b3d2b03686 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -929,8 +929,13 @@ macro_rules! common_visitor_and_walkers { } impl_walkable!(|&$($mut)? $($lt)? self: Impl, vis: &mut V| { - let Impl { defaultness, safety, generics, constness, polarity, of_trait, self_ty, items } = self; - visit_visitable!($($mut)? vis, defaultness, safety, generics, constness, polarity, of_trait, self_ty); + let Impl { generics, of_trait, self_ty, items } = self; + try_visit!(vis.visit_generics(generics)); + if let Some(box of_trait) = of_trait { + let TraitImplHeader { defaultness, safety, constness, polarity, trait_ref } = of_trait; + visit_visitable!($($mut)? vis, defaultness, safety, constness, polarity, trait_ref); + } + try_visit!(vis.visit_ty(self_ty)); visit_visitable_with!($($mut)? vis, items, AssocCtxt::Impl { of_trait: of_trait.is_some() }); V::Result::output() }); diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index ac6fac4c08e0..231752700ba0 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -340,13 +340,9 @@ impl<'hir> LoweringContext<'_, 'hir> { ); hir::ItemKind::Union(ident, generics, vdata) } - ItemKind::Impl(box Impl { - safety, - polarity, - defaultness, - constness, + ItemKind::Impl(Impl { generics: ast_generics, - of_trait: trait_ref, + of_trait, self_ty: ty, items: impl_items, }) => { @@ -373,10 +369,10 @@ impl<'hir> LoweringContext<'_, 'hir> { polarity: BoundPolarity::Positive, }; - let trait_ref = trait_ref.as_ref().map(|trait_ref| { + let trait_ref = of_trait.as_ref().map(|of_trait| { this.lower_trait_ref( modifiers, - trait_ref, + &of_trait.trait_ref, ImplTraitContext::Disallowed(ImplTraitPosition::Trait), ) }); @@ -396,14 +392,35 @@ impl<'hir> LoweringContext<'_, 'hir> { // `defaultness.has_value()` is never called for an `impl`, always `true` in order // to not cause an assertion failure inside the `lower_defaultness` function. let has_val = true; - let (defaultness, defaultness_span) = self.lower_defaultness(*defaultness, has_val); - let polarity = match polarity { - ImplPolarity::Positive => ImplPolarity::Positive, - ImplPolarity::Negative(s) => ImplPolarity::Negative(self.lower_span(*s)), + let (constness, safety, polarity, defaultness, defaultness_span) = match *of_trait { + Some(box TraitImplHeader { + constness, + safety, + polarity, + defaultness, + trait_ref: _, + }) => { + let constness = self.lower_constness(constness); + let safety = self.lower_safety(safety, hir::Safety::Safe); + let polarity = match polarity { + ImplPolarity::Positive => ImplPolarity::Positive, + ImplPolarity::Negative(s) => ImplPolarity::Negative(self.lower_span(s)), + }; + let (defaultness, defaultness_span) = + self.lower_defaultness(defaultness, has_val); + (constness, safety, polarity, defaultness, defaultness_span) + } + None => ( + hir::Constness::NotConst, + hir::Safety::Safe, + ImplPolarity::Positive, + hir::Defaultness::Final, + None, + ), }; hir::ItemKind::Impl(self.arena.alloc(hir::Impl { - constness: self.lower_constness(*constness), - safety: self.lower_safety(*safety, hir::Safety::Safe), + constness, + safety, polarity, defaultness, defaultness_span, diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index 1fffb617c507..dc2eb17589c6 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -954,13 +954,16 @@ impl<'a> Visitor<'a> for AstValidator<'a> { } match &item.kind { - ItemKind::Impl(box Impl { - safety, - polarity, - defaultness: _, - constness, + ItemKind::Impl(Impl { generics, - of_trait: Some(t), + of_trait: + Some(box TraitImplHeader { + safety, + polarity, + defaultness: _, + constness, + trait_ref: t, + }), self_ty, items, }) => { @@ -992,16 +995,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { walk_list!(this, visit_assoc_item, items, AssocCtxt::Impl { of_trait: true }); }); } - ItemKind::Impl(box Impl { - safety: _, - polarity: _, - defaultness: _, - constness: _, - generics, - of_trait: None, - self_ty, - items, - }) => { + ItemKind::Impl(Impl { generics, of_trait: None, self_ty, items }) => { self.visit_attrs_vis(&item.attrs, &item.vis); self.visibility_not_permitted( &item.vis, diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index 662357ce8841..c9344a76a7b3 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -217,18 +217,18 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { } } - ast::ItemKind::Impl(box ast::Impl { polarity, defaultness, of_trait, .. }) => { - if let &ast::ImplPolarity::Negative(span) = polarity { + ast::ItemKind::Impl(ast::Impl { of_trait: Some(of_trait), .. }) => { + if let ast::ImplPolarity::Negative(span) = of_trait.polarity { gate!( &self, negative_impls, - span.to(of_trait.as_ref().map_or(span, |t| t.path.span)), + span.to(of_trait.trait_ref.path.span), "negative trait bounds are not fully implemented; \ use marker types for now" ); } - if let ast::Defaultness::Default(_) = defaultness { + if let ast::Defaultness::Default(_) = of_trait.defaultness { gate!(&self, specialization, i.span, "specialization is unstable"); } } diff --git a/compiler/rustc_ast_pretty/src/pprust/state/item.rs b/compiler/rustc_ast_pretty/src/pprust/state/item.rs index 6e34d1b61db4..ab402cbb8dc1 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/item.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/item.rs @@ -308,39 +308,41 @@ impl<'a> State<'a> { let (cb, ib) = self.head(visibility_qualified(&item.vis, "union")); self.print_struct(struct_def, generics, *ident, item.span, true, cb, ib); } - ast::ItemKind::Impl(box ast::Impl { - safety, - polarity, - defaultness, - constness, - generics, - of_trait, - self_ty, - items, - }) => { + ast::ItemKind::Impl(ast::Impl { generics, of_trait, self_ty, items }) => { let (cb, ib) = self.head(""); self.print_visibility(&item.vis); - self.print_defaultness(*defaultness); - self.print_safety(*safety); - self.word("impl"); - if generics.params.is_empty() { - self.nbsp(); - } else { - self.print_generic_params(&generics.params); - self.space(); - } + let impl_generics = |this: &mut Self| { + this.word("impl"); - self.print_constness(*constness); + if generics.params.is_empty() { + this.nbsp(); + } else { + this.print_generic_params(&generics.params); + this.space(); + } + }; - if let ast::ImplPolarity::Negative(_) = polarity { - self.word("!"); - } - - if let Some(t) = of_trait { - self.print_trait_ref(t); + if let Some(box of_trait) = of_trait { + let ast::TraitImplHeader { + defaultness, + safety, + constness, + polarity, + ref trait_ref, + } = *of_trait; + self.print_defaultness(defaultness); + self.print_safety(safety); + impl_generics(self); + self.print_constness(constness); + if let ast::ImplPolarity::Negative(_) = polarity { + self.word("!"); + } + self.print_trait_ref(trait_ref); self.space(); self.word_space("for"); + } else { + impl_generics(self); } self.print_type(self_ty); diff --git a/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs b/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs index 6082e376435a..75db5d77783e 100644 --- a/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs +++ b/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs @@ -108,11 +108,7 @@ pub(crate) fn expand_deriving_coerce_pointee( cx.item( span, attrs.clone(), - ast::ItemKind::Impl(Box::new(ast::Impl { - safety: ast::Safety::Default, - polarity: ast::ImplPolarity::Positive, - defaultness: ast::Defaultness::Final, - constness: ast::Const::No, + ast::ItemKind::Impl(ast::Impl { generics: Generics { params: generics .params @@ -137,10 +133,16 @@ pub(crate) fn expand_deriving_coerce_pointee( where_clause: generics.where_clause.clone(), span: generics.span, }, - of_trait: Some(trait_ref), + of_trait: Some(Box::new(ast::TraitImplHeader { + safety: ast::Safety::Default, + polarity: ast::ImplPolarity::Positive, + defaultness: ast::Defaultness::Final, + constness: ast::Const::No, + trait_ref, + })), self_ty: self_type.clone(), items: ThinVec::new(), - })), + }), ), )); } @@ -152,16 +154,18 @@ pub(crate) fn expand_deriving_coerce_pointee( let item = cx.item( span, attrs.clone(), - ast::ItemKind::Impl(Box::new(ast::Impl { - safety: ast::Safety::Default, - polarity: ast::ImplPolarity::Positive, - defaultness: ast::Defaultness::Final, - constness: ast::Const::No, + ast::ItemKind::Impl(ast::Impl { generics, - of_trait: Some(trait_ref), + of_trait: Some(Box::new(ast::TraitImplHeader { + safety: ast::Safety::Default, + polarity: ast::ImplPolarity::Positive, + defaultness: ast::Defaultness::Final, + constness: ast::Const::No, + trait_ref, + })), self_ty: self_type.clone(), items: ThinVec::new(), - })), + }), ); push(Annotatable::Item(item)); }; diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs index 3f4b47152c40..3fcf9da94507 100644 --- a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs +++ b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs @@ -826,21 +826,25 @@ impl<'a> TraitDef<'a> { ) } - let opt_trait_ref = Some(trait_ref); - cx.item( self.span, attrs, - ast::ItemKind::Impl(Box::new(ast::Impl { - safety: ast::Safety::Default, - polarity: ast::ImplPolarity::Positive, - defaultness: ast::Defaultness::Final, - constness: if self.is_const { ast::Const::Yes(DUMMY_SP) } else { ast::Const::No }, + ast::ItemKind::Impl(ast::Impl { generics: trait_generics, - of_trait: opt_trait_ref, + of_trait: Some(Box::new(ast::TraitImplHeader { + safety: ast::Safety::Default, + polarity: ast::ImplPolarity::Positive, + defaultness: ast::Defaultness::Final, + constness: if self.is_const { + ast::Const::Yes(DUMMY_SP) + } else { + ast::Const::No + }, + trait_ref, + })), self_ty: self_type, items: methods.into_iter().chain(associated_types).collect(), - })), + }), ) } diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index c893b7233755..54769f9e097f 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -270,7 +270,10 @@ impl EarlyLintPass for UnsafeCode { self.report_unsafe(cx, it.span, BuiltinUnsafe::UnsafeTrait); } - ast::ItemKind::Impl(box ast::Impl { safety: ast::Safety::Unsafe(_), .. }) => { + ast::ItemKind::Impl(ast::Impl { + of_trait: Some(box ast::TraitImplHeader { safety: ast::Safety::Unsafe(_), .. }), + .. + }) => { self.report_unsafe(cx, it.span, BuiltinUnsafe::UnsafeImpl); } diff --git a/compiler/rustc_lint/src/internal.rs b/compiler/rustc_lint/src/internal.rs index 7dafcc199a32..9f02f1f0df38 100644 --- a/compiler/rustc_lint/src/internal.rs +++ b/compiler/rustc_lint/src/internal.rs @@ -411,11 +411,11 @@ declare_lint_pass!(LintPassImpl => [LINT_PASS_IMPL_WITHOUT_MACRO]); impl EarlyLintPass for LintPassImpl { fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) { - if let ast::ItemKind::Impl(box ast::Impl { of_trait: Some(lint_pass), .. }) = &item.kind - && let Some(last) = lint_pass.path.segments.last() + if let ast::ItemKind::Impl(ast::Impl { of_trait: Some(of_trait), .. }) = &item.kind + && let Some(last) = of_trait.trait_ref.path.segments.last() && last.ident.name == sym::LintPass { - let expn_data = lint_pass.path.span.ctxt().outer_expn_data(); + let expn_data = of_trait.trait_ref.path.span.ctxt().outer_expn_data(); let call_site = expn_data.call_site; if expn_data.kind != ExpnKind::Macro(MacroKind::Bang, sym::impl_lint_pass) && call_site.ctxt().outer_expn_data().kind @@ -423,7 +423,7 @@ impl EarlyLintPass for LintPassImpl { { cx.emit_span_lint( LINT_PASS_IMPL_WITHOUT_MACRO, - lint_pass.path.span, + of_trait.trait_ref.path.span, LintPassByHand, ); } diff --git a/compiler/rustc_lint/src/nonstandard_style.rs b/compiler/rustc_lint/src/nonstandard_style.rs index 7e5f43ba77f4..8fafaa33d0c4 100644 --- a/compiler/rustc_lint/src/nonstandard_style.rs +++ b/compiler/rustc_lint/src/nonstandard_style.rs @@ -187,7 +187,7 @@ impl EarlyLintPass for NonCamelCaseTypes { // N.B. This check is only for inherent associated types, so that we don't lint against // trait impls where we should have warned for the trait definition already. - ast::ItemKind::Impl(box ast::Impl { of_trait: None, items, .. }) => { + ast::ItemKind::Impl(ast::Impl { of_trait: None, items, .. }) => { for it in items { // FIXME: this doesn't respect `#[allow(..)]` on the item itself. if let ast::AssocItemKind::Type(alias) = &it.kind { diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 75cb103d5d63..607adaf08294 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -663,7 +663,14 @@ impl<'a> Parser<'a> { }; let trait_ref = TraitRef { path, ref_id: ty_first.id }; - (Some(trait_ref), ty_second) + let of_trait = Some(Box::new(TraitImplHeader { + defaultness, + safety, + constness, + polarity, + trait_ref, + })); + (of_trait, ty_second) } None => { let self_ty = ty_first; @@ -692,16 +699,8 @@ impl<'a> Parser<'a> { (None, self_ty) } }; - Ok(ItemKind::Impl(Box::new(Impl { - safety, - polarity, - defaultness, - constness, - generics, - of_trait, - self_ty, - items: impl_items, - }))) + + Ok(ItemKind::Impl(Impl { generics, of_trait, self_ty, items: impl_items })) } fn parse_item_delegation(&mut self) -> PResult<'a, ItemKind> { @@ -1389,10 +1388,10 @@ impl<'a> Parser<'a> { }; match &mut item_kind { - ItemKind::Impl(box Impl { of_trait: Some(trai), constness, .. }) => { - *constness = Const::Yes(const_span); + ItemKind::Impl(Impl { of_trait: Some(of_trait), .. }) => { + of_trait.constness = Const::Yes(const_span); - let before_trait = trai.path.span.shrink_to_lo(); + let before_trait = of_trait.trait_ref.path.span.shrink_to_lo(); let const_up_to_impl = const_span.with_hi(impl_span.lo()); err.with_multipart_suggestion( "you might have meant to write a const trait impl", diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs index d18d0fc16a8e..3fee2ab6afe1 100644 --- a/compiler/rustc_resolve/src/build_reduced_graph.rs +++ b/compiler/rustc_resolve/src/build_reduced_graph.rs @@ -11,7 +11,7 @@ use std::sync::Arc; use rustc_ast::visit::{self, AssocCtxt, Visitor, WalkItemKind}; use rustc_ast::{ self as ast, AssocItem, AssocItemKind, Block, ConstItem, Delegation, Fn, ForeignItem, - ForeignItemKind, Impl, Item, ItemKind, NodeId, StaticItem, StmtKind, TyAlias, + ForeignItemKind, Item, ItemKind, NodeId, StaticItem, StmtKind, TyAlias, }; use rustc_attr_parsing as attr; use rustc_attr_parsing::AttributeParser; @@ -906,10 +906,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { } // These items do not add names to modules. - ItemKind::Impl(box Impl { of_trait: Some(..), .. }) - | ItemKind::Impl { .. } - | ItemKind::ForeignMod(..) - | ItemKind::GlobalAsm(..) => {} + ItemKind::Impl { .. } | ItemKind::ForeignMod(..) | ItemKind::GlobalAsm(..) => {} ItemKind::MacroDef(..) | ItemKind::MacCall(_) | ItemKind::DelegationMac(..) => { unreachable!() diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 953b72fd72b8..e52cbeb733ac 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -2620,7 +2620,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { self.resolve_adt(item, generics); } - ItemKind::Impl(box Impl { + ItemKind::Impl(Impl { ref generics, ref of_trait, ref self_ty, @@ -2631,7 +2631,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { self.resolve_implementation( &item.attrs, generics, - of_trait, + of_trait.as_deref(), self_ty, item.id, impl_items, @@ -3177,7 +3177,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { &mut self, attrs: &[ast::Attribute], generics: &'ast Generics, - opt_trait_reference: &'ast Option, + of_trait: Option<&'ast ast::TraitImplHeader>, self_type: &'ast Ty, item_id: NodeId, impl_items: &'ast [Box], @@ -3201,7 +3201,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { |this| { // Resolve the trait reference, if necessary. this.with_optional_trait_ref( - opt_trait_reference.as_ref(), + of_trait.map(|t| &t.trait_ref), self_type, |this, trait_id| { this.resolve_doc_links(attrs, MaybeExported::Impl(trait_id)); @@ -3224,9 +3224,9 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { is_trait_impl: trait_id.is_some() }; this.with_self_rib(res, |this| { - if let Some(trait_ref) = opt_trait_reference.as_ref() { + if let Some(of_trait) = of_trait { // Resolve type arguments in the trait path. - visit::walk_trait_ref(this, trait_ref); + visit::walk_trait_ref(this, &of_trait.trait_ref); } // Resolve the self type. this.visit_ty(self_type); @@ -5183,7 +5183,7 @@ impl<'ast> Visitor<'ast> for ItemInfoCollector<'_, '_, '_> { | ItemKind::Enum(_, generics, _) | ItemKind::Struct(_, generics, _) | ItemKind::Union(_, generics, _) - | ItemKind::Impl(box Impl { generics, .. }) + | ItemKind::Impl(Impl { generics, .. }) | ItemKind::Trait(box Trait { generics, .. }) | ItemKind::TraitAlias(_, generics, _) => { if let ItemKind::Fn(box Fn { sig, .. }) = &item.kind { diff --git a/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs b/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs index 0312bf56e597..24e017f7cf7d 100644 --- a/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs +++ b/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs @@ -473,33 +473,27 @@ pub fn eq_item_kind(l: &ItemKind, r: &ItemKind) -> bool { eq_id(*li, *ri) && eq_generics(lg, rg) && over(lb, rb, eq_generic_bound) }, ( - Impl(box ast::Impl { - safety: lu, - polarity: lp, - defaultness: ld, - constness: lc, + Impl(ast::Impl { generics: lg, of_trait: lot, self_ty: lst, items: li, }), - Impl(box ast::Impl { - safety: ru, - polarity: rp, - defaultness: rd, - constness: rc, + Impl(ast::Impl { generics: rg, of_trait: rot, self_ty: rst, items: ri, }), ) => { - matches!(lu, Safety::Default) == matches!(ru, Safety::Default) - && matches!(lp, ImplPolarity::Positive) == matches!(rp, ImplPolarity::Positive) - && eq_defaultness(*ld, *rd) - && matches!(lc, ast::Const::No) == matches!(rc, ast::Const::No) - && eq_generics(lg, rg) - && both(lot.as_ref(), rot.as_ref(), |l, r| eq_path(&l.path, &r.path)) + eq_generics(lg, rg) + && both(lot.as_deref(), rot.as_deref(), |l, r| { + matches!(l.safety, Safety::Default) == matches!(r.safety, Safety::Default) + && matches!(l.polarity, ImplPolarity::Positive) == matches!(r.polarity, ImplPolarity::Positive) + && eq_defaultness(l.defaultness, r.defaultness) + && matches!(l.constness, ast::Const::No) == matches!(r.constness, ast::Const::No) + && eq_path(&l.trait_ref.path, &r.trait_ref.path) + }) && eq_ty(lst, rst) && over(li, ri, |l, r| eq_item(l, r, eq_assoc_item_kind)) }, diff --git a/src/tools/rustfmt/src/items.rs b/src/tools/rustfmt/src/items.rs index 57d4142ebe43..10df6f967026 100644 --- a/src/tools/rustfmt/src/items.rs +++ b/src/tools/rustfmt/src/items.rs @@ -958,20 +958,19 @@ fn format_impl_ref_and_type( offset: Indent, ) -> Option { let ast::Impl { - safety, - polarity, - defaultness, - constness, - ref generics, - of_trait: ref trait_ref, - ref self_ty, - .. - } = *iimpl; + generics, + of_trait, + self_ty, + items: _, + } = iimpl; let mut result = String::with_capacity(128); result.push_str(&format_visibility(context, &item.vis)); - result.push_str(format_defaultness(defaultness)); - result.push_str(format_safety(safety)); + + if let Some(of_trait) = of_trait.as_deref() { + result.push_str(format_defaultness(of_trait.defaultness)); + result.push_str(format_safety(of_trait.safety)); + } let shape = if context.config.style_edition() >= StyleEdition::Edition2024 { Shape::indented(offset + last_line_width(&result), context.config) @@ -984,28 +983,24 @@ fn format_impl_ref_and_type( }; let generics_str = rewrite_generics(context, "impl", generics, shape).ok()?; result.push_str(&generics_str); - result.push_str(format_constness_right(constness)); - let polarity_str = match polarity { - ast::ImplPolarity::Negative(_) => "!", - ast::ImplPolarity::Positive => "", - }; - - let polarity_overhead; let trait_ref_overhead; - if let Some(ref trait_ref) = *trait_ref { + if let Some(of_trait) = of_trait.as_deref() { + result.push_str(format_constness_right(of_trait.constness)); + let polarity_str = match of_trait.polarity { + ast::ImplPolarity::Negative(_) => "!", + ast::ImplPolarity::Positive => "", + }; let result_len = last_line_width(&result); result.push_str(&rewrite_trait_ref( context, - trait_ref, + &of_trait.trait_ref, offset, polarity_str, result_len, )?); - polarity_overhead = 0; // already written trait_ref_overhead = " for".len(); } else { - polarity_overhead = polarity_str.len(); trait_ref_overhead = 0; } @@ -1020,17 +1015,15 @@ fn format_impl_ref_and_type( } else { 0 }; - let used_space = - last_line_width(&result) + polarity_overhead + trait_ref_overhead + curly_brace_overhead; + let used_space = last_line_width(&result) + trait_ref_overhead + curly_brace_overhead; // 1 = space before the type. let budget = context.budget(used_space + 1); if let Some(self_ty_str) = self_ty.rewrite(context, Shape::legacy(budget, offset)) { if !self_ty_str.contains('\n') { - if trait_ref.is_some() { + if of_trait.is_some() { result.push_str(" for "); } else { result.push(' '); - result.push_str(polarity_str); } result.push_str(&self_ty_str); return Some(result); @@ -1042,12 +1035,10 @@ fn format_impl_ref_and_type( // Add indentation of one additional tab. let new_line_offset = offset.block_indent(context.config); result.push_str(&new_line_offset.to_string(context.config)); - if trait_ref.is_some() { + if of_trait.is_some() { result.push_str("for "); - } else { - result.push_str(polarity_str); } - let budget = context.budget(last_line_width(&result) + polarity_overhead); + let budget = context.budget(last_line_width(&result)); let type_offset = match context.config.indent_style() { IndentStyle::Visual => new_line_offset + trait_ref_overhead, IndentStyle::Block => new_line_offset, diff --git a/src/tools/rustfmt/tests/source/negative-impl.rs b/src/tools/rustfmt/tests/source/negative-impl.rs index da242d4f3dca..e8f9508e6563 100644 --- a/src/tools/rustfmt/tests/source/negative-impl.rs +++ b/src/tools/rustfmt/tests/source/negative-impl.rs @@ -1,7 +1,3 @@ impl ! Display for JoinHandle { } -impl ! Box < JoinHandle > { } - impl ! std :: fmt :: Display for JoinHandle < T : std :: future :: Future + std :: marker :: Send + std :: marker :: Sync > { } - -impl ! JoinHandle < T : std :: future :: Future < Output > + std :: marker :: Send + std :: marker :: Sync + 'static > + 'static { } diff --git a/src/tools/rustfmt/tests/target/negative-impl.rs b/src/tools/rustfmt/tests/target/negative-impl.rs index 16ce7e26a99a..bb53048dbc62 100644 --- a/src/tools/rustfmt/tests/target/negative-impl.rs +++ b/src/tools/rustfmt/tests/target/negative-impl.rs @@ -1,14 +1,6 @@ impl !Display for JoinHandle {} -impl !Box {} - impl !std::fmt::Display for JoinHandle { } - -impl - !JoinHandle + std::marker::Send + std::marker::Sync + 'static> - + 'static -{ -} From bf266dc8341755f0f7ac6de1cfa474875f79b3d9 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Thu, 24 Jul 2025 09:32:58 -0500 Subject: [PATCH 181/195] Propagate TraitImplHeader to hir --- compiler/rustc_ast_lowering/src/item.rs | 99 +++++++++---------- compiler/rustc_hir/src/hir.rs | 27 ++--- compiler/rustc_hir/src/intravisit.rs | 26 ++--- .../rustc_hir_analysis/src/check/wfcheck.rs | 93 +++++++++-------- .../src/coherence/builtin.rs | 6 +- .../src/coherence/orphan.rs | 4 +- compiler/rustc_hir_analysis/src/collect.rs | 36 +++---- .../src/collect/predicates_of.rs | 4 +- .../src/collect/resolve_bound_vars.rs | 17 ++-- .../rustc_hir_analysis/src/collect/type_of.rs | 2 +- .../errors/wrong_number_of_generic_args.rs | 6 +- .../src/hir_ty_lowering/lint.rs | 6 +- .../src/hir_ty_lowering/mod.rs | 2 +- .../rustc_hir_analysis/src/hir_wf_check.rs | 5 +- compiler/rustc_hir_pretty/src/lib.rs | 61 ++++++------ .../src/fn_ctxt/suggestions.rs | 2 +- .../rustc_hir_typeck/src/method/suggest.rs | 10 +- .../src/deref_into_dyn_supertrait.rs | 4 +- compiler/rustc_lint/src/internal.rs | 4 +- compiler/rustc_lint/src/non_local_def.rs | 4 +- compiler/rustc_passes/src/check_attr.rs | 7 +- compiler/rustc_passes/src/stability.rs | 24 ++--- compiler/rustc_privacy/src/lib.rs | 3 +- .../src/error_reporting/traits/suggestions.rs | 8 +- compiler/rustc_ty_utils/src/assoc.rs | 4 +- compiler/rustc_ty_utils/src/implied_bounds.rs | 8 +- compiler/rustc_ty_utils/src/sig_types.rs | 2 +- compiler/rustc_ty_utils/src/ty.rs | 6 +- src/librustdoc/clean/mod.rs | 9 +- .../src/arbitrary_source_item_ordering.rs | 1 + .../clippy/clippy_lints/src/copy_iterator.rs | 4 +- .../clippy_lints/src/derivable_impls.rs | 4 +- src/tools/clippy/clippy_lints/src/derive.rs | 3 +- .../clippy/clippy_lints/src/empty_drop.rs | 4 +- .../clippy_lints/src/error_impl_error.rs | 2 +- .../clippy/clippy_lints/src/format_impl.rs | 4 +- .../clippy/clippy_lints/src/from_over_into.rs | 4 +- .../src/functions/impl_trait_in_params.rs | 3 +- .../src/functions/renamed_function_params.rs | 4 +- .../impl_hash_with_borrow_str_and_bytes.rs | 8 +- .../clippy_lints/src/infallible_try_from.rs | 4 +- .../src/iter_without_into_iter.rs | 5 +- .../clippy/clippy_lints/src/lifetimes.rs | 6 +- .../src/missing_fields_in_debug.rs | 4 +- .../clippy_lints/src/missing_trait_methods.rs | 4 +- .../clippy/clippy_lints/src/non_copy_const.rs | 2 +- .../src/non_send_fields_in_send_ty.rs | 6 +- .../clippy_lints/src/operators/op_ref.rs | 2 +- .../clippy_lints/src/partialeq_ne_impl.rs | 6 +- .../clippy_lints/src/same_name_method.rs | 4 +- .../clippy/clippy_lints/src/serde_api.rs | 6 +- .../clippy_lints/src/to_string_trait_impl.rs | 4 +- .../src/unconditional_recursion.rs | 8 +- .../src/undocumented_unsafe_blocks.rs | 6 +- src/tools/clippy/clippy_lints/src/write.rs | 4 +- .../clippy_utils/src/check_proc_macro.rs | 6 +- src/tools/clippy/clippy_utils/src/lib.rs | 3 +- src/tools/clippy/clippy_utils/src/visitors.rs | 3 +- tests/ui/unpretty/exhaustive.hir.stdout | 2 +- 59 files changed, 316 insertions(+), 299 deletions(-) diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index 231752700ba0..235573c96e45 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -360,75 +360,30 @@ impl<'hir> LoweringContext<'_, 'hir> { // lifetime to be added, but rather a reference to a // parent lifetime. let itctx = ImplTraitContext::Universal; - let (generics, (trait_ref, lowered_ty)) = + let (generics, (of_trait, lowered_ty)) = self.lower_generics(ast_generics, id, itctx, |this| { - let modifiers = TraitBoundModifiers { - constness: BoundConstness::Never, - asyncness: BoundAsyncness::Normal, - // we don't use this in bound lowering - polarity: BoundPolarity::Positive, - }; - - let trait_ref = of_trait.as_ref().map(|of_trait| { - this.lower_trait_ref( - modifiers, - &of_trait.trait_ref, - ImplTraitContext::Disallowed(ImplTraitPosition::Trait), - ) - }); + let of_trait = of_trait + .as_deref() + .map(|of_trait| this.lower_trait_impl_header(of_trait)); let lowered_ty = this.lower_ty( ty, ImplTraitContext::Disallowed(ImplTraitPosition::ImplSelf), ); - (trait_ref, lowered_ty) + (of_trait, lowered_ty) }); let new_impl_items = self .arena .alloc_from_iter(impl_items.iter().map(|item| self.lower_impl_item_ref(item))); - // `defaultness.has_value()` is never called for an `impl`, always `true` in order - // to not cause an assertion failure inside the `lower_defaultness` function. - let has_val = true; - let (constness, safety, polarity, defaultness, defaultness_span) = match *of_trait { - Some(box TraitImplHeader { - constness, - safety, - polarity, - defaultness, - trait_ref: _, - }) => { - let constness = self.lower_constness(constness); - let safety = self.lower_safety(safety, hir::Safety::Safe); - let polarity = match polarity { - ImplPolarity::Positive => ImplPolarity::Positive, - ImplPolarity::Negative(s) => ImplPolarity::Negative(self.lower_span(s)), - }; - let (defaultness, defaultness_span) = - self.lower_defaultness(defaultness, has_val); - (constness, safety, polarity, defaultness, defaultness_span) - } - None => ( - hir::Constness::NotConst, - hir::Safety::Safe, - ImplPolarity::Positive, - hir::Defaultness::Final, - None, - ), - }; - hir::ItemKind::Impl(self.arena.alloc(hir::Impl { - constness, - safety, - polarity, - defaultness, - defaultness_span, + hir::ItemKind::Impl(hir::Impl { generics, - of_trait: trait_ref, + of_trait, self_ty: lowered_ty, items: new_impl_items, - })) + }) } ItemKind::Trait(box Trait { constness, @@ -999,6 +954,44 @@ impl<'hir> LoweringContext<'_, 'hir> { self.expr(span, hir::ExprKind::Err(guar)) } + fn lower_trait_impl_header( + &mut self, + trait_impl_header: &TraitImplHeader, + ) -> &'hir hir::TraitImplHeader<'hir> { + let TraitImplHeader { constness, safety, polarity, defaultness, ref trait_ref } = + *trait_impl_header; + let constness = self.lower_constness(constness); + let safety = self.lower_safety(safety, hir::Safety::Safe); + let polarity = match polarity { + ImplPolarity::Positive => ImplPolarity::Positive, + ImplPolarity::Negative(s) => ImplPolarity::Negative(self.lower_span(s)), + }; + // `defaultness.has_value()` is never called for an `impl`, always `true` in order + // to not cause an assertion failure inside the `lower_defaultness` function. + let has_val = true; + let (defaultness, defaultness_span) = self.lower_defaultness(defaultness, has_val); + let modifiers = TraitBoundModifiers { + constness: BoundConstness::Never, + asyncness: BoundAsyncness::Normal, + // we don't use this in bound lowering + polarity: BoundPolarity::Positive, + }; + let trait_ref = self.lower_trait_ref( + modifiers, + trait_ref, + ImplTraitContext::Disallowed(ImplTraitPosition::Trait), + ); + + self.arena.alloc(hir::TraitImplHeader { + constness, + safety, + polarity, + defaultness, + defaultness_span, + trait_ref, + }) + } + fn lower_impl_item( &mut self, i: &AssocItem, diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 34db6f92d928..d11ffce87cb3 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -4194,7 +4194,7 @@ impl<'hir> Item<'hir> { expect_trait_alias, (Ident, &'hir Generics<'hir>, GenericBounds<'hir>), ItemKind::TraitAlias(ident, generics, bounds), (*ident, generics, bounds); - expect_impl, &'hir Impl<'hir>, ItemKind::Impl(imp), imp; + expect_impl, &Impl<'hir>, ItemKind::Impl(imp), imp; } } @@ -4372,7 +4372,7 @@ pub enum ItemKind<'hir> { TraitAlias(Ident, &'hir Generics<'hir>, GenericBounds<'hir>), /// An implementation, e.g., `impl Trait for Foo { .. }`. - Impl(&'hir Impl<'hir>), + Impl(Impl<'hir>), } /// Represents an impl block declaration. @@ -4381,6 +4381,14 @@ pub enum ItemKind<'hir> { /// Refer to [`ImplItem`] for an associated item within an impl block. #[derive(Debug, Clone, Copy, HashStable_Generic)] pub struct Impl<'hir> { + pub generics: &'hir Generics<'hir>, + pub of_trait: Option<&'hir TraitImplHeader<'hir>>, + pub self_ty: &'hir Ty<'hir>, + pub items: &'hir [ImplItemId], +} + +#[derive(Debug, Clone, Copy, HashStable_Generic)] +pub struct TraitImplHeader<'hir> { pub constness: Constness, pub safety: Safety, pub polarity: ImplPolarity, @@ -4388,13 +4396,7 @@ pub struct Impl<'hir> { // We do not put a `Span` in `Defaultness` because it breaks foreign crate metadata // decoding as `Span`s cannot be decoded when a `Session` is not available. pub defaultness_span: Option, - pub generics: &'hir Generics<'hir>, - - /// The trait being implemented, if any. - pub of_trait: Option>, - - pub self_ty: &'hir Ty<'hir>, - pub items: &'hir [ImplItemId], + pub trait_ref: TraitRef<'hir>, } impl ItemKind<'_> { @@ -4756,8 +4758,8 @@ impl<'hir> Node<'hir> { /// Get a `hir::Impl` if the node is an impl block for the given `trait_def_id`. pub fn impl_block_of_trait(self, trait_def_id: DefId) -> Option<&'hir Impl<'hir>> { if let Node::Item(Item { kind: ItemKind::Impl(impl_block), .. }) = self - && let Some(trait_ref) = impl_block.of_trait - && let Some(trait_id) = trait_ref.trait_def_id() + && let Some(of_trait) = impl_block.of_trait + && let Some(trait_id) = of_trait.trait_ref.trait_def_id() && trait_id == trait_def_id { Some(impl_block) @@ -4952,7 +4954,7 @@ mod size_asserts { static_assert_size!(GenericArg<'_>, 16); static_assert_size!(GenericBound<'_>, 64); static_assert_size!(Generics<'_>, 56); - static_assert_size!(Impl<'_>, 80); + static_assert_size!(Impl<'_>, 40); static_assert_size!(ImplItem<'_>, 96); static_assert_size!(ImplItemKind<'_>, 40); static_assert_size!(Item<'_>, 88); @@ -4967,6 +4969,7 @@ mod size_asserts { static_assert_size!(Res, 12); static_assert_size!(Stmt<'_>, 32); static_assert_size!(StmtKind<'_>, 16); + static_assert_size!(TraitImplHeader<'_>, 48); static_assert_size!(TraitItem<'_>, 88); static_assert_size!(TraitItemKind<'_>, 48); static_assert_size!(Ty<'_>, 48); diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs index 23fa466859a0..9b2f8ae75fa7 100644 --- a/compiler/rustc_hir/src/intravisit.rs +++ b/compiler/rustc_hir/src/intravisit.rs @@ -590,21 +590,21 @@ pub fn walk_item<'v, V: Visitor<'v>>(visitor: &mut V, item: &'v Item<'v>) -> V:: try_visit!(visitor.visit_generics(generics)); try_visit!(visitor.visit_enum_def(enum_definition)); } - ItemKind::Impl(Impl { - constness: _, - safety: _, - defaultness: _, - polarity: _, - defaultness_span: _, - generics, - of_trait, - self_ty, - items, - }) => { + ItemKind::Impl(Impl { generics, of_trait, self_ty, items }) => { try_visit!(visitor.visit_generics(generics)); - visit_opt!(visitor, visit_trait_ref, of_trait); + if let Some(TraitImplHeader { + constness: _, + safety: _, + polarity: _, + defaultness: _, + defaultness_span: _, + trait_ref, + }) = of_trait + { + try_visit!(visitor.visit_trait_ref(trait_ref)); + } try_visit!(visitor.visit_ty_unambig(self_ty)); - walk_list!(visitor, visit_impl_item_ref, *items); + walk_list!(visitor, visit_impl_item_ref, items); } ItemKind::Struct(ident, ref generics, ref struct_definition) | ItemKind::Union(ident, ref generics, ref struct_definition) => { diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index a62efed13bc7..c642435b9893 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -244,48 +244,48 @@ pub(super) fn check_item<'tcx>( // // won't be allowed unless there's an *explicit* implementation of `Send` // for `T` - hir::ItemKind::Impl(impl_) => { - let header = tcx.impl_trait_header(def_id); - let is_auto = header - .is_some_and(|header| tcx.trait_is_auto(header.trait_ref.skip_binder().def_id)); - + hir::ItemKind::Impl(ref impl_) => { crate::impl_wf_check::check_impl_wf(tcx, def_id)?; let mut res = Ok(()); - if let (hir::Defaultness::Default { .. }, true) = (impl_.defaultness, is_auto) { - let sp = impl_.of_trait.as_ref().map_or(item.span, |t| t.path.span); - res = Err(tcx - .dcx() - .struct_span_err(sp, "impls of auto traits cannot be default") - .with_span_labels(impl_.defaultness_span, "default because of this") - .with_span_label(sp, "auto trait") - .emit()); - } - // We match on both `ty::ImplPolarity` and `ast::ImplPolarity` just to get the `!` span. - match header.map(|h| h.polarity) { - // `None` means this is an inherent impl - Some(ty::ImplPolarity::Positive) | None => { - res = res.and(check_impl(tcx, item, impl_.self_ty, &impl_.of_trait)); - } - Some(ty::ImplPolarity::Negative) => { - let ast::ImplPolarity::Negative(span) = impl_.polarity else { - bug!("impl_polarity query disagrees with impl's polarity in HIR"); - }; - // FIXME(#27579): what amount of WF checking do we need for neg impls? - if let hir::Defaultness::Default { .. } = impl_.defaultness { - let mut spans = vec![span]; - spans.extend(impl_.defaultness_span); - res = Err(struct_span_code_err!( - tcx.dcx(), - spans, - E0750, - "negative impls cannot be default impls" - ) + if let Some(of_trait) = impl_.of_trait { + let header = tcx.impl_trait_header(def_id).unwrap(); + let is_auto = tcx.trait_is_auto(header.trait_ref.skip_binder().def_id); + if let (hir::Defaultness::Default { .. }, true) = (of_trait.defaultness, is_auto) { + let sp = of_trait.trait_ref.path.span; + res = Err(tcx + .dcx() + .struct_span_err(sp, "impls of auto traits cannot be default") + .with_span_labels(of_trait.defaultness_span, "default because of this") + .with_span_label(sp, "auto trait") .emit()); + } + match header.polarity { + ty::ImplPolarity::Positive => { + res = res.and(check_impl(tcx, item, impl_)); + } + ty::ImplPolarity::Negative => { + let ast::ImplPolarity::Negative(span) = of_trait.polarity else { + bug!("impl_polarity query disagrees with impl's polarity in HIR"); + }; + // FIXME(#27579): what amount of WF checking do we need for neg impls? + if let hir::Defaultness::Default { .. } = of_trait.defaultness { + let mut spans = vec![span]; + spans.extend(of_trait.defaultness_span); + res = Err(struct_span_code_err!( + tcx.dcx(), + spans, + E0750, + "negative impls cannot be default impls" + ) + .emit()); + } + } + ty::ImplPolarity::Reservation => { + // FIXME: what amount of WF checking do we need for reservation impls? } } - Some(ty::ImplPolarity::Reservation) => { - // FIXME: what amount of WF checking do we need for reservation impls? - } + } else { + res = res.and(check_impl(tcx, item, impl_)); } res } @@ -1258,16 +1258,15 @@ pub(crate) fn check_const_item(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<() }) } -#[instrument(level = "debug", skip(tcx, hir_self_ty, hir_trait_ref))] +#[instrument(level = "debug", skip(tcx, impl_))] fn check_impl<'tcx>( tcx: TyCtxt<'tcx>, item: &'tcx hir::Item<'tcx>, - hir_self_ty: &hir::Ty<'_>, - hir_trait_ref: &Option>, + impl_: &hir::Impl<'_>, ) -> Result<(), ErrorGuaranteed> { enter_wf_checking_ctxt(tcx, item.owner_id.def_id, |wfcx| { - match hir_trait_ref { - Some(hir_trait_ref) => { + match impl_.of_trait { + Some(of_trait) => { // `#[rustc_reservation_impl]` impls are not real impls and // therefore don't need to be WF (the trait's `Self: Trait` predicate // won't hold). @@ -1275,7 +1274,7 @@ fn check_impl<'tcx>( // Avoid bogus "type annotations needed `Foo: Bar`" errors on `impl Bar for Foo` in case // other `Foo` impls are incoherent. tcx.ensure_ok().coherent_trait(trait_ref.def_id)?; - let trait_span = hir_trait_ref.path.span; + let trait_span = of_trait.trait_ref.path.span; let trait_ref = wfcx.deeply_normalize( trait_span, Some(WellFormedLoc::Ty(item.hir_id().expect_owner().def_id)), @@ -1299,12 +1298,12 @@ fn check_impl<'tcx>( if let Some(pred) = obligation.predicate.as_trait_clause() && pred.skip_binder().self_ty() == trait_ref.self_ty() { - obligation.cause.span = hir_self_ty.span; + obligation.cause.span = impl_.self_ty.span; } if let Some(pred) = obligation.predicate.as_projection_clause() && pred.skip_binder().self_ty() == trait_ref.self_ty() { - obligation.cause.span = hir_self_ty.span; + obligation.cause.span = impl_.self_ty.span; } } @@ -1321,7 +1320,7 @@ fn check_impl<'tcx>( wfcx.register_obligation(Obligation::new( tcx, ObligationCause::new( - hir_self_ty.span, + impl_.self_ty.span, wfcx.body_def_id, ObligationCauseCode::WellFormed(None), ), @@ -1342,7 +1341,7 @@ fn check_impl<'tcx>( self_ty, ); wfcx.register_wf_obligation( - hir_self_ty.span, + impl_.self_ty.span, Some(WellFormedLoc::Ty(item.hir_id().expect_owner().def_id)), self_ty.into(), ); diff --git a/compiler/rustc_hir_analysis/src/coherence/builtin.rs b/compiler/rustc_hir_analysis/src/coherence/builtin.rs index 27948f50a4ad..32b175611ceb 100644 --- a/compiler/rustc_hir_analysis/src/coherence/builtin.rs +++ b/compiler/rustc_hir_analysis/src/coherence/builtin.rs @@ -531,8 +531,10 @@ pub(crate) fn coerce_unsized_info<'tcx>( })); } else if diff_fields.len() > 1 { let item = tcx.hir_expect_item(impl_did); - let span = if let ItemKind::Impl(hir::Impl { of_trait: Some(t), .. }) = &item.kind { - t.path.span + let span = if let ItemKind::Impl(hir::Impl { of_trait: Some(of_trait), .. }) = + &item.kind + { + of_trait.trait_ref.path.span } else { tcx.def_span(impl_did) }; diff --git a/compiler/rustc_hir_analysis/src/coherence/orphan.rs b/compiler/rustc_hir_analysis/src/coherence/orphan.rs index c75fef9f716d..f707196c8163 100644 --- a/compiler/rustc_hir_analysis/src/coherence/orphan.rs +++ b/compiler/rustc_hir_analysis/src/coherence/orphan.rs @@ -384,7 +384,7 @@ fn emit_orphan_check_error<'tcx>( traits::OrphanCheckErr::NonLocalInputType(tys) => { let item = tcx.hir_expect_item(impl_def_id); let impl_ = item.expect_impl(); - let hir_trait_ref = impl_.of_trait.as_ref().unwrap(); + let of_trait = impl_.of_trait.unwrap(); let span = tcx.def_span(impl_def_id); let mut diag = tcx.dcx().create_err(match trait_ref.self_ty().kind() { @@ -401,7 +401,7 @@ fn emit_orphan_check_error<'tcx>( impl_.self_ty.span } else { // Point at `C` in `impl for C in D` - hir_trait_ref.path.span + of_trait.trait_ref.path.span }; ty = tcx.erase_regions(ty); diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs index 8ccbfbbb3b49..b72e743f95b0 100644 --- a/compiler/rustc_hir_analysis/src/collect.rs +++ b/compiler/rustc_hir_analysis/src/collect.rs @@ -1295,18 +1295,22 @@ fn impl_trait_header(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option, - def_id: LocalDefId, - impl_: &hir::Impl<'_>, - span: Span, + of_trait: &hir::TraitImplHeader<'_>, + is_rustc_reservation: bool, ) -> ty::ImplPolarity { - let is_rustc_reservation = tcx.has_attr(def_id, sym::rustc_reservation_impl); - match &impl_ { - hir::Impl { polarity: hir::ImplPolarity::Negative(span), of_trait, .. } => { + match of_trait.polarity { + hir::ImplPolarity::Negative(span) => { if is_rustc_reservation { - let span = span.to(of_trait.as_ref().map_or(*span, |t| t.path.span)); + let span = span.to(of_trait.trait_ref.path.span); tcx.dcx().span_err(span, "reservation impls can't be negative"); } ty::ImplPolarity::Negative } - hir::Impl { polarity: hir::ImplPolarity::Positive, of_trait: None, .. } => { - if is_rustc_reservation { - tcx.dcx().span_err(span, "reservation impls can't be inherent"); - } - ty::ImplPolarity::Positive - } - hir::Impl { polarity: hir::ImplPolarity::Positive, of_trait: Some(_), .. } => { + hir::ImplPolarity::Positive => { if is_rustc_reservation { ty::ImplPolarity::Reservation } else { diff --git a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs index 8dd13da4fa7b..b59dc4bd132f 100644 --- a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs @@ -158,7 +158,9 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen if let Node::Item(item) = node { match item.kind { ItemKind::Impl(impl_) => { - if impl_.defaultness.is_default() { + if let Some(of_trait) = impl_.of_trait + && of_trait.defaultness.is_default() + { is_default_impl_trait = tcx .impl_trait_ref(def_id) .map(|t| ty::Binder::dummy(t.instantiate_identity())); diff --git a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs index eb3492f5de6e..8133f9f68234 100644 --- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs +++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs @@ -604,13 +604,10 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { #[instrument(level = "debug", skip(self))] fn visit_item(&mut self, item: &'tcx hir::Item<'tcx>) { - match &item.kind { - hir::ItemKind::Impl(hir::Impl { of_trait, .. }) => { - if let Some(of_trait) = of_trait { - self.record_late_bound_vars(of_trait.hir_ref_id, Vec::default()); - } - } - _ => {} + if let hir::ItemKind::Impl(impl_) = item.kind + && let Some(of_trait) = impl_.of_trait + { + self.record_late_bound_vars(of_trait.trait_ref.hir_ref_id, Vec::default()); } match item.kind { hir::ItemKind::Fn { generics, .. } => { @@ -636,7 +633,7 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { | hir::ItemKind::Union(_, generics, _) | hir::ItemKind::Trait(_, _, _, _, generics, ..) | hir::ItemKind::TraitAlias(_, generics, ..) - | hir::ItemKind::Impl(&hir::Impl { generics, .. }) => { + | hir::ItemKind::Impl(hir::Impl { generics, .. }) => { // These kinds of items have only early-bound lifetime parameters. self.visit_early(item.hir_id(), generics, |this| intravisit::walk_item(this, item)); } @@ -2106,7 +2103,7 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { // If we have a self type alias (in an impl), try to resolve an // associated item from one of the supertraits of the impl's trait. Res::SelfTyAlias { alias_to: impl_def_id, is_trait_impl: true, .. } => { - let hir::ItemKind::Impl(hir::Impl { of_trait: Some(trait_ref), .. }) = self + let hir::ItemKind::Impl(hir::Impl { of_trait: Some(of_trait), .. }) = self .tcx .hir_node_by_def_id(impl_def_id.expect_local()) .expect_item() @@ -2114,7 +2111,7 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { else { return; }; - let Some(trait_def_id) = trait_ref.trait_def_id() else { + let Some(trait_def_id) = of_trait.trait_ref.trait_def_id() else { return; }; let Some((bound_vars, assoc_item)) = BoundVarContext::supertrait_hrtb_vars( diff --git a/compiler/rustc_hir_analysis/src/collect/type_of.rs b/compiler/rustc_hir_analysis/src/collect/type_of.rs index 22fb02714dd7..62125c99d802 100644 --- a/compiler/rustc_hir_analysis/src/collect/type_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/type_of.rs @@ -251,7 +251,7 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<'_ .emit_err(crate::errors::SelfInImplSelf { span: spans.into(), note: () }); Ty::new_error(tcx, guar) } - _ => icx.lower_ty(*self_ty), + _ => icx.lower_ty(self_ty), }, ItemKind::Fn { .. } => { let args = ty::GenericArgs::identity_for_item(tcx, def_id); diff --git a/compiler/rustc_hir_analysis/src/errors/wrong_number_of_generic_args.rs b/compiler/rustc_hir_analysis/src/errors/wrong_number_of_generic_args.rs index 835f8e8cdaee..8a9f9130feac 100644 --- a/compiler/rustc_hir_analysis/src/errors/wrong_number_of_generic_args.rs +++ b/compiler/rustc_hir_analysis/src/errors/wrong_number_of_generic_args.rs @@ -147,7 +147,11 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> { let hir::Node::Item(hir::Item { kind: hir::ItemKind::Impl(hir::Impl { - of_trait: Some(hir::TraitRef { hir_ref_id: id_in_of_trait, .. }), + of_trait: + Some(hir::TraitImplHeader { + trait_ref: hir::TraitRef { hir_ref_id: id_in_of_trait, .. }, + .. + }), .. }), .. diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs index 646ff3ca08d2..56998b5b53cc 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs @@ -200,7 +200,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { }) = tcx.hir_node_by_def_id(parent_id) && self_ty.hir_id == impl_self_ty.hir_id { - let Some(of_trait_ref) = of_trait else { + let Some(of_trait) = of_trait else { diag.span_suggestion_verbose( impl_self_ty.span.shrink_to_hi(), "you might have intended to implement this trait for a given type", @@ -209,10 +209,10 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { ); return; }; - if !of_trait_ref.trait_def_id().is_some_and(|def_id| def_id.is_local()) { + if !of_trait.trait_ref.trait_def_id().is_some_and(|def_id| def_id.is_local()) { return; } - let of_trait_span = of_trait_ref.path.span; + let of_trait_span = of_trait.trait_ref.path.span; // make sure that we are not calling unwrap to abort during the compilation let Ok(of_trait_name) = tcx.sess.source_map().span_to_snippet(of_trait_span) else { return; diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index 1675aecd2b84..c7b984d9b259 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -2732,7 +2732,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { }; let i = tcx.parent_hir_node(fn_hir_id).expect_item().expect_impl(); - let trait_ref = self.lower_impl_trait_ref(i.of_trait.as_ref()?, self.lower_ty(i.self_ty)); + let trait_ref = self.lower_impl_trait_ref(&i.of_trait?.trait_ref, self.lower_ty(i.self_ty)); let assoc = tcx.associated_items(trait_ref.def_id).find_by_ident_and_kind( tcx, diff --git a/compiler/rustc_hir_analysis/src/hir_wf_check.rs b/compiler/rustc_hir_analysis/src/hir_wf_check.rs index 3fddaee8cef4..d8578970adc9 100644 --- a/compiler/rustc_hir_analysis/src/hir_wf_check.rs +++ b/compiler/rustc_hir_analysis/src/hir_wf_check.rs @@ -154,8 +154,9 @@ pub(super) fn diagnostic_hir_wf_check<'tcx>( hir::ItemKind::TyAlias(_, _, ty) | hir::ItemKind::Static(_, _, ty, _) | hir::ItemKind::Const(_, _, ty, _) => vec![ty], - hir::ItemKind::Impl(impl_) => match &impl_.of_trait { - Some(t) => t + hir::ItemKind::Impl(impl_) => match impl_.of_trait { + Some(of_trait) => of_trait + .trait_ref .path .segments .last() diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index 235eec96d74f..be5859b57c5e 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -690,39 +690,44 @@ impl<'a> State<'a> { let (cb, ib) = self.head("union"); self.print_struct(ident.name, generics, struct_def, item.span, true, cb, ib); } - hir::ItemKind::Impl(&hir::Impl { - constness, - safety, - polarity, - defaultness, - defaultness_span: _, - generics, - ref of_trait, - self_ty, - items, - }) => { + hir::ItemKind::Impl(hir::Impl { generics, of_trait, self_ty, items }) => { let (cb, ib) = self.head(""); - self.print_defaultness(defaultness); - self.print_safety(safety); - self.word_nbsp("impl"); - if let hir::Constness::Const = constness { - self.word_nbsp("const"); - } + let impl_generics = |this: &mut Self| { + this.word_nbsp("impl"); + if !generics.params.is_empty() { + this.print_generic_params(generics.params); + this.space(); + } + }; - if !generics.params.is_empty() { - self.print_generic_params(generics.params); - self.space(); - } + match of_trait { + None => impl_generics(self), + Some(&hir::TraitImplHeader { + constness, + safety, + polarity, + defaultness, + defaultness_span: _, + ref trait_ref, + }) => { + self.print_defaultness(defaultness); + self.print_safety(safety); - if let hir::ImplPolarity::Negative(_) = polarity { - self.word("!"); - } + impl_generics(self); - if let Some(t) = of_trait { - self.print_trait_ref(t); - self.space(); - self.word_space("for"); + if let hir::Constness::Const = constness { + self.word_nbsp("const"); + } + + if let hir::ImplPolarity::Negative(_) = polarity { + self.word("!"); + } + + self.print_trait_ref(trait_ref); + self.space(); + self.word_space("for"); + } } self.print_type(self_ty); diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index 2345cdab208e..6013430e1ff3 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -936,7 +936,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Node::ImplItem(item) => { // If it doesn't impl a trait, we can add a return type let Node::Item(&hir::Item { - kind: hir::ItemKind::Impl(&hir::Impl { of_trait, .. }), + kind: hir::ItemKind::Impl(hir::Impl { of_trait, .. }), .. }) = self.tcx.parent_hir_node(item.hir_id()) else { diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index f7430f7af4e9..824d592fa6c7 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -1103,7 +1103,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self_ty.span.ctxt().outer_expn_data().kind, ExpnKind::Macro(MacroKind::Derive, _) ) || matches!( - of_trait.as_ref().map(|t| t.path.span.ctxt().outer_expn_data().kind), + of_trait.map(|t| t.trait_ref.path.span.ctxt().outer_expn_data().kind), Some(ExpnKind::Macro(MacroKind::Derive, _)) ) => { @@ -1165,13 +1165,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { entry.0.insert(cause_span); entry.1.insert((cause_span, "unsatisfied trait bound introduced here")); } else { - if let Some(trait_ref) = of_trait { - entry.0.insert(trait_ref.path.span); + if let Some(of_trait) = of_trait { + entry.0.insert(of_trait.trait_ref.path.span); } entry.0.insert(self_ty.span); }; - if let Some(trait_ref) = of_trait { - entry.1.insert((trait_ref.path.span, "")); + if let Some(of_trait) = of_trait { + entry.1.insert((of_trait.trait_ref.path.span, "")); } entry.1.insert((self_ty.span, "")); } diff --git a/compiler/rustc_lint/src/deref_into_dyn_supertrait.rs b/compiler/rustc_lint/src/deref_into_dyn_supertrait.rs index dd16117db1c5..943fcc0801b1 100644 --- a/compiler/rustc_lint/src/deref_into_dyn_supertrait.rs +++ b/compiler/rustc_lint/src/deref_into_dyn_supertrait.rs @@ -61,8 +61,8 @@ impl<'tcx> LateLintPass<'tcx> for DerefIntoDynSupertrait { // `Deref` is being implemented for `t` if let hir::ItemKind::Impl(impl_) = item.kind // the trait is a `Deref` implementation - && let Some(trait_) = &impl_.of_trait - && let Some(did) = trait_.trait_def_id() + && let Some(of_trait) = &impl_.of_trait + && let Some(did) = of_trait.trait_ref.trait_def_id() && tcx.is_lang_item(did, LangItem::Deref) // the self type is `dyn t_principal` && let self_ty = tcx.type_of(item.owner_id).instantiate_identity() diff --git a/compiler/rustc_lint/src/internal.rs b/compiler/rustc_lint/src/internal.rs index 9f02f1f0df38..016ff17f5d7e 100644 --- a/compiler/rustc_lint/src/internal.rs +++ b/compiler/rustc_lint/src/internal.rs @@ -582,8 +582,8 @@ impl Diagnostics { for (_hir_id, parent) in cx.tcx.hir_parent_iter(current_id) { debug!(?parent); if let hir::Node::Item(hir::Item { kind: hir::ItemKind::Impl(impl_), .. }) = parent - && let hir::Impl { of_trait: Some(of_trait), .. } = impl_ - && let Some(def_id) = of_trait.trait_def_id() + && let Some(of_trait) = impl_.of_trait + && let Some(def_id) = of_trait.trait_ref.trait_def_id() && let Some(name) = cx.tcx.get_diagnostic_name(def_id) && matches!(name, sym::Diagnostic | sym::Subdiagnostic | sym::LintDiagnostic) { diff --git a/compiler/rustc_lint/src/non_local_def.rs b/compiler/rustc_lint/src/non_local_def.rs index b877f909fc02..2dd3425e66cf 100644 --- a/compiler/rustc_lint/src/non_local_def.rs +++ b/compiler/rustc_lint/src/non_local_def.rs @@ -129,8 +129,8 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions { // of the `impl` definition let mut collector = PathCollector { paths: Vec::new() }; collector.visit_ty_unambig(&impl_.self_ty); - if let Some(of_trait) = &impl_.of_trait { - collector.visit_trait_ref(of_trait); + if let Some(of_trait) = impl_.of_trait { + collector.visit_trait_ref(&of_trait.trait_ref); } // 1.5. Remove any path that doesn't resolve to a `DefId` or if it resolve to a diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 10c532b436aa..dbdc8a3dbf5c 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -1155,8 +1155,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { let is_valid = doc_fake_variadic_is_allowed_self_ty(i.self_ty) || if let Some(&[hir::GenericArg::Type(ty)]) = i .of_trait - .as_ref() - .and_then(|trait_ref| trait_ref.path.segments.last()) + .and_then(|of_trait| of_trait.trait_ref.path.segments.last()) .map(|last_segment| last_segment.args().args) { matches!(&ty.kind, hir::TyKind::Tup([_])) @@ -1646,8 +1645,8 @@ impl<'tcx> CheckAttrVisitor<'tcx> { && let parent_hir_id = self.tcx.parent_hir_id(hir_id) && let hir::Node::Item(item) = self.tcx.hir_node(parent_hir_id) && let hir::ItemKind::Impl(impl_) = item.kind - && let Some(trait_) = impl_.of_trait - && let Some(def_id) = trait_.trait_def_id() + && let Some(of_trait) = impl_.of_trait + && let Some(def_id) = of_trait.trait_ref.trait_def_id() && self.tcx.is_lang_item(def_id, hir::LangItem::Drop) { return; diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs index e2f223325dfd..71650c6b9b93 100644 --- a/compiler/rustc_passes/src/stability.rs +++ b/compiler/rustc_passes/src/stability.rs @@ -590,9 +590,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'tcx> { // For implementations of traits, check the stability of each item // individually as it's possible to have a stable trait with unstable // items. - hir::ItemKind::Impl(hir::Impl { - of_trait: Some(t), self_ty, items, constness, .. - }) => { + hir::ItemKind::Impl(hir::Impl { of_trait: Some(of_trait), self_ty, items, .. }) => { let features = self.tcx.features(); if features.staged_api() { let attrs = self.tcx.hir_attrs(item.hir_id()); @@ -628,7 +626,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'tcx> { { let mut c = CheckTraitImplStable { tcx: self.tcx, fully_stable: true }; c.visit_ty_unambig(self_ty); - c.visit_trait_ref(t); + c.visit_trait_ref(&of_trait.trait_ref); // Skip the lint if the impl is marked as unstable using // #[unstable_feature_bound(..)] @@ -641,7 +639,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'tcx> { // do not lint when the trait isn't resolved, since resolution error should // be fixed first - if t.path.res != Res::Err + if of_trait.trait_ref.path.res != Res::Err && c.fully_stable && !unstable_feature_bound_in_effect { @@ -655,7 +653,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'tcx> { } if features.const_trait_impl() - && let hir::Constness::Const = constness + && let hir::Constness::Const = of_trait.constness { let stable_or_implied_stable = match const_stab { None => true, @@ -671,7 +669,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'tcx> { Some(_) => false, }; - if let Some(trait_id) = t.trait_def_id() + if let Some(trait_id) = of_trait.trait_ref.trait_def_id() && let Some(const_stab) = self.tcx.lookup_const_stability(trait_id) { // the const stability of a trait impl must match the const stability on the trait. @@ -699,14 +697,18 @@ impl<'tcx> Visitor<'tcx> for Checker<'tcx> { } } - if let hir::Constness::Const = constness - && let Some(def_id) = t.trait_def_id() + if let hir::Constness::Const = of_trait.constness + && let Some(def_id) = of_trait.trait_ref.trait_def_id() { // FIXME(const_trait_impl): Improve the span here. - self.tcx.check_const_stability(def_id, t.path.span, t.path.span); + self.tcx.check_const_stability( + def_id, + of_trait.trait_ref.path.span, + of_trait.trait_ref.path.span, + ); } - for impl_item_ref in *items { + for impl_item_ref in items { let impl_item = self.tcx.associated_item(impl_item_ref.owner_id); if let Some(def_id) = impl_item.trait_item_def_id { diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs index 5d02c02b23c2..1bddbd03cc3d 100644 --- a/compiler/rustc_privacy/src/lib.rs +++ b/compiler/rustc_privacy/src/lib.rs @@ -1779,7 +1779,8 @@ fn check_mod_privacy(tcx: TyCtxt<'_>, module_def_id: LocalModDefId) { if let DefKind::Impl { of_trait: true } = tcx.def_kind(def_id) { let trait_ref = tcx.impl_trait_ref(def_id).unwrap(); let trait_ref = trait_ref.instantiate_identity(); - visitor.span = tcx.hir_expect_item(def_id).expect_impl().of_trait.unwrap().path.span; + visitor.span = + tcx.hir_expect_item(def_id).expect_impl().of_trait.unwrap().trait_ref.path.span; let _ = visitor.visit_def_id(trait_ref.def_id, "trait", &trait_ref.print_only_trait_path()); } diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs index ae72178c052e..bc8c8a444058 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -3471,8 +3471,8 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { .. })) => { let mut spans = Vec::with_capacity(2); - if let Some(trait_ref) = of_trait { - spans.push(trait_ref.path.span); + if let Some(of_trait) = of_trait { + spans.push(of_trait.trait_ref.path.span); } spans.push(self_ty.span); let mut spans: MultiSpan = spans.into(); @@ -3480,7 +3480,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { self_ty.span.ctxt().outer_expn_data().kind, ExpnKind::Macro(MacroKind::Derive, _) ) || matches!( - of_trait.as_ref().map(|t| t.path.span.ctxt().outer_expn_data().kind), + of_trait.map(|t| t.trait_ref.path.span.ctxt().outer_expn_data().kind), Some(ExpnKind::Macro(MacroKind::Derive, _)) ) { spans.push_span_label( @@ -3592,7 +3592,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { .. })) => { let mut spans = vec![self_ty.span]; - spans.extend(of_trait.as_ref().map(|t| t.path.span)); + spans.extend(of_trait.map(|t| t.trait_ref.path.span)); let mut spans: MultiSpan = spans.into(); spans.push_span_label(data.span, "unsatisfied trait bound introduced here"); err.span_note(spans, msg); diff --git a/compiler/rustc_ty_utils/src/assoc.rs b/compiler/rustc_ty_utils/src/assoc.rs index 37cb64511c7a..e9629e31482a 100644 --- a/compiler/rustc_ty_utils/src/assoc.rs +++ b/compiler/rustc_ty_utils/src/assoc.rs @@ -174,10 +174,10 @@ fn associated_types_for_impl_traits_in_trait_or_impl<'tcx>( }) .collect(), ItemKind::Impl(impl_) => { - let Some(trait_ref) = impl_.of_trait else { + let Some(of_trait) = impl_.of_trait else { return Default::default(); }; - let Some(trait_def_id) = trait_ref.trait_def_id() else { + let Some(trait_def_id) = of_trait.trait_ref.trait_def_id() else { return Default::default(); }; let in_trait_def = tcx.associated_types_for_impl_traits_in_trait_or_impl(trait_def_id); diff --git a/compiler/rustc_ty_utils/src/implied_bounds.rs b/compiler/rustc_ty_utils/src/implied_bounds.rs index 6fa763f18ef1..cdfb93c4e7d5 100644 --- a/compiler/rustc_ty_utils/src/implied_bounds.rs +++ b/compiler/rustc_ty_utils/src/implied_bounds.rs @@ -172,10 +172,12 @@ fn impl_spans(tcx: TyCtxt<'_>, def_id: LocalDefId) -> impl Iterator let trait_args = impl_ .of_trait .into_iter() - .flat_map(|trait_ref| trait_ref.path.segments.last().unwrap().args().args) + .flat_map(|of_trait| of_trait.trait_ref.path.segments.last().unwrap().args().args) .map(|arg| arg.span()); - let dummy_spans_for_default_args = - impl_.of_trait.into_iter().flat_map(|trait_ref| iter::repeat(trait_ref.path.span)); + let dummy_spans_for_default_args = impl_ + .of_trait + .into_iter() + .flat_map(|of_trait| iter::repeat(of_trait.trait_ref.path.span)); iter::once(impl_.self_ty.span).chain(trait_args).chain(dummy_spans_for_default_args) } else { bug!("unexpected item for impl {def_id:?}: {item:?}") diff --git a/compiler/rustc_ty_utils/src/sig_types.rs b/compiler/rustc_ty_utils/src/sig_types.rs index dc6009116ac5..d95660810e5f 100644 --- a/compiler/rustc_ty_utils/src/sig_types.rs +++ b/compiler/rustc_ty_utils/src/sig_types.rs @@ -87,7 +87,7 @@ pub fn walk_types<'tcx, V: SpannedTypeVisitor<'tcx>>( DefKind::InlineConst | DefKind::Closure | DefKind::SyntheticCoroutineBody => {} DefKind::Impl { of_trait } => { if of_trait { - let span = tcx.hir_node_by_def_id(item).expect_item().expect_impl().of_trait.unwrap().path.span; + let span = tcx.hir_node_by_def_id(item).expect_item().expect_impl().of_trait.unwrap().trait_ref.path.span; let args = &tcx.impl_trait_ref(item).unwrap().instantiate_identity().args[1..]; try_visit!(visitor.visit(span, args)); } diff --git a/compiler/rustc_ty_utils/src/ty.rs b/compiler/rustc_ty_utils/src/ty.rs index 2e0b16d92276..b22c326b9f2a 100644 --- a/compiler/rustc_ty_utils/src/ty.rs +++ b/compiler/rustc_ty_utils/src/ty.rs @@ -81,7 +81,11 @@ fn sizedness_constraint_for_ty<'tcx>( fn defaultness(tcx: TyCtxt<'_>, def_id: LocalDefId) -> hir::Defaultness { match tcx.hir_node_by_def_id(def_id) { hir::Node::Item(hir::Item { - kind: hir::ItemKind::Impl(hir::Impl { defaultness, of_trait: Some(_), .. }), + kind: + hir::ItemKind::Impl(hir::Impl { + of_trait: Some(hir::TraitImplHeader { defaultness, .. }), + .. + }), .. }) | hir::Node::ImplItem(hir::ImplItem { defaultness, .. }) diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 7194c2fceded..890bfaced6cd 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -2768,7 +2768,7 @@ fn clean_maybe_renamed_item<'tcx>( // These kinds of item either don't need a `name` or accept a `None` one so we handle them // before. match item.kind { - ItemKind::Impl(impl_) => return clean_impl(impl_, item.owner_id.def_id, cx), + ItemKind::Impl(ref impl_) => return clean_impl(impl_, item.owner_id.def_id, cx), ItemKind::Use(path, kind) => { return clean_use_statement( item, @@ -2896,7 +2896,7 @@ fn clean_impl<'tcx>( ) -> Vec { let tcx = cx.tcx; let mut ret = Vec::new(); - let trait_ = impl_.of_trait.as_ref().map(|t| clean_trait_ref(t, cx)); + let trait_ = impl_.of_trait.map(|t| clean_trait_ref(&t.trait_ref, cx)); let items = impl_ .items .iter() @@ -2922,7 +2922,10 @@ fn clean_impl<'tcx>( }); let mut make_item = |trait_: Option, for_: Type, items: Vec| { let kind = ImplItem(Box::new(Impl { - safety: impl_.safety, + safety: match impl_.of_trait { + Some(of_trait) => of_trait.safety, + None => hir::Safety::Safe, + }, generics: clean_generics(impl_.generics, cx), trait_, for_, diff --git a/src/tools/clippy/clippy_lints/src/arbitrary_source_item_ordering.rs b/src/tools/clippy/clippy_lints/src/arbitrary_source_item_ordering.rs index d6469d32931d..36498adff502 100644 --- a/src/tools/clippy/clippy_lints/src/arbitrary_source_item_ordering.rs +++ b/src/tools/clippy/clippy_lints/src/arbitrary_source_item_ordering.rs @@ -534,6 +534,7 @@ fn get_item_name(item: &Item<'_>) -> Option { if let Some(of_trait) = im.of_trait { let mut trait_segs: Vec = of_trait + .trait_ref .path .segments .iter() diff --git a/src/tools/clippy/clippy_lints/src/copy_iterator.rs b/src/tools/clippy/clippy_lints/src/copy_iterator.rs index 4ecf3e41611b..51aebd8b0cfb 100644 --- a/src/tools/clippy/clippy_lints/src/copy_iterator.rs +++ b/src/tools/clippy/clippy_lints/src/copy_iterator.rs @@ -37,12 +37,12 @@ declare_lint_pass!(CopyIterator => [COPY_ITERATOR]); impl<'tcx> LateLintPass<'tcx> for CopyIterator { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { if let ItemKind::Impl(Impl { - of_trait: Some(trait_ref), + of_trait: Some(of_trait), .. }) = item.kind && let ty = cx.tcx.type_of(item.owner_id).instantiate_identity() && is_copy(cx, ty) - && let Some(trait_id) = trait_ref.trait_def_id() + && let Some(trait_id) = of_trait.trait_ref.trait_def_id() && cx.tcx.is_diagnostic_item(sym::Iterator, trait_id) { span_lint_and_note( diff --git a/src/tools/clippy/clippy_lints/src/derivable_impls.rs b/src/tools/clippy/clippy_lints/src/derivable_impls.rs index 0a481ddcd12e..7580d6cab66d 100644 --- a/src/tools/clippy/clippy_lints/src/derivable_impls.rs +++ b/src/tools/clippy/clippy_lints/src/derivable_impls.rs @@ -183,14 +183,14 @@ fn check_enum<'tcx>(cx: &LateContext<'tcx>, item: &'tcx Item<'_>, func_expr: &Ex impl<'tcx> LateLintPass<'tcx> for DerivableImpls { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { if let ItemKind::Impl(Impl { - of_trait: Some(trait_ref), + of_trait: Some(of_trait), items: [child], self_ty, .. }) = item.kind && !cx.tcx.is_automatically_derived(item.owner_id.to_def_id()) && !item.span.from_expansion() - && let Some(def_id) = trait_ref.trait_def_id() + && let Some(def_id) = of_trait.trait_ref.trait_def_id() && cx.tcx.is_diagnostic_item(sym::Default, def_id) && let impl_item_hir = child.hir_id() && let Node::ImplItem(impl_item) = cx.tcx.hir_node(impl_item_hir) diff --git a/src/tools/clippy/clippy_lints/src/derive.rs b/src/tools/clippy/clippy_lints/src/derive.rs index 49dd1bb09c61..c53a957f6a8b 100644 --- a/src/tools/clippy/clippy_lints/src/derive.rs +++ b/src/tools/clippy/clippy_lints/src/derive.rs @@ -201,10 +201,11 @@ declare_lint_pass!(Derive => [ impl<'tcx> LateLintPass<'tcx> for Derive { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { if let ItemKind::Impl(Impl { - of_trait: Some(trait_ref), + of_trait: Some(of_trait), .. }) = item.kind { + let trait_ref = &of_trait.trait_ref; let ty = cx.tcx.type_of(item.owner_id).instantiate_identity(); let is_automatically_derived = cx.tcx.is_automatically_derived(item.owner_id.to_def_id()); diff --git a/src/tools/clippy/clippy_lints/src/empty_drop.rs b/src/tools/clippy/clippy_lints/src/empty_drop.rs index 4e948701da4f..2b8221884344 100644 --- a/src/tools/clippy/clippy_lints/src/empty_drop.rs +++ b/src/tools/clippy/clippy_lints/src/empty_drop.rs @@ -36,11 +36,11 @@ declare_lint_pass!(EmptyDrop => [EMPTY_DROP]); impl LateLintPass<'_> for EmptyDrop { fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { if let ItemKind::Impl(Impl { - of_trait: Some(trait_ref), + of_trait: Some(of_trait), items: [child], .. }) = item.kind - && trait_ref.trait_def_id() == cx.tcx.lang_items().drop_trait() + && of_trait.trait_ref.trait_def_id() == cx.tcx.lang_items().drop_trait() && let impl_item_hir = child.hir_id() && let Node::ImplItem(impl_item) = cx.tcx.hir_node(impl_item_hir) && let ImplItemKind::Fn(_, b) = &impl_item.kind diff --git a/src/tools/clippy/clippy_lints/src/error_impl_error.rs b/src/tools/clippy/clippy_lints/src/error_impl_error.rs index 6525648efb1e..3018e1f12734 100644 --- a/src/tools/clippy/clippy_lints/src/error_impl_error.rs +++ b/src/tools/clippy/clippy_lints/src/error_impl_error.rs @@ -52,7 +52,7 @@ impl<'tcx> LateLintPass<'tcx> for ErrorImplError { ); }, ItemKind::Impl(imp) - if let Some(trait_def_id) = imp.of_trait.and_then(|t| t.trait_def_id()) + if let Some(trait_def_id) = imp.of_trait.and_then(|t| t.trait_ref.trait_def_id()) && let Some(error_def_id) = cx.tcx.get_diagnostic_item(sym::Error) && error_def_id == trait_def_id && let Some(def_id) = path_res(cx, imp.self_ty).opt_def_id().and_then(DefId::as_local) diff --git a/src/tools/clippy/clippy_lints/src/format_impl.rs b/src/tools/clippy/clippy_lints/src/format_impl.rs index 0535ecf5240f..416aea51ea19 100644 --- a/src/tools/clippy/clippy_lints/src/format_impl.rs +++ b/src/tools/clippy/clippy_lints/src/format_impl.rs @@ -254,10 +254,10 @@ fn is_format_trait_impl(cx: &LateContext<'_>, impl_item: &ImplItem<'_>) -> Optio if impl_item.ident.name == sym::fmt && let ImplItemKind::Fn(_, body_id) = impl_item.kind && let Some(Impl { - of_trait: Some(trait_ref), + of_trait: Some(of_trait), .. }) = get_parent_as_impl(cx.tcx, impl_item.hir_id()) - && let Some(did) = trait_ref.trait_def_id() + && let Some(did) = of_trait.trait_ref.trait_def_id() && let Some(name) = cx.tcx.get_diagnostic_name(did) && matches!(name, sym::Debug | sym::Display) { diff --git a/src/tools/clippy/clippy_lints/src/from_over_into.rs b/src/tools/clippy/clippy_lints/src/from_over_into.rs index 1da6952eb64c..e3bb5ee10db7 100644 --- a/src/tools/clippy/clippy_lints/src/from_over_into.rs +++ b/src/tools/clippy/clippy_lints/src/from_over_into.rs @@ -67,12 +67,12 @@ impl_lint_pass!(FromOverInto => [FROM_OVER_INTO]); impl<'tcx> LateLintPass<'tcx> for FromOverInto { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { if let ItemKind::Impl(Impl { - of_trait: Some(hir_trait_ref), + of_trait: Some(of_trait), self_ty, items: [impl_item_ref], .. }) = item.kind - && let Some(into_trait_seg) = hir_trait_ref.path.segments.last() + && let Some(into_trait_seg) = of_trait.trait_ref.path.segments.last() // `impl Into for self_ty` && let Some(GenericArgs { args: [GenericArg::Type(target_ty)], .. }) = into_trait_seg.args && span_is_local(item.span) diff --git a/src/tools/clippy/clippy_lints/src/functions/impl_trait_in_params.rs b/src/tools/clippy/clippy_lints/src/functions/impl_trait_in_params.rs index cb83b1395d26..3105e303ae31 100644 --- a/src/tools/clippy/clippy_lints/src/functions/impl_trait_in_params.rs +++ b/src/tools/clippy/clippy_lints/src/functions/impl_trait_in_params.rs @@ -54,8 +54,7 @@ pub(super) fn check_impl_item(cx: &LateContext<'_>, impl_item: &ImplItem<'_>) { if let ImplItemKind::Fn(_, body_id) = impl_item.kind && let hir::Node::Item(item) = cx.tcx.parent_hir_node(impl_item.hir_id()) && let hir::ItemKind::Impl(impl_) = item.kind - && let hir::Impl { of_trait, .. } = *impl_ - && of_trait.is_none() + && let hir::Impl { of_trait: None, .. } = impl_ && let body = cx.tcx.hir_body(body_id) && cx.tcx.visibility(cx.tcx.hir_body_owner_def_id(body.id())).is_public() && !is_in_test(cx.tcx, impl_item.hir_id()) diff --git a/src/tools/clippy/clippy_lints/src/functions/renamed_function_params.rs b/src/tools/clippy/clippy_lints/src/functions/renamed_function_params.rs index 0d6191f2c971..0a7c6e9d5f8e 100644 --- a/src/tools/clippy/clippy_lints/src/functions/renamed_function_params.rs +++ b/src/tools/clippy/clippy_lints/src/functions/renamed_function_params.rs @@ -15,11 +15,11 @@ pub(super) fn check_impl_item(cx: &LateContext<'_>, item: &ImplItem<'_>, ignored && let parent_node = cx.tcx.parent_hir_node(item.hir_id()) && let Node::Item(parent_item) = parent_node && let ItemKind::Impl(Impl { - of_trait: Some(trait_ref), + of_trait: Some(of_trait), .. }) = &parent_item.kind && let Some(did) = trait_item_def_id_of_impl(cx, item.owner_id) - && !is_from_ignored_trait(trait_ref, ignored_traits) + && !is_from_ignored_trait(&of_trait.trait_ref, ignored_traits) { let mut param_idents_iter = cx.tcx.hir_body_param_idents(body_id); let mut default_param_idents_iter = cx.tcx.fn_arg_idents(did).iter().copied(); diff --git a/src/tools/clippy/clippy_lints/src/impl_hash_with_borrow_str_and_bytes.rs b/src/tools/clippy/clippy_lints/src/impl_hash_with_borrow_str_and_bytes.rs index 940adbae428e..f73182d3af0d 100644 --- a/src/tools/clippy/clippy_lints/src/impl_hash_with_borrow_str_and_bytes.rs +++ b/src/tools/clippy/clippy_lints/src/impl_hash_with_borrow_str_and_bytes.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::ty::implements_trait; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::{Item, ItemKind, Path, TraitRef}; +use rustc_hir::{Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::Ty; use rustc_session::declare_lint_pass; @@ -76,10 +76,10 @@ impl LateLintPass<'_> for ImplHashWithBorrowStrBytes { /// three of `Hash`, `Borrow` and `Borrow<[u8]>`. fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { if let ItemKind::Impl(imp) = item.kind - && let Some(TraitRef {path: Path {span, res, ..}, ..}) = imp.of_trait + && let Some(of_trait) = imp.of_trait && let ty = cx.tcx.type_of(item.owner_id).instantiate_identity() && let Some(hash_id) = cx.tcx.get_diagnostic_item(sym::Hash) - && Res::Def(DefKind::Trait, hash_id) == *res + && Res::Def(DefKind::Trait, hash_id) == of_trait.trait_ref.path.res && let Some(borrow_id) = cx.tcx.get_diagnostic_item(sym::Borrow) // since we are in the `Hash` impl, we don't need to check for that. // we need only to check for `Borrow` and `Borrow<[u8]>` @@ -89,7 +89,7 @@ impl LateLintPass<'_> for ImplHashWithBorrowStrBytes { span_lint_and_then( cx, IMPL_HASH_BORROW_WITH_STR_AND_BYTES, - *span, + of_trait.trait_ref.path.span, "the semantics of `Borrow` around `Hash` can't be satisfied when both `Borrow` and `Borrow<[u8]>` are implemented", |diag| { diag.note("the `Borrow` semantics require that `Hash` must behave the same for all implementations of Borrow"); diff --git a/src/tools/clippy/clippy_lints/src/infallible_try_from.rs b/src/tools/clippy/clippy_lints/src/infallible_try_from.rs index 589c294a678a..36df07a43705 100644 --- a/src/tools/clippy/clippy_lints/src/infallible_try_from.rs +++ b/src/tools/clippy/clippy_lints/src/infallible_try_from.rs @@ -45,8 +45,8 @@ declare_lint_pass!(InfallibleTryFrom => [INFALLIBLE_TRY_FROM]); impl<'tcx> LateLintPass<'tcx> for InfallibleTryFrom { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { let ItemKind::Impl(imp) = item.kind else { return }; - let Some(r#trait) = imp.of_trait else { return }; - let Some(trait_def_id) = r#trait.trait_def_id() else { + let Some(of_trait) = imp.of_trait else { return }; + let Some(trait_def_id) = of_trait.trait_ref.trait_def_id() else { return; }; if !cx.tcx.is_diagnostic_item(sym::TryFrom, trait_def_id) { diff --git a/src/tools/clippy/clippy_lints/src/iter_without_into_iter.rs b/src/tools/clippy/clippy_lints/src/iter_without_into_iter.rs index b89f91f7255f..645e0f981f27 100644 --- a/src/tools/clippy/clippy_lints/src/iter_without_into_iter.rs +++ b/src/tools/clippy/clippy_lints/src/iter_without_into_iter.rs @@ -125,8 +125,9 @@ impl LateLintPass<'_> for IterWithoutIntoIter { fn check_item(&mut self, cx: &LateContext<'_>, item: &rustc_hir::Item<'_>) { if let ItemKind::Impl(imp) = item.kind && let TyKind::Ref(_, self_ty_without_ref) = &imp.self_ty.kind - && let Some(trait_ref) = imp.of_trait - && trait_ref + && let Some(of_trait) = imp.of_trait + && of_trait + .trait_ref .trait_def_id() .is_some_and(|did| cx.tcx.is_diagnostic_item(sym::IntoIterator, did)) && !item.span.in_external_macro(cx.sess().source_map()) diff --git a/src/tools/clippy/clippy_lints/src/lifetimes.rs b/src/tools/clippy/clippy_lints/src/lifetimes.rs index 35c9d2fd4eb8..149ae5e710c1 100644 --- a/src/tools/clippy/clippy_lints/src/lifetimes.rs +++ b/src/tools/clippy/clippy_lints/src/lifetimes.rs @@ -150,7 +150,7 @@ impl<'tcx> LateLintPass<'tcx> for Lifetimes { } = item.kind { check_fn_inner(cx, sig, Some(id), None, generics, item.span, true, self.msrv); - } else if let ItemKind::Impl(impl_) = item.kind + } else if let ItemKind::Impl(impl_) = &item.kind && !item.span.from_expansion() { report_extra_impl_lifetimes(cx, impl_); @@ -712,8 +712,8 @@ fn report_extra_impl_lifetimes<'tcx>(cx: &LateContext<'tcx>, impl_: &'tcx Impl<' let mut checker = LifetimeChecker::::new(cx, impl_.generics); walk_generics(&mut checker, impl_.generics); - if let Some(ref trait_ref) = impl_.of_trait { - walk_trait_ref(&mut checker, trait_ref); + if let Some(of_trait) = impl_.of_trait { + walk_trait_ref(&mut checker, &of_trait.trait_ref); } walk_unambig_ty(&mut checker, impl_.self_ty); for &item in impl_.items { diff --git a/src/tools/clippy/clippy_lints/src/missing_fields_in_debug.rs b/src/tools/clippy/clippy_lints/src/missing_fields_in_debug.rs index 18e2b384a463..8822b32b1c3d 100644 --- a/src/tools/clippy/clippy_lints/src/missing_fields_in_debug.rs +++ b/src/tools/clippy/clippy_lints/src/missing_fields_in_debug.rs @@ -198,8 +198,8 @@ fn check_struct<'tcx>( impl<'tcx> LateLintPass<'tcx> for MissingFieldsInDebug { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { // is this an `impl Debug for X` block? - if let ItemKind::Impl(Impl { of_trait: Some(trait_ref), self_ty, .. }) = item.kind - && let Res::Def(DefKind::Trait, trait_def_id) = trait_ref.path.res + if let ItemKind::Impl(Impl { of_trait: Some(of_trait), self_ty, .. }) = item.kind + && let Res::Def(DefKind::Trait, trait_def_id) = of_trait.trait_ref.path.res && let TyKind::Path(QPath::Resolved(_, self_path)) = &self_ty.kind // make sure that the self type is either a struct, an enum or a union // this prevents ICEs such as when self is a type parameter or a primitive type diff --git a/src/tools/clippy/clippy_lints/src/missing_trait_methods.rs b/src/tools/clippy/clippy_lints/src/missing_trait_methods.rs index 399bf4e18064..9cc93bf06531 100644 --- a/src/tools/clippy/clippy_lints/src/missing_trait_methods.rs +++ b/src/tools/clippy/clippy_lints/src/missing_trait_methods.rs @@ -61,10 +61,10 @@ impl<'tcx> LateLintPass<'tcx> for MissingTraitMethods { if !is_lint_allowed(cx, MISSING_TRAIT_METHODS, item.hir_id()) && span_is_local(item.span) && let ItemKind::Impl(Impl { - of_trait: Some(trait_ref), + of_trait: Some(of_trait), .. }) = item.kind - && let Some(trait_id) = trait_ref.trait_def_id() + && let Some(trait_id) = of_trait.trait_ref.trait_def_id() { let trait_item_ids: DefIdSet = cx .tcx diff --git a/src/tools/clippy/clippy_lints/src/non_copy_const.rs b/src/tools/clippy/clippy_lints/src/non_copy_const.rs index 388c029c9ef4..8a5a6f4a4dc1 100644 --- a/src/tools/clippy/clippy_lints/src/non_copy_const.rs +++ b/src/tools/clippy/clippy_lints/src/non_copy_const.rs @@ -778,7 +778,7 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst<'tcx> { if let Node::Item(parent_item) = cx.tcx.parent_hir_node(item.hir_id()) && let ItemKind::Impl(impl_block) = parent_item.kind && let Some(of_trait) = impl_block.of_trait - && let Some(trait_id) = of_trait.trait_def_id() + && let Some(trait_id) = of_trait.trait_ref.trait_def_id() { // Replace all instances of `::AssocType` with the // unit type and check again. If the result is the same then the diff --git a/src/tools/clippy/clippy_lints/src/non_send_fields_in_send_ty.rs b/src/tools/clippy/clippy_lints/src/non_send_fields_in_send_ty.rs index 8ff78ec7c580..b810bc01fbdc 100644 --- a/src/tools/clippy/clippy_lints/src/non_send_fields_in_send_ty.rs +++ b/src/tools/clippy/clippy_lints/src/non_send_fields_in_send_ty.rs @@ -83,10 +83,10 @@ impl<'tcx> LateLintPass<'tcx> for NonSendFieldInSendTy { if !item.span.in_external_macro(cx.tcx.sess.source_map()) && let Some(send_trait) = cx.tcx.get_diagnostic_item(sym::Send) && let ItemKind::Impl(hir_impl) = &item.kind - && let Some(trait_ref) = &hir_impl.of_trait - && let Some(trait_id) = trait_ref.trait_def_id() + && let Some(of_trait) = &hir_impl.of_trait + && let Some(trait_id) = of_trait.trait_ref.trait_def_id() && send_trait == trait_id - && hir_impl.polarity == ImplPolarity::Positive + && of_trait.polarity == ImplPolarity::Positive && let Some(ty_trait_ref) = cx.tcx.impl_trait_ref(item.owner_id) && let self_ty = ty_trait_ref.instantiate_identity().self_ty() && let ty::Adt(adt_def, impl_trait_args) = self_ty.kind() diff --git a/src/tools/clippy/clippy_lints/src/operators/op_ref.rs b/src/tools/clippy/clippy_lints/src/operators/op_ref.rs index 0a1f2625f4cf..9c160ff680ec 100644 --- a/src/tools/clippy/clippy_lints/src/operators/op_ref.rs +++ b/src/tools/clippy/clippy_lints/src/operators/op_ref.rs @@ -183,7 +183,7 @@ fn in_impl<'tcx>( && let item = cx.tcx.hir_expect_item(impl_def_id.expect_local()) && let ItemKind::Impl(item) = &item.kind && let Some(of_trait) = &item.of_trait - && let Some(seg) = of_trait.path.segments.last() + && let Some(seg) = of_trait.trait_ref.path.segments.last() && let Res::Def(_, trait_id) = seg.res && trait_id == bin_op && let Some(generic_args) = seg.args diff --git a/src/tools/clippy/clippy_lints/src/partialeq_ne_impl.rs b/src/tools/clippy/clippy_lints/src/partialeq_ne_impl.rs index 301b2cd4bf2f..77751e75a8e1 100644 --- a/src/tools/clippy/clippy_lints/src/partialeq_ne_impl.rs +++ b/src/tools/clippy/clippy_lints/src/partialeq_ne_impl.rs @@ -34,15 +34,15 @@ declare_lint_pass!(PartialEqNeImpl => [PARTIALEQ_NE_IMPL]); impl<'tcx> LateLintPass<'tcx> for PartialEqNeImpl { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { if let ItemKind::Impl(Impl { - of_trait: Some(trait_ref), + of_trait: Some(of_trait), items: impl_items, .. }) = item.kind && !cx.tcx.is_automatically_derived(item.owner_id.to_def_id()) && let Some(eq_trait) = cx.tcx.lang_items().eq_trait() - && trait_ref.path.res.def_id() == eq_trait + && of_trait.trait_ref.path.res.def_id() == eq_trait { - for impl_item in *impl_items { + for impl_item in impl_items { if cx.tcx.item_name(impl_item.owner_id) == sym::ne { span_lint_hir( cx, diff --git a/src/tools/clippy/clippy_lints/src/same_name_method.rs b/src/tools/clippy/clippy_lints/src/same_name_method.rs index 67eb71f7d074..b87751f49864 100644 --- a/src/tools/clippy/clippy_lints/src/same_name_method.rs +++ b/src/tools/clippy/clippy_lints/src/same_name_method.rs @@ -68,9 +68,9 @@ impl<'tcx> LateLintPass<'tcx> for SameNameMethod { let existing_name = map.get_mut(res).unwrap(); match of_trait { - Some(trait_ref) => { + Some(of_trait) => { let mut methods_in_trait: BTreeSet = if let Node::TraitRef(TraitRef { path, .. }) = - cx.tcx.hir_node(trait_ref.hir_ref_id) + cx.tcx.hir_node(of_trait.trait_ref.hir_ref_id) && let Res::Def(DefKind::Trait, did) = path.res { // FIXME: if diff --git a/src/tools/clippy/clippy_lints/src/serde_api.rs b/src/tools/clippy/clippy_lints/src/serde_api.rs index 2de22e4b6a36..01c7f394b9ad 100644 --- a/src/tools/clippy/clippy_lints/src/serde_api.rs +++ b/src/tools/clippy/clippy_lints/src/serde_api.rs @@ -26,16 +26,16 @@ declare_lint_pass!(SerdeApi => [SERDE_API_MISUSE]); impl<'tcx> LateLintPass<'tcx> for SerdeApi { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { if let ItemKind::Impl(Impl { - of_trait: Some(trait_ref), + of_trait: Some(of_trait), items, .. }) = item.kind { - let did = trait_ref.path.res.def_id(); + let did = of_trait.trait_ref.path.res.def_id(); if paths::SERDE_DE_VISITOR.matches(cx, did) { let mut seen_str = None; let mut seen_string = None; - for item in *items { + for item in items { match cx.tcx.item_name(item.owner_id) { sym::visit_str => seen_str = Some(cx.tcx.def_span(item.owner_id)), sym::visit_string => seen_string = Some(cx.tcx.def_span(item.owner_id)), diff --git a/src/tools/clippy/clippy_lints/src/to_string_trait_impl.rs b/src/tools/clippy/clippy_lints/src/to_string_trait_impl.rs index 9596b85664b3..303f6028bd51 100644 --- a/src/tools/clippy/clippy_lints/src/to_string_trait_impl.rs +++ b/src/tools/clippy/clippy_lints/src/to_string_trait_impl.rs @@ -48,10 +48,10 @@ declare_lint_pass!(ToStringTraitImpl => [TO_STRING_TRAIT_IMPL]); impl<'tcx> LateLintPass<'tcx> for ToStringTraitImpl { fn check_item(&mut self, cx: &LateContext<'tcx>, it: &'tcx Item<'tcx>) { if let ItemKind::Impl(Impl { - of_trait: Some(trait_ref), + of_trait: Some(of_trait), .. }) = it.kind - && let Some(trait_did) = trait_ref.trait_def_id() + && let Some(trait_did) = of_trait.trait_ref.trait_def_id() && cx.tcx.is_diagnostic_item(sym::ToString, trait_did) { span_lint_and_help( diff --git a/src/tools/clippy/clippy_lints/src/unconditional_recursion.rs b/src/tools/clippy/clippy_lints/src/unconditional_recursion.rs index dcddff557d1c..e843e169113e 100644 --- a/src/tools/clippy/clippy_lints/src/unconditional_recursion.rs +++ b/src/tools/clippy/clippy_lints/src/unconditional_recursion.rs @@ -137,9 +137,9 @@ fn get_impl_trait_def_id(cx: &LateContext<'_>, method_def_id: LocalDefId) -> Opt // We exclude `impl` blocks generated from rustc's proc macros. && !cx.tcx.is_automatically_derived(owner_id.to_def_id()) // It is a implementation of a trait. - && let Some(trait_) = impl_.of_trait + && let Some(of_trait) = impl_.of_trait { - trait_.trait_def_id() + of_trait.trait_ref.trait_def_id() } else { None } @@ -242,8 +242,8 @@ fn check_to_string(cx: &LateContext<'_>, method_span: Span, method_def_id: Local // We exclude `impl` blocks generated from rustc's proc macros. && !cx.tcx.is_automatically_derived(owner_id.to_def_id()) // It is a implementation of a trait. - && let Some(trait_) = impl_.of_trait - && let Some(trait_def_id) = trait_.trait_def_id() + && let Some(of_trait) = impl_.of_trait + && let Some(trait_def_id) = of_trait.trait_ref.trait_def_id() // The trait is `ToString`. && cx.tcx.is_diagnostic_item(sym::ToString, trait_def_id) { diff --git a/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs b/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs index cf603c6190ba..1c52de52619e 100644 --- a/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs +++ b/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs @@ -8,7 +8,7 @@ use clippy_utils::source::walk_span_to_context; use clippy_utils::visitors::{Descend, for_each_expr}; use hir::HirId; use rustc_hir as hir; -use rustc_hir::{Block, BlockCheckMode, ItemKind, Node, UnsafeSource}; +use rustc_hir::{Block, BlockCheckMode, Impl, ItemKind, Node, UnsafeSource}; use rustc_lexer::{FrontmatterAllowed, TokenKind, tokenize}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::impl_lint_pass; @@ -204,7 +204,7 @@ impl<'tcx> LateLintPass<'tcx> for UndocumentedUnsafeBlocks { let item_has_safety_comment = item_has_safety_comment(cx, item); match (&item.kind, item_has_safety_comment) { // lint unsafe impl without safety comment - (ItemKind::Impl(impl_), HasSafetyComment::No) if impl_.safety.is_unsafe() => { + (ItemKind::Impl(Impl { of_trait: Some(of_trait), .. }), HasSafetyComment::No) if of_trait.safety.is_unsafe() => { if !is_lint_allowed(cx, UNDOCUMENTED_UNSAFE_BLOCKS, item.hir_id()) && !is_unsafe_from_proc_macro(cx, item.span) { @@ -228,7 +228,7 @@ impl<'tcx> LateLintPass<'tcx> for UndocumentedUnsafeBlocks { } }, // lint safe impl with unnecessary safety comment - (ItemKind::Impl(impl_), HasSafetyComment::Yes(pos)) if impl_.safety.is_safe() => { + (ItemKind::Impl(Impl { of_trait: Some(of_trait), .. }), HasSafetyComment::Yes(pos)) if of_trait.safety.is_safe() => { if !is_lint_allowed(cx, UNNECESSARY_SAFETY_COMMENT, item.hir_id()) { let (span, help_span) = mk_spans(pos); diff --git a/src/tools/clippy/clippy_lints/src/write.rs b/src/tools/clippy/clippy_lints/src/write.rs index d9a007635cae..a15e4e42e71d 100644 --- a/src/tools/clippy/clippy_lints/src/write.rs +++ b/src/tools/clippy/clippy_lints/src/write.rs @@ -347,10 +347,10 @@ impl<'tcx> LateLintPass<'tcx> for Write { fn is_debug_impl(cx: &LateContext<'_>, item: &Item<'_>) -> bool { if let ItemKind::Impl(Impl { - of_trait: Some(trait_ref), + of_trait: Some(of_trait), .. }) = &item.kind - && let Some(trait_id) = trait_ref.trait_def_id() + && let Some(trait_id) = of_trait.trait_ref.trait_def_id() { cx.tcx.is_diagnostic_item(sym::Debug, trait_id) } else { diff --git a/src/tools/clippy/clippy_utils/src/check_proc_macro.rs b/src/tools/clippy/clippy_utils/src/check_proc_macro.rs index dc31ed08fb71..e0c1b9d445a2 100644 --- a/src/tools/clippy/clippy_utils/src/check_proc_macro.rs +++ b/src/tools/clippy/clippy_utils/src/check_proc_macro.rs @@ -19,8 +19,8 @@ use rustc_ast::token::CommentKind; use rustc_hir::intravisit::FnKind; use rustc_hir::{ Block, BlockCheckMode, Body, Closure, Destination, Expr, ExprKind, FieldDef, FnHeader, FnRetTy, HirId, Impl, - ImplItem, ImplItemKind, IsAuto, Item, ItemKind, Lit, LoopSource, MatchSource, MutTy, Node, Path, QPath, Safety, - TraitItem, TraitItemKind, Ty, TyKind, UnOp, UnsafeSource, Variant, VariantData, YieldSource, + ImplItem, ImplItemKind, TraitImplHeader, IsAuto, Item, ItemKind, Lit, LoopSource, MatchSource, MutTy, Node, Path, + QPath, Safety, TraitItem, TraitItemKind, Ty, TyKind, UnOp, UnsafeSource, Variant, VariantData, YieldSource, }; use rustc_lint::{EarlyContext, LateContext, LintContext}; use rustc_middle::ty::TyCtxt; @@ -254,7 +254,7 @@ fn item_search_pat(item: &Item<'_>) -> (Pat, Pat) { ItemKind::Union(..) => (Pat::Str("union"), Pat::Str("}")), ItemKind::Trait(_, _, Safety::Unsafe, ..) | ItemKind::Impl(Impl { - safety: Safety::Unsafe, .. + of_trait: Some(TraitImplHeader { safety: Safety::Unsafe, .. }), .. }) => (Pat::Str("unsafe"), Pat::Str("}")), ItemKind::Trait(_, IsAuto::Yes, ..) => (Pat::Str("auto"), Pat::Str("}")), ItemKind::Trait(..) => (Pat::Str("trait"), Pat::Str("}")), diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs index fc716d86fc62..fcc120656e3e 100644 --- a/src/tools/clippy/clippy_utils/src/lib.rs +++ b/src/tools/clippy/clippy_utils/src/lib.rs @@ -528,8 +528,9 @@ pub fn path_def_id<'tcx>(cx: &LateContext<'_>, maybe_path: &impl MaybePath<'tcx> pub fn trait_ref_of_method<'tcx>(cx: &LateContext<'tcx>, owner: OwnerId) -> Option<&'tcx TraitRef<'tcx>> { if let Node::Item(item) = cx.tcx.hir_node(cx.tcx.hir_owner_parent(owner)) && let ItemKind::Impl(impl_) = &item.kind + && let Some(of_trait) = impl_.of_trait { - return impl_.of_trait.as_ref(); + return Some(&of_trait.trait_ref); } None } diff --git a/src/tools/clippy/clippy_utils/src/visitors.rs b/src/tools/clippy/clippy_utils/src/visitors.rs index ba5cbc738361..c9f5401ebe77 100644 --- a/src/tools/clippy/clippy_utils/src/visitors.rs +++ b/src/tools/clippy/clippy_utils/src/visitors.rs @@ -460,7 +460,8 @@ pub fn is_expr_unsafe<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> bool { } fn visit_nested_item(&mut self, id: ItemId) -> Self::Result { if let ItemKind::Impl(i) = &self.cx.tcx.hir_item(id).kind - && i.safety.is_unsafe() + && let Some(of_trait) = i.of_trait + && of_trait.safety.is_unsafe() { ControlFlow::Break(()) } else { diff --git a/tests/ui/unpretty/exhaustive.hir.stdout b/tests/ui/unpretty/exhaustive.hir.stdout index 9cfa65f58012..e9823c9575bc 100644 --- a/tests/ui/unpretty/exhaustive.hir.stdout +++ b/tests/ui/unpretty/exhaustive.hir.stdout @@ -509,7 +509,7 @@ mod items { impl () { } impl () { } impl Default for () { } - impl const Default for () { } + impl const Default for () { } } /// ItemKind::MacCall mod item_mac_call { } From f336cc777b84c29776e8cc1b148cf1065b9fae2d Mon Sep 17 00:00:00 2001 From: "Tim (Theemathas) Chirananthavat" Date: Tue, 12 Aug 2025 08:26:11 +0700 Subject: [PATCH 182/195] Make I-miscompile imply I-prioritize Since I-unsound already implies I-prioritize, it makes sense that I-miscompile should do the same. --- triagebot.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/triagebot.toml b/triagebot.toml index 71f1ed0fda3f..6f6e95c5b50e 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -207,6 +207,7 @@ trigger_labels = [ "regression-from-stable-to-beta", "regression-from-stable-to-nightly", "I-unsound", + "I-miscompile", ] exclude_labels = [ "P-*", From 1ce4b370e44fbac89b75438c8e75b26bff18d496 Mon Sep 17 00:00:00 2001 From: SabrinaJewson Date: Tue, 12 Aug 2025 05:12:34 +0100 Subject: [PATCH 183/195] Handle the `capacity == 0` case --- library/alloc/src/vec/mod.rs | 61 ++++++++++++++++++++---------------- 1 file changed, 34 insertions(+), 27 deletions(-) diff --git a/library/alloc/src/vec/mod.rs b/library/alloc/src/vec/mod.rs index e02265dcafca..8001faf9d20a 100644 --- a/library/alloc/src/vec/mod.rs +++ b/library/alloc/src/vec/mod.rs @@ -51,19 +51,20 @@ //! //! # Memory layout //! -//! For non-zero-sized types, [`Vec`] uses the [`Global`] allocator for its allocation. It is -//! valid to convert both ways between a [`Vec`] and a raw pointer allocated with the [`Global`] -//! allocator, provided that the [`Layout`] used with the allocator is correct for a sequence of -//! `capacity` elements of the type, and the first `len` values pointed to by the raw pointer are -//! valid. More precisely, a `ptr: *mut T` that has been allocated with the [`Global`] allocator -//! with [`Layout::array::(capacity)`][Layout::array] may be converted into a vec using -//! [`Vec::::from_raw_parts(ptr, len, capacity)`](Vec::from_raw_parts). -//! Conversely, the memory backing a `value: *mut T` obtained from [`Vec::::as_mut_ptr`] may be -//! deallocated using the [`Global`] allocator with the same layout. +//! When the type is non-zero-sized and the capacity is nonzero, [`Vec`] uses the [`Global`] +//! allocator for its allocation. It is valid to convert both ways between such a [`Vec`] and a raw +//! pointer allocated with the [`Global`] allocator, provided that the [`Layout`] used with the +//! allocator is correct for a sequence of `capacity` elements of the type, and the first `len` +//! values pointed to by the raw pointer are valid. More precisely, a `ptr: *mut T` that has been +//! allocated with the [`Global`] allocator with [`Layout::array::(capacity)`][Layout::array] may +//! be converted into a vec using +//! [`Vec::::from_raw_parts(ptr, len, capacity)`](Vec::from_raw_parts). Conversely, the memory +//! backing a `value: *mut T` obtained from [`Vec::::as_mut_ptr`] may be deallocated using the +//! [`Global`] allocator with the same layout. //! -//! For zero-sized types (ZSTs), the `Vec` pointer must be non-null and sufficiently aligned. -//! The recommended way to build a `Vec` of ZSTs if [`vec!`] cannot be used is to use -//! [`ptr::NonNull::dangling`]. +//! For zero-sized types (ZSTs), or when the capacity is zero, the `Vec` pointer must be non-null +//! and sufficiently aligned. The recommended way to build a `Vec` of ZSTs if [`vec!`] cannot be +//! used is to use [`ptr::NonNull::dangling`]. //! //! [`push`]: Vec::push //! [`ptr::NonNull::dangling`]: NonNull::dangling @@ -542,18 +543,23 @@ impl Vec { /// This is highly unsafe, due to the number of invariants that aren't /// checked: /// - /// * `ptr` must have been allocated using the global allocator, such as via - /// the [`alloc::alloc`] function. - /// * `T` needs to have the same alignment as what `ptr` was allocated with. + /// * If `T` is not a zero-sized type and the capacity is nonzero, `ptr` must have + /// been allocated using the global allocator, such as via the [`alloc::alloc`] + /// function. If `T` is a zero-sized type or the capacity is zero, `ptr` need + /// only be non-null and aligned. + /// * `T` needs to have the same alignment as what `ptr` was allocated with, + /// if the pointer is required to be allocated. /// (`T` having a less strict alignment is not sufficient, the alignment really /// needs to be equal to satisfy the [`dealloc`] requirement that memory must be /// allocated and deallocated with the same layout.) - /// * The size of `T` times the `capacity` (ie. the allocated size in bytes) needs - /// to be the same size as the pointer was allocated with. (Because similar to - /// alignment, [`dealloc`] must be called with the same layout `size`.) + /// * The size of `T` times the `capacity` (ie. the allocated size in bytes), if + /// nonzero, needs to be the same size as the pointer was allocated with. + /// (Because similar to alignment, [`dealloc`] must be called with the same + /// layout `size`.) /// * `length` needs to be less than or equal to `capacity`. /// * The first `length` values must be properly initialized values of type `T`. - /// * `capacity` needs to be the capacity that the pointer was allocated with. + /// * `capacity` needs to be the capacity that the pointer was allocated with, + /// if the pointer is required to be allocated. /// * The allocated size in bytes must be no larger than `isize::MAX`. /// See the safety documentation of [`pointer::offset`]. /// @@ -792,9 +798,10 @@ impl Vec { /// memory previously managed by the `Vec`. Most often, one does /// this by converting the raw pointer, length, and capacity back /// into a `Vec` with the [`from_raw_parts`] function; more generally, - /// if `T` is non-zero-sized one may use any method that calls - /// [`dealloc`] with a layout of `Layout::array::(capacity)`, - /// and if `T` is zero-sized nothing needs to be done. + /// if `T` is non-zero-sized and the capacity is nonzero, one may use + /// any method that calls [`dealloc`] with a layout of + /// `Layout::array::(capacity)`; if `T` is zero-sized or the + /// capacity is zero, nothing needs to be done. /// /// [`from_raw_parts`]: Vec::from_raw_parts /// [`dealloc`]: crate::alloc::GlobalAlloc::dealloc @@ -1777,11 +1784,11 @@ impl Vec { /// may still invalidate this pointer. /// See the second example below for how this guarantee can be used. /// - /// The method also guarantees that, as long as `T` is not zero-sized, the pointer may be - /// passed into [`dealloc`] with a layout of `Layout::array::(capacity)` in order to - /// deallocate the backing memory. If this is done, be careful not to run the destructor - /// of the `Vec`, as dropping it will result in double-frees. Wrapping the `Vec` in a - /// [`ManuallyDrop`] is the typical way to achieve this. + /// The method also guarantees that, as long as `T` is not zero-sized and the capacity is + /// nonzero, the pointer may be passed into [`dealloc`] with a layout of + /// `Layout::array::(capacity)` in order to deallocate the backing memory. If this is done, + /// be careful not to run the destructor of the `Vec`, as dropping it will result in + /// double-frees. Wrapping the `Vec` in a [`ManuallyDrop`] is the typical way to achieve this. /// /// # Examples /// From e4540224229d10a9a537519cad8a64f01060c0ea Mon Sep 17 00:00:00 2001 From: Zalathar Date: Tue, 12 Aug 2025 17:11:58 +1000 Subject: [PATCH 184/195] bootstrap: Only warn about `rust.debug-assertions` if downloading rustc --- src/bootstrap/src/core/config/config.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index c505cacadb5d..a656927b1f64 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -955,7 +955,7 @@ impl Config { config.download_rustc_commit = download_ci_rustc_commit(dwn_ctx, rust_download_rustc, config.llvm_assertions); - if debug_assertions_requested { + if debug_assertions_requested && config.download_rustc_commit.is_some() { eprintln!( "WARN: `rust.debug-assertions = true` will prevent downloading CI rustc as alt CI \ rustc is not currently built with debug assertions." From 5e4f465655157365c016834dd3db6045d649483c Mon Sep 17 00:00:00 2001 From: Ada Alakbarova <58857108+ada4a@users.noreply.github.com> Date: Tue, 12 Aug 2025 09:43:17 +0200 Subject: [PATCH 185/195] fix typo --- library/core/src/ops/range.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/core/src/ops/range.rs b/library/core/src/ops/range.rs index f33a33e6b752..95d1e2069ac9 100644 --- a/library/core/src/ops/range.rs +++ b/library/core/src/ops/range.rs @@ -853,7 +853,7 @@ pub trait RangeBounds { /// assert!( RangeBounds::is_empty(&(f32::NAN..5.0))); /// ``` /// - /// But never empty is either side is unbounded: + /// But never empty if either side is unbounded: /// /// ``` /// #![feature(range_bounds_is_empty)] From a279ba4349d26062ef24cdc7d90ae09437b97a5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Tue, 5 Aug 2025 17:46:56 +0200 Subject: [PATCH 186/195] Consolidate stageN directories in the build directory Now stageN-X corresponds to stage N X, as it should. --- src/bootstrap/src/bin/rustc.rs | 2 +- src/bootstrap/src/bin/rustdoc.rs | 4 ++-- src/bootstrap/src/core/build_steps/clean.rs | 2 +- src/bootstrap/src/core/build_steps/tool.rs | 6 +++--- src/bootstrap/src/lib.rs | 23 +++++++++++++-------- src/bootstrap/src/utils/shared_helpers.rs | 5 +++-- 6 files changed, 24 insertions(+), 18 deletions(-) diff --git a/src/bootstrap/src/bin/rustc.rs b/src/bootstrap/src/bin/rustc.rs index 0364c664ba51..5865df67b669 100644 --- a/src/bootstrap/src/bin/rustc.rs +++ b/src/bootstrap/src/bin/rustc.rs @@ -258,7 +258,7 @@ fn main() { eprintln!("{prefix} libdir: {libdir:?}"); } - maybe_dump(format!("stage{stage}-rustc"), &cmd); + maybe_dump(format!("stage{}-rustc", stage + 1), &cmd); let start = Instant::now(); let (child, status) = { diff --git a/src/bootstrap/src/bin/rustdoc.rs b/src/bootstrap/src/bin/rustdoc.rs index a338b9c80801..efb51bdce1e0 100644 --- a/src/bootstrap/src/bin/rustdoc.rs +++ b/src/bootstrap/src/bin/rustdoc.rs @@ -56,11 +56,11 @@ fn main() { // Thus, if we are on stage 0, we explicitly set `--cfg=bootstrap`. // We also declare that the flag is expected, which we need to do to not // get warnings about it being unexpected. - if stage == "0" { + if stage == 0 { cmd.arg("--cfg=bootstrap"); } - maybe_dump(format!("stage{stage}-rustdoc"), &cmd); + maybe_dump(format!("stage{}-rustdoc", stage + 1), &cmd); if verbose > 1 { eprintln!( diff --git a/src/bootstrap/src/core/build_steps/clean.rs b/src/bootstrap/src/core/build_steps/clean.rs index f67569d14860..8712aeb81eb8 100644 --- a/src/bootstrap/src/core/build_steps/clean.rs +++ b/src/bootstrap/src/core/build_steps/clean.rs @@ -129,7 +129,7 @@ fn clean_specific_stage(build: &Build, stage: u32) { for entry in entries { let entry = t!(entry); - let stage_prefix = format!("stage{stage}"); + let stage_prefix = format!("stage{}", stage + 1); // if current entry is not related with the target stage, continue if !entry.file_name().to_str().unwrap_or("").contains(&stage_prefix) { diff --git a/src/bootstrap/src/core/build_steps/tool.rs b/src/bootstrap/src/core/build_steps/tool.rs index 42f59f00e5a1..5e4ebb2755da 100644 --- a/src/bootstrap/src/core/build_steps/tool.rs +++ b/src/bootstrap/src/core/build_steps/tool.rs @@ -380,13 +380,13 @@ pub(crate) fn get_tool_target_compiler( /// tools directory. fn copy_link_tool_bin( builder: &Builder<'_>, - compiler: Compiler, + build_compiler: Compiler, target: TargetSelection, mode: Mode, name: &str, ) -> PathBuf { - let cargo_out = builder.cargo_out(compiler, mode, target).join(exe(name, target)); - let bin = builder.tools_dir(compiler).join(exe(name, target)); + let cargo_out = builder.cargo_out(build_compiler, mode, target).join(exe(name, target)); + let bin = builder.tools_dir(build_compiler).join(exe(name, target)); builder.copy_link(&cargo_out, &bin, FileType::Executable); bin } diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs index 4abf386e5de6..341ddf8da4f4 100644 --- a/src/bootstrap/src/lib.rs +++ b/src/bootstrap/src/lib.rs @@ -859,14 +859,17 @@ impl Build { if self.config.rust_optimize.is_release() { "release" } else { "debug" } } - fn tools_dir(&self, compiler: Compiler) -> PathBuf { - let out = self.out.join(compiler.host).join(format!("stage{}-tools-bin", compiler.stage)); + fn tools_dir(&self, build_compiler: Compiler) -> PathBuf { + let out = self + .out + .join(build_compiler.host) + .join(format!("stage{}-tools-bin", build_compiler.stage + 1)); t!(fs::create_dir_all(&out)); out } /// Returns the root directory for all output generated in a particular - /// stage when running with a particular host compiler. + /// stage when being built with a particular build compiler. /// /// The mode indicates what the root directory is for. fn stage_out(&self, build_compiler: Compiler, mode: Mode) -> PathBuf { @@ -876,15 +879,17 @@ impl Build { (None, "bootstrap-tools") } fn staged_tool(build_compiler: Compiler) -> (Option, &'static str) { - (Some(build_compiler.stage), "tools") + (Some(build_compiler.stage + 1), "tools") } let (stage, suffix) = match mode { + // Std is special, stage N std is built with stage N rustc Mode::Std => (Some(build_compiler.stage), "std"), - Mode::Rustc => (Some(build_compiler.stage), "rustc"), - Mode::Codegen => (Some(build_compiler.stage), "codegen"), + // The rest of things are built with stage N-1 rustc + Mode::Rustc => (Some(build_compiler.stage + 1), "rustc"), + Mode::Codegen => (Some(build_compiler.stage + 1), "codegen"), Mode::ToolBootstrap => bootstrap_tool(), - Mode::ToolStd | Mode::ToolRustc => (Some(build_compiler.stage), "tools"), + Mode::ToolStd | Mode::ToolRustc => (Some(build_compiler.stage + 1), "tools"), Mode::ToolTarget => { // If we're not cross-compiling (the common case), share the target directory with // bootstrap tools to reuse the build cache. @@ -907,8 +912,8 @@ impl Build { /// Returns the root output directory for all Cargo output in a given stage, /// running a particular compiler, whether or not we're building the /// standard library, and targeting the specified architecture. - fn cargo_out(&self, compiler: Compiler, mode: Mode, target: TargetSelection) -> PathBuf { - self.stage_out(compiler, mode).join(target).join(self.cargo_dir()) + fn cargo_out(&self, build_compiler: Compiler, mode: Mode, target: TargetSelection) -> PathBuf { + self.stage_out(build_compiler, mode).join(target).join(self.cargo_dir()) } /// Root output directory of LLVM for `target` diff --git a/src/bootstrap/src/utils/shared_helpers.rs b/src/bootstrap/src/utils/shared_helpers.rs index 9428e221f412..d620cc4bbb6b 100644 --- a/src/bootstrap/src/utils/shared_helpers.rs +++ b/src/bootstrap/src/utils/shared_helpers.rs @@ -81,10 +81,11 @@ pub fn parse_rustc_verbose() -> usize { } /// Parses the value of the "RUSTC_STAGE" environment variable and returns it as a `String`. +/// This is the stage of the *build compiler*, which we are wrapping using a rustc/rustdoc wrapper. /// /// If "RUSTC_STAGE" was not set, the program will be terminated with 101. -pub fn parse_rustc_stage() -> String { - env::var("RUSTC_STAGE").unwrap_or_else(|_| { +pub fn parse_rustc_stage() -> u32 { + env::var("RUSTC_STAGE").ok().and_then(|v| v.parse().ok()).unwrap_or_else(|| { // Don't panic here; it's reasonable to try and run these shims directly. Give a helpful error instead. eprintln!("rustc shim: FATAL: RUSTC_STAGE was not set"); eprintln!("rustc shim: NOTE: use `x.py build -vvv` to see all environment variables set by bootstrap"); From fbfcfa6bcebe939a89670baea09ef8b56f2dcf1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Tue, 12 Aug 2025 10:15:16 +0200 Subject: [PATCH 187/195] Unify all groups in bootstrap to use `Builder::msg` --- src/bootstrap/src/core/build_steps/check.rs | 38 +++--- src/bootstrap/src/core/build_steps/clippy.rs | 57 +++++---- src/bootstrap/src/core/build_steps/compile.rs | 74 ++++++------ src/bootstrap/src/core/build_steps/doc.rs | 33 +++--- src/bootstrap/src/core/build_steps/install.rs | 8 +- src/bootstrap/src/core/build_steps/test.rs | 90 ++++++-------- src/bootstrap/src/core/build_steps/tool.rs | 34 +----- src/bootstrap/src/lib.rs | 111 ++++++++---------- 8 files changed, 206 insertions(+), 239 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/check.rs b/src/bootstrap/src/core/build_steps/check.rs index f931aae3c2e0..0cbf8f55e993 100644 --- a/src/bootstrap/src/core/build_steps/check.rs +++ b/src/bootstrap/src/core/build_steps/check.rs @@ -72,7 +72,6 @@ impl Step for Std { fn run(self, builder: &Builder<'_>) { let build_compiler = self.build_compiler; - let stage = build_compiler.stage; let target = self.target; let mut cargo = builder::Cargo::new( @@ -94,10 +93,12 @@ impl Step for Std { cargo.arg("-p").arg(krate); } - let _guard = builder.msg_check( + let _guard = builder.msg( + Kind::Check, format_args!("library artifacts{}", crate_description(&self.crates)), + Mode::Std, + self.build_compiler, target, - Some(stage), ); let stamp = build_stamp::libstd_stamp(builder, build_compiler, target).with_prefix("check"); @@ -136,7 +137,13 @@ impl Step for Std { let stamp = build_stamp::libstd_stamp(builder, build_compiler, target).with_prefix("check-test"); - let _guard = builder.msg_check("library test/bench/example targets", target, Some(stage)); + let _guard = builder.msg( + Kind::Check, + "library test/bench/example targets", + Mode::Std, + self.build_compiler, + target, + ); run_cargo(builder, cargo, builder.config.free_args.clone(), &stamp, vec![], true, false); } @@ -227,10 +234,12 @@ impl Step for Rustc { cargo.arg("-p").arg(krate); } - let _guard = builder.msg_check( + let _guard = builder.msg( + Kind::Check, format_args!("compiler artifacts{}", crate_description(&self.crates)), + Mode::Rustc, + self.build_compiler, target, - None, ); let stamp = @@ -357,7 +366,13 @@ impl Step for CodegenBackend { .arg(builder.src.join(format!("compiler/{}/Cargo.toml", backend.crate_name()))); rustc_cargo_env(builder, &mut cargo, target); - let _guard = builder.msg_check(backend.crate_name(), target, None); + let _guard = builder.msg( + Kind::Check, + backend.crate_name(), + Mode::Codegen, + self.build_compiler, + target, + ); let stamp = build_stamp::codegen_backend_stamp(builder, build_compiler, target, &backend) .with_prefix("check"); @@ -482,14 +497,7 @@ fn run_tool_check_step( let stamp = BuildStamp::new(&builder.cargo_out(build_compiler, mode, target)) .with_prefix(&format!("{display_name}-check")); - let stage = match mode { - // Mode::ToolRustc is included here because of how msg_sysroot_tool prints stages - Mode::Std | Mode::ToolRustc => build_compiler.stage, - _ => build_compiler.stage + 1, - }; - - let _guard = - builder.msg_tool(builder.kind, mode, display_name, stage, &build_compiler.host, &target); + let _guard = builder.msg(builder.kind, display_name, mode, build_compiler, target); run_cargo(builder, cargo, builder.config.free_args.clone(), &stamp, vec![], true, false); } diff --git a/src/bootstrap/src/core/build_steps/clippy.rs b/src/bootstrap/src/core/build_steps/clippy.rs index 93c767bdd253..4d734fe5c667 100644 --- a/src/bootstrap/src/core/build_steps/clippy.rs +++ b/src/bootstrap/src/core/build_steps/clippy.rs @@ -143,11 +143,11 @@ impl Step for Std { fn run(self, builder: &Builder<'_>) { let target = self.target; - let compiler = builder.compiler(builder.top_stage, builder.config.host_target); + let build_compiler = builder.compiler(builder.top_stage, builder.config.host_target); let mut cargo = builder::Cargo::new( builder, - compiler, + build_compiler, Mode::Std, SourceType::InTree, target, @@ -160,14 +160,19 @@ impl Step for Std { cargo.arg("-p").arg(krate); } - let _guard = - builder.msg_clippy(format_args!("library{}", crate_description(&self.crates)), target); + let _guard = builder.msg( + Kind::Clippy, + format_args!("library{}", crate_description(&self.crates)), + Mode::Std, + build_compiler, + target, + ); run_cargo( builder, cargo, lint_args(builder, &self.config, IGNORED_RULES_FOR_STD_AND_RUSTC), - &build_stamp::libstd_stamp(builder, compiler, target), + &build_stamp::libstd_stamp(builder, build_compiler, target), vec![], true, false, @@ -203,33 +208,33 @@ impl Step for Rustc { /// This will lint the compiler for a particular stage of the build using /// the `compiler` targeting the `target` architecture. fn run(self, builder: &Builder<'_>) { - let compiler = builder.compiler(builder.top_stage, builder.config.host_target); + let build_compiler = builder.compiler(builder.top_stage, builder.config.host_target); let target = self.target; if !builder.download_rustc() { - if compiler.stage != 0 { + if build_compiler.stage != 0 { // If we're not in stage 0, then we won't have a std from the beta // compiler around. That means we need to make sure there's one in // the sysroot for the compiler to find. Otherwise, we're going to // fail when building crates that need to generate code (e.g., build // scripts and their dependencies). - builder.std(compiler, compiler.host); - builder.std(compiler, target); + builder.std(build_compiler, build_compiler.host); + builder.std(build_compiler, target); } else { - builder.ensure(check::Std::new(compiler, target)); + builder.ensure(check::Std::new(build_compiler, target)); } } let mut cargo = builder::Cargo::new( builder, - compiler, + build_compiler, Mode::Rustc, SourceType::InTree, target, Kind::Clippy, ); - rustc_cargo(builder, &mut cargo, target, &compiler, &self.crates); + rustc_cargo(builder, &mut cargo, target, &build_compiler, &self.crates); // Explicitly pass -p for all compiler crates -- this will force cargo // to also lint the tests/benches/examples for these crates, rather @@ -238,14 +243,19 @@ impl Step for Rustc { cargo.arg("-p").arg(krate); } - let _guard = - builder.msg_clippy(format_args!("compiler{}", crate_description(&self.crates)), target); + let _guard = builder.msg( + Kind::Clippy, + format_args!("compiler{}", crate_description(&self.crates)), + Mode::Rustc, + build_compiler, + target, + ); run_cargo( builder, cargo, lint_args(builder, &self.config, IGNORED_RULES_FOR_STD_AND_RUSTC), - &build_stamp::librustc_stamp(builder, compiler, target), + &build_stamp::librustc_stamp(builder, build_compiler, target), vec![], true, false, @@ -284,16 +294,16 @@ macro_rules! lint_any { } fn run(self, builder: &Builder<'_>) -> Self::Output { - let compiler = builder.compiler(builder.top_stage, builder.config.host_target); + let build_compiler = builder.compiler(builder.top_stage, builder.config.host_target); let target = self.target; if !builder.download_rustc() { - builder.ensure(check::Rustc::new(builder, compiler, target)); + builder.ensure(check::Rustc::new(builder, build_compiler, target)); }; let cargo = prepare_tool_cargo( builder, - compiler, + build_compiler, Mode::ToolRustc, target, Kind::Clippy, @@ -302,17 +312,16 @@ macro_rules! lint_any { &[], ); - let _guard = builder.msg_tool( + let _guard = builder.msg( Kind::Clippy, - Mode::ToolRustc, $readable_name, - compiler.stage, - &compiler.host, - &target, + Mode::ToolRustc, + build_compiler, + target, ); let stringified_name = stringify!($name).to_lowercase(); - let stamp = BuildStamp::new(&builder.cargo_out(compiler, Mode::ToolRustc, target)) + let stamp = BuildStamp::new(&builder.cargo_out(build_compiler, Mode::ToolRustc, target)) .with_prefix(&format!("{}-check", stringified_name)); run_cargo( diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs index 4519731ada7f..c04f08035652 100644 --- a/src/bootstrap/src/core/build_steps/compile.rs +++ b/src/bootstrap/src/core/build_steps/compile.rs @@ -159,7 +159,7 @@ impl Step for Std { return; } - let compiler = if builder.download_rustc() && self.force_recompile { + let build_compiler = if builder.download_rustc() && self.force_recompile { // When there are changes in the library tree with CI-rustc, we want to build // the stageN library and that requires using stageN-1 compiler. builder.compiler(self.compiler.stage.saturating_sub(1), builder.config.host_target) @@ -173,7 +173,8 @@ impl Step for Std { && builder.config.is_host_target(target) && !self.force_recompile { - let sysroot = builder.ensure(Sysroot { compiler, force_recompile: false }); + let sysroot = + builder.ensure(Sysroot { compiler: build_compiler, force_recompile: false }); cp_rustc_component_to_ci_sysroot( builder, &sysroot, @@ -182,53 +183,58 @@ impl Step for Std { return; } - if builder.config.keep_stage.contains(&compiler.stage) - || builder.config.keep_stage_std.contains(&compiler.stage) + if builder.config.keep_stage.contains(&build_compiler.stage) + || builder.config.keep_stage_std.contains(&build_compiler.stage) { trace!(keep_stage = ?builder.config.keep_stage); trace!(keep_stage_std = ?builder.config.keep_stage_std); builder.info("WARNING: Using a potentially old libstd. This may not behave well."); - builder.ensure(StartupObjects { compiler, target }); + builder.ensure(StartupObjects { compiler: build_compiler, target }); - self.copy_extra_objects(builder, &compiler, target); + self.copy_extra_objects(builder, &build_compiler, target); - builder.ensure(StdLink::from_std(self, compiler)); + builder.ensure(StdLink::from_std(self, build_compiler)); return; } - let mut target_deps = builder.ensure(StartupObjects { compiler, target }); + let mut target_deps = builder.ensure(StartupObjects { compiler: build_compiler, target }); - let compiler_to_use = builder.compiler_for(compiler.stage, compiler.host, target); + let compiler_to_use = + builder.compiler_for(build_compiler.stage, build_compiler.host, target); trace!(?compiler_to_use); - if compiler_to_use != compiler + if compiler_to_use != build_compiler // Never uplift std unless we have compiled stage 1; if stage 1 is compiled, // uplift it from there. // // FIXME: improve `fn compiler_for` to avoid adding stage condition here. - && compiler.stage > 1 + && build_compiler.stage > 1 { - trace!(?compiler_to_use, ?compiler, "compiler != compiler_to_use, uplifting library"); + trace!( + ?compiler_to_use, + ?build_compiler, + "compiler != compiler_to_use, uplifting library" + ); builder.std(compiler_to_use, target); let msg = if compiler_to_use.host == target { format!( "Uplifting library (stage{} -> stage{})", - compiler_to_use.stage, compiler.stage + compiler_to_use.stage, build_compiler.stage ) } else { format!( "Uplifting library (stage{}:{} -> stage{}:{})", - compiler_to_use.stage, compiler_to_use.host, compiler.stage, target + compiler_to_use.stage, compiler_to_use.host, build_compiler.stage, target ) }; builder.info(&msg); // Even if we're not building std this stage, the new sysroot must // still contain the third party objects needed by various targets. - self.copy_extra_objects(builder, &compiler, target); + self.copy_extra_objects(builder, &build_compiler, target); builder.ensure(StdLink::from_std(self, compiler_to_use)); return; @@ -236,11 +242,11 @@ impl Step for Std { trace!( ?compiler_to_use, - ?compiler, + ?build_compiler, "compiler == compiler_to_use, handling not-cross-compile scenario" ); - target_deps.extend(self.copy_extra_objects(builder, &compiler, target)); + target_deps.extend(self.copy_extra_objects(builder, &build_compiler, target)); // We build a sysroot for mir-opt tests using the same trick that Miri does: A check build // with -Zalways-encode-mir. This frees us from the need to have a target linker, and the @@ -249,7 +255,7 @@ impl Step for Std { trace!("building special sysroot for mir-opt tests"); let mut cargo = builder::Cargo::new_for_mir_opt_tests( builder, - compiler, + build_compiler, Mode::Std, SourceType::InTree, target, @@ -262,7 +268,7 @@ impl Step for Std { trace!("building regular sysroot"); let mut cargo = builder::Cargo::new( builder, - compiler, + build_compiler, Mode::Std, SourceType::InTree, target, @@ -285,16 +291,16 @@ impl Step for Std { let _guard = builder.msg( Kind::Build, - compiler.stage, format_args!("library artifacts{}", crate_description(&self.crates)), - compiler.host, + Mode::Std, + build_compiler, target, ); run_cargo( builder, cargo, vec![], - &build_stamp::libstd_stamp(builder, compiler, target), + &build_stamp::libstd_stamp(builder, build_compiler, target), target_deps, self.is_for_mir_opt_tests, // is_check false, @@ -302,7 +308,7 @@ impl Step for Std { builder.ensure(StdLink::from_std( self, - builder.compiler(compiler.stage, builder.config.host_target), + builder.compiler(build_compiler.stage, builder.config.host_target), )); } @@ -1126,11 +1132,11 @@ impl Step for Rustc { cargo.env("RUSTC_BOLT_LINK_FLAGS", "1"); } - let _guard = builder.msg_rustc_tool( + let _guard = builder.msg( Kind::Build, - build_compiler.stage, format_args!("compiler artifacts{}", crate_description(&self.crates)), - build_compiler.host, + Mode::Rustc, + build_compiler, target, ); let stamp = build_stamp::librustc_stamp(builder, build_compiler, target); @@ -1603,11 +1609,11 @@ impl Step for GccCodegenBackend { let gcc = builder.ensure(Gcc { target }); add_cg_gcc_cargo_flags(&mut cargo, &gcc); - let _guard = builder.msg_rustc_tool( + let _guard = builder.msg( Kind::Build, - build_compiler.stage, - "codegen backend gcc", - build_compiler.host, + format_args!("codegen backend gcc"), + Mode::Codegen, + build_compiler, target, ); let files = run_cargo(builder, cargo, vec![], &stamp, vec![], false, false); @@ -1687,11 +1693,11 @@ impl Step for CraneliftCodegenBackend { .arg(builder.src.join("compiler/rustc_codegen_cranelift/Cargo.toml")); rustc_cargo_env(builder, &mut cargo, target); - let _guard = builder.msg_rustc_tool( + let _guard = builder.msg( Kind::Build, - build_compiler.stage, - "codegen backend cranelift", - build_compiler.host, + format_args!("codegen backend cranelift"), + Mode::Codegen, + build_compiler, target, ); let files = run_cargo(builder, cargo, vec![], &stamp, vec![], false, false); diff --git a/src/bootstrap/src/core/build_steps/doc.rs b/src/bootstrap/src/core/build_steps/doc.rs index ca7a6dc8e07e..6f0d5203d117 100644 --- a/src/bootstrap/src/core/build_steps/doc.rs +++ b/src/bootstrap/src/core/build_steps/doc.rs @@ -239,7 +239,7 @@ impl Step for TheBook { fn run(self, builder: &Builder<'_>) { builder.require_submodule("src/doc/book", None); - let compiler = self.build_compiler; + let build_compiler = self.build_compiler; let target = self.target; let absolute_path = builder.src.join("src/doc/book"); @@ -273,20 +273,20 @@ impl Step for TheBook { let shared_assets = builder.ensure(SharedAssets { target }); // build the redirect pages - let _guard = builder.msg_doc(compiler, "book redirect pages", target); + let _guard = builder.msg(Kind::Doc, "book redirect pages", None, build_compiler, target); for file in t!(fs::read_dir(redirect_path)) { let file = t!(file); let path = file.path(); let path = path.to_str().unwrap(); - invoke_rustdoc(builder, compiler, &shared_assets, target, path); + invoke_rustdoc(builder, build_compiler, &shared_assets, target, path); } } } fn invoke_rustdoc( builder: &Builder<'_>, - compiler: Compiler, + build_compiler: Compiler, shared_assets: &SharedAssetsPaths, target: TargetSelection, markdown: &str, @@ -298,7 +298,7 @@ fn invoke_rustdoc( let header = builder.src.join("src/doc/redirect.inc"); let footer = builder.src.join("src/doc/footer.inc"); - let mut cmd = builder.rustdoc_cmd(compiler); + let mut cmd = builder.rustdoc_cmd(build_compiler); let out = out.join("book"); @@ -362,7 +362,7 @@ impl Step for Standalone { fn run(self, builder: &Builder<'_>) { let target = self.target; let build_compiler = self.build_compiler; - let _guard = builder.msg_doc(build_compiler, "standalone", target); + let _guard = builder.msg(Kind::Doc, "standalone", None, build_compiler, target); let out = builder.doc_out(target); t!(fs::create_dir_all(&out)); @@ -469,7 +469,7 @@ impl Step for Releases { fn run(self, builder: &Builder<'_>) { let target = self.target; let build_compiler = self.build_compiler; - let _guard = builder.msg_doc(build_compiler, "releases", target); + let _guard = builder.msg(Kind::Doc, "releases", None, build_compiler, target); let out = builder.doc_out(target); t!(fs::create_dir_all(&out)); @@ -784,7 +784,7 @@ fn doc_std( let description = format!("library{} in {} format", crate_description(requested_crates), format.as_str()); - let _guard = builder.msg_doc(build_compiler, description, target); + let _guard = builder.msg(Kind::Doc, description, None, build_compiler, target); cargo.into_cmd().run(builder); builder.cp_link_r(&out_dir, out); @@ -861,11 +861,11 @@ impl Step for Rustc { let build_compiler = self.build_compiler; builder.std(build_compiler, builder.config.host_target); - let _guard = builder.msg_rustc_tool( + let _guard = builder.msg( Kind::Doc, - build_compiler.stage, format!("compiler{}", crate_description(&self.crates)), - build_compiler.host, + Mode::Rustc, + build_compiler, target, ); @@ -1059,7 +1059,7 @@ macro_rules! tool_doc { let proc_macro_out_dir = builder.stage_out(build_compiler, mode).join("doc"); symlink_dir_force(&builder.config, &out, &proc_macro_out_dir); - let _guard = builder.msg_doc(build_compiler, stringify!($tool).to_lowercase(), target); + let _guard = builder.msg(Kind::Doc, stringify!($tool).to_lowercase(), None, build_compiler, target); cargo.into_cmd().run(builder); if !builder.config.dry_run() { @@ -1314,13 +1314,8 @@ impl Step for RustcBook { // bootstrap.toml), then this needs to explicitly update the dylib search // path. builder.add_rustc_lib_path(self.build_compiler, &mut cmd); - let doc_generator_guard = builder.msg( - Kind::Run, - self.build_compiler.stage, - "lint-docs", - self.build_compiler.host, - self.target, - ); + let doc_generator_guard = + builder.msg(Kind::Run, "lint-docs", None, self.build_compiler, self.target); cmd.run(builder); drop(doc_generator_guard); diff --git a/src/bootstrap/src/core/build_steps/install.rs b/src/bootstrap/src/core/build_steps/install.rs index acee78dcf59b..6d09e41e646c 100644 --- a/src/bootstrap/src/core/build_steps/install.rs +++ b/src/bootstrap/src/core/build_steps/install.rs @@ -68,7 +68,13 @@ fn install_sh( host: Option, tarball: &GeneratedTarball, ) { - let _guard = builder.msg(Kind::Install, stage, package, host, host); + let _guard = builder.msg( + Kind::Install, + package, + None, + (host.unwrap_or(builder.host_target), stage), + host, + ); let prefix = default_path(&builder.config.prefix, "/usr/local"); let sysconfdir = prefix.join(default_path(&builder.config.sysconfdir, "/etc")); diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index 2f933baa4a4c..fa72e93f503c 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -161,8 +161,7 @@ You can skip linkcheck with --skip src/tools/linkchecker" let linkchecker = builder.tool_cmd(Tool::Linkchecker); // Run the linkchecker. - let _guard = - builder.msg(Kind::Test, compiler.stage, "Linkcheck", bootstrap_host, bootstrap_host); + let _guard = builder.msg(Kind::Test, "Linkcheck", None, compiler, bootstrap_host); let _time = helpers::timeit(builder); linkchecker.delay_failure().arg(builder.out.join(host).join("doc")).run(builder); } @@ -541,8 +540,7 @@ impl Miri { cargo.env("MIRI_SYSROOT", &miri_sysroot); let mut cargo = BootstrapCommand::from(cargo); - let _guard = - builder.msg(Kind::Build, compiler.stage, "miri sysroot", compiler.host, target); + let _guard = builder.msg(Kind::Build, "miri sysroot", Mode::ToolRustc, compiler, target); cargo.run(builder); // # Determine where Miri put its sysroot. @@ -643,7 +641,8 @@ impl Step for Miri { cargo.env("MIRI_TEST_TARGET", target.rustc_target_arg()); { - let _guard = builder.msg_rustc_tool(Kind::Test, stage, "miri", host, target); + let _guard = + builder.msg(Kind::Test, "miri", Mode::ToolRustc, miri.build_compiler, target); let _time = helpers::timeit(builder); cargo.run(builder); } @@ -659,11 +658,11 @@ impl Step for Miri { cargo.args(["tests/pass", "tests/panic"]); { - let _guard = builder.msg_rustc_tool( + let _guard = builder.msg( Kind::Test, - stage, "miri (mir-opt-level 4)", - host, + Mode::ToolRustc, + miri.build_compiler, target, ); let _time = helpers::timeit(builder); @@ -703,7 +702,7 @@ impl Step for CargoMiri { } // This compiler runs on the host, we'll just use it for the target. - let compiler = builder.compiler(stage, host); + let build_compiler = builder.compiler(stage, host); // Run `cargo miri test`. // This is just a smoke test (Miri's own CI invokes this in a bunch of different ways and ensures @@ -711,7 +710,7 @@ impl Step for CargoMiri { // itself executes properly under Miri, and that all the logic in `cargo-miri` does not explode. let mut cargo = tool::prepare_tool_cargo( builder, - compiler, + build_compiler, Mode::ToolStd, // it's unclear what to use here, we're not building anything just doing a smoke test! target, Kind::MiriTest, @@ -736,7 +735,8 @@ impl Step for CargoMiri { // Finally, run everything. let mut cargo = BootstrapCommand::from(cargo); { - let _guard = builder.msg_rustc_tool(Kind::Test, stage, "cargo-miri", host, target); + let _guard = + builder.msg(Kind::Test, "cargo-miri", Mode::ToolRustc, (host, stage), target); let _time = helpers::timeit(builder); cargo.run(builder); } @@ -830,7 +830,7 @@ impl Step for Clippy { /// Runs `cargo test` for clippy. fn run(self, builder: &Builder<'_>) { - let host = self.compilers.target(); + let target = self.compilers.target(); // We need to carefully distinguish the compiler that builds clippy, and the compiler // that is linked into the clippy being tested. `target_compiler` is the latter, @@ -844,7 +844,7 @@ impl Step for Clippy { builder, build_compiler, Mode::ToolRustc, - host, + target, Kind::Test, "src/tools/clippy", SourceType::InTree, @@ -858,7 +858,7 @@ impl Step for Clippy { cargo.env("HOST_LIBS", host_libs); // Build the standard library that the tests can use. - builder.std(target_compiler, host); + builder.std(target_compiler, target); cargo.env("TEST_SYSROOT", builder.sysroot(target_compiler)); cargo.env("TEST_RUSTC", builder.rustc(target_compiler)); cargo.env("TEST_RUSTC_LIB", builder.rustc_libdir(target_compiler)); @@ -881,9 +881,9 @@ impl Step for Clippy { } cargo.add_rustc_lib_path(builder); - let cargo = prepare_cargo_test(cargo, &[], &[], host, builder); + let cargo = prepare_cargo_test(cargo, &[], &[], target, builder); - let _guard = builder.msg_rustc_tool(Kind::Test, build_compiler.stage, "clippy", host, host); + let _guard = builder.msg(Kind::Test, "clippy", Mode::ToolRustc, build_compiler, target); // Clippy reports errors if it blessed the outputs if cargo.allow_failure().run(builder) { @@ -990,9 +990,9 @@ impl Step for RustdocJSStd { )); let _guard = builder.msg( Kind::Test, - builder.top_stage, "rustdoc-js-std", - builder.config.host_target, + None, + (builder.config.host_target, builder.top_stage), self.target, ); command.run(builder); @@ -1141,13 +1141,7 @@ impl Step for RustdocGUI { } let _time = helpers::timeit(builder); - let _guard = builder.msg_rustc_tool( - Kind::Test, - self.compiler.stage, - "rustdoc-gui", - self.compiler.host, - self.target, - ); + let _guard = builder.msg(Kind::Test, "rustdoc-gui", None, self.compiler, self.target); try_run_tests(builder, &mut cmd, true); } } @@ -2237,9 +2231,9 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the let _group = builder.msg( Kind::Test, - compiler.stage, format!("compiletest suite={suite} mode={mode}"), - compiler.host, + Mode::ToolBootstrap, + compiler, target, ); try_run_tests(builder, &mut cmd, false); @@ -2381,9 +2375,9 @@ impl BookTest { builder.add_rust_test_threads(&mut rustbook_cmd); let _guard = builder.msg( Kind::Test, - compiler.stage, format_args!("mdbook {}", self.path.display()), - compiler.host, + None, + compiler, compiler.host, ); let _time = helpers::timeit(builder); @@ -2402,8 +2396,7 @@ impl BookTest { builder.std(compiler, host); - let _guard = - builder.msg(Kind::Test, compiler.stage, format!("book {}", self.name), host, host); + let _guard = builder.msg(Kind::Test, format!("book {}", self.name), None, compiler, host); // Do a breadth-first traversal of the `src/doc` directory and just run // tests for all files that end in `*.md` @@ -2547,9 +2540,9 @@ impl Step for ErrorIndex { let guard = builder.msg( Kind::Test, - target_compiler.stage, "error-index", - target_compiler.host, + None, + self.compilers.build_compiler(), target_compiler.host, ); let _time = helpers::timeit(builder); @@ -2650,9 +2643,8 @@ fn run_cargo_test<'a>( let compiler = cargo.compiler(); let mut cargo = prepare_cargo_test(cargo, libtest_args, crates, target, builder); let _time = helpers::timeit(builder); - let _group = description.into().and_then(|what| { - builder.msg_rustc_tool(Kind::Test, compiler.stage, what, compiler.host, target) - }); + let _group = + description.into().and_then(|what| builder.msg(Kind::Test, what, None, compiler, target)); #[cfg(feature = "build-metrics")] builder.metrics.begin_test_suite( @@ -3176,8 +3168,9 @@ impl Step for Bootstrap { /// Tests the build system itself. fn run(self, builder: &Builder<'_>) { let host = builder.config.host_target; - let compiler = builder.compiler(0, host); - let _guard = builder.msg(Kind::Test, 0, "bootstrap", host, host); + let build_compiler = builder.compiler(0, host); + let _guard = + builder.msg(Kind::Test, "bootstrap", Mode::ToolBootstrap, build_compiler, host); // Some tests require cargo submodule to be present. builder.build.require_submodule("src/tools/cargo", None); @@ -3196,7 +3189,7 @@ impl Step for Bootstrap { let mut cargo = tool::prepare_tool_cargo( builder, - compiler, + build_compiler, Mode::ToolBootstrap, host, Kind::Test, @@ -3276,9 +3269,9 @@ impl Step for TierCheck { let _guard = builder.msg( Kind::Test, - self.compiler.stage, "platform support check", - self.compiler.host, + None, + self.compiler, self.compiler.host, ); BootstrapCommand::from(cargo).delay_failure().run(builder); @@ -3326,10 +3319,10 @@ impl Step for RustInstaller { /// Ensure the version placeholder replacement tool builds fn run(self, builder: &Builder<'_>) { let bootstrap_host = builder.config.host_target; - let compiler = builder.compiler(0, bootstrap_host); + let build_compiler = builder.compiler(0, bootstrap_host); let cargo = tool::prepare_tool_cargo( builder, - compiler, + build_compiler, Mode::ToolBootstrap, bootstrap_host, Kind::Test, @@ -3338,13 +3331,8 @@ impl Step for RustInstaller { &[], ); - let _guard = builder.msg( - Kind::Test, - compiler.stage, - "rust-installer", - bootstrap_host, - bootstrap_host, - ); + let _guard = + builder.msg(Kind::Test, "rust-installer", None, build_compiler, bootstrap_host); run_cargo_test(cargo, &[], &[], None, bootstrap_host, builder); // We currently don't support running the test.sh script outside linux(?) environments. @@ -3355,7 +3343,7 @@ impl Step for RustInstaller { } let mut cmd = command(builder.src.join("src/tools/rust-installer/test.sh")); - let tmpdir = testdir(builder, compiler.host).join("rust-installer"); + let tmpdir = testdir(builder, build_compiler.host).join("rust-installer"); let _ = std::fs::remove_dir_all(&tmpdir); let _ = std::fs::create_dir_all(&tmpdir); cmd.current_dir(&tmpdir); diff --git a/src/bootstrap/src/core/build_steps/tool.rs b/src/bootstrap/src/core/build_steps/tool.rs index 5e4ebb2755da..793e6b629c99 100644 --- a/src/bootstrap/src/core/build_steps/tool.rs +++ b/src/bootstrap/src/core/build_steps/tool.rs @@ -26,7 +26,7 @@ use crate::core::builder::{ use crate::core::config::{DebuginfoLevel, RustcLto, TargetSelection}; use crate::utils::exec::{BootstrapCommand, command}; use crate::utils::helpers::{add_dylib_path, exe, t}; -use crate::{Compiler, FileType, Kind, Mode, gha}; +use crate::{Compiler, FileType, Kind, Mode}; #[derive(Debug, Clone, Hash, PartialEq, Eq)] pub enum SourceType { @@ -58,28 +58,6 @@ struct ToolBuild { artifact_kind: ToolArtifactKind, } -impl Builder<'_> { - #[track_caller] - pub(crate) fn msg_tool( - &self, - kind: Kind, - mode: Mode, - tool: &str, - build_stage: u32, - host: &TargetSelection, - target: &TargetSelection, - ) -> Option { - match mode { - // depends on compiler stage, different to host compiler - Mode::ToolRustc => { - self.msg_rustc_tool(kind, build_stage, format_args!("tool {tool}"), *host, *target) - } - // doesn't depend on compiler, same as host compiler - _ => self.msg(kind, build_stage, format_args!("tool {tool}"), *host, *target), - } - } -} - /// Result of the tool build process. Each `Step` in this module is responsible /// for using this type as `type Output = ToolBuildResult;` #[derive(Clone)] @@ -166,14 +144,8 @@ impl Step for ToolBuild { cargo.args(self.cargo_args); - let _guard = builder.msg_tool( - Kind::Build, - self.mode, - self.tool, - self.build_compiler.stage, - &self.build_compiler.host, - &self.target, - ); + let _guard = + builder.msg(Kind::Build, self.tool, self.mode, self.build_compiler, self.target); // we check this below let build_success = compile::stream_cargo(builder, cargo, vec![], &mut |_| {}); diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs index 341ddf8da4f4..24ba4b12264c 100644 --- a/src/bootstrap/src/lib.rs +++ b/src/bootstrap/src/lib.rs @@ -410,6 +410,25 @@ forward! { download_rustc() -> bool, } +/// A mostly temporary helper struct before we can migrate everything in bootstrap to use +/// the concept of a build compiler. +struct HostAndStage { + host: TargetSelection, + stage: u32, +} + +impl From<(TargetSelection, u32)> for HostAndStage { + fn from((host, stage): (TargetSelection, u32)) -> Self { + Self { host, stage } + } +} + +impl From for HostAndStage { + fn from(compiler: Compiler) -> Self { + Self { host: compiler.host, stage: compiler.stage } + } +} + impl Build { /// Creates a new set of build configuration from the `flags` on the command /// line and the filesystem `config`. @@ -1072,45 +1091,14 @@ impl Build { } } - #[must_use = "Groups should not be dropped until the Step finishes running"] - #[track_caller] - fn msg_clippy( - &self, - what: impl Display, - target: impl Into>, - ) -> Option { - self.msg(Kind::Clippy, self.config.stage, what, self.config.host_target, target) - } - - #[must_use = "Groups should not be dropped until the Step finishes running"] - #[track_caller] - fn msg_check( - &self, - what: impl Display, - target: impl Into>, - custom_stage: Option, - ) -> Option { - self.msg( - Kind::Check, - custom_stage.unwrap_or(self.config.stage), - what, - self.config.host_target, - target, - ) - } - - #[must_use = "Groups should not be dropped until the Step finishes running"] - #[track_caller] - fn msg_doc( - &self, - compiler: Compiler, - what: impl Display, - target: impl Into> + Copy, - ) -> Option { - self.msg(Kind::Doc, compiler.stage, what, compiler.host, target.into()) - } - - /// Return a `Group` guard for a [`Step`] that is built for each `--stage`. + /// Return a `Group` guard for a [`Step`] that: + /// - Performs `action` + /// - On `what` + /// - Where `what` possibly corresponds to a `mode` + /// - `action` is performed using the given build compiler (`host_and_stage`). + /// - Since some steps do not use the concept of a build compiler yet, it is also possible + /// to pass the host and stage explicitly. + /// - With a given `target`. /// /// [`Step`]: crate::core::builder::Step #[must_use = "Groups should not be dropped until the Step finishes running"] @@ -1118,15 +1106,31 @@ impl Build { fn msg( &self, action: impl Into, - stage: u32, what: impl Display, - host: impl Into>, + mode: impl Into>, + host_and_stage: impl Into, target: impl Into>, ) -> Option { + let host_and_stage = host_and_stage.into(); + let actual_stage = match mode.into() { + // Std has the same stage as the compiler that builds it + Some(Mode::Std) => host_and_stage.stage, + // Other things have stage corresponding to their build compiler + 1 + Some( + Mode::Rustc + | Mode::Codegen + | Mode::ToolBootstrap + | Mode::ToolTarget + | Mode::ToolStd + | Mode::ToolRustc, + ) + | None => host_and_stage.stage + 1, + }; + let action = action.into().description(); - let msg = |fmt| format!("{action} stage{stage} {what}{fmt}"); + let msg = |fmt| format!("{action} stage{actual_stage} {what}{fmt}"); let msg = if let Some(target) = target.into() { - let host = host.into().unwrap(); + let host = host_and_stage.host; if host == target { msg(format_args!(" ({target})")) } else { @@ -1154,27 +1158,6 @@ impl Build { self.group(&msg) } - #[must_use = "Groups should not be dropped until the Step finishes running"] - #[track_caller] - fn msg_rustc_tool( - &self, - action: impl Into, - build_stage: u32, - what: impl Display, - host: TargetSelection, - target: TargetSelection, - ) -> Option { - let action = action.into().description(); - let msg = |fmt| format!("{action} {what} {fmt}"); - - let msg = if host == target { - msg(format_args!("(stage{build_stage} -> stage{}, {target})", build_stage + 1)) - } else { - msg(format_args!("(stage{build_stage}:{host} -> stage{}:{target})", build_stage + 1)) - }; - self.group(&msg) - } - #[track_caller] fn group(&self, msg: &str) -> Option { match self.config.get_dry_run() { From df2da84a619afcc8e0adfff142cb28af0ada6abe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Tue, 12 Aug 2025 10:42:14 +0200 Subject: [PATCH 188/195] Change format of messages in `Builder::fmt` --- src/bootstrap/src/lib.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs index 24ba4b12264c..0b65ebc06718 100644 --- a/src/bootstrap/src/lib.rs +++ b/src/bootstrap/src/lib.rs @@ -1130,11 +1130,12 @@ impl Build { let action = action.into().description(); let msg = |fmt| format!("{action} stage{actual_stage} {what}{fmt}"); let msg = if let Some(target) = target.into() { + let build_stage = host_and_stage.stage; let host = host_and_stage.host; if host == target { - msg(format_args!(" ({target})")) + msg(format_args!(" (stage{build_stage} -> stage{actual_stage}, {target})")) } else { - msg(format_args!(" ({host} -> {target})")) + msg(format_args!(" (stage{build_stage}:{host} -> stage{actual_stage}:{target})")) } } else { msg(format_args!("")) From 7aa8707639ede0605d7182bc13047b6e4549e5b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jana=20D=C3=B6nszelmann?= Date: Tue, 29 Jul 2025 20:51:46 +0200 Subject: [PATCH 189/195] make no_mangle explicit on foreign items --- .../src/back/symbol_export.rs | 2 +- .../rustc_codegen_ssa/src/codegen_attrs.rs | 21 +++++++ .../src/middle/codegen_fn_attrs.rs | 7 ++- compiler/rustc_middle/src/mir/mono.rs | 2 +- .../src/cross_crate_inline.rs | 2 +- compiler/rustc_passes/src/check_attr.rs | 36 ++++++------ compiler/rustc_passes/src/dead.rs | 2 +- compiler/rustc_passes/src/reachable.rs | 5 +- compiler/rustc_symbol_mangling/src/lib.rs | 55 ++++++++++--------- .../clippy/clippy_lints/src/missing_inline.rs | 2 +- src/tools/miri/src/bin/miri.rs | 2 +- src/tools/miri/src/helpers.rs | 2 +- tests/run-make/wasm-panic-small/rmake.rs | 2 +- 13 files changed, 85 insertions(+), 55 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs index 7e124f65324a..0494666bda92 100644 --- a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs +++ b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs @@ -569,7 +569,7 @@ fn symbol_export_level(tcx: TyCtxt<'_>, sym_def_id: DefId) -> SymbolExportLevel // core/std/allocators/etc. For example symbols used to hook up allocation // are not considered for export let codegen_fn_attrs = tcx.codegen_fn_attrs(sym_def_id); - let is_extern = codegen_fn_attrs.contains_extern_indicator(); + let is_extern = codegen_fn_attrs.contains_extern_indicator(tcx, sym_def_id); let std_internal = codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL); diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs index 7f54a47327af..287787eb3d1f 100644 --- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs +++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs @@ -443,6 +443,27 @@ fn apply_overrides(tcx: TyCtxt<'_>, did: LocalDefId, codegen_fn_attrs: &mut Code if tcx.should_inherit_track_caller(did) { codegen_fn_attrs.flags |= CodegenFnAttrFlags::TRACK_CALLER; } + + // Foreign items by default use no mangling for their symbol name. + if tcx.is_foreign_item(did) { + // There's a few exceptions to this rule though: + if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL) { + // * `#[rustc_std_internal_symbol]` mangles the symbol name in a special way + // both for exports and imports through foreign items. This is handled further, + // during symbol mangling logic. + } else if codegen_fn_attrs.link_name.is_some() { + // * This can be overridden with the `#[link_name]` attribute + } else { + // NOTE: there's one more exception that we cannot apply here. On wasm, + // some items cannot be `no_mangle`. + // However, we don't have enough information here to determine that. + // As such, no_mangle foreign items on wasm that have the same defid as some + // import will *still* be mangled despite this. + // + // if none of the exceptions apply; apply no_mangle + codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_MANGLE; + } + } } fn check_result( diff --git a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs index 94384e64afd1..52341df07408 100644 --- a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs +++ b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs @@ -3,6 +3,7 @@ use std::borrow::Cow; use rustc_abi::Align; use rustc_ast::expand::autodiff_attrs::AutoDiffAttrs; use rustc_hir::attrs::{InlineAttr, InstructionSetAttr, OptimizeAttr}; +use rustc_hir::def_id::DefId; use rustc_macros::{HashStable, TyDecodable, TyEncodable}; use rustc_span::Symbol; use rustc_target::spec::SanitizerSet; @@ -193,7 +194,11 @@ impl CodegenFnAttrs { /// * `#[linkage]` is present /// /// Keep this in sync with the logic for the unused_attributes for `#[inline]` lint. - pub fn contains_extern_indicator(&self) -> bool { + pub fn contains_extern_indicator(&self, tcx: TyCtxt<'_>, did: DefId) -> bool { + if tcx.is_foreign_item(did) { + return false; + } + self.flags.contains(CodegenFnAttrFlags::NO_MANGLE) || self.flags.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL) || self.export_name.is_some() diff --git a/compiler/rustc_middle/src/mir/mono.rs b/compiler/rustc_middle/src/mir/mono.rs index e5864660575c..3afd946b30a2 100644 --- a/compiler/rustc_middle/src/mir/mono.rs +++ b/compiler/rustc_middle/src/mir/mono.rs @@ -151,7 +151,7 @@ impl<'tcx> MonoItem<'tcx> { // instantiation: // We emit an unused_attributes lint for this case, which should be kept in sync if possible. let codegen_fn_attrs = tcx.codegen_instance_attrs(instance.def); - if codegen_fn_attrs.contains_extern_indicator() + if codegen_fn_attrs.contains_extern_indicator(tcx, instance.def.def_id()) || codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NAKED) { return InstantiationMode::GloballyShared { may_conflict: false }; diff --git a/compiler/rustc_mir_transform/src/cross_crate_inline.rs b/compiler/rustc_mir_transform/src/cross_crate_inline.rs index b186c2bd7758..03fdf9fbac53 100644 --- a/compiler/rustc_mir_transform/src/cross_crate_inline.rs +++ b/compiler/rustc_mir_transform/src/cross_crate_inline.rs @@ -18,7 +18,7 @@ fn cross_crate_inlinable(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { let codegen_fn_attrs = tcx.codegen_fn_attrs(def_id); // If this has an extern indicator, then this function is globally shared and thus will not // generate cgu-internal copies which would make it cross-crate inlinable. - if codegen_fn_attrs.contains_extern_indicator() { + if codegen_fn_attrs.contains_extern_indicator(tcx, def_id.into()) { return false; } diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 10c532b436aa..22e2a887b61a 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -561,7 +561,24 @@ impl<'tcx> CheckAttrVisitor<'tcx> { match target { Target::Fn | Target::Closure - | Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent) => {} + | Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent) => { + // `#[inline]` is ignored if the symbol must be codegened upstream because it's exported. + if let Some(did) = hir_id.as_owner() + && self.tcx.def_kind(did).has_codegen_attrs() + && kind != &InlineAttr::Never + { + let attrs = self.tcx.codegen_fn_attrs(did); + // Not checking naked as `#[inline]` is forbidden for naked functions anyways. + if attrs.contains_extern_indicator(self.tcx, did.into()) { + self.tcx.emit_node_span_lint( + UNUSED_ATTRIBUTES, + hir_id, + attr_span, + errors::InlineIgnoredForExported {}, + ); + } + } + } Target::Method(MethodKind::Trait { body: false }) | Target::ForeignFn => { self.tcx.emit_node_span_lint( UNUSED_ATTRIBUTES, @@ -588,23 +605,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { self.dcx().emit_err(errors::InlineNotFnOrClosure { attr_span, defn_span }); } } - - // `#[inline]` is ignored if the symbol must be codegened upstream because it's exported. - if let Some(did) = hir_id.as_owner() - && self.tcx.def_kind(did).has_codegen_attrs() - && kind != &InlineAttr::Never - { - let attrs = self.tcx.codegen_fn_attrs(did); - // Not checking naked as `#[inline]` is forbidden for naked functions anyways. - if attrs.contains_extern_indicator() { - self.tcx.emit_node_span_lint( - UNUSED_ATTRIBUTES, - hir_id, - attr_span, - errors::InlineIgnoredForExported {}, - ); - } - } } /// Checks that `#[coverage(..)]` is applied to a function/closure/method, diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs index fa9d0c7b1b7d..1c76e4e7166d 100644 --- a/compiler/rustc_passes/src/dead.rs +++ b/compiler/rustc_passes/src/dead.rs @@ -701,7 +701,7 @@ fn has_allow_dead_code_or_lang_attr( // #[used], #[no_mangle], #[export_name], etc also keeps the item alive // forcefully, e.g., for placing it in a specific section. - cg_attrs.contains_extern_indicator() + cg_attrs.contains_extern_indicator(tcx, def_id.into()) || cg_attrs.flags.contains(CodegenFnAttrFlags::USED_COMPILER) || cg_attrs.flags.contains(CodegenFnAttrFlags::USED_LINKER) } diff --git a/compiler/rustc_passes/src/reachable.rs b/compiler/rustc_passes/src/reachable.rs index 2f78c22c7483..6cd8a54ecf43 100644 --- a/compiler/rustc_passes/src/reachable.rs +++ b/compiler/rustc_passes/src/reachable.rs @@ -183,7 +183,7 @@ impl<'tcx> ReachableContext<'tcx> { } else { CodegenFnAttrs::EMPTY }; - let is_extern = codegen_attrs.contains_extern_indicator(); + let is_extern = codegen_attrs.contains_extern_indicator(self.tcx, search_item.into()); if is_extern { self.reachable_symbols.insert(search_item); } @@ -423,8 +423,9 @@ fn has_custom_linkage(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { if !tcx.def_kind(def_id).has_codegen_attrs() { return false; } + let codegen_attrs = tcx.codegen_fn_attrs(def_id); - codegen_attrs.contains_extern_indicator() + codegen_attrs.contains_extern_indicator(tcx, def_id.into()) // FIXME(nbdd0121): `#[used]` are marked as reachable here so it's picked up by // `linked_symbols` in cg_ssa. They won't be exported in binary or cdylib due to their // `SymbolExportLevel::Rust` export level but may end up being exported in dylibs. diff --git a/compiler/rustc_symbol_mangling/src/lib.rs b/compiler/rustc_symbol_mangling/src/lib.rs index 6bcb7f6e093c..f3c96f641902 100644 --- a/compiler/rustc_symbol_mangling/src/lib.rs +++ b/compiler/rustc_symbol_mangling/src/lib.rs @@ -218,32 +218,33 @@ fn compute_symbol_name<'tcx>( } } - // Foreign items by default use no mangling for their symbol name. There's a - // few exceptions to this rule though: - // - // * This can be overridden with the `#[link_name]` attribute - // - // * On the wasm32 targets there is a bug (or feature) in LLD [1] where the - // same-named symbol when imported from different wasm modules will get - // hooked up incorrectly. As a result foreign symbols, on the wasm target, - // with a wasm import module, get mangled. Additionally our codegen will - // deduplicate symbols based purely on the symbol name, but for wasm this - // isn't quite right because the same-named symbol on wasm can come from - // different modules. For these reasons if `#[link(wasm_import_module)]` - // is present we mangle everything on wasm because the demangled form will - // show up in the `wasm-import-name` custom attribute in LLVM IR. - // - // * `#[rustc_std_internal_symbol]` mangles the symbol name in a special way - // both for exports and imports through foreign items. This is handled above. - // [1]: https://bugs.llvm.org/show_bug.cgi?id=44316 - if tcx.is_foreign_item(def_id) - && (!tcx.sess.target.is_like_wasm - || !tcx.wasm_import_module_map(def_id.krate).contains_key(&def_id)) + let wasm_import_module_exception_force_mangling = { + // * On the wasm32 targets there is a bug (or feature) in LLD [1] where the + // same-named symbol when imported from different wasm modules will get + // hooked up incorrectly. As a result foreign symbols, on the wasm target, + // with a wasm import module, get mangled. Additionally our codegen will + // deduplicate symbols based purely on the symbol name, but for wasm this + // isn't quite right because the same-named symbol on wasm can come from + // different modules. For these reasons if `#[link(wasm_import_module)]` + // is present we mangle everything on wasm because the demangled form will + // show up in the `wasm-import-name` custom attribute in LLVM IR. + // + // [1]: https://bugs.llvm.org/show_bug.cgi?id=44316 + // + // So, on wasm if a foreign item loses its `#[no_mangle]`, it might *still* + // be mangled if we're forced to. Note: I don't like this. + // These kinds of exceptions should be added during the `codegen_attrs` query. + // However, we don't have the wasm import module map there yet. + tcx.is_foreign_item(def_id) + && tcx.sess.target.is_like_wasm + && tcx.wasm_import_module_map(LOCAL_CRATE).contains_key(&def_id.into()) + }; + + if let Some(name) = attrs.link_name + && !wasm_import_module_exception_force_mangling { - if let Some(name) = attrs.link_name { - return name.to_string(); - } - return tcx.item_name(def_id).to_string(); + // Use provided name + return name.to_string(); } if let Some(name) = attrs.export_name { @@ -251,7 +252,9 @@ fn compute_symbol_name<'tcx>( return name.to_string(); } - if attrs.flags.contains(CodegenFnAttrFlags::NO_MANGLE) { + if attrs.flags.contains(CodegenFnAttrFlags::NO_MANGLE) + && !wasm_import_module_exception_force_mangling + { // Don't mangle return tcx.item_name(def_id).to_string(); } diff --git a/src/tools/clippy/clippy_lints/src/missing_inline.rs b/src/tools/clippy/clippy_lints/src/missing_inline.rs index d02952eb4870..b16924babd1c 100644 --- a/src/tools/clippy/clippy_lints/src/missing_inline.rs +++ b/src/tools/clippy/clippy_lints/src/missing_inline.rs @@ -190,5 +190,5 @@ impl<'tcx> LateLintPass<'tcx> for MissingInline { /// and a rustc warning would be triggered, see #15301 fn fn_is_externally_exported(cx: &LateContext<'_>, def_id: DefId) -> bool { let attrs = cx.tcx.codegen_fn_attrs(def_id); - attrs.contains_extern_indicator() + attrs.contains_extern_indicator(cx.tcx, def_id) } diff --git a/src/tools/miri/src/bin/miri.rs b/src/tools/miri/src/bin/miri.rs index ae1b25f8857a..d9e374c414ca 100644 --- a/src/tools/miri/src/bin/miri.rs +++ b/src/tools/miri/src/bin/miri.rs @@ -279,7 +279,7 @@ impl rustc_driver::Callbacks for MiriBeRustCompilerCalls { return None; } let codegen_fn_attrs = tcx.codegen_fn_attrs(local_def_id); - if codegen_fn_attrs.contains_extern_indicator() + if codegen_fn_attrs.contains_extern_indicator(tcx, local_def_id.into()) || codegen_fn_attrs .flags .contains(CodegenFnAttrFlags::USED_COMPILER) diff --git a/src/tools/miri/src/helpers.rs b/src/tools/miri/src/helpers.rs index 43cb1c9ae053..a8e2151afe61 100644 --- a/src/tools/miri/src/helpers.rs +++ b/src/tools/miri/src/helpers.rs @@ -132,7 +132,7 @@ pub fn iter_exported_symbols<'tcx>( for def_id in crate_items.definitions() { let exported = tcx.def_kind(def_id).has_codegen_attrs() && { let codegen_attrs = tcx.codegen_fn_attrs(def_id); - codegen_attrs.contains_extern_indicator() + codegen_attrs.contains_extern_indicator(tcx, def_id.into()) || codegen_attrs.flags.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL) || codegen_attrs.flags.contains(CodegenFnAttrFlags::USED_COMPILER) || codegen_attrs.flags.contains(CodegenFnAttrFlags::USED_LINKER) diff --git a/tests/run-make/wasm-panic-small/rmake.rs b/tests/run-make/wasm-panic-small/rmake.rs index e69fbac96356..ea0b6faf037c 100644 --- a/tests/run-make/wasm-panic-small/rmake.rs +++ b/tests/run-make/wasm-panic-small/rmake.rs @@ -24,5 +24,5 @@ fn test(cfg: &str) { let bytes = rfs::read("foo.wasm"); println!("{}", bytes.len()); - assert!(bytes.len() < 40_000); + assert!(bytes.len() < 40_000, "bytes len was: {}", bytes.len()); } From f7defc0fe5ea6270de7be2159f91b4cc7afabf47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Tue, 12 Aug 2025 15:01:33 +0200 Subject: [PATCH 190/195] Resolve review remarks --- src/bootstrap/src/core/build_steps/compile.rs | 13 ++++--------- src/bootstrap/src/core/build_steps/test.rs | 1 + 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs index c04f08035652..c8feba48d84b 100644 --- a/src/bootstrap/src/core/build_steps/compile.rs +++ b/src/bootstrap/src/core/build_steps/compile.rs @@ -215,7 +215,7 @@ impl Step for Std { trace!( ?compiler_to_use, ?build_compiler, - "compiler != compiler_to_use, uplifting library" + "build_compiler != compiler_to_use, uplifting library" ); builder.std(compiler_to_use, target); @@ -1609,13 +1609,8 @@ impl Step for GccCodegenBackend { let gcc = builder.ensure(Gcc { target }); add_cg_gcc_cargo_flags(&mut cargo, &gcc); - let _guard = builder.msg( - Kind::Build, - format_args!("codegen backend gcc"), - Mode::Codegen, - build_compiler, - target, - ); + let _guard = + builder.msg(Kind::Build, "codegen backend gcc", Mode::Codegen, build_compiler, target); let files = run_cargo(builder, cargo, vec![], &stamp, vec![], false, false); write_codegen_backend_stamp(stamp, files, builder.config.dry_run()) } @@ -1695,7 +1690,7 @@ impl Step for CraneliftCodegenBackend { let _guard = builder.msg( Kind::Build, - format_args!("codegen backend cranelift"), + "codegen backend cranelift", Mode::Codegen, build_compiler, target, diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index fa72e93f503c..f1be0af31837 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -2232,6 +2232,7 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the let _group = builder.msg( Kind::Test, format!("compiletest suite={suite} mode={mode}"), + // FIXME: compiletest sometimes behaves as ToolStd, we could expose that difference here Mode::ToolBootstrap, compiler, target, From 72b606db89c7d2ce76ea41ea80ec92e6320bf20f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Tue, 12 Aug 2025 18:39:09 +0200 Subject: [PATCH 191/195] Replace `stage0-tools-bin` with `stage1-tools-bin` --- src/bootstrap/src/core/builder/mod.rs | 2 +- src/ci/docker/host-aarch64/dist-aarch64-linux/Dockerfile | 2 +- src/ci/docker/host-x86_64/dist-x86_64-linux/dist.sh | 2 +- src/ci/github-actions/jobs.yml | 2 +- src/doc/rustc-dev-guide/src/building/optimized-build.md | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/bootstrap/src/core/builder/mod.rs b/src/bootstrap/src/core/builder/mod.rs index 163a498d4b48..de4b941ac908 100644 --- a/src/bootstrap/src/core/builder/mod.rs +++ b/src/bootstrap/src/core/builder/mod.rs @@ -1597,7 +1597,7 @@ You have to build a stage1 compiler for `{}` first, and then use it to build a s cmd.env("CARGO", &self.initial_cargo); // Need to add the `run_compiler` libs. Those are the libs produces *by* `build_compiler` // in `tool::ToolBuild` step, so they match the Miri we just built. However this means they - // are actually living one stage up, i.e. we are running `stage0-tools-bin/miri` with the + // are actually living one stage up, i.e. we are running `stage1-tools-bin/miri` with the // libraries in `stage1/lib`. This is an unfortunate off-by-1 caused (possibly) by the fact // that Miri doesn't have an "assemble" step like rustc does that would cross the stage boundary. // We can't use `add_rustc_lib_path` as that's a NOP on Windows but we do need these libraries diff --git a/src/ci/docker/host-aarch64/dist-aarch64-linux/Dockerfile b/src/ci/docker/host-aarch64/dist-aarch64-linux/Dockerfile index 2b8a3f829c60..e726329753f2 100644 --- a/src/ci/docker/host-aarch64/dist-aarch64-linux/Dockerfile +++ b/src/ci/docker/host-aarch64/dist-aarch64-linux/Dockerfile @@ -96,7 +96,7 @@ ENV RUST_CONFIGURE_ARGS \ --set rust.codegen-units=1 ENV SCRIPT python3 ../x.py build --set rust.debug=true opt-dist && \ - ./build/$HOSTS/stage0-tools-bin/opt-dist linux-ci -- python3 ../x.py dist \ + ./build/$HOSTS/stage1-tools-bin/opt-dist linux-ci -- python3 ../x.py dist \ --host $HOSTS --target $HOSTS --include-default-paths build-manifest bootstrap ENV CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=clang diff --git a/src/ci/docker/host-x86_64/dist-x86_64-linux/dist.sh b/src/ci/docker/host-x86_64/dist-x86_64-linux/dist.sh index 924bdbc76150..4c95d4a401e5 100755 --- a/src/ci/docker/host-x86_64/dist-x86_64-linux/dist.sh +++ b/src/ci/docker/host-x86_64/dist-x86_64-linux/dist.sh @@ -4,7 +4,7 @@ set -eux python3 ../x.py build --set rust.debug=true opt-dist -./build/$HOSTS/stage0-tools-bin/opt-dist linux-ci -- python3 ../x.py dist \ +./build/$HOSTS/stage1-tools-bin/opt-dist linux-ci -- python3 ../x.py dist \ --host $HOSTS --target $HOSTS \ --include-default-paths \ build-manifest bootstrap diff --git a/src/ci/github-actions/jobs.yml b/src/ci/github-actions/jobs.yml index 48c570bfa11a..ae13d14c3805 100644 --- a/src/ci/github-actions/jobs.yml +++ b/src/ci/github-actions/jobs.yml @@ -656,7 +656,7 @@ auto: --enable-full-tools --enable-profiler --set rust.codegen-units=1 - SCRIPT: python x.py build --set rust.debug=true opt-dist && PGO_HOST=x86_64-pc-windows-msvc ./build/x86_64-pc-windows-msvc/stage0-tools-bin/opt-dist windows-ci -- python x.py dist bootstrap --include-default-paths + SCRIPT: python x.py build --set rust.debug=true opt-dist && PGO_HOST=x86_64-pc-windows-msvc ./build/x86_64-pc-windows-msvc/stage1-tools-bin/opt-dist windows-ci -- python x.py dist bootstrap --include-default-paths DIST_REQUIRE_ALL_TOOLS: 1 CODEGEN_BACKENDS: llvm,cranelift <<: *job-windows-8c diff --git a/src/doc/rustc-dev-guide/src/building/optimized-build.md b/src/doc/rustc-dev-guide/src/building/optimized-build.md index 863ed9749fb7..46def66d1782 100644 --- a/src/doc/rustc-dev-guide/src/building/optimized-build.md +++ b/src/doc/rustc-dev-guide/src/building/optimized-build.md @@ -118,7 +118,7 @@ Here is an example of how can `opt-dist` be used locally (outside of CI): ``` 3. Run the tool with the `local` mode and provide necessary parameters: ```bash - ./build/host/stage0-tools-bin/opt-dist local \ + ./build/host/stage1-tools-bin/opt-dist local \ --target-triple \ # select target, e.g. "x86_64-unknown-linux-gnu" --checkout-dir \ # path to rust checkout, e.g. "." --llvm-dir \ # path to built LLVM toolchain, e.g. "/foo/bar/llvm/install" From 4360c52c4fafb38bd5514220ee2cd2b8c260c286 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Tue, 12 Aug 2025 18:40:42 +0200 Subject: [PATCH 192/195] Add change tracker entry --- src/bootstrap/src/utils/change_tracker.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/bootstrap/src/utils/change_tracker.rs b/src/bootstrap/src/utils/change_tracker.rs index 091956e7e5f3..cd7fba39a845 100644 --- a/src/bootstrap/src/utils/change_tracker.rs +++ b/src/bootstrap/src/utils/change_tracker.rs @@ -496,4 +496,9 @@ pub const CONFIG_CHANGE_HISTORY: &[ChangeInfo] = &[ severity: ChangeSeverity::Warning, summary: "It is no longer possible to `x doc` with stage 0. All doc commands have to be on stage 1+.", }, + ChangeInfo { + change_id: 145295, + severity: ChangeSeverity::Warning, + summary: "The names of stageN directories in the build directory have been consolidated with the new (post-stage-0-redesign) staging scheme. Some tools and binaries might be located in a different build directory than before.", + }, ]; From 3339fa5fcce74dd219f23988d6d9cabfd6871cb6 Mon Sep 17 00:00:00 2001 From: lcnr Date: Tue, 12 Aug 2025 16:16:08 +0200 Subject: [PATCH 193/195] check args in new_coroutine_witness --- compiler/rustc_middle/src/ty/sty.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index 72474a605669..9a2a140efd3c 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -821,10 +821,13 @@ impl<'tcx> Ty<'tcx> { #[inline] pub fn new_coroutine_witness( tcx: TyCtxt<'tcx>, - id: DefId, + def_id: DefId, args: GenericArgsRef<'tcx>, ) -> Ty<'tcx> { - Ty::new(tcx, CoroutineWitness(id, args)) + if cfg!(debug_assertions) { + tcx.debug_assert_args_compatible(tcx.typeck_root_def_id(def_id), args); + } + Ty::new(tcx, CoroutineWitness(def_id, args)) } // misc From f979bf018ff9a44118fabb96ea039903c47fb5e3 Mon Sep 17 00:00:00 2001 From: lcnr Date: Tue, 12 Aug 2025 16:33:21 +0200 Subject: [PATCH 194/195] `fn new_coroutine_witness_for_coroutine` woops --- compiler/rustc_middle/src/ty/sty.rs | 33 ++++++++++ .../src/solve/assembly/structural_traits.rs | 24 ++------ .../src/traits/select/mod.rs | 61 ++++++------------- compiler/rustc_type_ir/src/inherent.rs | 6 ++ 4 files changed, 63 insertions(+), 61 deletions(-) diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index 9a2a140efd3c..755fc68d86f3 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -830,6 +830,31 @@ impl<'tcx> Ty<'tcx> { Ty::new(tcx, CoroutineWitness(def_id, args)) } + pub fn new_coroutine_witness_for_coroutine( + tcx: TyCtxt<'tcx>, + def_id: DefId, + coroutine_args: GenericArgsRef<'tcx>, + ) -> Ty<'tcx> { + tcx.debug_assert_args_compatible(def_id, coroutine_args); + // HACK: Coroutine witness types are lifetime erased, so they + // never reference any lifetime args from the coroutine. We erase + // the regions here since we may get into situations where a + // coroutine is recursively contained within itself, leading to + // witness types that differ by region args. This means that + // cycle detection in fulfillment will not kick in, which leads + // to unnecessary overflows in async code. See the issue: + // . + let args = + ty::GenericArgs::for_item(tcx, tcx.typeck_root_def_id(def_id), |def, _| { + match def.kind { + ty::GenericParamDefKind::Lifetime => tcx.lifetimes.re_erased.into(), + ty::GenericParamDefKind::Type { .. } + | ty::GenericParamDefKind::Const { .. } => coroutine_args[def.index as usize], + } + }); + Ty::new_coroutine_witness(tcx, def_id, args) + } + // misc #[inline] @@ -988,6 +1013,14 @@ impl<'tcx> rustc_type_ir::inherent::Ty> for Ty<'tcx> { Ty::new_coroutine_witness(interner, def_id, args) } + fn new_coroutine_witness_for_coroutine( + interner: TyCtxt<'tcx>, + def_id: DefId, + coroutine_args: ty::GenericArgsRef<'tcx>, + ) -> Self { + Ty::new_coroutine_witness_for_coroutine(interner, def_id, coroutine_args) + } + fn new_ptr(interner: TyCtxt<'tcx>, ty: Self, mutbl: hir::Mutability) -> Self { Ty::new_ptr(interner, ty, mutbl) } diff --git a/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs b/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs index faa86734d08c..d25e74e73358 100644 --- a/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs @@ -75,17 +75,10 @@ where Ok(ty::Binder::dummy(vec![args.as_coroutine_closure().tupled_upvars_ty()])) } - ty::Coroutine(def_id, args) => { - let coroutine_args = args.as_coroutine(); - Ok(ty::Binder::dummy(vec![ - coroutine_args.tupled_upvars_ty(), - Ty::new_coroutine_witness( - ecx.cx(), - def_id, - ecx.cx().mk_args(coroutine_args.parent_args().as_slice()), - ), - ])) - } + ty::Coroutine(def_id, args) => Ok(ty::Binder::dummy(vec![ + args.as_coroutine().tupled_upvars_ty(), + Ty::new_coroutine_witness_for_coroutine(ecx.cx(), def_id, args), + ])), ty::CoroutineWitness(def_id, args) => Ok(ecx .cx() @@ -251,14 +244,9 @@ where Movability::Static => Err(NoSolution), Movability::Movable => { if ecx.cx().features().coroutine_clone() { - let coroutine = args.as_coroutine(); Ok(ty::Binder::dummy(vec![ - coroutine.tupled_upvars_ty(), - Ty::new_coroutine_witness( - ecx.cx(), - def_id, - ecx.cx().mk_args(coroutine.parent_args().as_slice()), - ), + args.as_coroutine().tupled_upvars_ty(), + Ty::new_coroutine_witness_for_coroutine(ecx.cx(), def_id, args), ])) } else { Err(NoSolution) diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 468c42abf486..1dd31990ab73 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -4,9 +4,9 @@ use std::assert_matches::assert_matches; use std::cell::{Cell, RefCell}; +use std::cmp; use std::fmt::{self, Display}; use std::ops::ControlFlow; -use std::{cmp, iter}; use hir::def::DefKind; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; @@ -2185,32 +2185,23 @@ impl<'tcx> SelectionContext<'_, 'tcx> { ty::Binder::dummy(vec![ty]) } - ty::Coroutine(coroutine_def_id, args) => { - match self.tcx().coroutine_movability(coroutine_def_id) { - hir::Movability::Static => { - unreachable!("tried to assemble `Sized` for static coroutine") - } - hir::Movability::Movable => { - if self.tcx().features().coroutine_clone() { - ty::Binder::dummy( - args.as_coroutine() - .upvar_tys() - .iter() - .chain([Ty::new_coroutine_witness( - self.tcx(), - coroutine_def_id, - self.tcx().mk_args(args.as_coroutine().parent_args()), - )]) - .collect::>(), - ) - } else { - unreachable!( - "tried to assemble `Sized` for coroutine without enabled feature" - ) - } + ty::Coroutine(def_id, args) => match self.tcx().coroutine_movability(def_id) { + hir::Movability::Static => { + unreachable!("tried to assemble `Clone` for static coroutine") + } + hir::Movability::Movable => { + if self.tcx().features().coroutine_clone() { + ty::Binder::dummy(vec![ + args.as_coroutine().tupled_upvars_ty(), + Ty::new_coroutine_witness_for_coroutine(self.tcx(), def_id, args), + ]) + } else { + unreachable!( + "tried to assemble `Clone` for coroutine without enabled feature" + ) } } - } + }, ty::CoroutineWitness(def_id, args) => self .infcx @@ -2334,25 +2325,9 @@ impl<'tcx> SelectionContext<'_, 'tcx> { ty::Coroutine(def_id, args) => { let ty = self.infcx.shallow_resolve(args.as_coroutine().tupled_upvars_ty()); let tcx = self.tcx(); - let witness = Ty::new_coroutine_witness( - tcx, - def_id, - ty::GenericArgs::for_item(tcx, def_id, |def, _| match def.kind { - // HACK: Coroutine witnesse types are lifetime erased, so they - // never reference any lifetime args from the coroutine. We erase - // the regions here since we may get into situations where a - // coroutine is recursively contained within itself, leading to - // witness types that differ by region args. This means that - // cycle detection in fulfillment will not kick in, which leads - // to unnecessary overflows in async code. See the issue: - // . - ty::GenericParamDefKind::Lifetime => tcx.lifetimes.re_erased.into(), - ty::GenericParamDefKind::Type { .. } - | ty::GenericParamDefKind::Const { .. } => args[def.index as usize], - }), - ); + let witness = Ty::new_coroutine_witness_for_coroutine(tcx, def_id, args); ty::Binder::dummy(AutoImplConstituents { - types: [ty].into_iter().chain(iter::once(witness)).collect(), + types: vec![ty, witness], assumptions: vec![], }) } diff --git a/compiler/rustc_type_ir/src/inherent.rs b/compiler/rustc_type_ir/src/inherent.rs index 1a6c99ce7dec..569570b5783c 100644 --- a/compiler/rustc_type_ir/src/inherent.rs +++ b/compiler/rustc_type_ir/src/inherent.rs @@ -91,6 +91,12 @@ pub trait Ty>: fn new_coroutine_witness(interner: I, def_id: I::DefId, args: I::GenericArgs) -> Self; + fn new_coroutine_witness_for_coroutine( + interner: I, + def_id: I::DefId, + coroutine_args: I::GenericArgs, + ) -> Self; + fn new_ptr(interner: I, ty: Self, mutbl: Mutability) -> Self; fn new_ref(interner: I, region: I::Region, ty: Self, mutbl: Mutability) -> Self; From 4d841497da34dbe1c51071d38b5b1c440ae308b7 Mon Sep 17 00:00:00 2001 From: lcnr Date: Tue, 12 Aug 2025 17:04:10 +0200 Subject: [PATCH 195/195] add test --- ...sync-auto-trait-overflow-only-parent-args.rs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 tests/ui/async-await/recursive-async-auto-trait-overflow-only-parent-args.rs diff --git a/tests/ui/async-await/recursive-async-auto-trait-overflow-only-parent-args.rs b/tests/ui/async-await/recursive-async-auto-trait-overflow-only-parent-args.rs new file mode 100644 index 000000000000..9681f66412a0 --- /dev/null +++ b/tests/ui/async-await/recursive-async-auto-trait-overflow-only-parent-args.rs @@ -0,0 +1,17 @@ +// Regression test for #145288. This is the same issue as #145151 +// which we fixed in #145194. However in that PR we accidentally created +// a `CoroutineWitness` which referenced all generic arguments of the +// coroutine, including upvars and the signature. + +//@ edition: 2024 +//@ check-pass + +async fn process<'a>(x: &'a u32) { + Box::pin(process(x)).await; +} + +fn require_send(_: impl Send) {} + +fn main() { + require_send(process(&1)); +}