From 1fc55c7a3492bb3bad4241c33e0625c1110c60c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jana=20D=C3=B6nszelmann?= Date: Thu, 25 Jun 2026 15:27:24 +0200 Subject: [PATCH 01/26] comment about empty run_passes --- compiler/rustc_mir_transform/src/shim.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/compiler/rustc_mir_transform/src/shim.rs b/compiler/rustc_mir_transform/src/shim.rs index 6d9b8feea05f4..f4cfda9d50f66 100644 --- a/compiler/rustc_mir_transform/src/shim.rs +++ b/compiler/rustc_mir_transform/src/shim.rs @@ -1074,6 +1074,8 @@ pub(super) fn build_adt_ctor(tcx: TyCtxt<'_>, ctor_id: DefId) -> Body<'_> { // so this would otherwise not get filled). body.set_mentioned_items(Vec::new()); + // We don't pass any passes here, just to force a phase change to `Optimized`. + // Otherwise this bit of MIR will trigger assertions trying to detect MIR with an invalid phase. pm::run_passes_no_validate( tcx, &mut body, From e54776fbdb9c233ebbc164fbb9f97e82f879f28c Mon Sep 17 00:00:00 2001 From: Yukang Date: Wed, 15 Jul 2026 20:38:46 +0800 Subject: [PATCH 02/26] Honor field-level lint attributes in non_snake_case --- compiler/rustc_lint/src/nonstandard_style.rs | 8 ++--- .../field-lint-expectation-issue-159323.rs | 33 +++++++++++++++++++ 2 files changed, 36 insertions(+), 5 deletions(-) create mode 100644 tests/ui/lint/non-snake-case/field-lint-expectation-issue-159323.rs diff --git a/compiler/rustc_lint/src/nonstandard_style.rs b/compiler/rustc_lint/src/nonstandard_style.rs index e3653c55f53a4..f7f5708bd424e 100644 --- a/compiler/rustc_lint/src/nonstandard_style.rs +++ b/compiler/rustc_lint/src/nonstandard_style.rs @@ -211,7 +211,7 @@ impl EarlyLintPass for NonCamelCaseTypes { declare_lint! { /// The `non_snake_case` lint detects variables, methods, functions, - /// lifetime parameters and modules that don't have snake case names. + /// lifetime parameters, named fields and modules that don't have snake case names. /// /// ### Example /// @@ -452,10 +452,8 @@ impl<'tcx> LateLintPass<'tcx> for NonSnakeCase { } } - fn check_struct_def(&mut self, cx: &LateContext<'_>, s: &hir::VariantData<'_>) { - for sf in s.fields() { - self.check_snake_case(cx, "structure field", &sf.ident); - } + fn check_field_def(&mut self, cx: &LateContext<'_>, field: &hir::FieldDef<'_>) { + self.check_snake_case(cx, "structure field", &field.ident); } } diff --git a/tests/ui/lint/non-snake-case/field-lint-expectation-issue-159323.rs b/tests/ui/lint/non-snake-case/field-lint-expectation-issue-159323.rs new file mode 100644 index 0000000000000..ca7aa97475a07 --- /dev/null +++ b/tests/ui/lint/non-snake-case/field-lint-expectation-issue-159323.rs @@ -0,0 +1,33 @@ +//@ check-pass + +// Regression test for issue #159323 +// Field-level lint attributes must control `non_snake_case` diagnostics for that field. + +#![deny(non_snake_case, unfulfilled_lint_expectations)] + +pub struct Struct { + #[expect(non_snake_case)] + pub expected_Field: bool, + + #[allow(non_snake_case)] + pub allowed_Field: bool, +} + +#[expect(non_snake_case)] +pub struct ParentExpectation { + pub expected_Field: bool, +} + +pub enum Enum { + Variant { + #[expect(non_snake_case)] + expected_Field: bool, + }, +} + +pub union Union { + #[expect(non_snake_case)] + pub expected_Field: bool, +} + +fn main() {} From b72727495c15ce53a0ca5ec0c559e1f9bbe7dd83 Mon Sep 17 00:00:00 2001 From: Connor Tsui Date: Tue, 14 Jul 2026 15:24:18 -0400 Subject: [PATCH 03/26] make debug builders with closures impl with cell Signed-off-by: Connor Tsui --- library/core/src/fmt/builders.rs | 156 ++++++++++++++++++------------- 1 file changed, 92 insertions(+), 64 deletions(-) diff --git a/library/core/src/fmt/builders.rs b/library/core/src/fmt/builders.rs index 19dd13967fdd6..ceec98c8659fb 100644 --- a/library/core/src/fmt/builders.rs +++ b/library/core/src/fmt/builders.rs @@ -1,5 +1,6 @@ #![allow(unused_imports)] +use crate::cell::Cell; use crate::fmt::{self, Debug, Formatter}; struct PadAdapter<'buf, 'state> { @@ -50,6 +51,29 @@ impl fmt::Write for PadAdapter<'_, '_> { } } +/// Wraps an `FnOnce` formatting closure in a type that implements [`fmt::Debug`] by calling the +/// closure, allowing the `*_with` builder methods to forward to their `&dyn fmt::Debug` +/// counterparts. +/// +/// By doing this, the builder logic is monomorphized only once and not for every closure type +/// (see #149745). +/// +/// Formatting a `DebugOnce` consumes the closure, so attempting to format it more than once +/// panics. This never happens because the debug builders format each value exactly once. +struct DebugOnce(Cell>); + +impl fmt::Debug for DebugOnce +where + F: FnOnce(&mut fmt::Formatter<'_>) -> fmt::Result, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self.0.take() { + Some(value_fmt) => value_fmt(f), + None => panic!("formatting closure called more than once"), + } + } +} + /// A struct to help with [`fmt::Debug`](Debug) implementations. /// /// This is useful when you wish to output a formatted struct as a part of your @@ -130,18 +154,6 @@ impl<'a, 'b: 'a> DebugStruct<'a, 'b> { /// ``` #[stable(feature = "debug_builders", since = "1.2.0")] pub fn field(&mut self, name: &str, value: &dyn fmt::Debug) -> &mut Self { - self.field_with(name, |f| value.fmt(f)) - } - - /// Adds a new field to the generated struct output. - /// - /// This method is equivalent to [`DebugStruct::field`], but formats the - /// value using a provided closure rather than by calling [`Debug::fmt`]. - #[unstable(feature = "debug_closure_helpers", issue = "117729")] - pub fn field_with(&mut self, name: &str, value_fmt: F) -> &mut Self - where - F: FnOnce(&mut fmt::Formatter<'_>) -> fmt::Result, - { self.result = self.result.and_then(|_| { if self.is_pretty() { if !self.has_fields { @@ -152,14 +164,14 @@ impl<'a, 'b: 'a> DebugStruct<'a, 'b> { let mut writer = PadAdapter::wrap(self.fmt, &mut slot, &mut state); writer.write_str(name)?; writer.write_str(": ")?; - value_fmt(&mut writer)?; + value.fmt(&mut writer)?; writer.write_str(",\n") } else { let prefix = if self.has_fields { ", " } else { " { " }; self.fmt.write_str(prefix)?; self.fmt.write_str(name)?; self.fmt.write_str(": ")?; - value_fmt(self.fmt) + value.fmt(self.fmt) } }); @@ -167,6 +179,18 @@ impl<'a, 'b: 'a> DebugStruct<'a, 'b> { self } + /// Adds a new field to the generated struct output. + /// + /// This method is equivalent to [`DebugStruct::field`], but formats the + /// value using a provided closure rather than by calling [`Debug::fmt`]. + #[unstable(feature = "debug_closure_helpers", issue = "117729")] + pub fn field_with(&mut self, name: &str, value_fmt: F) -> &mut Self + where + F: FnOnce(&mut fmt::Formatter<'_>) -> fmt::Result, + { + self.field(name, &DebugOnce(Cell::new(Some(value_fmt)))) + } + /// Marks the struct as non-exhaustive, indicating to the reader that there are some other /// fields that are not shown in the debug representation. /// @@ -327,18 +351,6 @@ impl<'a, 'b: 'a> DebugTuple<'a, 'b> { /// ``` #[stable(feature = "debug_builders", since = "1.2.0")] pub fn field(&mut self, value: &dyn fmt::Debug) -> &mut Self { - self.field_with(|f| value.fmt(f)) - } - - /// Adds a new field to the generated tuple struct output. - /// - /// This method is equivalent to [`DebugTuple::field`], but formats the - /// value using a provided closure rather than by calling [`Debug::fmt`]. - #[unstable(feature = "debug_closure_helpers", issue = "117729")] - pub fn field_with(&mut self, value_fmt: F) -> &mut Self - where - F: FnOnce(&mut fmt::Formatter<'_>) -> fmt::Result, - { self.result = self.result.and_then(|_| { if self.is_pretty() { if self.fields == 0 { @@ -347,12 +359,12 @@ impl<'a, 'b: 'a> DebugTuple<'a, 'b> { let mut slot = None; let mut state = Default::default(); let mut writer = PadAdapter::wrap(self.fmt, &mut slot, &mut state); - value_fmt(&mut writer)?; + value.fmt(&mut writer)?; writer.write_str(",\n") } else { let prefix = if self.fields == 0 { "(" } else { ", " }; self.fmt.write_str(prefix)?; - value_fmt(self.fmt) + value.fmt(self.fmt) } }); @@ -360,6 +372,18 @@ impl<'a, 'b: 'a> DebugTuple<'a, 'b> { self } + /// Adds a new field to the generated tuple struct output. + /// + /// This method is equivalent to [`DebugTuple::field`], but formats the + /// value using a provided closure rather than by calling [`Debug::fmt`]. + #[unstable(feature = "debug_closure_helpers", issue = "117729")] + pub fn field_with(&mut self, value_fmt: F) -> &mut Self + where + F: FnOnce(&mut fmt::Formatter<'_>) -> fmt::Result, + { + self.field(&DebugOnce(Cell::new(Some(value_fmt)))) + } + /// Marks the tuple struct as non-exhaustive, indicating to the reader that there are some /// other fields that are not shown in the debug representation. /// @@ -453,10 +477,7 @@ struct DebugInner<'a, 'b: 'a> { } impl<'a, 'b: 'a> DebugInner<'a, 'b> { - fn entry_with(&mut self, entry_fmt: F) - where - F: FnOnce(&mut fmt::Formatter<'_>) -> fmt::Result, - { + fn entry(&mut self, entry: &dyn fmt::Debug) { self.result = self.result.and_then(|_| { if self.is_pretty() { if !self.has_fields { @@ -465,19 +486,26 @@ impl<'a, 'b: 'a> DebugInner<'a, 'b> { let mut slot = None; let mut state = Default::default(); let mut writer = PadAdapter::wrap(self.fmt, &mut slot, &mut state); - entry_fmt(&mut writer)?; + entry.fmt(&mut writer)?; writer.write_str(",\n") } else { if self.has_fields { self.fmt.write_str(", ")? } - entry_fmt(self.fmt) + entry.fmt(self.fmt) } }); self.has_fields = true; } + fn entry_with(&mut self, entry_fmt: F) + where + F: FnOnce(&mut fmt::Formatter<'_>) -> fmt::Result, + { + self.entry(&DebugOnce(Cell::new(Some(entry_fmt)))); + } + fn is_pretty(&self) -> bool { self.fmt.alternate() } @@ -546,7 +574,7 @@ impl<'a, 'b: 'a> DebugSet<'a, 'b> { /// ``` #[stable(feature = "debug_builders", since = "1.2.0")] pub fn entry(&mut self, entry: &dyn fmt::Debug) -> &mut Self { - self.inner.entry_with(|f| entry.fmt(f)); + self.inner.entry(entry); self } @@ -738,7 +766,7 @@ impl<'a, 'b: 'a> DebugList<'a, 'b> { /// ``` #[stable(feature = "debug_builders", since = "1.2.0")] pub fn entry(&mut self, entry: &dyn fmt::Debug) -> &mut Self { - self.inner.entry_with(|f| entry.fmt(f)); + self.inner.entry(entry); self } @@ -969,18 +997,6 @@ impl<'a, 'b: 'a> DebugMap<'a, 'b> { /// ``` #[stable(feature = "debug_map_key_value", since = "1.42.0")] pub fn key(&mut self, key: &dyn fmt::Debug) -> &mut Self { - self.key_with(|f| key.fmt(f)) - } - - /// Adds the key part of a new entry to the map output. - /// - /// This method is equivalent to [`DebugMap::key`], but formats the - /// key using a provided closure rather than by calling [`Debug::fmt`]. - #[unstable(feature = "debug_closure_helpers", issue = "117729")] - pub fn key_with(&mut self, key_fmt: F) -> &mut Self - where - F: FnOnce(&mut fmt::Formatter<'_>) -> fmt::Result, - { self.result = self.result.and_then(|_| { assert!( !self.has_key, @@ -995,13 +1011,13 @@ impl<'a, 'b: 'a> DebugMap<'a, 'b> { let mut slot = None; self.state = Default::default(); let mut writer = PadAdapter::wrap(self.fmt, &mut slot, &mut self.state); - key_fmt(&mut writer)?; + key.fmt(&mut writer)?; writer.write_str(": ")?; } else { if self.has_fields { self.fmt.write_str(", ")? } - key_fmt(self.fmt)?; + key.fmt(self.fmt)?; self.fmt.write_str(": ")?; } @@ -1012,6 +1028,18 @@ impl<'a, 'b: 'a> DebugMap<'a, 'b> { self } + /// Adds the key part of a new entry to the map output. + /// + /// This method is equivalent to [`DebugMap::key`], but formats the + /// key using a provided closure rather than by calling [`Debug::fmt`]. + #[unstable(feature = "debug_closure_helpers", issue = "117729")] + pub fn key_with(&mut self, key_fmt: F) -> &mut Self + where + F: FnOnce(&mut fmt::Formatter<'_>) -> fmt::Result, + { + self.key(&DebugOnce(Cell::new(Some(key_fmt)))) + } + /// Adds the value part of a new entry to the map output. /// /// This method, together with `key`, is an alternative to `entry` that @@ -1045,28 +1073,16 @@ impl<'a, 'b: 'a> DebugMap<'a, 'b> { /// ``` #[stable(feature = "debug_map_key_value", since = "1.42.0")] pub fn value(&mut self, value: &dyn fmt::Debug) -> &mut Self { - self.value_with(|f| value.fmt(f)) - } - - /// Adds the value part of a new entry to the map output. - /// - /// This method is equivalent to [`DebugMap::value`], but formats the - /// value using a provided closure rather than by calling [`Debug::fmt`]. - #[unstable(feature = "debug_closure_helpers", issue = "117729")] - pub fn value_with(&mut self, value_fmt: F) -> &mut Self - where - F: FnOnce(&mut fmt::Formatter<'_>) -> fmt::Result, - { self.result = self.result.and_then(|_| { assert!(self.has_key, "attempted to format a map value before its key"); if self.is_pretty() { let mut slot = None; let mut writer = PadAdapter::wrap(self.fmt, &mut slot, &mut self.state); - value_fmt(&mut writer)?; + value.fmt(&mut writer)?; writer.write_str(",\n")?; } else { - value_fmt(self.fmt)?; + value.fmt(self.fmt)?; } self.has_key = false; @@ -1077,6 +1093,18 @@ impl<'a, 'b: 'a> DebugMap<'a, 'b> { self } + /// Adds the value part of a new entry to the map output. + /// + /// This method is equivalent to [`DebugMap::value`], but formats the + /// value using a provided closure rather than by calling [`Debug::fmt`]. + #[unstable(feature = "debug_closure_helpers", issue = "117729")] + pub fn value_with(&mut self, value_fmt: F) -> &mut Self + where + F: FnOnce(&mut fmt::Formatter<'_>) -> fmt::Result, + { + self.value(&DebugOnce(Cell::new(Some(value_fmt)))) + } + /// Adds the contents of an iterator of entries to the map output. /// /// # Examples From d395b0e67f137027396dea78cfe2fffefb58da19 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Wed, 15 Jul 2026 17:22:57 +0200 Subject: [PATCH 04/26] Rename `rustc_codegen_ssa/errors.rs` into `rustc_codegen_ssa/diagnostics.rs` --- .../src/assert_module_sources.rs | 16 ++- compiler/rustc_codegen_ssa/src/back/apple.rs | 2 +- .../rustc_codegen_ssa/src/back/archive.rs | 6 +- compiler/rustc_codegen_ssa/src/back/link.rs | 107 ++++++++++-------- .../src/back/link/raw_dylib.rs | 6 +- compiler/rustc_codegen_ssa/src/back/linker.rs | 26 ++--- compiler/rustc_codegen_ssa/src/back/lto.rs | 2 +- .../rustc_codegen_ssa/src/back/metadata.rs | 4 +- compiler/rustc_codegen_ssa/src/back/write.rs | 25 ++-- compiler/rustc_codegen_ssa/src/base.rs | 8 +- .../rustc_codegen_ssa/src/codegen_attrs.rs | 6 +- .../src/{errors.rs => diagnostics.rs} | 0 compiler/rustc_codegen_ssa/src/lib.rs | 2 +- compiler/rustc_codegen_ssa/src/mir/block.rs | 2 +- .../rustc_codegen_ssa/src/mir/constant.rs | 6 +- .../rustc_codegen_ssa/src/mir/intrinsic.rs | 2 +- .../rustc_codegen_ssa/src/target_features.rs | 28 ++--- 17 files changed, 133 insertions(+), 115 deletions(-) rename compiler/rustc_codegen_ssa/src/{errors.rs => diagnostics.rs} (100%) diff --git a/compiler/rustc_codegen_ssa/src/assert_module_sources.rs b/compiler/rustc_codegen_ssa/src/assert_module_sources.rs index f34a7b956e040..a25131c5ff9b4 100644 --- a/compiler/rustc_codegen_ssa/src/assert_module_sources.rs +++ b/compiler/rustc_codegen_ssa/src/assert_module_sources.rs @@ -37,7 +37,7 @@ use rustc_session::Session; use rustc_span::{Span, Symbol}; use tracing::debug; -use crate::errors; +use crate::diagnostics; #[allow(missing_docs)] pub fn assert_module_sources(tcx: TyCtxt<'_>, set_reuse: &dyn Fn(&mut CguReuseTracker)) { @@ -107,7 +107,7 @@ impl<'tcx> AssertModuleSource<'tcx> { | CguFields::PartitionReused { cfg, module }) = cgu_fields; if !self.tcx.sess.opts.unstable_opts.query_dep_graph { - self.tcx.dcx().emit_fatal(errors::MissingQueryDepGraph { span }); + self.tcx.dcx().emit_fatal(diagnostics::MissingQueryDepGraph { span }); } if !self.check_config(cfg) { @@ -120,7 +120,11 @@ impl<'tcx> AssertModuleSource<'tcx> { let crate_name = crate_name.as_str(); if !user_path.starts_with(&crate_name) { - self.tcx.dcx().emit_fatal(errors::MalformedCguName { span, user_path, crate_name }); + self.tcx.dcx().emit_fatal(diagnostics::MalformedCguName { + span, + user_path, + crate_name, + }); } // Split of the "special suffix" if there is one. @@ -149,7 +153,7 @@ impl<'tcx> AssertModuleSource<'tcx> { if !self.available_cgus.contains(&cgu_name) { let cgu_names: Vec<&str> = self.available_cgus.items().map(|cgu| cgu.as_str()).into_sorted_stable_ord(); - self.tcx.dcx().emit_err(errors::NoModuleNamed { + self.tcx.dcx().emit_err(diagnostics::NoModuleNamed { span, user_path, cgu_name, @@ -273,7 +277,7 @@ impl CguReuseTracker { if error { let at_least = if at_least { 1 } else { 0 }; - sess.dcx().emit_err(errors::IncorrectCguReuseType { + sess.dcx().emit_err(diagnostics::IncorrectCguReuseType { span: *error_span, cgu_user_name, actual_reuse, @@ -282,7 +286,7 @@ impl CguReuseTracker { }); } } else { - sess.dcx().emit_fatal(errors::CguNotRecorded { cgu_user_name, cgu_name }); + sess.dcx().emit_fatal(diagnostics::CguNotRecorded { cgu_user_name, cgu_name }); } } } diff --git a/compiler/rustc_codegen_ssa/src/back/apple.rs b/compiler/rustc_codegen_ssa/src/back/apple.rs index 1b707a24066e1..104a3de012615 100644 --- a/compiler/rustc_codegen_ssa/src/back/apple.rs +++ b/compiler/rustc_codegen_ssa/src/back/apple.rs @@ -10,7 +10,7 @@ pub(super) use rustc_target::spec::apple::OSVersion; use rustc_target::spec::{Arch, Env, Os, Target}; use tracing::debug; -use crate::errors::{XcrunError, XcrunSdkPathWarning}; +use crate::diagnostics::{XcrunError, XcrunSdkPathWarning}; #[cfg(test)] mod tests; diff --git a/compiler/rustc_codegen_ssa/src/back/archive.rs b/compiler/rustc_codegen_ssa/src/back/archive.rs index 4fc516b244857..c4107b4a60f27 100644 --- a/compiler/rustc_codegen_ssa/src/back/archive.rs +++ b/compiler/rustc_codegen_ssa/src/back/archive.rs @@ -26,8 +26,8 @@ use super::rmeta_link::{self, RmetaLinkCache}; use super::symbol_edit::{apply_edits, collect_internal_names}; use crate::common; // Public for ArchiveBuilderBuilder::extract_bundled_libs -pub use crate::errors::ExtractBundledLibsError; -use crate::errors::{ +pub use crate::diagnostics::ExtractBundledLibsError; +use crate::diagnostics::{ ArchiveBuildFailure, DlltoolFailImportLibrary, ErrorCallingDllTool, ErrorCreatingImportLibrary, ErrorWritingDEFFile, UnknownArchiveKind, }; @@ -502,7 +502,7 @@ impl<'a> ArchiveBuilder for ArArchiveBuilder<'a> { { let actual_kind = archive.kind(); if !archive_kinds_compatible(actual_kind, expected_kind) { - self.sess.dcx().emit_warn(crate::errors::IncompatibleArchiveFormat { + self.sess.dcx().emit_warn(crate::diagnostics::IncompatibleArchiveFormat { path: archive_path.clone(), actual: archive_kind_display_name(actual_kind), expected: archive_kind_display_name(expected_kind), diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index 45c9495570ed2..539b228e2b4b2 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -66,7 +66,7 @@ use super::{apple, rmeta_link, versioned_llvm_target}; use crate::base::needs_allocator_shim_for_linking; use crate::{ CodegenLintLevelSpecs, CompiledModule, CompiledModules, CrateInfo, NativeLib, SymbolExport, - errors, + diagnostics, }; pub fn ensure_removed(dcx: DiagCtxtHandle<'_>, path: &Path) { @@ -187,7 +187,9 @@ pub fn link_binary( let tmpdir = TempDirBuilder::new() .prefix("rustc") .tempdir_in(output.parent().unwrap_or_else(|| Path::new("."))) - .unwrap_or_else(|error| sess.dcx().emit_fatal(errors::CreateTempDir { error })); + .unwrap_or_else(|error| { + sess.dcx().emit_fatal(diagnostics::CreateTempDir { error }) + }); let path = MaybeTempDir::new(tmpdir, sess.opts.cg.save_temps); let crate_name = format!("{}", crate_info.local_crate_name); @@ -259,11 +261,15 @@ pub fn link_binary( if output.is_stdout() { if output.is_tty() { - sess.dcx().emit_err(errors::BinaryOutputToTty { + sess.dcx().emit_err(diagnostics::BinaryOutputToTty { shorthand: OutputType::Exe.shorthand(), }); } else if let Err(e) = copy_to_stdout(&out_filename) { - sess.dcx().emit_err(errors::CopyPath::new(&out_filename, output.as_path(), e)); + sess.dcx().emit_err(diagnostics::CopyPath::new( + &out_filename, + output.as_path(), + e, + )); } tempfiles_for_stdout_output.push(out_filename); } @@ -327,18 +333,18 @@ pub fn each_linked_rlib( info: &CrateInfo, crate_type: Option, f: &mut dyn FnMut(CrateNum, &Path), -) -> Result<(), errors::LinkRlibError> { +) -> Result<(), diagnostics::LinkRlibError> { let fmts = if let Some(crate_type) = crate_type { let Some(fmts) = info.dependency_formats.get(&crate_type) else { - return Err(errors::LinkRlibError::MissingFormat); + return Err(diagnostics::LinkRlibError::MissingFormat); }; fmts } else { let mut dep_formats = info.dependency_formats.iter(); - let (ty1, list1) = dep_formats.next().ok_or(errors::LinkRlibError::MissingFormat)?; + let (ty1, list1) = dep_formats.next().ok_or(diagnostics::LinkRlibError::MissingFormat)?; if let Some((ty2, list2)) = dep_formats.find(|(_, list2)| list1 != *list2) { - return Err(errors::LinkRlibError::IncompatibleDependencyFormats { + return Err(diagnostics::LinkRlibError::IncompatibleDependencyFormats { ty1: format!("{ty1:?}"), ty2: format!("{ty2:?}"), list1: format!("{list1:?}"), @@ -353,16 +359,16 @@ pub fn each_linked_rlib( match fmts.get(cnum) { Some(&Linkage::NotLinked | &Linkage::Dynamic | &Linkage::IncludedFromDylib) => continue, Some(_) => {} - None => return Err(errors::LinkRlibError::MissingFormat), + None => return Err(diagnostics::LinkRlibError::MissingFormat), } let crate_name = info.crate_name[&cnum]; let used_crate_source = &info.used_crate_source[&cnum]; if let Some(path) = &used_crate_source.rlib { f(cnum, path); } else if used_crate_source.rmeta.is_some() { - return Err(errors::LinkRlibError::OnlyRmetaFound { crate_name }); + return Err(diagnostics::LinkRlibError::OnlyRmetaFound { crate_name }); } else { - return Err(errors::LinkRlibError::NotFound { crate_name }); + return Err(diagnostics::LinkRlibError::NotFound { crate_name }); } } Ok(()) @@ -505,15 +511,16 @@ fn link_rlib<'a>( && let Some(filename) = native_lib_filenames[i] { let path = find_native_static_library(filename.as_str(), true, sess); - let src = read(path) - .unwrap_or_else(|e| sess.dcx().emit_fatal(errors::ReadFileError { message: e })); + let src = read(path).unwrap_or_else(|e| { + sess.dcx().emit_fatal(diagnostics::ReadFileError { message: e }) + }); let (data, _) = create_wrapper_file(sess, ".bundled_lib".to_string(), &src); let wrapper_file = emit_wrapper_file(sess, &data, tmpdir.as_ref(), filename.as_str()); packed_bundled_libs.push(wrapper_file); } else { let path = find_native_static_library(lib.name.as_str(), lib.verbatim, sess); ab.add_archive(&path, AddArchiveKind::Other).unwrap_or_else(|error| { - sess.dcx().emit_fatal(errors::AddNativeLibrary { library_path: path, error }) + sess.dcx().emit_fatal(diagnostics::AddNativeLibrary { library_path: path, error }) }); } } @@ -531,7 +538,7 @@ fn link_rlib<'a>( ) { ab.add_archive(&output_path, AddArchiveKind::Other).unwrap_or_else(|error| { sess.dcx() - .emit_fatal(errors::AddNativeLibrary { library_path: output_path, error }); + .emit_fatal(diagnostics::AddNativeLibrary { library_path: output_path, error }); }); } } @@ -676,12 +683,12 @@ fn link_staticlib( let exported_symbols = if hide || rename { if !matches!(sess.target.binary_format, BinaryFormat::Elf | BinaryFormat::MachO) { if hide { - sess.dcx().emit_warn(errors::StaticlibHideInternalSymbolsUnsupported { + sess.dcx().emit_warn(diagnostics::StaticlibHideInternalSymbolsUnsupported { binary_format: sess.target.archive_format.to_string(), }); } if rename { - sess.dcx().emit_warn(errors::StaticlibRenameInternalSymbolsUnsupported { + sess.dcx().emit_warn(diagnostics::StaticlibRenameInternalSymbolsUnsupported { binary_format: sess.target.archive_format.to_string(), }); } @@ -721,9 +728,9 @@ fn link_staticlib( if let Some(path) = &used_crate_source.dylib { all_rust_dylibs.push(&**path); } else if used_crate_source.rmeta.is_some() { - sess.dcx().emit_fatal(errors::LinkRlibError::OnlyRmetaFound { crate_name }); + sess.dcx().emit_fatal(diagnostics::LinkRlibError::OnlyRmetaFound { crate_name }); } else { - sess.dcx().emit_fatal(errors::LinkRlibError::NotFound { crate_name }); + sess.dcx().emit_fatal(diagnostics::LinkRlibError::NotFound { crate_name }); } } @@ -844,7 +851,7 @@ fn link_dwarf_object( Ok(()) }) { Ok(()) => {} - Err(e) => sess.dcx().emit_fatal(errors::ThorinErrorWrapper(e)), + Err(e) => sess.dcx().emit_fatal(diagnostics::ThorinErrorWrapper(e)), } } @@ -1226,7 +1233,7 @@ fn link_natively( let mut output = prog.stderr.clone(); output.extend_from_slice(&prog.stdout); let escaped_output = escape_linker_output(&output, flavor); - let err = errors::LinkingFailed { + let err = diagnostics::LinkingFailed { linker_path: &linker_path, exit_status: prog.status, command: cmd, @@ -1247,25 +1254,25 @@ fn link_natively( find_msvc_tools::find_tool(sess.target.arch.desc(), "link.exe") .is_some(); - sess.dcx().emit_note(errors::LinkExeUnexpectedError); + sess.dcx().emit_note(diagnostics::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); + sess.dcx().emit_note(diagnostics::LinkExeStatusStackBufferOverrun); } if is_vs_installed && has_linker { // the linker is broken - sess.dcx().emit_note(errors::RepairVSBuildTools); - sess.dcx().emit_note(errors::MissingCppBuildToolComponent); + sess.dcx().emit_note(diagnostics::RepairVSBuildTools); + sess.dcx().emit_note(diagnostics::MissingCppBuildToolComponent); } else if is_vs_installed { // the linker is not installed - sess.dcx().emit_note(errors::SelectCppBuildToolWorkload); + sess.dcx().emit_note(diagnostics::SelectCppBuildToolWorkload); } else { // visual studio is not installed - sess.dcx().emit_note(errors::VisualStudioNotInstalled); + sess.dcx().emit_note(diagnostics::VisualStudioNotInstalled); } } } @@ -1280,9 +1287,9 @@ fn link_natively( let linker_not_found = e.kind() == io::ErrorKind::NotFound; let err = if linker_not_found { - sess.dcx().emit_err(errors::LinkerNotFound { linker_path, error: e }) + sess.dcx().emit_err(diagnostics::LinkerNotFound { linker_path, error: e }) } else { - sess.dcx().emit_err(errors::UnableToExeLinker { + sess.dcx().emit_err(diagnostics::UnableToExeLinker { linker_path, error: e, command_formatted: format!("{cmd:?}"), @@ -1290,9 +1297,9 @@ fn link_natively( }; if sess.target.is_like_msvc && linker_not_found { - sess.dcx().emit_note(errors::MsvcMissingLinker); - sess.dcx().emit_note(errors::CheckInstalledVisualStudio); - sess.dcx().emit_note(errors::InsufficientVSCodeProduct); + sess.dcx().emit_note(diagnostics::MsvcMissingLinker); + sess.dcx().emit_note(diagnostics::CheckInstalledVisualStudio); + sess.dcx().emit_note(diagnostics::InsufficientVSCodeProduct); } err.raise_fatal(); } @@ -1317,13 +1324,13 @@ fn link_natively( if !prog.status.success() { let mut output = prog.stderr.clone(); output.extend_from_slice(&prog.stdout); - sess.dcx().emit_warn(errors::ProcessingDymutilFailed { + sess.dcx().emit_warn(diagnostics::ProcessingDymutilFailed { status: prog.status, output: escape_string(&output), }); } } - Err(error) => sess.dcx().emit_fatal(errors::UnableToRunDsymutil { error }), + Err(error) => sess.dcx().emit_fatal(diagnostics::UnableToRunDsymutil { error }), } } @@ -1382,7 +1389,7 @@ fn link_natively( if sess.target.is_like_aix { // `llvm-strip` doesn't work for AIX - their strip must be used. if !sess.host.is_like_aix { - sess.dcx().emit_warn(errors::AixStripNotUsed); + sess.dcx().emit_warn(diagnostics::AixStripNotUsed); } let stripcmd = "/usr/bin/strip"; match strip { @@ -1421,14 +1428,14 @@ fn strip_with_external_utility(sess: &Session, util: &str, out_filename: &Path, if !prog.status.success() { let mut output = prog.stderr.clone(); output.extend_from_slice(&prog.stdout); - sess.dcx().emit_warn(errors::StrippingDebugInfoFailed { + sess.dcx().emit_warn(diagnostics::StrippingDebugInfoFailed { util, status: prog.status, output: escape_string(&output), }); } } - Err(error) => sess.dcx().emit_fatal(errors::UnableToRun { util, error }), + Err(error) => sess.dcx().emit_fatal(diagnostics::UnableToRun { util, error }), } } @@ -1692,7 +1699,7 @@ pub fn linker_and_flavor(sess: &Session) -> (PathBuf, LinkerFlavor) { )), (Some(linker), None) => { let stem = linker.file_stem().and_then(|stem| stem.to_str()).unwrap_or_else(|| { - sess.dcx().emit_fatal(errors::LinkerFileStem); + sess.dcx().emit_fatal(diagnostics::LinkerFileStem); }); let flavor = sess.target.linker_flavor.with_linker_hints(stem); let flavor = adjust_flavor_to_features(flavor, features); @@ -1863,10 +1870,10 @@ fn print_native_static_libs( match out { OutFileName::Real(path) => { out.overwrite(&lib_args.join(" "), sess); - sess.dcx().emit_note(errors::StaticLibraryNativeArtifactsToFile { path }); + sess.dcx().emit_note(diagnostics::StaticLibraryNativeArtifactsToFile { path }); } OutFileName::Stdout => { - sess.dcx().emit_note(errors::StaticLibraryNativeArtifacts); + sess.dcx().emit_note(diagnostics::StaticLibraryNativeArtifacts); // Prefix for greppability // Note: This must not be translated as tools are allowed to depend on this exact string. sess.dcx().note(format!("native-static-libs: {}", lib_args.join(" "))); @@ -2114,7 +2121,7 @@ fn self_contained_components( // Emit an error if the user requested self-contained mode on the CLI but the target // explicitly refuses it. if sess.target.link_self_contained.is_disabled() { - sess.dcx().emit_err(errors::UnsupportedLinkSelfContained); + sess.dcx().emit_err(diagnostics::UnsupportedLinkSelfContained); } self_contained } else { @@ -2202,14 +2209,14 @@ fn add_link_script(cmd: &mut dyn Linker, sess: &Session, tmpdir: &Path, crate_ty match (crate_type, &sess.target.link_script) { (CrateType::Cdylib | CrateType::Executable, Some(script)) => { if !sess.target.linker_flavor.is_gnu() { - sess.dcx().emit_fatal(errors::LinkScriptUnavailable); + sess.dcx().emit_fatal(diagnostics::LinkScriptUnavailable); } let file_name = ["rustc", &sess.target.llvm_target, "linkfile.ld"].join("-"); let path = tmpdir.join(file_name); if let Err(error) = fs::write(&path, script.as_ref()) { - sess.dcx().emit_fatal(errors::LinkScriptWriteFailure { path, error }); + sess.dcx().emit_fatal(diagnostics::LinkScriptWriteFailure { path, error }); } cmd.link_arg("--script").link_arg(path); @@ -2428,7 +2435,7 @@ fn add_linked_symbol_object( let path = tmpdir.join("symbols.o"); let result = std::fs::write(&path, file.write().unwrap()); if let Err(error) = result { - sess.dcx().emit_fatal(errors::FailedToWrite { path, error }); + sess.dcx().emit_fatal(diagnostics::FailedToWrite { path, error }); } cmd.add_object(&path); } @@ -2868,7 +2875,7 @@ fn linker_with_args( // the directory of the stub to the linker search path. // We make an extra directory for this to avoid polluting the search path. if let Err(error) = fs::create_dir(&raw_dylib_dir) { - sess.dcx().emit_fatal(errors::CreateTempDir { error }) + sess.dcx().emit_fatal(diagnostics::CreateTempDir { error }) } cmd.include_path(&raw_dylib_dir); } @@ -3140,7 +3147,7 @@ fn collect_natvis_visualizers( visualizer_paths.push(visualizer_out_file); } Err(error) => { - sess.dcx().emit_warn(errors::UnableToWriteDebuggerVisualizer { + sess.dcx().emit_warn(diagnostics::UnableToWriteDebuggerVisualizer { path: visualizer_out_file, error, }); @@ -3562,8 +3569,10 @@ fn add_static_crate( false }), ) { - sess.dcx() - .emit_fatal(errors::RlibArchiveBuildFailure { path: cratepath.clone(), error }); + sess.dcx().emit_fatal(diagnostics::RlibArchiveBuildFailure { + path: cratepath.clone(), + error, + }); } if archive.build(&dst, None) { link_upstream(&dst); @@ -3910,7 +3919,7 @@ fn add_lld_args( if !linker_path_exists { // As a sanity check, we emit an error if none of these paths exist: we want // self-contained linking and have no linker. - sess.dcx().emit_fatal(errors::SelfContainedLinkerMissing); + sess.dcx().emit_fatal(diagnostics::SelfContainedLinkerMissing); } } diff --git a/compiler/rustc_codegen_ssa/src/back/link/raw_dylib.rs b/compiler/rustc_codegen_ssa/src/back/link/raw_dylib.rs index 956373381122d..dbc0abdb50da8 100644 --- a/compiler/rustc_codegen_ssa/src/back/link/raw_dylib.rs +++ b/compiler/rustc_codegen_ssa/src/back/link/raw_dylib.rs @@ -15,8 +15,8 @@ use rustc_target::spec::Arch; use crate::back::archive::ImportLibraryItem; use crate::back::link::ArchiveBuilderBuilder; -use crate::errors::ErrorCreatingImportLibrary; -use crate::{NativeLib, common, errors}; +use crate::diagnostics::ErrorCreatingImportLibrary; +use crate::{NativeLib, common, diagnostics}; /// Extract all symbols defined in raw-dylib libraries, collated by library name. /// @@ -41,7 +41,7 @@ fn collate_raw_dylibs_windows<'a>( // FIXME: when we add support for ordinals, figure out if we need to do anything // if we have two DllImport values with the same name but different ordinals. if import.calling_convention != old_import.calling_convention { - sess.dcx().emit_err(errors::MultipleExternalFuncDecl { + sess.dcx().emit_err(diagnostics::MultipleExternalFuncDecl { span: import.span, function: import.name, library_name: &name, diff --git a/compiler/rustc_codegen_ssa/src/back/linker.rs b/compiler/rustc_codegen_ssa/src/back/linker.rs index e24e7bad2b945..e1b4ce372507c 100644 --- a/compiler/rustc_codegen_ssa/src/back/linker.rs +++ b/compiler/rustc_codegen_ssa/src/back/linker.rs @@ -25,7 +25,7 @@ use super::command::Command; use super::symbol_export; use crate::back::symbol_export::allocator_shim_symbols; use crate::base::needs_allocator_shim_for_linking; -use crate::{SymbolExport, errors}; +use crate::{SymbolExport, diagnostics}; #[cfg(test)] mod tests; @@ -484,11 +484,11 @@ impl<'a> GccLinker<'a> { // FIXME(81490): ld64 doesn't support these flags but macOS 11 // has -needed-l{} / -needed_library {} // but we have no way to detect that here. - self.sess.dcx().emit_warn(errors::Ld64UnimplementedModifier); + self.sess.dcx().emit_warn(diagnostics::Ld64UnimplementedModifier); } else if self.is_gnu && !self.sess.target.is_like_windows { self.link_arg("--no-as-needed"); } else { - self.sess.dcx().emit_warn(errors::LinkerUnsupportedModifier); + self.sess.dcx().emit_warn(diagnostics::LinkerUnsupportedModifier); } } @@ -619,7 +619,7 @@ impl<'a> Linker for GccLinker<'a> { // FIXME(81490): ld64 as of macOS 11 supports the -needed_framework // flag but we have no way to detect that here. // self.link_or_cc_arg("-needed_framework").link_or_cc_arg(name); - self.sess.dcx().emit_warn(errors::Ld64UnimplementedModifier); + self.sess.dcx().emit_warn(diagnostics::Ld64UnimplementedModifier); } self.link_or_cc_args(&["-framework", name]); } @@ -822,7 +822,7 @@ impl<'a> Linker for GccLinker<'a> { } }; if let Err(error) = res { - self.sess.dcx().emit_fatal(errors::LibDefWriteFailure { error }); + self.sess.dcx().emit_fatal(diagnostics::LibDefWriteFailure { error }); } self.link_arg("-exported_symbols_list").link_arg(path); } else if self.sess.target.is_like_windows { @@ -842,7 +842,7 @@ impl<'a> Linker for GccLinker<'a> { } }; if let Err(error) = res { - self.sess.dcx().emit_fatal(errors::LibDefWriteFailure { error }); + self.sess.dcx().emit_fatal(diagnostics::LibDefWriteFailure { error }); } self.link_arg(path); } else if self.sess.target.is_like_wasm { @@ -861,7 +861,7 @@ impl<'a> Linker for GccLinker<'a> { writeln!(f, "}};")?; }; if let Err(error) = res { - self.sess.dcx().emit_fatal(errors::VersionScriptWriteFailure { error }); + self.sess.dcx().emit_fatal(diagnostics::VersionScriptWriteFailure { error }); } self.link_arg("--dynamic-list").link_arg(path); } else { @@ -879,7 +879,7 @@ impl<'a> Linker for GccLinker<'a> { writeln!(f, "\n local:\n *;\n}};")?; }; if let Err(error) = res { - self.sess.dcx().emit_fatal(errors::VersionScriptWriteFailure { error }); + self.sess.dcx().emit_fatal(diagnostics::VersionScriptWriteFailure { error }); } if self.sess.target.is_like_solaris { self.link_arg("-M").link_arg(path); @@ -1103,7 +1103,7 @@ impl<'a> Linker for MsvcLinker<'a> { } } Err(error) => { - self.sess.dcx().emit_warn(errors::NoNatvisDirectory { error }); + self.sess.dcx().emit_warn(diagnostics::NoNatvisDirectory { error }); } } } @@ -1136,7 +1136,7 @@ impl<'a> Linker for MsvcLinker<'a> { writeln!(f, "EXPORTS")?; }; if let Err(error) = res { - self.sess.dcx().emit_fatal(errors::LibDefWriteFailure { error }); + self.sess.dcx().emit_fatal(diagnostics::LibDefWriteFailure { error }); } let mut arg = OsString::from("/DEF:"); arg.push(path); @@ -1556,7 +1556,7 @@ impl<'a> Linker for L4Bender<'a> { fn export_symbols(&mut self, _: &Path, _: CrateType, _: &[SymbolExport]) { // ToDo, not implemented, copy from GCC - self.sess.dcx().emit_warn(errors::L4BenderExportingSymbolsUnimplemented); + self.sess.dcx().emit_warn(diagnostics::L4BenderExportingSymbolsUnimplemented); } fn windows_subsystem(&mut self, subsystem: WindowsSubsystemKind) { @@ -2012,7 +2012,7 @@ impl<'a> Linker for BpfLinker<'a> { } fn link_staticlib_by_name(&mut self, _name: &str, _verbatim: bool, _whole_archive: bool) { - self.sess.dcx().emit_fatal(errors::BpfStaticlibNotSupported) + self.sess.dcx().emit_fatal(diagnostics::BpfStaticlibNotSupported) } fn link_staticlib_by_path(&mut self, path: &Path, _whole_archive: bool) { @@ -2061,7 +2061,7 @@ impl<'a> Linker for BpfLinker<'a> { } }; if let Err(error) = res { - self.sess.dcx().emit_fatal(errors::SymbolFileWriteFailure { error }); + self.sess.dcx().emit_fatal(diagnostics::SymbolFileWriteFailure { error }); } else { self.link_arg("--export-symbols").link_arg(&path); } diff --git a/compiler/rustc_codegen_ssa/src/back/lto.rs b/compiler/rustc_codegen_ssa/src/back/lto.rs index ed36fae3cdc4d..0b33c1db8e5dc 100644 --- a/compiler/rustc_codegen_ssa/src/back/lto.rs +++ b/compiler/rustc_codegen_ssa/src/back/lto.rs @@ -14,7 +14,7 @@ use tracing::info; use crate::back::symbol_export::{self, allocator_shim_symbols, symbol_name_for_instance_in_crate}; use crate::back::write::CodegenContext; use crate::base::allocator_kind_for_codegen; -use crate::errors::{DynamicLinkingWithLTO, LtoDisallowed, LtoDylib, LtoProcMacro}; +use crate::diagnostics::{DynamicLinkingWithLTO, LtoDisallowed, LtoDylib, LtoProcMacro}; use crate::traits::*; pub struct ThinModule { diff --git a/compiler/rustc_codegen_ssa/src/back/metadata.rs b/compiler/rustc_codegen_ssa/src/back/metadata.rs index 6f526879f7766..951a60426b5d5 100644 --- a/compiler/rustc_codegen_ssa/src/back/metadata.rs +++ b/compiler/rustc_codegen_ssa/src/back/metadata.rs @@ -24,7 +24,7 @@ use rustc_target::spec::{CfgAbi, LlvmAbi, Os, RelocModel, Target, ef_avr_arch}; use tracing::debug; use super::apple; -use crate::errors; +use crate::diagnostics; /// The default metadata loader. This is used by cg_llvm and cg_clif. /// @@ -371,7 +371,7 @@ pub(super) fn elf_e_flags(architecture: Architecture, sess: &Session) -> u32 { if let Some(ref cpu) = sess.opts.cg.target_cpu { ef_avr_arch(cpu) } else { - sess.dcx().emit_fatal(errors::CpuRequired) + sess.dcx().emit_fatal(diagnostics::CpuRequired) } } Architecture::Csky => { diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index 1419fcaf733f3..88abbf8efe905 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -33,11 +33,11 @@ use tracing::debug; use crate::back::link::ensure_removed; use crate::back::lto::{self, SerializedModule, check_lto_allowed}; -use crate::errors::ErrorCreatingRemarkDir; +use crate::diagnostics::ErrorCreatingRemarkDir; use crate::traits::*; use crate::{ CachedModuleCodegen, CompiledModule, CompiledModules, CrateInfo, ModuleCodegen, ModuleKind, - errors, + diagnostics, }; const PRE_LTO_BC_EXT: &str = "pre-lto.bc"; @@ -515,10 +515,10 @@ pub fn produce_final_output_artifacts( // Produce final compile outputs. let copy_gracefully = |from: &Path, to: &OutFileName| match to { OutFileName::Stdout if let Err(e) = copy_to_stdout(from) => { - sess.dcx().emit_err(errors::CopyPath::new(from, to.as_path(), e)); + sess.dcx().emit_err(diagnostics::CopyPath::new(from, to.as_path(), e)); } OutFileName::Real(path) if let Err(e) = fs::copy(from, path) => { - sess.dcx().emit_err(errors::CopyPath::new(from, path, e)); + sess.dcx().emit_err(diagnostics::CopyPath::new(from, path, e)); } _ => {} }; @@ -530,8 +530,9 @@ pub fn produce_final_output_artifacts( let path = crate_output.temp_path_for_cgu(output_type, &module.name); let output = crate_output.path(output_type); if !output_type.is_text_output() && output.is_tty() { - sess.dcx() - .emit_err(errors::BinaryOutputToTty { shorthand: output_type.shorthand() }); + sess.dcx().emit_err(diagnostics::BinaryOutputToTty { + shorthand: output_type.shorthand(), + }); } else { copy_gracefully(&path, &output); } @@ -543,12 +544,14 @@ pub fn produce_final_output_artifacts( if crate_output.outputs.contains_explicit_name(&output_type) { // 2) Multiple codegen units, with `--emit foo=some_name`. We have // no good solution for this case, so warn the user. - sess.dcx() - .emit_warn(errors::IgnoringEmitPath { extension: output_type.extension() }); + sess.dcx().emit_warn(diagnostics::IgnoringEmitPath { + extension: output_type.extension(), + }); } else if crate_output.single_output_file.is_some() { // 3) Multiple codegen units, with `-o some_name`. We have // no good solution for this case, so warn the user. - sess.dcx().emit_warn(errors::IgnoringOutput { extension: output_type.extension() }); + sess.dcx() + .emit_warn(diagnostics::IgnoringOutput { extension: output_type.extension() }); } else { // 4) Multiple codegen units, but no explicit name. We // just leave the `foo.0.x` files in place. @@ -900,7 +903,7 @@ fn execute_copy_from_cache_work_item( Some(output_path) } Err(error) => { - dcx.emit_err(errors::CopyPathBuf { + dcx.emit_err(diagnostics::CopyPathBuf { source_file: source_file_in_incr_comp_dir, output_path, error, @@ -945,7 +948,7 @@ fn execute_copy_from_cache_work_item( None }; if should_emit_obj && object.is_none() { - dcx.emit_fatal(errors::NoSavedObjectFile { cgu_name: &module.name }) + dcx.emit_fatal(diagnostics::NoSavedObjectFile { cgu_name: &module.name }) } CompiledModule { diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index 43d5e312c6b40..ae049cfc37f95 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -51,7 +51,7 @@ use crate::mir::place::PlaceRef; use crate::traits::*; use crate::{ CachedModuleCodegen, CodegenLintLevelSpecs, CrateInfo, EiiLinkageImplInfo, EiiLinkageInfo, - ModuleCodegen, errors, meth, mir, + ModuleCodegen, diagnostics, meth, mir, }; pub(crate) fn bin_op_to_icmp_predicate(op: BinOp, signed: bool) -> IntPredicate { @@ -529,7 +529,7 @@ pub fn maybe_create_entry_wrapper<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( let Some(llfn) = cx.declare_c_main(llfty) else { // FIXME: We should be smart and show a better diagnostic here. let span = cx.tcx().def_span(rust_main_def_id); - cx.tcx().dcx().emit_fatal(errors::MultipleMainFunctions { span }); + cx.tcx().dcx().emit_fatal(diagnostics::MultipleMainFunctions { span }); }; // `main` should respect same config for frame pointer elimination as rest of code @@ -698,14 +698,14 @@ pub fn codegen_crate< ) -> OngoingCodegen { if tcx.sess.target.need_explicit_cpu && tcx.sess.opts.cg.target_cpu.is_none() { // The target has no default cpu, but none is set explicitly - tcx.dcx().emit_fatal(errors::CpuRequired); + tcx.dcx().emit_fatal(diagnostics::CpuRequired); } if let Some(target_cpu) = &tcx.sess.opts.cg.target_cpu && tcx.sess.target.unsupported_cpus.contains(&target_cpu.into()) { // The target cpu is explicitly listed as an unsupported cpu - tcx.dcx().emit_fatal(errors::CpuUnsupported { target_cpu: target_cpu.clone() }); + tcx.dcx().emit_fatal(diagnostics::CpuUnsupported { target_cpu: target_cpu.clone() }); } let cgu_name_builder = &mut CodegenUnitNameBuilder::new(tcx); diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs index fb21f9ddbf2fa..691b13f03e965 100644 --- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs +++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs @@ -19,7 +19,7 @@ use rustc_session::lint; use rustc_span::{Span, sym}; use rustc_target::spec::Os; -use crate::errors; +use crate::diagnostics; use crate::target_features::{ check_target_feature_trait_unsafe, check_tied_features, from_target_feature_attr, }; @@ -517,10 +517,10 @@ fn check_result( .unwrap_or_else(|| tcx.def_span(did)); tcx.dcx() - .create_err(errors::TargetFeatureDisableOrEnable { + .create_err(diagnostics::TargetFeatureDisableOrEnable { features, span: Some(span), - missing_features: Some(errors::MissingFeatures), + missing_features: Some(diagnostics::MissingFeatures), }) .emit(); } diff --git a/compiler/rustc_codegen_ssa/src/errors.rs b/compiler/rustc_codegen_ssa/src/diagnostics.rs similarity index 100% rename from compiler/rustc_codegen_ssa/src/errors.rs rename to compiler/rustc_codegen_ssa/src/diagnostics.rs diff --git a/compiler/rustc_codegen_ssa/src/lib.rs b/compiler/rustc_codegen_ssa/src/lib.rs index 3608f18345ed0..9a42debe1dd97 100644 --- a/compiler/rustc_codegen_ssa/src/lib.rs +++ b/compiler/rustc_codegen_ssa/src/lib.rs @@ -48,7 +48,7 @@ pub mod base; pub mod codegen_attrs; pub mod common; pub mod debuginfo; -pub mod errors; +pub mod diagnostics; pub mod meth; pub mod mir; pub mod mono_item; diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index fba0cff0e6e14..99075536d04a8 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -26,7 +26,7 @@ use super::place::{PlaceRef, PlaceValue}; use super::{CachedLlbb, FunctionCx, LocalRef}; use crate::base::{self, is_call_from_compiler_builtins_to_upstream_monomorphization}; use crate::common::{self, IntPredicate}; -use crate::errors::CompilerBuiltinsCannotCall; +use crate::diagnostics::CompilerBuiltinsCannotCall; use crate::mir::IntrinsicResult; use crate::traits::*; use crate::{MemFlags, meth}; diff --git a/compiler/rustc_codegen_ssa/src/mir/constant.rs b/compiler/rustc_codegen_ssa/src/mir/constant.rs index d4747c1cbafc9..7d35d4b72bd1e 100644 --- a/compiler/rustc_codegen_ssa/src/mir/constant.rs +++ b/compiler/rustc_codegen_ssa/src/mir/constant.rs @@ -5,7 +5,7 @@ use rustc_middle::ty::{self, Ty}; use rustc_middle::{bug, mir, span_bug}; use super::FunctionCx; -use crate::errors; +use crate::diagnostics; use crate::mir::operand::OperandRef; use crate::traits::*; @@ -98,7 +98,9 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { bx.const_vector(&values) }) .unwrap_or_else(|| { - bx.tcx().dcx().emit_err(errors::ShuffleIndicesEvaluation { span: constant.span }); + bx.tcx() + .dcx() + .emit_err(diagnostics::ShuffleIndicesEvaluation { span: constant.span }); // We've errored, so we don't have to produce working code. let llty = bx.backend_type(bx.layout_of(ty)); bx.const_undef(llty) diff --git a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs index 6116c0fa9eb98..284b7d3f14fed 100644 --- a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs +++ b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs @@ -10,7 +10,7 @@ use super::operand::{OperandRef, OperandValue}; use super::place::PlaceValue; use super::{FunctionCx, IntrinsicResult}; use crate::common::{AtomicRmwBinOp, SynchronizationScope}; -use crate::errors::InvalidMonomorphization; +use crate::diagnostics::InvalidMonomorphization; use crate::mir::operand::OperandRefBuilder; use crate::traits::*; use crate::{MemFlags, meth, size_of_val}; diff --git a/compiler/rustc_codegen_ssa/src/target_features.rs b/compiler/rustc_codegen_ssa/src/target_features.rs index b23472c794b88..8f459e5a218d2 100644 --- a/compiler/rustc_codegen_ssa/src/target_features.rs +++ b/compiler/rustc_codegen_ssa/src/target_features.rs @@ -14,8 +14,8 @@ use rustc_target::spec::{Arch, SanitizerSet}; use rustc_target::target_features::{RUSTC_SPECIFIC_FEATURES, Stability}; use smallvec::SmallVec; -use crate::errors::{CrossArchFeatureNote, FeatureNotValid, FeatureNotValidHint}; -use crate::{errors, target_features}; +use crate::diagnostics::{CrossArchFeatureNote, FeatureNotValid, FeatureNotValidHint}; +use crate::{diagnostics, target_features}; /// Compute the enabled target features from the `#[target_feature]` function attribute. /// Enabled target features are added to `target_features`. @@ -72,7 +72,7 @@ pub(crate) fn from_target_feature_attr( // Only allow target features whose feature gates have been enabled // and which are permitted to be toggled. if let Err(reason) = stability.toggle_allowed() { - tcx.dcx().emit_err(errors::ForbiddenTargetFeatureAttr { + tcx.dcx().emit_err(diagnostics::ForbiddenTargetFeatureAttr { span: feature_span, feature: feature_str, reason, @@ -104,10 +104,10 @@ pub(crate) fn from_target_feature_attr( AARCH64_SOFTFLOAT_NEON, tcx.local_def_id_to_hir_id(did), feature_span, - errors::Aarch64SoftfloatNeon, + diagnostics::Aarch64SoftfloatNeon, ); } else { - tcx.dcx().emit_err(errors::ForbiddenTargetFeatureAttr { + tcx.dcx().emit_err(diagnostics::ForbiddenTargetFeatureAttr { span: feature_span, feature: name.as_str(), reason: "this feature is incompatible with the target ABI", @@ -156,7 +156,7 @@ pub(crate) fn check_target_feature_trait_unsafe(tcx: TyCtxt<'_>, id: LocalDefId, if let DefKind::AssocFn = tcx.def_kind(id) { let parent_id = tcx.local_parent(id); if let DefKind::Trait | DefKind::Impl { of_trait: true } = tcx.def_kind(parent_id) { - tcx.dcx().emit_err(errors::TargetFeatureSafeTrait { + tcx.dcx().emit_err(diagnostics::TargetFeatureSafeTrait { span: attr_span, def: tcx.def_span(id), }); @@ -275,7 +275,7 @@ pub fn cfg_target_feature<'a, const N: usize>( &sess.opts.cg.target_feature, /* err_callback */ |feature| { - sess.dcx().emit_warn(errors::UnknownCTargetFeaturePrefix { feature }); + sess.dcx().emit_warn(diagnostics::UnknownCTargetFeaturePrefix { feature }); }, |base_feature, new_features, enable| { // Iteration order is irrelevant since this only influences an `FxHashMap`. @@ -310,21 +310,21 @@ pub fn cfg_target_feature<'a, const N: usize>( } }); let unknown_feature = if let Some(rust_feature) = rust_feature { - errors::UnknownCTargetFeature { + diagnostics::UnknownCTargetFeature { feature: base_feature, - rust_feature: errors::PossibleFeature::Some { rust_feature }, + rust_feature: diagnostics::PossibleFeature::Some { rust_feature }, } } else { - errors::UnknownCTargetFeature { + diagnostics::UnknownCTargetFeature { feature: base_feature, - rust_feature: errors::PossibleFeature::None, + rust_feature: diagnostics::PossibleFeature::None, } }; sess.dcx().emit_warn(unknown_feature); } Some((_, stability, _)) => { if let Stability::Forbidden { reason, hard_error } = stability { - let diag = errors::ForbiddenCTargetFeature { + let diag = diagnostics::ForbiddenCTargetFeature { feature: base_feature, enabled: if enable { "enabled" } else { "disabled" }, reason, @@ -345,7 +345,7 @@ pub fn cfg_target_feature<'a, const N: usize>( } else { "this feature is not stably supported" }; - sess.dcx().emit_warn(errors::UnstableCTargetFeature { + sess.dcx().emit_warn(diagnostics::UnstableCTargetFeature { feature: base_feature, note, }); @@ -356,7 +356,7 @@ pub fn cfg_target_feature<'a, const N: usize>( ); if let Some(f) = check_tied_features(sess, &enabled_disabled_features) { - sess.dcx().emit_err(errors::TargetFeatureDisableOrEnable { + sess.dcx().emit_err(diagnostics::TargetFeatureDisableOrEnable { features: f, span: None, missing_features: None, From db1695d47e016664c536f495a1fe68421bf70467 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Wed, 15 Jul 2026 17:23:13 +0200 Subject: [PATCH 05/26] Update uses of `rustc_codegen_ssa::errors` --- compiler/rustc_codegen_cranelift/src/abi/mod.rs | 2 +- compiler/rustc_codegen_gcc/src/context.rs | 2 +- compiler/rustc_codegen_gcc/src/intrinsic/mod.rs | 2 +- compiler/rustc_codegen_gcc/src/intrinsic/simd.rs | 4 ++-- compiler/rustc_codegen_llvm/src/context.rs | 2 +- compiler/rustc_codegen_llvm/src/intrinsic.rs | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/compiler/rustc_codegen_cranelift/src/abi/mod.rs b/compiler/rustc_codegen_cranelift/src/abi/mod.rs index 478f5d30a255c..491463865b5db 100644 --- a/compiler/rustc_codegen_cranelift/src/abi/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/abi/mod.rs @@ -14,7 +14,7 @@ use cranelift_codegen::isa::CallConv; use cranelift_module::ModuleError; use rustc_abi::{CanonAbi, ExternAbi, X86Call}; use rustc_codegen_ssa::base::is_call_from_compiler_builtins_to_upstream_monomorphization; -use rustc_codegen_ssa::errors::CompilerBuiltinsCannotCall; +use rustc_codegen_ssa::diagnostics::CompilerBuiltinsCannotCall; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::ty::layout::FnAbiOf; use rustc_middle::ty::print::with_no_trimmed_paths; diff --git a/compiler/rustc_codegen_gcc/src/context.rs b/compiler/rustc_codegen_gcc/src/context.rs index 0e3fa72fbcfb3..184db4cb25778 100644 --- a/compiler/rustc_codegen_gcc/src/context.rs +++ b/compiler/rustc_codegen_gcc/src/context.rs @@ -4,7 +4,7 @@ use std::collections::HashMap; use gccjit::{Block, CType, Context, Function, FunctionType, LValue, Location, RValue, Type}; use rustc_abi::{Align, HasDataLayout, PointeeInfo, Size, TargetDataLayout, VariantIdx}; use rustc_codegen_ssa::base::wants_msvc_seh; -use rustc_codegen_ssa::errors as ssa_errors; +use rustc_codegen_ssa::diagnostics as ssa_errors; use rustc_codegen_ssa::traits::{BackendTypes, BaseTypeCodegenMethods, MiscCodegenMethods}; use rustc_data_structures::base_n::{ALPHANUMERIC_ONLY, ToBaseN}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; diff --git a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs index 06fbd287435d6..09ad3254e5714 100644 --- a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs +++ b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs @@ -8,7 +8,7 @@ use gccjit::{ComparisonOp, Function, FunctionType, RValue, ToRValue, Type, Unary use rustc_abi::{Align, BackendRepr, HasDataLayout, WrappingRange}; use rustc_codegen_ssa::base::wants_msvc_seh; use rustc_codegen_ssa::common::IntPredicate; -use rustc_codegen_ssa::errors::InvalidMonomorphization; +use rustc_codegen_ssa::diagnostics::InvalidMonomorphization; use rustc_codegen_ssa::mir::IntrinsicResult; use rustc_codegen_ssa::mir::operand::{OperandRef, OperandValue}; use rustc_codegen_ssa::mir::place::{PlaceRef, PlaceValue}; diff --git a/compiler/rustc_codegen_gcc/src/intrinsic/simd.rs b/compiler/rustc_codegen_gcc/src/intrinsic/simd.rs index 82ef99703b253..1416f4eec9c4a 100644 --- a/compiler/rustc_codegen_gcc/src/intrinsic/simd.rs +++ b/compiler/rustc_codegen_gcc/src/intrinsic/simd.rs @@ -7,8 +7,8 @@ use rustc_abi::{Align, Size}; use rustc_codegen_ssa::base::compare_simd_types; use rustc_codegen_ssa::common::{IntPredicate, TypeKind}; #[cfg(feature = "master")] -use rustc_codegen_ssa::errors::ExpectedPointerMutability; -use rustc_codegen_ssa::errors::InvalidMonomorphization; +use rustc_codegen_ssa::diagnostics::ExpectedPointerMutability; +use rustc_codegen_ssa::diagnostics::InvalidMonomorphization; use rustc_codegen_ssa::mir::operand::OperandRef; use rustc_codegen_ssa::mir::place::PlaceRef; use rustc_codegen_ssa::traits::{BaseTypeCodegenMethods, BuilderMethods}; diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index 7ea30a5b4db6d..c018ab23c849a 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -8,7 +8,7 @@ use std::str; use rustc_abi::{HasDataLayout, Size, TargetDataLayout, VariantIdx}; use rustc_codegen_ssa::back::versioned_llvm_target; use rustc_codegen_ssa::base::{wants_msvc_seh, wants_wasm_eh}; -use rustc_codegen_ssa::errors as ssa_errors; +use rustc_codegen_ssa::diagnostics as ssa_errors; use rustc_codegen_ssa::traits::*; use rustc_data_structures::base_n::{ALPHANUMERIC_ONLY, ToBaseN}; use rustc_data_structures::fx::FxHashMap; diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index 0d11f1e4fb69b..edf943c81a755 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -9,7 +9,7 @@ use rustc_abi::{ use rustc_codegen_ssa::RetagInfo; use rustc_codegen_ssa::base::{compare_simd_types, wants_msvc_seh, wants_wasm_eh}; use rustc_codegen_ssa::common::{IntPredicate, TypeKind}; -use rustc_codegen_ssa::errors::{ExpectedPointerMutability, InvalidMonomorphization}; +use rustc_codegen_ssa::diagnostics::{ExpectedPointerMutability, InvalidMonomorphization}; use rustc_codegen_ssa::mir::IntrinsicResult; use rustc_codegen_ssa::mir::operand::{OperandRef, OperandValue}; use rustc_codegen_ssa::mir::place::{PlaceRef, PlaceValue}; From 9c81f660da8aa35f57b5a37628e0a06a4ab64850 Mon Sep 17 00:00:00 2001 From: albab-hasan Date: Thu, 16 Jul 2026 13:00:44 +0600 Subject: [PATCH 06/26] point at method call chain when a return-position `impl Trait` assoc type diverges a mismatch on `-> impl Iterator` only pointed at the signature, not at the call in the returned chain where `Item` actually changed. the failed predicate is useless for this, its already rewritten through the impls it was derived from, so the expected assoc types are read from the opaques own bounds and probed against the returned expression. on divergence the existing `point_at_chain` walk kicks in, same as it does for function arguments. handles the chain at the tail expression, behind a `let` binding, and derived through adapters like `flatten`. unrelated failures stay silent since the probe just doesnt resolve there. fixes https://github.com/rust-lang/rust/issues/106993 --- .../src/error_reporting/traits/suggestions.rs | 126 ++++++++++++++++++ .../duplicate-bound-err.stderr | 8 ++ ...valid-iterator-chain-in-return-position.rs | 39 ++++++ ...d-iterator-chain-in-return-position.stderr | 69 ++++++++++ tests/ui/lint/issue-106991.stderr | 9 ++ 5 files changed, 251 insertions(+) create mode 100644 tests/ui/iterators/invalid-iterator-chain-in-return-position.rs create mode 100644 tests/ui/iterators/invalid-iterator-chain-in-return-position.stderr 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 ff823594ce1c0..2a1f0e7935ea0 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -4487,6 +4487,29 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { tcx.disabled_nightly_features(err, [(String::new(), sym::trivial_bounds)]); } ObligationCauseCode::OpaqueReturnType(expr_info) => { + // Point at the method call in the returned expression's chain where an + // associated type diverged from what the signature's opaque type expects, + // regardless of how the failed predicate was derived from that expectation. + if let Some(typeck_results) = self.typeck_results.as_deref() { + let chain_expr = match expr_info { + Some((_, hir_id)) => Some(tcx.hir_expect_expr(hir_id)), + None => tcx.hir_node_by_def_id(body_def_id).body_id().and_then(|body_id| { + match tcx.hir_body(body_id).value.kind { + hir::ExprKind::Block(block, _) => block.expr, + _ => None, + } + }), + }; + if let Some(chain_expr) = chain_expr { + self.point_at_chain_in_return_position( + body_def_id, + chain_expr, + typeck_results, + param_env, + err, + ); + } + } let (expr_ty, expr) = if let Some((expr_ty, hir_id)) = expr_info { let expr = tcx.hir_expect_expr(hir_id); (expr_ty, expr) @@ -5438,6 +5461,109 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { assocs_in_this_method } + /// When a `-> impl Trait` return type obligation fails, walk the method call + /// chain in the returned expression to point at where the associated type diverged from + /// what the signature expects. + /// + /// ```text + /// note: the method call chain might not have had the expected associated types + /// --> $DIR/invalid-iterator-chain-in-return-position.rs:16:18 + /// | + /// LL | x.iter_mut().map(foo) + /// | - ---------- ^^^^^^^^ `Iterator::Item` changed to `()` here + /// | | | + /// | | `Iterator::Item` is `&mut Vec` here + /// | this expression has type `Vec>` + /// ``` + fn point_at_chain_in_return_position( + &self, + body_def_id: LocalDefId, + expr: &hir::Expr<'_>, + typeck_results: &TypeckResults<'tcx>, + param_env: ty::ParamEnv<'tcx>, + err: &mut Diag<'_, G>, + ) { + let tcx = self.tcx; + if !matches!(tcx.def_kind(body_def_id), DefKind::Fn | DefKind::AssocFn) { + return; + } + let output = + tcx.fn_sig(body_def_id).instantiate_identity().skip_norm_wip().output().skip_binder(); + let &ty::Alias(_, ty::AliasTy { kind: ty::Opaque { def_id: opaque_def_id }, args, .. }) = + output.kind() + else { + return; + }; + + // The predicate that reaches here has been rewritten through the impls it was + // derived from (e.g. `Iterator for Map` turns `Iterator::Item` requirements + // into requirements on `F`'s return type), so the associated types the user wrote + // in the signature are recovered from the opaque's bounds instead. + let mut probe_diffs = vec![]; + for clause in tcx.item_bounds(opaque_def_id).instantiate(tcx, args).skip_norm_wip() { + let Some(proj) = clause.as_projection_clause() else { continue }; + let proj = self.instantiate_binder_with_fresh_vars( + expr.span, + BoundRegionConversionTime::FnCall, + proj, + ); + let Some(expected_term) = proj.term.as_type() else { continue }; + // Only the projection (for its `DefId`) is used when probing the chain; the + // bound's own term is carried in `found` for the divergence check below and + // is replaced with the probed type afterwards. + probe_diffs.push(TypeError::Sorts(ty::error::ExpectedFound { + expected: proj.projection_term.expect_ty().to_ty(tcx, ty::IsRigid::No), + found: expected_term, + })); + } + if probe_diffs.is_empty() { + return; + } + + // If the returned expression is a binding, walk the chain that created it instead. + let expr = if let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = expr.kind + && let hir::Path { res: Res::Local(hir_id), .. } = path + && let hir::Node::Pat(binding) = tcx.hir_node(*hir_id) + && let hir::Node::LetStmt(local) = tcx.parent_hir_node(binding.hir_id) + && let Some(binding_expr) = local.init + { + binding_expr + } else { + expr + }; + + // Resolve what each bound associated type actually is for the returned expression, + // and keep only the ones that diverged from the signature. + let expr_ty = self.resolve_vars_if_possible( + typeck_results.expr_ty_adjusted_opt(expr).unwrap_or(Ty::new_misc_error(tcx)), + ); + let assocs = self.probe_assoc_types_at_expr( + &probe_diffs, + expr.span, + expr_ty, + expr.hir_id, + param_env, + ); + let mut type_diffs = vec![]; + for (probe_diff, assoc) in iter::zip(probe_diffs, assocs) { + let TypeError::Sorts(ty::error::ExpectedFound { expected, found: expected_term }) = + probe_diff + else { + continue; + }; + let Some((_, (_, actual_ty))) = assoc else { continue }; + if !self.can_eq(param_env, expected_term, actual_ty) { + type_diffs.push(TypeError::Sorts(ty::error::ExpectedFound { + expected, + found: actual_ty, + })); + } + } + if !type_diffs.is_empty() { + self.point_at_chain(expr, typeck_results, type_diffs, param_env, err); + } + } + /// If the type that failed selection is an array or a reference to an array, /// but the trait is implemented for slices, suggest that the user converts /// the array into a slice. diff --git a/tests/ui/associated-type-bounds/duplicate-bound-err.stderr b/tests/ui/associated-type-bounds/duplicate-bound-err.stderr index 8b172cd5ca133..f685b01cd8cc3 100644 --- a/tests/ui/associated-type-bounds/duplicate-bound-err.stderr +++ b/tests/ui/associated-type-bounds/duplicate-bound-err.stderr @@ -107,6 +107,14 @@ LL | fn foo() -> impl Iterator { ... LL | [2u32].into_iter() | ------------------ return type was inferred to be `std::array::IntoIter` here + | +note: the method call chain might not have had the expected associated types + --> $DIR/duplicate-bound-err.rs:110:16 + | +LL | [2u32].into_iter() + | ------ ^^^^^^^^^^^ `Iterator::Item` is `u32` here + | | + | this expression has type `[u32; 1]` error[E0271]: expected `impl Iterator` to be an iterator that yields `i32`, but it yields `u32` --> $DIR/duplicate-bound-err.rs:107:17 diff --git a/tests/ui/iterators/invalid-iterator-chain-in-return-position.rs b/tests/ui/iterators/invalid-iterator-chain-in-return-position.rs new file mode 100644 index 0000000000000..c87438818e17d --- /dev/null +++ b/tests/ui/iterators/invalid-iterator-chain-in-return-position.rs @@ -0,0 +1,39 @@ +//! Check that a `-> impl Iterator` return type mismatch points at the +//! method call in the returned expression's chain where `Iterator::Item` diverged +//! from the signature's expectation, instead of only pointing at the signature. +//! Regression test for . + +fn foo(items: &mut Vec) { + items.sort(); +} + +fn bar() -> impl Iterator { + //~^ ERROR expected `foo` to return `i32`, but it returns `()` + let mut x: Vec> = vec![ + vec![0, 2, 1], + vec![5, 4, 3], + ]; + x.iter_mut().map(foo) +} + +fn baz() -> impl Iterator { + //~^ ERROR expected `foo` to return `i32`, but it returns `()` + let mut x: Vec> = vec![ + vec![0, 2, 1], + vec![5, 4, 3], + ]; + let it = x.iter_mut().map(foo); + it +} + +fn chained() -> impl Iterator { + //~^ ERROR expected `IntoIter` to be an iterator that yields `i32`, but it yields `u32` + let x = vec![0u32, 1, 2]; + x.into_iter().filter(|x| *x > 0).map(|x| x.checked_add(1)).flatten() +} + +fn main() { + bar(); + baz(); + chained(); +} diff --git a/tests/ui/iterators/invalid-iterator-chain-in-return-position.stderr b/tests/ui/iterators/invalid-iterator-chain-in-return-position.stderr new file mode 100644 index 0000000000000..a21acc3dd84b3 --- /dev/null +++ b/tests/ui/iterators/invalid-iterator-chain-in-return-position.stderr @@ -0,0 +1,69 @@ +error[E0271]: expected `foo` to return `i32`, but it returns `()` + --> $DIR/invalid-iterator-chain-in-return-position.rs:10:13 + | +LL | fn bar() -> impl Iterator { + | ^^^^^^^^^^^^^^^^^^^^^^^^^ expected `i32`, found `()` +... +LL | x.iter_mut().map(foo) + | --------------------- return type was inferred to be `Map>, for<'a> fn(&'a mut Vec) {foo}>` here + | + = note: required for `Map>, for<'a> fn(&'a mut Vec) {foo}>` to implement `Iterator` +note: the method call chain might not have had the expected associated types + --> $DIR/invalid-iterator-chain-in-return-position.rs:16:18 + | +LL | let mut x: Vec> = vec![ + | _______________________________- +LL | | vec![0, 2, 1], +LL | | vec![5, 4, 3], +LL | | ]; + | |_____- this expression has type `Vec>` +LL | x.iter_mut().map(foo) + | ---------- ^^^^^^^^ `Iterator::Item` changed to `()` here + | | + | `Iterator::Item` is `&mut Vec` here + +error[E0271]: expected `foo` to return `i32`, but it returns `()` + --> $DIR/invalid-iterator-chain-in-return-position.rs:19:13 + | +LL | fn baz() -> impl Iterator { + | ^^^^^^^^^^^^^^^^^^^^^^^^^ expected `i32`, found `()` +... +LL | it + | -- return type was inferred to be `Map>, for<'a> fn(&'a mut Vec) {foo}>` here + | + = note: required for `Map>, for<'a> fn(&'a mut Vec) {foo}>` to implement `Iterator` +note: the method call chain might not have had the expected associated types + --> $DIR/invalid-iterator-chain-in-return-position.rs:25:27 + | +LL | let mut x: Vec> = vec![ + | _______________________________- +LL | | vec![0, 2, 1], +LL | | vec![5, 4, 3], +LL | | ]; + | |_____- this expression has type `Vec>` +LL | let it = x.iter_mut().map(foo); + | ---------- ^^^^^^^^ `Iterator::Item` changed to `()` here + | | + | `Iterator::Item` is `&mut Vec` here + +error[E0271]: expected `IntoIter` to be an iterator that yields `i32`, but it yields `u32` + --> $DIR/invalid-iterator-chain-in-return-position.rs:29:17 + | +LL | fn chained() -> impl Iterator { + | ^^^^^^^^^^^^^^^^^^^^^^^^^ expected `i32`, found `u32` + | +note: the method call chain might not have had the expected associated types + --> $DIR/invalid-iterator-chain-in-return-position.rs:32:7 + | +LL | let x = vec![0u32, 1, 2]; + | ---------------- this expression has type `Vec` +LL | x.into_iter().filter(|x| *x > 0).map(|x| x.checked_add(1)).flatten() + | ^^^^^^^^^^^ ------------------ ------------------------- ^^^^^^^^^ `Iterator::Item` changed to `u32` here + | | | | + | | | `Iterator::Item` changed to `Option` here + | | `Iterator::Item` remains `u32` here + | `Iterator::Item` is `u32` here + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0271`. diff --git a/tests/ui/lint/issue-106991.stderr b/tests/ui/lint/issue-106991.stderr index 15e0ba5337f14..5c31c240dfff5 100644 --- a/tests/ui/lint/issue-106991.stderr +++ b/tests/ui/lint/issue-106991.stderr @@ -8,6 +8,15 @@ LL | x.iter_mut().map(foo) | --------------------- return type was inferred to be `Map>, for<'a> fn(&'a mut Vec) {foo}>` here | = note: required for `Map>, for<'a> fn(&'a mut Vec) {foo}>` to implement `Iterator` +note: the method call chain might not have had the expected associated types + --> $DIR/issue-106991.rs:8:18 + | +LL | let mut x: Vec> = vec![vec![0, 2, 1], vec![5, 4, 3]]; + | ---------------------------------- this expression has type `Vec>` +LL | x.iter_mut().map(foo) + | ---------- ^^^^^^^^ `Iterator::Item` changed to `()` here + | | + | `Iterator::Item` is `&mut Vec` here error: aborting due to 1 previous error From e2159107f6ebc615ba6a92960d65528baf90cd35 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Mon, 13 Jul 2026 15:17:30 +0200 Subject: [PATCH 07/26] Update global_asm tests for LLVM 23 There is now only a single "module asm", not one before each line. --- tests/codegen-llvm/asm/global_asm.rs | 4 ++-- tests/codegen-llvm/asm/global_asm_include.rs | 4 ++-- tests/codegen-llvm/asm/global_asm_x2.rs | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/codegen-llvm/asm/global_asm.rs b/tests/codegen-llvm/asm/global_asm.rs index 32075daa3cf23..8c1c4a3b5881f 100644 --- a/tests/codegen-llvm/asm/global_asm.rs +++ b/tests/codegen-llvm/asm/global_asm.rs @@ -7,10 +7,10 @@ use std::arch::global_asm; -// CHECK-LABEL: foo // CHECK: module asm +// CHECK-LABEL: foo // this regex will capture the correct unconditional branch inst. -// CHECK: module asm "{{[[:space:]]+}}jmp baz" +// CHECK: "{{[[:space:]]+}}jmp baz" global_asm!( r#" .global foo diff --git a/tests/codegen-llvm/asm/global_asm_include.rs b/tests/codegen-llvm/asm/global_asm_include.rs index 98be9c3e33322..cad9901336bf3 100644 --- a/tests/codegen-llvm/asm/global_asm_include.rs +++ b/tests/codegen-llvm/asm/global_asm_include.rs @@ -7,9 +7,9 @@ use std::arch::global_asm; -// CHECK-LABEL: foo // CHECK: module asm -// CHECK: module asm "{{[[:space:]]+}}jmp baz" +// CHECK-LABEL: foo +// CHECK: "{{[[:space:]]+}}jmp baz" global_asm!(include_str!("foo.s")); extern "C" { diff --git a/tests/codegen-llvm/asm/global_asm_x2.rs b/tests/codegen-llvm/asm/global_asm_x2.rs index 9e3a00f068053..8c6f38289de33 100644 --- a/tests/codegen-llvm/asm/global_asm_x2.rs +++ b/tests/codegen-llvm/asm/global_asm_x2.rs @@ -8,12 +8,12 @@ use core::arch::global_asm; -// CHECK-LABEL: foo // CHECK: module asm -// CHECK: module asm "{{[[:space:]]+}}jmp baz" +// CHECK-LABEL: foo +// CHECK: "{{[[:space:]]+}}jmp baz" // any other global_asm will be appended to this first block, so: // CHECK-LABEL: bar -// CHECK: module asm "{{[[:space:]]+}}jmp quux" +// CHECK: "{{[[:space:]]+}}jmp quux" global_asm!( r#" .global foo From d4a24cd6e18b98326429d8c7e7bbb269da14a5aa Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Mon, 13 Jul 2026 15:21:18 +0200 Subject: [PATCH 08/26] Adjust codegen test for LLVM 23 The assume here is not really relevant to the purpose of the test, and it's position changed in LLVM 23. --- tests/codegen-llvm/issues/issue-107681-unwrap_unchecked.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/codegen-llvm/issues/issue-107681-unwrap_unchecked.rs b/tests/codegen-llvm/issues/issue-107681-unwrap_unchecked.rs index 5834255f3d313..c3090a20e4d14 100644 --- a/tests/codegen-llvm/issues/issue-107681-unwrap_unchecked.rs +++ b/tests/codegen-llvm/issues/issue-107681-unwrap_unchecked.rs @@ -14,7 +14,6 @@ pub unsafe fn foo(x: &mut Copied>) -> u32 { // CHECK-NOT: br {{.*}} // CHECK-NOT: select // CHECK: [[RET:%.*]] = load i32, ptr - // CHECK-NEXT: assume - // CHECK-NEXT: ret i32 [[RET]] + // CHECK: ret i32 [[RET]] x.next().unwrap_unchecked() } From 283a60b64339d611b76a4ed2f9286f417b269b2f Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Mon, 13 Jul 2026 16:18:03 +0200 Subject: [PATCH 09/26] Update simd mask test for LLVM 23 Codegen changed in: https://github.com/llvm/llvm-project/commit/66c1b6f0194bec99396e358f997d771d5e3bd28d --- .../simd-intrinsic-mask-reduce.rs | 34 ++++++++++++++----- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/tests/assembly-llvm/simd-intrinsic-mask-reduce.rs b/tests/assembly-llvm/simd-intrinsic-mask-reduce.rs index 40d16886c6d7d..d3698fda5fe85 100644 --- a/tests/assembly-llvm/simd-intrinsic-mask-reduce.rs +++ b/tests/assembly-llvm/simd-intrinsic-mask-reduce.rs @@ -1,12 +1,16 @@ // verify that simd mask reductions do not introduce additional bit shift operations //@ add-minicore -//@ revisions: x86 aarch64 +//@ revisions: x86 aarch64-llvm22 aarch64 //@ [x86] compile-flags: --target=x86_64-unknown-linux-gnu -C llvm-args=-x86-asm-syntax=intel // Set the base cpu explicitly, in case the default has been changed. //@ [x86] compile-flags: -C target-cpu=x86-64 //@ [x86] needs-llvm-components: x86 +//@ [aarch64-llvm22] compile-flags: --target=aarch64-unknown-linux-gnu +//@ [aarch64-llvm22] needs-llvm-components: aarch64 +//@ [aarch64-llvm22] max-llvm-major-version: 22 //@ [aarch64] compile-flags: --target=aarch64-unknown-linux-gnu //@ [aarch64] needs-llvm-components: aarch64 +//@ [aarch64] min-llvm-version: 23 //@ assembly-output: emit-asm //@ compile-flags: --crate-type=lib -Copt-level=3 -C panic=abort @@ -33,12 +37,19 @@ pub unsafe extern "C" fn mask_reduce_all(m: mask8x16) -> bool { // x86-NEXT: {{cmp ax, -1|cmp eax, 65535|xor eax, 65535}} // x86-NEXT: sete al // + // aarch64-llvm22-NOT: shl + // aarch64-llvm22: cmge v0.16b, v0.16b, #0 + // aarch64-llvm22-DAG: mov [[REG1:[a-z0-9]+]], #1 + // aarch64-llvm22-DAG: umaxv b0, v0.16b + // aarch64-llvm22-NEXT: fmov [[REG2:[a-z0-9]+]], s0 + // aarch64-llvm22-NEXT: bic w0, [[REG1]], [[REG2]] + // // aarch64-NOT: shl // aarch64: cmge v0.16b, v0.16b, #0 - // aarch64-DAG: mov [[REG1:[a-z0-9]+]], #1 - // aarch64-DAG: umaxv b0, v0.16b - // aarch64-NEXT: fmov [[REG2:[a-z0-9]+]], s0 - // aarch64-NEXT: bic w0, [[REG1]], [[REG2]] + // aarch64-NEXT: addp [[REG1:d[0-9]+]], v0.2d + // aarch64-NEXT: fmov [[REG2:x[0-9]+]], [[REG1]] + // aarch64-NEXT: cmp [[REG2]], #0 + // aarch64-NEXT: cset w0, eq simd_reduce_all(m) } @@ -50,10 +61,17 @@ pub unsafe extern "C" fn mask_reduce_any(m: mask8x16) -> bool { // x86-NEXT: test eax, eax // x86-NEXT: setne al // + // aarch64-llvm22-NOT: shl + // aarch64-llvm22: cmlt v0.16b, v0.16b, #0 + // aarch64-llvm22-NEXT: umaxv b0, v0.16b + // aarch64-llvm22-NEXT: fmov [[REG:[a-z0-9]+]], s0 + // aarch64-llvm22-NEXT: and w0, [[REG]], #0x1 + // // aarch64-NOT: shl // aarch64: cmlt v0.16b, v0.16b, #0 - // aarch64-NEXT: umaxv b0, v0.16b - // aarch64-NEXT: fmov [[REG:[a-z0-9]+]], s0 - // aarch64-NEXT: and w0, [[REG]], #0x1 + // aarch64-NEXT: addp [[REG1:d[0-9]+]], v0.2d + // aarch64-NEXT: fmov [[REG2:x[0-9]+]], [[REG1]] + // aarch64-NEXT: cmp [[REG2]], #0 + // aarch64-NEXT: cset w0, ne simd_reduce_any(m) } From 61d9ba28a99f073fbeb19dbb552ddf343aef62f0 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Mon, 13 Jul 2026 16:37:25 +0200 Subject: [PATCH 10/26] Disable slice is_ascii() test on LLVM 23 This doesn't optimize as desired since: https://github.com/llvm/llvm-project/commit/21f439f13250bd9b7c19c8dd838177a04bf091ef --- tests/assembly-llvm/slice-is_ascii.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/assembly-llvm/slice-is_ascii.rs b/tests/assembly-llvm/slice-is_ascii.rs index e53cd5160cf56..6d660e78147c2 100644 --- a/tests/assembly-llvm/slice-is_ascii.rs +++ b/tests/assembly-llvm/slice-is_ascii.rs @@ -6,6 +6,11 @@ //@ only-x86_64 //@ ignore-sgx +// No longer optimizes as desired, tracked at: +// https://github.com/rust-lang/rust/issues/154141 +// https://github.com/llvm/llvm-project/issues/209216 +//@ max-llvm-major-version: 22 + #![feature(str_internals)] // CHECK-LABEL: is_ascii_simple_demo: From df6afdf60f37a0b552b764ccb7bf9ecae21039c7 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Tue, 14 Jul 2026 14:29:17 +0200 Subject: [PATCH 11/26] Adjust PGO tests for LLVM 23 These now have additional !guid metadata. --- tests/run-make/pgo-branch-weights/filecheck-patterns.txt | 6 +++--- tests/run-make/pgo-embed-bc-lto/interesting.rs | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/run-make/pgo-branch-weights/filecheck-patterns.txt b/tests/run-make/pgo-branch-weights/filecheck-patterns.txt index 70d5a645c1454..ac9de0b16e49d 100644 --- a/tests/run-make/pgo-branch-weights/filecheck-patterns.txt +++ b/tests/run-make/pgo-branch-weights/filecheck-patterns.txt @@ -2,16 +2,16 @@ # First, establish that certain !prof labels are attached to the expected # functions and branching instructions. -CHECK: define void @function_called_twice(i32 {{.*}} !prof [[function_called_twice_id:![0-9]+]] { +CHECK: define void @function_called_twice(i32 {{.*}} !prof [[function_called_twice_id:![0-9]+]] CHECK: br i1 {{.*}}, label {{.*}}, label {{.*}}, !prof [[branch_weights0:![0-9]+]] -CHECK: define void @function_called_42_times(i32{{.*}} %c) {{.*}} !prof [[function_called_42_times_id:![0-9]+]] { +CHECK: define void @function_called_42_times(i32{{.*}} %c) {{.*}} !prof [[function_called_42_times_id:![0-9]+]] CHECK: switch i32 %c, label {{.*}} [ CHECK-NEXT: i32 97, label {{.*}} CHECK-NEXT: i32 98, label {{.*}} CHECK-NEXT: ], !prof [[branch_weights1:![0-9]+]] -CHECK: define void @function_called_never(i32 {{.*}} !prof [[function_called_never_id:![0-9]+]] { +CHECK: define void @function_called_never(i32 {{.*}} !prof [[function_called_never_id:![0-9]+]] diff --git a/tests/run-make/pgo-embed-bc-lto/interesting.rs b/tests/run-make/pgo-embed-bc-lto/interesting.rs index 13105c17e126d..94c7621457917 100644 --- a/tests/run-make/pgo-embed-bc-lto/interesting.rs +++ b/tests/run-make/pgo-embed-bc-lto/interesting.rs @@ -10,7 +10,7 @@ pub fn function_called_once() { } // CHECK-LABEL: @function_called_once -// CHECK-SAME: !prof [[function_called_once_id:![0-9]+]] { +// CHECK-SAME: !prof [[function_called_once_id:![0-9]+]] // CHECK: "CG Profile" // CHECK-NOT: "CG Profile" // CHECK-DAG: [[function_called_once_id]] = !{!"function_entry_count", i64 1} From 810a7aa41f08354e2829ccdb03a767b40d5c04e7 Mon Sep 17 00:00:00 2001 From: Jieyou Xu Date: Fri, 17 Jul 2026 00:16:09 +0800 Subject: [PATCH 12/26] tests: gate `tests/deubinfo/function-call.rs` on min GDB 15.1 See RUST-159073, this test has been flaky with ``` /checkout/obj/build/x86_64-unknown-linux-gnu/test/debuginfo/function-call.gdb/function-call.debugger.script:11: Error in sourced command file: Couldn't write extended state status: Bad address. ``` on linux CI jobs under various environments and hosts/targets. I tried running this under GDB 15.1 locally (WSL, `x86_64-unknown-linux-gnu`) but could not reproduce. CI typically uses GDB 12.1 with Ubuntu 22.04. So gate this test with min GDB 15.1 which prevents it from running in CI to workaround the flakiness but still allow running it locally under GDB >= 15.1. It's *possible* there's a genuine problem here. --- tests/debuginfo/function-call.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/debuginfo/function-call.rs b/tests/debuginfo/function-call.rs index 37eda165d99ca..104b43012a83e 100644 --- a/tests/debuginfo/function-call.rs +++ b/tests/debuginfo/function-call.rs @@ -1,5 +1,9 @@ // This test does not passed with gdb < 8.0. See #53497. -//@ min-gdb-version: 10.1 +// +// This test seems to have become very flaky with "Couldn't write extended state status: Bad +// address." since around June 2026, where CI typically uses GDB 12.1 on Ubuntu 22.04. I tried +// running this locally with GDB 15.1 and could not reproduce the flakiness. See #159073. +//@ min-gdb-version: 15.1 //@ compile-flags:-g //@ ignore-backends: gcc From 05bebcde5cb1db1f65de4c1ddf77bf2db980608a Mon Sep 17 00:00:00 2001 From: rustbot <47979223+rustbot@users.noreply.github.com> Date: Thu, 16 Jul 2026 18:18:53 +0200 Subject: [PATCH 13/26] Update books --- src/doc/book | 2 +- src/doc/edition-guide | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/doc/book b/src/doc/book index dd7ab4f4f4541..917544888a55e 160000 --- a/src/doc/book +++ b/src/doc/book @@ -1 +1 @@ -Subproject commit dd7ab4f4f4541adf4aa2a872cdac06c206b73288 +Subproject commit 917544888a55e4da7109bdba8c88c893c0da70f4 diff --git a/src/doc/edition-guide b/src/doc/edition-guide index 53686db907c45..5a14f7276ebc0 160000 --- a/src/doc/edition-guide +++ b/src/doc/edition-guide @@ -1 +1 @@ -Subproject commit 53686db907c45268d1b323afd9a3545a37abbced +Subproject commit 5a14f7276ebc0bd100974f02b918f30472c782fc From cbd88d9f5b3896f5350a5236e92b711f43f6b05c Mon Sep 17 00:00:00 2001 From: Yilin Chen <1479826151@qq.com> Date: Fri, 17 Jul 2026 01:01:18 +0800 Subject: [PATCH 14/26] Fix safety doc in intrinsics::simd --- library/core/src/intrinsics/simd/mod.rs | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/library/core/src/intrinsics/simd/mod.rs b/library/core/src/intrinsics/simd/mod.rs index 9311dcc9bd00e..09eda851bc3d8 100644 --- a/library/core/src/intrinsics/simd/mod.rs +++ b/library/core/src/intrinsics/simd/mod.rs @@ -109,13 +109,13 @@ pub const unsafe fn simd_rem(lhs: T, rhs: T) -> T; /// Shifts vector left elementwise, with UB on overflow. /// -/// Shifts `lhs` left by `rhs`, shifting in sign bits for signed types. +/// Shifts `lhs` left by `rhs`, shifting in zeros. /// /// `T` must be a vector of integers. /// /// # Safety /// -/// Each element of `rhs` must be less than `::BITS`. +/// Each element of `rhs` must be in `0..::BITS`. #[rustc_intrinsic] #[rustc_nounwind] pub const unsafe fn simd_shl(lhs: T, rhs: T) -> T; @@ -128,7 +128,7 @@ pub const unsafe fn simd_shl(lhs: T, rhs: T) -> T; /// /// # Safety /// -/// Each element of `rhs` must be less than `::BITS`. +/// Each element of `rhs` must be in `0..::BITS`. #[rustc_intrinsic] #[rustc_nounwind] pub const unsafe fn simd_shr(lhs: T, rhs: T) -> T; @@ -145,7 +145,7 @@ pub const unsafe fn simd_shr(lhs: T, rhs: T) -> T; /// /// # Safety /// -/// Each element of `shift` must be less than `::BITS`. +/// Each element of `shift` must be in `0..::BITS`. #[rustc_intrinsic] #[rustc_nounwind] pub const unsafe fn simd_funnel_shl(a: T, b: T, shift: T) -> T; @@ -162,7 +162,7 @@ pub const unsafe fn simd_funnel_shl(a: T, b: T, shift: T) -> T; /// /// # Safety /// -/// Each element of `shift` must be less than `::BITS`. +/// Each element of `shift` must be in `0..::BITS`. #[rustc_intrinsic] #[rustc_nounwind] pub const unsafe fn simd_funnel_shr(a: T, b: T, shift: T) -> T; @@ -433,6 +433,9 @@ pub enum SimdAlign { /// # Safety /// `ptr` must be aligned according to the `ALIGN` parameter, see [`SimdAlign`] for details. /// +/// Each pointer offset from `ptr` whose corresponding value in `mask` is `!0` must be readable as if +/// by [`ptr::read`][crate::ptr::read]. +/// /// `mask` must only contain `0` or `!0` values. #[rustc_intrinsic] #[rustc_nounwind] @@ -455,6 +458,9 @@ pub const unsafe fn simd_masked_load(mask: V, p /// # Safety /// `ptr` must be aligned according to the `ALIGN` parameter, see [`SimdAlign`] for details. /// +/// Each pointer offset from `ptr` whose corresponding value in `mask` is `!0` must be writable as if +/// by [`ptr::write`][crate::ptr::write]. +/// /// `mask` must only contain `0` or `!0` values. #[rustc_intrinsic] #[rustc_nounwind] From 14713e5af87efe93a2bd4e21a9ca084ccbe49576 Mon Sep 17 00:00:00 2001 From: Daniel Paoliello Date: Thu, 16 Jul 2026 10:47:53 -0700 Subject: [PATCH 15/26] [aarch64][win] Pass oversized c-variadic args indirectly on Arm64EC On Arm64EC the variadic portion of a c-variadic call must follow the MS x64 ABI: any argument that does not fit in 8 bytes, or whose size is not 1, 2, 4, or 8 bytes, is passed by reference. rustc's `va_arg` implementation already reads such arguments indirectly, but the caller side in `aarch64::compute_abi_info` passed a scalar `i128` by value (it is not an aggregate, so it bypassed `classify_arg`). The callee then dereferenced the inline value as a pointer, causing an access violation (0xC0000005) at runtime. Mark variadic-tail arguments larger than 8 bytes, or whose size is not a power of two, as indirect on Arm64EC so the caller and the `va_arg` reader agree. Fixed arguments are unaffected: a fixed `i128` is still passed by value, matching MSVC and Clang. Add a codegen test verifying that a variadic `i128` is passed as a pointer while a fixed `i128` is passed by value. --- compiler/rustc_target/src/callconv/aarch64.rs | 20 +++++++++- tests/codegen-llvm/arm64ec-c-variadic-i128.rs | 39 +++++++++++++++++++ 2 files changed, 57 insertions(+), 2 deletions(-) create mode 100644 tests/codegen-llvm/arm64ec-c-variadic-i128.rs diff --git a/compiler/rustc_target/src/callconv/aarch64.rs b/compiler/rustc_target/src/callconv/aarch64.rs index ec2c30756ddc0..0162aa838cb6b 100644 --- a/compiler/rustc_target/src/callconv/aarch64.rs +++ b/compiler/rustc_target/src/callconv/aarch64.rs @@ -3,7 +3,7 @@ use std::iter; use rustc_abi::{BackendRepr, HasDataLayout, Primitive, TyAbiInterface}; use crate::callconv::{ArgAbi, FnAbi, Reg, RegKind, Uniform}; -use crate::spec::{HasTargetSpec, RustcAbi, Target}; +use crate::spec::{Arch, HasTargetSpec, RustcAbi, Target}; /// Indicates the variant of the AArch64 ABI we are compiling for. /// Used to accommodate Apple and Microsoft's deviations from the usual AAPCS ABI. @@ -166,10 +166,26 @@ where classify_ret(cx, &mut fn_abi.ret, kind); } - for arg in fn_abi.args.iter_mut() { + // On Arm64EC the variadic portion of a c-variadic call follows the MS x64 ABI: + // "Any argument that doesn't fit in 8 bytes, or is not 1, 2, 4, or 8 bytes, must + // be passed by reference". + let c_variadic = fn_abi.c_variadic; + let fixed_count = fn_abi.fixed_count as usize; + let is_arm64ec = cx.target_spec().arch == Arch::Arm64EC; + + for (idx, arg) in fn_abi.args.iter_mut().enumerate() { if arg.is_ignore() { continue; } + + if is_arm64ec && c_variadic && idx >= fixed_count { + let size = arg.layout.size.bytes(); + if size > 8 || !size.is_power_of_two() { + arg.make_indirect(); + continue; + } + } + classify_arg(cx, arg, kind); } } diff --git a/tests/codegen-llvm/arm64ec-c-variadic-i128.rs b/tests/codegen-llvm/arm64ec-c-variadic-i128.rs new file mode 100644 index 0000000000000..b98463469eee3 --- /dev/null +++ b/tests/codegen-llvm/arm64ec-c-variadic-i128.rs @@ -0,0 +1,39 @@ +//! Verify the arm64ec calling convention for `i128` passed through the variadic +//! portion of a C-variadic call. +//! +//! On arm64ec the variadic tail follows the MS x64 ABI: any argument that does not +//! fit in 8 bytes, or is not 1/2/4/8 bytes, is passed by reference. So a variadic +//! `i128`/`u128` is passed indirectly (as a pointer), which must stay in sync with +//! how `va_arg` reads it back. A *fixed* `i128` argument is still passed by value. + +//@ add-minicore +//@ compile-flags: -Copt-level=3 --target arm64ec-pc-windows-msvc +//@ needs-llvm-components: aarch64 + +#![crate_type = "lib"] +#![no_std] +#![no_core] +#![feature(no_core)] + +extern crate minicore; + +extern "C" { + fn variadic(fixed: u32, ...); + fn fixed(arg: i128); +} + +// A variadic `i128` argument is passed by reference (as a pointer). +#[no_mangle] +pub unsafe extern "C" fn pass_variadic_i128(x: i128) { + // CHECK-LABEL: @pass_variadic_i128( + // CHECK: call void (i32, ...) @variadic(i32 {{.*}}, ptr {{.*}}) + variadic(0, x); +} + +// A fixed `i128` argument is still passed by value. +#[no_mangle] +pub unsafe extern "C" fn pass_fixed_i128(x: i128) { + // CHECK-LABEL: @pass_fixed_i128( + // CHECK: call void @fixed(i128 + fixed(x); +} From 5245634306972ba8dd02f56a7e78f882c464194c Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Thu, 16 Jul 2026 15:38:47 +0200 Subject: [PATCH 16/26] add a fallback for `fmuladdf*` --- library/core/src/intrinsics/mod.rs | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index 58c68408e6a80..c5da5055e16d9 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -1432,7 +1432,7 @@ pub fn log2f128(x: f128) -> f128 { libm::maybe_available::log2f128(x) } -/// Returns `a * b + c` for `f16` values. +/// Returns `a * b + c` without rounding the intermediate result for `f16` values. /// /// The stabilized version of this intrinsic is /// [`f16::mul_add`](../../std/primitive.f16.html#method.mul_add) @@ -1440,7 +1440,7 @@ pub fn log2f128(x: f128) -> f128 { #[rustc_intrinsic] #[rustc_nounwind] pub const fn fmaf16(a: f16, b: f16, c: f16) -> f16; -/// Returns `a * b + c` for `f32` values. +/// Returns `a * b + c` without rounding the intermediate result for `f32` values. /// /// The stabilized version of this intrinsic is /// [`f32::mul_add`](../../std/primitive.f32.html#method.mul_add) @@ -1448,7 +1448,7 @@ pub const fn fmaf16(a: f16, b: f16, c: f16) -> f16; #[rustc_intrinsic] #[rustc_nounwind] pub const fn fmaf32(a: f32, b: f32, c: f32) -> f32; -/// Returns `a * b + c` for `f64` values. +/// Returns `a * b + c` without rounding the intermediate result for `f64` values. /// /// The stabilized version of this intrinsic is /// [`f64::mul_add`](../../std/primitive.f64.html#method.mul_add) @@ -1456,7 +1456,7 @@ pub const fn fmaf32(a: f32, b: f32, c: f32) -> f32; #[rustc_intrinsic] #[rustc_nounwind] pub const fn fmaf64(a: f64, b: f64, c: f64) -> f64; -/// Returns `a * b + c` for `f128` values. +/// Returns `a * b + c` without rounding the intermediate result for `f128` values. /// /// The stabilized version of this intrinsic is /// [`f128::mul_add`](../../std/primitive.f128.html#method.mul_add) @@ -1475,9 +1475,12 @@ pub const fn fmaf128(a: f128, b: f128, c: f128) -> f128; /// and add instructions. It is unspecified whether or not a fused operation /// is selected, and that may depend on optimization level and context, for /// example. +#[inline] #[rustc_intrinsic] #[rustc_nounwind] -pub const fn fmuladdf16(a: f16, b: f16, c: f16) -> f16; +pub const fn fmuladdf16(a: f16, b: f16, c: f16) -> f16 { + a * b + c +} /// Returns `a * b + c` for `f32` values, non-deterministically executing /// either a fused multiply-add or two operations with rounding of the /// intermediate result. @@ -1488,9 +1491,12 @@ pub const fn fmuladdf16(a: f16, b: f16, c: f16) -> f16; /// and add instructions. It is unspecified whether or not a fused operation /// is selected, and that may depend on optimization level and context, for /// example. +#[inline] #[rustc_intrinsic] #[rustc_nounwind] -pub const fn fmuladdf32(a: f32, b: f32, c: f32) -> f32; +pub const fn fmuladdf32(a: f32, b: f32, c: f32) -> f32 { + a * b + c +} /// Returns `a * b + c` for `f64` values, non-deterministically executing /// either a fused multiply-add or two operations with rounding of the /// intermediate result. @@ -1501,9 +1507,12 @@ pub const fn fmuladdf32(a: f32, b: f32, c: f32) -> f32; /// and add instructions. It is unspecified whether or not a fused operation /// is selected, and that may depend on optimization level and context, for /// example. +#[inline] #[rustc_intrinsic] #[rustc_nounwind] -pub const fn fmuladdf64(a: f64, b: f64, c: f64) -> f64; +pub const fn fmuladdf64(a: f64, b: f64, c: f64) -> f64 { + a * b + c +} /// Returns `a * b + c` for `f128` values, non-deterministically executing /// either a fused multiply-add or two operations with rounding of the /// intermediate result. @@ -1514,9 +1523,12 @@ pub const fn fmuladdf64(a: f64, b: f64, c: f64) -> f64; /// and add instructions. It is unspecified whether or not a fused operation /// is selected, and that may depend on optimization level and context, for /// example. +#[inline] #[rustc_intrinsic] #[rustc_nounwind] -pub const fn fmuladdf128(a: f128, b: f128, c: f128) -> f128; +pub const fn fmuladdf128(a: f128, b: f128, c: f128) -> f128 { + a * b + c +} /// Returns the largest integer less than or equal to an `f16`. /// From 71321f5593f47330d4b14bad1047e468acdd0196 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Thu, 16 Jul 2026 20:01:51 +0200 Subject: [PATCH 17/26] Manually implement Clone for GrowableBitSet Forwards `clone_from` to `DenseBitSet`'s manual implementation. I don't believe this is currently used but I think it doesn't hurt, since it could be a small performance footgun. --- compiler/rustc_index/src/bit_set.rs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_index/src/bit_set.rs b/compiler/rustc_index/src/bit_set.rs index 2910ba7c46851..797e929c304ce 100644 --- a/compiler/rustc_index/src/bit_set.rs +++ b/compiler/rustc_index/src/bit_set.rs @@ -1287,11 +1287,22 @@ impl<'a, T: Idx> Iterator for MixedBitIter<'a, T> { /// /// All operations that involve an element will panic if the element is equal /// to or greater than the domain size. -#[derive(Clone, Debug, PartialEq)] +#[derive(Debug, PartialEq)] pub struct GrowableBitSet { bit_set: DenseBitSet, } +// Manually implemented to forward `clone_from`, and to avoid the `T: Clone` bound. +impl Clone for GrowableBitSet { + fn clone(&self) -> Self { + Self { bit_set: self.bit_set.clone() } + } + + fn clone_from(&mut self, source: &Self) { + self.bit_set.clone_from(&source.bit_set); + } +} + impl Default for GrowableBitSet { fn default() -> Self { GrowableBitSet::new_empty() From 403637b8d3eb65f957ef96ece92074238f6ce1b0 Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Thu, 16 Jul 2026 14:04:53 -0700 Subject: [PATCH 18/26] rustdoc: remove old `--emit` types We deprecated these awhile ago, and now all the usages are changed. --- src/librustdoc/config.rs | 3 --- src/librustdoc/html/render/write_shared.rs | 2 +- tests/run-make/emit-shared-files/rmake.rs | 2 +- tests/run-make/rustdoc-scrape-examples-dep-info/rmake.rs | 2 +- 4 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/librustdoc/config.rs b/src/librustdoc/config.rs index a8c27c5615007..27e3c49c36ef9 100644 --- a/src/librustdoc/config.rs +++ b/src/librustdoc/config.rs @@ -336,9 +336,6 @@ impl FromStr for EmitType { fn from_str(s: &str) -> Result { match s { - // old nightly-only choices that are going away soon - "toolchain-shared-resources" => Ok(Self::HtmlStaticFiles), - "invocation-specific" => Ok(Self::HtmlNonStaticFiles), // modern choices "html-static-files" => Ok(Self::HtmlStaticFiles), "html-non-static-files" => Ok(Self::HtmlNonStaticFiles), diff --git a/src/librustdoc/html/render/write_shared.rs b/src/librustdoc/html/render/write_shared.rs index ad8c6588e521f..f98e42057fbef 100644 --- a/src/librustdoc/html/render/write_shared.rs +++ b/src/librustdoc/html/render/write_shared.rs @@ -11,7 +11,7 @@ //! contents, so they do not include a hash in their filename and are not safe to //! cache with `Cache-Control: immutable`. They include the contents of the //! --resource-suffix flag and are emitted when --emit-type is empty (default) -//! or contains "invocation-specific". +//! or contains "html-non-static-files". use std::cell::RefCell; use std::ffi::{OsStr, OsString}; diff --git a/tests/run-make/emit-shared-files/rmake.rs b/tests/run-make/emit-shared-files/rmake.rs index 9841dce27fa2f..326aaa54bbc03 100644 --- a/tests/run-make/emit-shared-files/rmake.rs +++ b/tests/run-make/emit-shared-files/rmake.rs @@ -12,7 +12,7 @@ use run_make_support::{has_extension, has_prefix, path, rustdoc, shallow_find_fi fn main() { rustdoc() .arg("-Zunstable-options") - .arg("--emit=invocation-specific") + .arg("--emit=html-non-static-files") .out_dir("invocation-only") .arg("--resource-suffix=-xxx") .args(&["--theme", "y.css"]) diff --git a/tests/run-make/rustdoc-scrape-examples-dep-info/rmake.rs b/tests/run-make/rustdoc-scrape-examples-dep-info/rmake.rs index 5a612fd130052..13c62906f1687 100644 --- a/tests/run-make/rustdoc-scrape-examples-dep-info/rmake.rs +++ b/tests/run-make/rustdoc-scrape-examples-dep-info/rmake.rs @@ -9,7 +9,7 @@ fn main() { scrape::scrape( &["--scrape-tests", "--emit=dep-info"], - &["--emit=dep-info,invocation-specific"], + &["--emit=dep-info,html-non-static-files"], ); let content = rfs::read_to_string("rustdoc/foobar.d").replace(r"\", "/"); From 07069b3a38463a00e74f82b266ad977fe49fef95 Mon Sep 17 00:00:00 2001 From: cezarbbb Date: Mon, 6 Jul 2026 15:18:28 +0800 Subject: [PATCH 19/26] Fix ICE in `write_interface` when the interface file can't be written --- compiler/rustc_interface/src/diagnostics.rs | 2 +- compiler/rustc_interface/src/passes.rs | 8 +++++--- compiler/rustc_middle/src/error.rs | 2 +- .../write-interface-nonexistent-dir/libr.rs | 13 +++++++++++++ .../write-interface-nonexistent-dir/rmake.rs | 18 ++++++++++++++++++ 5 files changed, 38 insertions(+), 5 deletions(-) create mode 100644 tests/run-make/export/write-interface-nonexistent-dir/libr.rs create mode 100644 tests/run-make/export/write-interface-nonexistent-dir/rmake.rs diff --git a/compiler/rustc_interface/src/diagnostics.rs b/compiler/rustc_interface/src/diagnostics.rs index 7cae1aa54d2e7..44d6073b93037 100644 --- a/compiler/rustc_interface/src/diagnostics.rs +++ b/compiler/rustc_interface/src/diagnostics.rs @@ -84,7 +84,7 @@ pub(crate) struct TempsDirError; pub(crate) struct OutDirError; #[derive(Diagnostic)] -#[diag("failed to write file {$path}: {$error}\"")] +#[diag("failed to write file {$path}: {$error}")] pub(crate) struct FailedWritingFile<'a> { pub path: &'a Path, pub error: io::Error, diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index ca6e48cb67fe5..50fa712a909c4 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -881,9 +881,11 @@ pub fn write_interface<'tcx>(tcx: TyCtxt<'tcx>) { &tcx.sess.psess.attr_id_generator, ); let export_output = tcx.output_filenames(()).interface_path(); - let mut file = fs::File::create_buffered(export_output).unwrap(); - if let Err(err) = write!(file, "{}", krate) { - tcx.dcx().fatal(format!("error writing interface file: {}", err)); + let mut file = fs::File::create_buffered(&export_output).unwrap_or_else(|error| { + tcx.dcx().emit_fatal(diagnostics::FailedWritingFile { path: &export_output, error }) + }); + if let Err(error) = write!(file, "{}", krate) { + tcx.dcx().emit_fatal(diagnostics::FailedWritingFile { path: &export_output, error }); } } diff --git a/compiler/rustc_middle/src/error.rs b/compiler/rustc_middle/src/error.rs index 2823b7ba4e22e..e0ce5b8ccc843 100644 --- a/compiler/rustc_middle/src/error.rs +++ b/compiler/rustc_middle/src/error.rs @@ -18,7 +18,7 @@ pub(crate) struct DropCheckOverflow<'tcx> { } #[derive(Diagnostic)] -#[diag("failed to write file {$path}: {$error}\"")] +#[diag("failed to write file {$path}: {$error}")] pub(crate) struct FailedWritingFile<'a> { pub path: &'a Path, pub error: io::Error, diff --git a/tests/run-make/export/write-interface-nonexistent-dir/libr.rs b/tests/run-make/export/write-interface-nonexistent-dir/libr.rs new file mode 100644 index 0000000000000..c6aeabf3228f0 --- /dev/null +++ b/tests/run-make/export/write-interface-nonexistent-dir/libr.rs @@ -0,0 +1,13 @@ +#![feature(export_stable)] +#![crate_type = "sdylib"] + +pub mod m { + #[repr(C)] + pub struct S { + pub x: i32, + } + + pub extern "C" fn foo1(x: S) -> i32 { + x.x + } +} diff --git a/tests/run-make/export/write-interface-nonexistent-dir/rmake.rs b/tests/run-make/export/write-interface-nonexistent-dir/rmake.rs new file mode 100644 index 0000000000000..636a8ab7ed9b4 --- /dev/null +++ b/tests/run-make/export/write-interface-nonexistent-dir/rmake.rs @@ -0,0 +1,18 @@ +// Regression test for . + +//@ ignore-cross-compile + +// NOTE: `sdylib`'s platform support is basically that of `dylib`. +//@ needs-crate-type: dylib + +use run_make_support::rustc; + +fn main() { + rustc() + .input("libr.rs") + .output("does-not-exist/output") + .run_fail() + .assert_exit_code(1) + .assert_stderr_contains("failed to write file") + .assert_not_ice(); +} From 50fd1f18d1d0d6cb3b0d041aa6e3f5d4b3684afe Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Thu, 16 Jul 2026 18:49:50 -0700 Subject: [PATCH 20/26] rustdoc: rename the doc parts metadata params --- src/doc/rustdoc/src/unstable-features.md | 29 +++-- src/librustdoc/config.rs | 104 +++++++++++------- src/librustdoc/lib.rs | 35 ++++-- .../output-default.stdout | 26 ++--- .../rustdoc-merge-directory-alias/rmake.rs | 28 ++--- .../run-make/rustdoc-merge-directory/rmake.rs | 13 +-- .../rustdoc-merge-no-input-finalize/rmake.rs | 8 +- .../auxiliary/quebec.rs | 1 - .../auxiliary/tango.rs | 1 - .../cargo-transitive-read-write/sierra.rs | 3 +- .../auxiliary/quebec.rs | 3 +- .../auxiliary/romeo.rs | 3 +- .../auxiliary/sierra.rs | 3 +- .../auxiliary/tango.rs | 3 +- .../kitchen-sink-separate-dirs/indigo.rs | 9 +- .../no-merge-separate/auxiliary/quebec.rs | 2 +- .../no-merge-separate/auxiliary/tango.rs | 2 +- .../no-merge-separate/sierra.rs | 4 +- .../no-merge-write-anyway/auxiliary/quebec.rs | 3 +- .../no-merge-write-anyway/auxiliary/tango.rs | 3 +- .../no-merge-write-anyway/sierra.rs | 5 +- .../overwrite-but-include/auxiliary/quebec.rs | 3 +- .../overwrite-but-include/auxiliary/tango.rs | 3 +- .../overwrite-but-include/sierra.rs | 3 +- .../auxiliary/quebec.rs | 3 +- .../overwrite-but-separate/auxiliary/tango.rs | 3 +- .../overwrite-but-separate/sierra.rs | 5 +- .../overwrite/auxiliary/quebec.rs | 2 +- .../overwrite/auxiliary/tango.rs | 2 +- .../overwrite/sierra.rs | 3 +- .../single-crate-finalize/quebec.rs | 1 - .../single-crate-read-write/quebec.rs | 1 - .../single-crate-write-anyway/quebec.rs | 4 +- .../single-merge-none-useless-write/quebec.rs | 11 -- .../transitive-finalize/auxiliary/quebec.rs | 5 - .../transitive-finalize/auxiliary/tango.rs | 8 -- .../transitive-finalize/sierra.rs | 20 ---- .../transitive-merge-none/auxiliary/quebec.rs | 3 +- .../transitive-merge-none/auxiliary/tango.rs | 3 +- .../transitive-merge-none/sierra.rs | 7 +- .../auxiliary/quebec.rs | 1 - .../auxiliary/tango.rs | 1 - .../transitive-merge-read-write/sierra.rs | 3 +- .../transitive-no-info/auxiliary/quebec.rs | 2 +- .../transitive-no-info/auxiliary/tango.rs | 2 +- .../transitive-no-info/sierra.rs | 5 +- .../two-separate-out-dir/auxiliary/foxtrot.rs | 2 +- .../two-separate-out-dir/echo.rs | 2 +- tests/rustdoc-js/auxiliary/merged-dep.rs | 3 +- tests/rustdoc-js/merged-doc.rs | 3 +- 50 files changed, 179 insertions(+), 223 deletions(-) delete mode 100644 tests/rustdoc-html/merge-cross-crate-info/single-merge-none-useless-write/quebec.rs delete mode 100644 tests/rustdoc-html/merge-cross-crate-info/transitive-finalize/auxiliary/quebec.rs delete mode 100644 tests/rustdoc-html/merge-cross-crate-info/transitive-finalize/auxiliary/tango.rs delete mode 100644 tests/rustdoc-html/merge-cross-crate-info/transitive-finalize/sierra.rs diff --git a/src/doc/rustdoc/src/unstable-features.md b/src/doc/rustdoc/src/unstable-features.md index cc6cf2f8a648c..26985e67abb6e 100644 --- a/src/doc/rustdoc/src/unstable-features.md +++ b/src/doc/rustdoc/src/unstable-features.md @@ -197,15 +197,26 @@ themselves marked as unstable. To use any of these options, pass `-Z unstable-op the flag in question to Rustdoc on the command-line. To do this from Cargo, you can either use the `RUSTDOCFLAGS` environment variable or the `cargo rustdoc` command. -### `--merge`, `--parts-out-dir`, and `--include-parts-dir` +### `--write-doc-meta-dir`, and `--read-doc-meta-dir` These options control how rustdoc handles files that combine data from multiple crates. -By default, they act like `--merge=shared` is set, and `--parts-out-dir` and `--include-parts-dir` -are turned off. The `--merge=shared` mode causes rustdoc to load the existing data in the out-dir, -combine the new crate data into it, and write the result. This is very easy to use in scripts that -manually invoke rustdoc, but it's also slow, because it performs O(crates) work on -every crate, meaning it performs O(crates2) work. +By default, rustdoc will read the doc meta from the doc output dir itself and merge them together. +This is very easy to use in scripts that manually invoke rustdoc, but it's also slow, because it +performs O(crates) work on every crate, meaning it performs O(crates2) work. When +`--write-doc-meta-dir` and/or `--read-doc-meta-dir` are supplied, this is turned off. + +When `--write-doc-meta-dir` is supplied, rustdoc will write the crate's metadata to that directory. +If this parameter is supplied but `--read-doc-meta-dir` isn't, it runs in *intermediate mode*: +some pages may be written to the output dir, but there is a lot of functionality that won't work +until rustdoc is run in *finalize mode*. + +When `--read-doc-meta-dir` is supplied, rustdoc runs in *finalize mode*. It will read the data from +the supplied directory, and will write it to the doc output directory in the form that the web +frontend will use. + +If both `--write-doc-meta-dir` and `--read-doc-meta-dir` are specified, the crate metadata will be +written to both the HTML `--out-dir` and to the supplied `--write-doc-meta-dir`. ```console $ rustdoc crate1.rs --out-dir=doc @@ -217,13 +228,13 @@ rd_("fcrate1fcrate2") ``` To delay shared-data merging until the end of a build, so that you only have to perform O(crates) -work, use `--merge=none` on every crate except the last one, which will use `--merge=finalize`. +work, use `--write-doc-meta-dir` on every crate, and the last will use `--read-doc-meta-dir`. ```console -$ rustdoc +nightly crate1.rs --merge=none --parts-out-dir=crate1.d -Zunstable-options +$ rustdoc +nightly crate1.rs --write-doc-meta=crate1.d -Zunstable-options $ cat doc/search.index/crateNames/* cat: 'doc/search.index/crateNames/*': No such file or directory -$ rustdoc +nightly crate2.rs --merge=finalize --include-parts-dir=crate1.d -Zunstable-options +$ rustdoc +nightly crate2.rs --read-doc-meta=crate1.d -Zunstable-options $ cat doc/search.index/crateNames/* rd_("fcrate1fcrate2") ``` diff --git a/src/librustdoc/config.rs b/src/librustdoc/config.rs index a8c27c5615007..87d2bdcd00d29 100644 --- a/src/librustdoc/config.rs +++ b/src/librustdoc/config.rs @@ -601,34 +601,66 @@ impl Options { let (lint_opts, describe_lints, lint_cap) = get_cmd_lint_options(early_dcx, matches); - let input = if describe_lints { - InputMode::HasFile(make_input(early_dcx, "")) - } else { - match matches.free.as_slice() { - [] if matches.opt_str("merge").as_deref() == Some("finalize") => { - InputMode::NoInputMergeFinalize - } - [] => dcx.fatal("missing file operand"), - [input] => InputMode::HasFile(make_input(early_dcx, input)), - _ => dcx.fatal("too many file operands"), - } - }; - let externs = parse_externs(early_dcx, matches, &unstable_opts); let extern_html_root_urls = match parse_extern_html_roots(matches) { Ok(ex) => ex, Err(err) => dcx.fatal(err), }; - let parts_out_dir = - match matches.opt_str("parts-out-dir").map(PathToParts::from_flag).transpose() { + let mut parts_out_dir = + match matches.opt_str("write-doc-meta-dir").map(PathToParts::from_flag).transpose() { Ok(parts_out_dir) => parts_out_dir, Err(e) => dcx.fatal(e), }; - let include_parts_dir = match parse_include_parts_dir(matches) { + let mut include_parts_dir = match parse_read_doc_meta(matches, "read-doc-meta-dir") { Ok(include_parts_dir) => include_parts_dir, Err(e) => dcx.fatal(e), }; + let mut should_merge = compute_should_merge(matches); + if parts_out_dir.is_none() && include_parts_dir.is_empty() { + // we'll need to get rid of this stuff once Cargo stops using them + parts_out_dir = + match matches.opt_str("parts-out-dir").map(PathToParts::from_flag).transpose() { + Ok(parts_out_dir) => parts_out_dir, + Err(e) => dcx.fatal(e), + }; + include_parts_dir = match parse_read_doc_meta(matches, "include-parts-dir") { + Ok(include_parts_dir) => include_parts_dir, + Err(e) => dcx.fatal(e), + }; + should_merge = match matches.opt_str("merge").as_deref() { + None => ShouldMerge { read_rendered_cci: true, write_rendered_cci: true }, + Some("none") => ShouldMerge { read_rendered_cci: false, write_rendered_cci: false }, + Some("shared") => ShouldMerge { read_rendered_cci: true, write_rendered_cci: true }, + Some("finalize") => { + ShouldMerge { read_rendered_cci: false, write_rendered_cci: true } + } + Some(_) => dcx.fatal("argument to --merge must be `none`, `shared`, or `finalize`"), + }; + } else if matches.opt_str("parts-out-dir").is_some() { + dcx.fatal( + "deprecated version of write-doc-meta-dir is used with new doc-meta-dir stuff", + ); + } else if matches.opt_str("include-parts-dir").is_some() { + dcx.fatal( + "deprecated version of read-doc-meta-dir is used with new doc-meta-dir stuff", + ); + } else if matches.opt_str("merge").is_some() { + dcx.fatal("deprecated parameter merge is used with new doc-meta-dir stuff"); + } + + let input = if describe_lints { + InputMode::HasFile(make_input(early_dcx, "")) + } else { + match matches.free.as_slice() { + [] if !include_parts_dir.is_empty() && should_merge.write_rendered_cci => { + InputMode::NoInputMergeFinalize + } + [] => dcx.fatal("missing file operand"), + [input] => InputMode::HasFile(make_input(early_dcx, input)), + _ => dcx.fatal("too many file operands"), + } + }; let default_settings: Vec> = vec![ matches @@ -853,10 +885,6 @@ impl Options { let extern_html_root_takes_precedence = matches.opt_present("extern-html-root-takes-precedence"); let html_no_source = matches.opt_present("html-no-source"); - let should_merge = match parse_merge(matches) { - Ok(result) => result, - Err(e) => dcx.fatal(format!("--merge option error: {e}")), - }; let merge_doctests = parse_merge_doctests(matches, edition, dcx); tracing::debug!("merge_doctests: {merge_doctests:?}"); @@ -1051,7 +1079,7 @@ impl PathToParts { // check here is for diagnostics if path.exists() && !path.is_dir() { Err(format!( - "--parts-out-dir and --include-parts-dir expect directories, found: {}", + "--write-doc-meta-dir and --read-doc-meta-dir expect directories, found: {}", path.display(), )) } else { @@ -1061,15 +1089,15 @@ impl PathToParts { } } -/// Reports error if --include-parts-dir is not a directory -fn parse_include_parts_dir(m: &getopts::Matches) -> Result, String> { +/// Reports error if --read-doc-meta-dir is not a directory +fn parse_read_doc_meta(m: &getopts::Matches, name: &str) -> Result, String> { let mut ret = Vec::new(); - for p in m.opt_strs("include-parts-dir") { + for p in m.opt_strs(name) { let p = PathToParts::from_flag(p)?; // this is just for diagnostic if !p.0.is_dir() { return Err(format!( - "--include-parts-dir expected {} to be a directory", + "--read-doc-meta-dir expected {} to be a directory", p.0.display() )); } @@ -1089,23 +1117,15 @@ pub(crate) struct ShouldMerge { /// Extracts read_rendered_cci and write_rendered_cci from command line arguments, or /// reports an error if an invalid option was provided -fn parse_merge(m: &getopts::Matches) -> Result { - match m.opt_str("merge").as_deref() { - // default = read-write - None => Ok(ShouldMerge { read_rendered_cci: true, write_rendered_cci: true }), - Some("none") if m.opt_present("include-parts-dir") => { - Err("--include-parts-dir not allowed if --merge=none") - } - Some("none") => Ok(ShouldMerge { read_rendered_cci: false, write_rendered_cci: false }), - Some("shared") if m.opt_present("parts-out-dir") || m.opt_present("include-parts-dir") => { - Err("--parts-out-dir and --include-parts-dir not allowed if --merge=shared") - } - Some("shared") => Ok(ShouldMerge { read_rendered_cci: true, write_rendered_cci: true }), - Some("finalize") if m.opt_present("parts-out-dir") => { - Err("--parts-out-dir not allowed if --merge=finalize") - } - Some("finalize") => Ok(ShouldMerge { read_rendered_cci: false, write_rendered_cci: true }), - Some(_) => Err("argument to --merge must be `none`, `shared`, or `finalize`"), +fn compute_should_merge(m: &getopts::Matches) -> ShouldMerge { + match (m.opt_present("read-doc-meta-dir"), m.opt_present("write-doc-meta-dir")) { + // shared mode + (false, false) => ShouldMerge { read_rendered_cci: true, write_rendered_cci: true }, + // intermediate mode + (false, true) => ShouldMerge { read_rendered_cci: false, write_rendered_cci: false }, + // finalize mode + (true, false) => ShouldMerge { read_rendered_cci: false, write_rendered_cci: true }, + (true, true) => ShouldMerge { read_rendered_cci: false, write_rendered_cci: true }, } } diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index 17f5af024ac7f..be830cad6c735 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -613,28 +613,41 @@ fn opts() -> Vec { Unstable, Opt, "", - "merge", - "Controls how rustdoc handles files from previously documented crates in the doc root\n\ - none = Do not write cross-crate information to the --out-dir\n\ - shared = Append current crate's info to files found in the --out-dir\n\ - finalize = Write current crate's info and --include-parts-dir info to the --out-dir, overwriting conflicting files", - "none|shared|finalize", + "write-doc-meta-dir", + "Writes trait implementations and other info for the current crate to provided path", + "path/to/doc.meta", + ), + opt( + Unstable, + Multi, + "", + "read-doc-meta-dir", + "Includes trait implementations and other crate info from provided path", + "path/to/doc.meta", ), opt( Unstable, Opt, "", "parts-out-dir", - "Writes trait implementations and other info for the current crate to provided path. Only use with --merge=none", - "path/to/doc.parts/", + "Deprecated synonym of write-doc-meta-dir", + "path/to/doc.meta", ), opt( Unstable, Multi, "", "include-parts-dir", - "Includes trait implementations and other crate info from provided path. Only use with --merge=finalize", - "path/to/doc.parts/", + "Deprecated synonym of read-doc-meta-dir", + "path/to/doc.meta", + ), + opt( + Unstable, + Opt, + "", + "merge", + "Deprecated option to specify read/write-doc-meta-dir mode", + "none, shared, finalize", ), opt(Unstable, Flag, "", "html-no-source", "Disable HTML source code pages generation", ""), opt( @@ -758,7 +771,7 @@ fn run_renderer< /// Renders and writes cross-crate info files, like the search index. This function exists so that /// we can run rustdoc without a crate root in the `--merge=finalize` mode. Cross-crate info files -/// discovered via `--include-parts-dir` are combined and written to the doc root. +/// discovered via `--read-doc-meta-dir` are combined and written to the doc root. fn run_merge_finalize(opt: config::RenderOptions) -> Result<(), error::Error> { assert!( opt.should_merge.write_rendered_cci, diff --git a/tests/run-make/rustdoc-default-output/output-default.stdout b/tests/run-make/rustdoc-default-output/output-default.stdout index 0a2da1099b820..78dfbf03c1b10 100644 --- a/tests/run-make/rustdoc-default-output/output-default.stdout +++ b/tests/run-make/rustdoc-default-output/output-default.stdout @@ -176,23 +176,19 @@ Options: --scrape-tests Include test code when scraping examples --with-examples path to function call information (for displaying examples in the documentation) - --merge none|shared|finalize - Controls how rustdoc handles files from previously - documented crates in the doc root - none = Do not write cross-crate information to the - --out-dir - shared = Append current crate's info to files found in - the --out-dir - finalize = Write current crate's info and - --include-parts-dir info to the --out-dir, overwriting - conflicting files - --parts-out-dir path/to/doc.parts/ + --write-doc-meta-dir path/to/doc.meta Writes trait implementations and other info for the - current crate to provided path. Only use with - --merge=none - --include-parts-dir path/to/doc.parts/ + current crate to provided path + --read-doc-meta-dir path/to/doc.meta Includes trait implementations and other crate info - from provided path. Only use with --merge=finalize + from provided path + --parts-out-dir path/to/doc.meta + Deprecated synonym of write-doc-meta-dir + --include-parts-dir path/to/doc.meta + Deprecated synonym of read-doc-meta-dir + --merge none, shared, finalize + Deprecated option to specify read/write-doc-meta-dir + mode --html-no-source Disable HTML source code pages generation --doctest-build-arg ARG diff --git a/tests/run-make/rustdoc-merge-directory-alias/rmake.rs b/tests/run-make/rustdoc-merge-directory-alias/rmake.rs index 096eb4a487c15..8a5c1aa2af723 100644 --- a/tests/run-make/rustdoc-merge-directory-alias/rmake.rs +++ b/tests/run-make/rustdoc-merge-directory-alias/rmake.rs @@ -1,4 +1,4 @@ -// Running --merge=finalize without an input crate root should not trigger ICE. +// Running --read-doc-meta-dir without an input crate root should not trigger ICE. // Issue: https://github.com/rust-lang/rust/issues/146646 //@ needs-target-std @@ -14,16 +14,14 @@ fn main() { .input("dep1.rs") .out_dir(&out_dir) .arg("-Zunstable-options") - .arg(format!("--parts-out-dir={}", parts_out_dir.display())) - .arg("--merge=none") + .arg(format!("--write-doc-meta-dir={}", parts_out_dir.display())) .run(); assert!(parts_out_dir.join("dep1.json").exists()); let output = rustdoc() .arg("-Zunstable-options") .out_dir(&out_dir) - .arg(format!("--include-parts-dir={}", parts_out_dir.display())) - .arg("--merge=finalize") + .arg(format!("--read-doc-meta-dir={}", parts_out_dir.display())) .run(); output.assert_stderr_not_contains("error: the compiler unexpectedly panicked. this is a bug."); @@ -31,16 +29,14 @@ fn main() { .input("dep2.rs") .out_dir(&out_dir) .arg("-Zunstable-options") - .arg(format!("--parts-out-dir={}", parts_out_dir.display())) - .arg("--merge=none") + .arg(format!("--write-doc-meta-dir={}", parts_out_dir.display())) .run(); assert!(parts_out_dir.join("dep2.json").exists()); let output2 = rustdoc() .arg("-Zunstable-options") .out_dir(&out_dir) - .arg(format!("--include-parts-dir={}", parts_out_dir.display())) - .arg("--merge=finalize") + .arg(format!("--read-doc-meta-dir={}", parts_out_dir.display())) .run(); output2.assert_stderr_not_contains("error: the compiler unexpectedly panicked. this is a bug."); @@ -48,20 +44,18 @@ fn main() { .input("dep1.rs") .out_dir(&out_dir) .arg("-Zunstable-options") - .arg(format!("--parts-out-dir={}", parts_out_dir.display())) - .arg("--merge=none") + .arg(format!("--write-doc-meta-dir={}", parts_out_dir.display())) .run(); assert!(parts_out_dir.join("dep1.json").exists()); let output3 = rustdoc() .arg("-Zunstable-options") .out_dir(&out_dir) - .arg(format!("--include-parts-dir={}", parts_out_dir.display())) - .arg("--merge=finalize") + .arg(format!("--read-doc-meta-dir={}", parts_out_dir.display())) .run(); output3.assert_stderr_not_contains("error: the compiler unexpectedly panicked. this is a bug."); - // dep_missing is different, because --parts-out-dir is not supplied + // dep_missing is different, because --write-doc-meta-dir is not supplied rustdoc().input("dep_missing.rs").out_dir(&out_dir).run(); assert!(parts_out_dir.join("dep2.json").exists()); @@ -69,16 +63,14 @@ fn main() { .input("dep1.rs") .out_dir(&out_dir) .arg("-Zunstable-options") - .arg(format!("--parts-out-dir={}", parts_out_dir.display())) - .arg("--merge=none") + .arg(format!("--write-doc-meta-dir={}", parts_out_dir.display())) .run(); assert!(parts_out_dir.join("dep1.json").exists()); let output4 = rustdoc() .arg("-Zunstable-options") .out_dir(&out_dir) - .arg(format!("--include-parts-dir={}", parts_out_dir.display())) - .arg("--merge=finalize") + .arg(format!("--read-doc-meta-dir={}", parts_out_dir.display())) .run(); output4.assert_stderr_not_contains("error: the compiler unexpectedly panicked. this is a bug."); diff --git a/tests/run-make/rustdoc-merge-directory/rmake.rs b/tests/run-make/rustdoc-merge-directory/rmake.rs index e4695ddad0b48..d2d31138bf3c6 100644 --- a/tests/run-make/rustdoc-merge-directory/rmake.rs +++ b/tests/run-make/rustdoc-merge-directory/rmake.rs @@ -1,4 +1,4 @@ -// Running --merge=finalize without an input crate root should not trigger ICE. +// Running --read-doc-meta without an input crate root should not trigger ICE. // Issue: https://github.com/rust-lang/rust/issues/146646 //@ needs-target-std @@ -14,8 +14,7 @@ fn main() { .input("dep1.rs") .out_dir(&out_dir) .arg("-Zunstable-options") - .arg(format!("--parts-out-dir={}", parts_out_dir.display())) - .arg("--merge=none") + .arg(format!("--write-doc-meta-dir={}", parts_out_dir.display())) .run(); assert!(parts_out_dir.join("dep1.json").exists()); @@ -23,20 +22,18 @@ fn main() { .input("dep2.rs") .out_dir(&out_dir) .arg("-Zunstable-options") - .arg(format!("--parts-out-dir={}", parts_out_dir.display())) - .arg("--merge=none") + .arg(format!("--write-doc-meta-dir={}", parts_out_dir.display())) .run(); assert!(parts_out_dir.join("dep2.json").exists()); - // dep_missing is different, because --parts-out-dir is not supplied + // dep_missing is different, because --write-doc-meta-dir is not supplied rustdoc().input("dep_missing.rs").out_dir(&out_dir).run(); assert!(parts_out_dir.join("dep2.json").exists()); let output = rustdoc() .arg("-Zunstable-options") .out_dir(&out_dir) - .arg(format!("--include-parts-dir={}", parts_out_dir.display())) - .arg("--merge=finalize") + .arg(format!("--read-doc-meta-dir={}", parts_out_dir.display())) .run(); output.assert_stderr_not_contains("error: the compiler unexpectedly panicked. this is a bug."); diff --git a/tests/run-make/rustdoc-merge-no-input-finalize/rmake.rs b/tests/run-make/rustdoc-merge-no-input-finalize/rmake.rs index 4dad01f341fef..6f7028bfbfbc1 100644 --- a/tests/run-make/rustdoc-merge-no-input-finalize/rmake.rs +++ b/tests/run-make/rustdoc-merge-no-input-finalize/rmake.rs @@ -1,4 +1,4 @@ -// Running --merge=finalize without an input crate root should not trigger ICE. +// Running --write-doc-meta-dir without an input crate root should not trigger ICE. // Issue: https://github.com/rust-lang/rust/issues/146646 //@ needs-target-std @@ -13,16 +13,14 @@ fn main() { .input("sierra.rs") .out_dir(&out_dir) .arg("-Zunstable-options") - .arg(format!("--parts-out-dir={}", parts_out_dir.display())) - .arg("--merge=none") + .arg(format!("--write-doc-meta-dir={}", parts_out_dir.display())) .run(); assert!(parts_out_dir.join("sierra.json").exists()); let output = rustdoc() .arg("-Zunstable-options") .out_dir(&out_dir) - .arg(format!("--include-parts-dir={}", parts_out_dir.display())) - .arg("--merge=finalize") + .arg(format!("--read-doc-meta-dir={}", parts_out_dir.display())) .run(); output.assert_not_ice(); } diff --git a/tests/rustdoc-html/merge-cross-crate-info/cargo-transitive-read-write/auxiliary/quebec.rs b/tests/rustdoc-html/merge-cross-crate-info/cargo-transitive-read-write/auxiliary/quebec.rs index fdafb3b7ac341..c4b3ce498576c 100644 --- a/tests/rustdoc-html/merge-cross-crate-info/cargo-transitive-read-write/auxiliary/quebec.rs +++ b/tests/rustdoc-html/merge-cross-crate-info/cargo-transitive-read-write/auxiliary/quebec.rs @@ -1,4 +1,3 @@ -//@ doc-flags:--merge=shared //@ doc-flags:--enable-index-page //@ doc-flags:-Zunstable-options diff --git a/tests/rustdoc-html/merge-cross-crate-info/cargo-transitive-read-write/auxiliary/tango.rs b/tests/rustdoc-html/merge-cross-crate-info/cargo-transitive-read-write/auxiliary/tango.rs index ff12fe98d82c2..299a673418713 100644 --- a/tests/rustdoc-html/merge-cross-crate-info/cargo-transitive-read-write/auxiliary/tango.rs +++ b/tests/rustdoc-html/merge-cross-crate-info/cargo-transitive-read-write/auxiliary/tango.rs @@ -1,6 +1,5 @@ //@ aux-build:quebec.rs //@ build-aux-docs -//@ doc-flags:--merge=shared //@ doc-flags:--enable-index-page //@ doc-flags:-Zunstable-options diff --git a/tests/rustdoc-html/merge-cross-crate-info/cargo-transitive-read-write/sierra.rs b/tests/rustdoc-html/merge-cross-crate-info/cargo-transitive-read-write/sierra.rs index 26292c50d35e6..10f12ad8d1184 100644 --- a/tests/rustdoc-html/merge-cross-crate-info/cargo-transitive-read-write/sierra.rs +++ b/tests/rustdoc-html/merge-cross-crate-info/cargo-transitive-read-write/sierra.rs @@ -1,6 +1,5 @@ //@ aux-build:tango.rs //@ build-aux-docs -//@ doc-flags:--merge=shared //@ doc-flags:--enable-index-page //@ doc-flags:-Zunstable-options @@ -18,7 +17,7 @@ //@ hasraw search.index/name/*.js 'Sierra' //@ hasraw search.index/name/*.js 'Quebec' -// similar to cargo-workflow-transitive, but we use --merge=read-write, +// similar to cargo-workflow-transitive, but we use shared mode, // which is the default. extern crate tango; pub struct Sierra; diff --git a/tests/rustdoc-html/merge-cross-crate-info/kitchen-sink-separate-dirs/auxiliary/quebec.rs b/tests/rustdoc-html/merge-cross-crate-info/kitchen-sink-separate-dirs/auxiliary/quebec.rs index d10bc0316ccad..d2af9af496bc3 100644 --- a/tests/rustdoc-html/merge-cross-crate-info/kitchen-sink-separate-dirs/auxiliary/quebec.rs +++ b/tests/rustdoc-html/merge-cross-crate-info/kitchen-sink-separate-dirs/auxiliary/quebec.rs @@ -1,6 +1,5 @@ //@ unique-doc-out-dir -//@ doc-flags:--merge=none -//@ doc-flags:--parts-out-dir=info/doc.parts/quebec +//@ doc-flags:--write-doc-meta-dir=info/doc.parts/quebec //@ doc-flags:--enable-index-page //@ doc-flags:-Zunstable-options diff --git a/tests/rustdoc-html/merge-cross-crate-info/kitchen-sink-separate-dirs/auxiliary/romeo.rs b/tests/rustdoc-html/merge-cross-crate-info/kitchen-sink-separate-dirs/auxiliary/romeo.rs index 6d0c8651db53f..bcccc05299a79 100644 --- a/tests/rustdoc-html/merge-cross-crate-info/kitchen-sink-separate-dirs/auxiliary/romeo.rs +++ b/tests/rustdoc-html/merge-cross-crate-info/kitchen-sink-separate-dirs/auxiliary/romeo.rs @@ -1,8 +1,7 @@ //@ aux-build:sierra.rs //@ build-aux-docs //@ unique-doc-out-dir -//@ doc-flags:--merge=none -//@ doc-flags:--parts-out-dir=info/doc.parts/romeo +//@ doc-flags:--write-doc-meta-dir=info/doc.parts/romeo //@ doc-flags:--enable-index-page //@ doc-flags:-Zunstable-options diff --git a/tests/rustdoc-html/merge-cross-crate-info/kitchen-sink-separate-dirs/auxiliary/sierra.rs b/tests/rustdoc-html/merge-cross-crate-info/kitchen-sink-separate-dirs/auxiliary/sierra.rs index 10898f3886477..9d5e564720169 100644 --- a/tests/rustdoc-html/merge-cross-crate-info/kitchen-sink-separate-dirs/auxiliary/sierra.rs +++ b/tests/rustdoc-html/merge-cross-crate-info/kitchen-sink-separate-dirs/auxiliary/sierra.rs @@ -1,8 +1,7 @@ //@ aux-build:tango.rs //@ build-aux-docs //@ unique-doc-out-dir -//@ doc-flags:--merge=none -//@ doc-flags:--parts-out-dir=info/doc.parts/sierra +//@ doc-flags:--write-doc-meta-dir=info/doc.parts/sierra //@ doc-flags:--enable-index-page //@ doc-flags:-Zunstable-options diff --git a/tests/rustdoc-html/merge-cross-crate-info/kitchen-sink-separate-dirs/auxiliary/tango.rs b/tests/rustdoc-html/merge-cross-crate-info/kitchen-sink-separate-dirs/auxiliary/tango.rs index 3c3721ee6eb7d..577c486a3adfb 100644 --- a/tests/rustdoc-html/merge-cross-crate-info/kitchen-sink-separate-dirs/auxiliary/tango.rs +++ b/tests/rustdoc-html/merge-cross-crate-info/kitchen-sink-separate-dirs/auxiliary/tango.rs @@ -1,8 +1,7 @@ //@ aux-build:quebec.rs //@ build-aux-docs //@ unique-doc-out-dir -//@ doc-flags:--merge=none -//@ doc-flags:--parts-out-dir=info/doc.parts/tango +//@ doc-flags:--write-doc-meta-dir=info/doc.parts/tango //@ doc-flags:--enable-index-page //@ doc-flags:-Zunstable-options diff --git a/tests/rustdoc-html/merge-cross-crate-info/kitchen-sink-separate-dirs/indigo.rs b/tests/rustdoc-html/merge-cross-crate-info/kitchen-sink-separate-dirs/indigo.rs index fd6ee0cbf2407..986c58deb5d6b 100644 --- a/tests/rustdoc-html/merge-cross-crate-info/kitchen-sink-separate-dirs/indigo.rs +++ b/tests/rustdoc-html/merge-cross-crate-info/kitchen-sink-separate-dirs/indigo.rs @@ -3,11 +3,10 @@ //@ aux-build:quebec.rs //@ aux-build:sierra.rs //@ build-aux-docs -//@ doc-flags:--merge=finalize -//@ doc-flags:--include-parts-dir=info/doc.parts/tango -//@ doc-flags:--include-parts-dir=info/doc.parts/romeo -//@ doc-flags:--include-parts-dir=info/doc.parts/quebec -//@ doc-flags:--include-parts-dir=info/doc.parts/sierra +//@ doc-flags:--read-doc-meta-dir=info/doc.parts/tango +//@ doc-flags:--read-doc-meta-dir=info/doc.parts/romeo +//@ doc-flags:--read-doc-meta-dir=info/doc.parts/quebec +//@ doc-flags:--read-doc-meta-dir=info/doc.parts/sierra //@ doc-flags:--enable-index-page //@ doc-flags:-Zunstable-options diff --git a/tests/rustdoc-html/merge-cross-crate-info/no-merge-separate/auxiliary/quebec.rs b/tests/rustdoc-html/merge-cross-crate-info/no-merge-separate/auxiliary/quebec.rs index 61210529fa6bc..d2af9af496bc3 100644 --- a/tests/rustdoc-html/merge-cross-crate-info/no-merge-separate/auxiliary/quebec.rs +++ b/tests/rustdoc-html/merge-cross-crate-info/no-merge-separate/auxiliary/quebec.rs @@ -1,5 +1,5 @@ //@ unique-doc-out-dir -//@ doc-flags:--merge=none +//@ doc-flags:--write-doc-meta-dir=info/doc.parts/quebec //@ doc-flags:--enable-index-page //@ doc-flags:-Zunstable-options diff --git a/tests/rustdoc-html/merge-cross-crate-info/no-merge-separate/auxiliary/tango.rs b/tests/rustdoc-html/merge-cross-crate-info/no-merge-separate/auxiliary/tango.rs index 70d8a4b91f549..577c486a3adfb 100644 --- a/tests/rustdoc-html/merge-cross-crate-info/no-merge-separate/auxiliary/tango.rs +++ b/tests/rustdoc-html/merge-cross-crate-info/no-merge-separate/auxiliary/tango.rs @@ -1,7 +1,7 @@ //@ aux-build:quebec.rs //@ build-aux-docs //@ unique-doc-out-dir -//@ doc-flags:--merge=none +//@ doc-flags:--write-doc-meta-dir=info/doc.parts/tango //@ doc-flags:--enable-index-page //@ doc-flags:-Zunstable-options diff --git a/tests/rustdoc-html/merge-cross-crate-info/no-merge-separate/sierra.rs b/tests/rustdoc-html/merge-cross-crate-info/no-merge-separate/sierra.rs index c3b8200f15107..9a6ca3c6dd41c 100644 --- a/tests/rustdoc-html/merge-cross-crate-info/no-merge-separate/sierra.rs +++ b/tests/rustdoc-html/merge-cross-crate-info/no-merge-separate/sierra.rs @@ -1,6 +1,6 @@ //@ aux-build:tango.rs //@ build-aux-docs -//@ doc-flags:--merge=none +//@ doc-flags:--write-doc-meta-dir=info/doc.parts/sierra //@ doc-flags:--enable-index-page //@ doc-flags:-Zunstable-options @@ -10,7 +10,7 @@ //@ !has trait.impl/tango/trait.Tango.js //@ !has search.index/name/*.js -// we don't generate any cross-crate info if --merge=none, even if we +// we don't generate any cross-crate info if --write-doc-meta-dir, even if we // document crates separately extern crate tango; pub struct Sierra; diff --git a/tests/rustdoc-html/merge-cross-crate-info/no-merge-write-anyway/auxiliary/quebec.rs b/tests/rustdoc-html/merge-cross-crate-info/no-merge-write-anyway/auxiliary/quebec.rs index 6ab921533b037..442eacb91f480 100644 --- a/tests/rustdoc-html/merge-cross-crate-info/no-merge-write-anyway/auxiliary/quebec.rs +++ b/tests/rustdoc-html/merge-cross-crate-info/no-merge-write-anyway/auxiliary/quebec.rs @@ -1,5 +1,4 @@ -//@ doc-flags:--merge=none -//@ doc-flags:--parts-out-dir=info/doc.parts/quebec +//@ doc-flags:--write-doc-meta-dir=info/doc.parts/quebec //@ doc-flags:--enable-index-page //@ doc-flags:-Zunstable-options diff --git a/tests/rustdoc-html/merge-cross-crate-info/no-merge-write-anyway/auxiliary/tango.rs b/tests/rustdoc-html/merge-cross-crate-info/no-merge-write-anyway/auxiliary/tango.rs index 9fa99d3be8af4..e10bab806ea0c 100644 --- a/tests/rustdoc-html/merge-cross-crate-info/no-merge-write-anyway/auxiliary/tango.rs +++ b/tests/rustdoc-html/merge-cross-crate-info/no-merge-write-anyway/auxiliary/tango.rs @@ -1,7 +1,6 @@ //@ aux-build:quebec.rs //@ build-aux-docs -//@ doc-flags:--merge=none -//@ doc-flags:--parts-out-dir=info/doc.parts/tango +//@ doc-flags:--write-doc-meta-dir=info/doc.parts/tango //@ doc-flags:--enable-index-page //@ doc-flags:-Zunstable-options diff --git a/tests/rustdoc-html/merge-cross-crate-info/no-merge-write-anyway/sierra.rs b/tests/rustdoc-html/merge-cross-crate-info/no-merge-write-anyway/sierra.rs index 2e47d42daff94..9d5a1a3c17bfa 100644 --- a/tests/rustdoc-html/merge-cross-crate-info/no-merge-write-anyway/sierra.rs +++ b/tests/rustdoc-html/merge-cross-crate-info/no-merge-write-anyway/sierra.rs @@ -1,7 +1,6 @@ //@ aux-build:tango.rs //@ build-aux-docs -//@ doc-flags:--merge=none -//@ doc-flags:--parts-out-dir=info/doc.parts/sierra +//@ doc-flags:--write-doc-meta-dir=info/doc.parts/sierra //@ doc-flags:--enable-index-page //@ doc-flags:-Zunstable-options @@ -12,7 +11,7 @@ //@ !has trait.impl/tango/trait.Tango.js //@ !has search.index/name/*.js -// we --merge=none, so --parts-out-dir doesn't do anything +// we don't use --read-doc-meta-dir, so no metadata is loaded extern crate tango; pub struct Sierra; impl tango::Tango for Sierra {} diff --git a/tests/rustdoc-html/merge-cross-crate-info/overwrite-but-include/auxiliary/quebec.rs b/tests/rustdoc-html/merge-cross-crate-info/overwrite-but-include/auxiliary/quebec.rs index 6ab921533b037..442eacb91f480 100644 --- a/tests/rustdoc-html/merge-cross-crate-info/overwrite-but-include/auxiliary/quebec.rs +++ b/tests/rustdoc-html/merge-cross-crate-info/overwrite-but-include/auxiliary/quebec.rs @@ -1,5 +1,4 @@ -//@ doc-flags:--merge=none -//@ doc-flags:--parts-out-dir=info/doc.parts/quebec +//@ doc-flags:--write-doc-meta-dir=info/doc.parts/quebec //@ doc-flags:--enable-index-page //@ doc-flags:-Zunstable-options diff --git a/tests/rustdoc-html/merge-cross-crate-info/overwrite-but-include/auxiliary/tango.rs b/tests/rustdoc-html/merge-cross-crate-info/overwrite-but-include/auxiliary/tango.rs index 9fa99d3be8af4..e10bab806ea0c 100644 --- a/tests/rustdoc-html/merge-cross-crate-info/overwrite-but-include/auxiliary/tango.rs +++ b/tests/rustdoc-html/merge-cross-crate-info/overwrite-but-include/auxiliary/tango.rs @@ -1,7 +1,6 @@ //@ aux-build:quebec.rs //@ build-aux-docs -//@ doc-flags:--merge=none -//@ doc-flags:--parts-out-dir=info/doc.parts/tango +//@ doc-flags:--write-doc-meta-dir=info/doc.parts/tango //@ doc-flags:--enable-index-page //@ doc-flags:-Zunstable-options diff --git a/tests/rustdoc-html/merge-cross-crate-info/overwrite-but-include/sierra.rs b/tests/rustdoc-html/merge-cross-crate-info/overwrite-but-include/sierra.rs index 337dc558f35d8..2d94321ed7465 100644 --- a/tests/rustdoc-html/merge-cross-crate-info/overwrite-but-include/sierra.rs +++ b/tests/rustdoc-html/merge-cross-crate-info/overwrite-but-include/sierra.rs @@ -1,7 +1,6 @@ //@ aux-build:tango.rs //@ build-aux-docs -//@ doc-flags:--merge=finalize -//@ doc-flags:--include-parts-dir=info/doc.parts/tango +//@ doc-flags:--read-doc-meta-dir=info/doc.parts/tango //@ doc-flags:--enable-index-page //@ doc-flags:-Zunstable-options diff --git a/tests/rustdoc-html/merge-cross-crate-info/overwrite-but-separate/auxiliary/quebec.rs b/tests/rustdoc-html/merge-cross-crate-info/overwrite-but-separate/auxiliary/quebec.rs index d10bc0316ccad..d2af9af496bc3 100644 --- a/tests/rustdoc-html/merge-cross-crate-info/overwrite-but-separate/auxiliary/quebec.rs +++ b/tests/rustdoc-html/merge-cross-crate-info/overwrite-but-separate/auxiliary/quebec.rs @@ -1,6 +1,5 @@ //@ unique-doc-out-dir -//@ doc-flags:--merge=none -//@ doc-flags:--parts-out-dir=info/doc.parts/quebec +//@ doc-flags:--write-doc-meta-dir=info/doc.parts/quebec //@ doc-flags:--enable-index-page //@ doc-flags:-Zunstable-options diff --git a/tests/rustdoc-html/merge-cross-crate-info/overwrite-but-separate/auxiliary/tango.rs b/tests/rustdoc-html/merge-cross-crate-info/overwrite-but-separate/auxiliary/tango.rs index 3c3721ee6eb7d..577c486a3adfb 100644 --- a/tests/rustdoc-html/merge-cross-crate-info/overwrite-but-separate/auxiliary/tango.rs +++ b/tests/rustdoc-html/merge-cross-crate-info/overwrite-but-separate/auxiliary/tango.rs @@ -1,8 +1,7 @@ //@ aux-build:quebec.rs //@ build-aux-docs //@ unique-doc-out-dir -//@ doc-flags:--merge=none -//@ doc-flags:--parts-out-dir=info/doc.parts/tango +//@ doc-flags:--write-doc-meta-dir=info/doc.parts/tango //@ doc-flags:--enable-index-page //@ doc-flags:-Zunstable-options diff --git a/tests/rustdoc-html/merge-cross-crate-info/overwrite-but-separate/sierra.rs b/tests/rustdoc-html/merge-cross-crate-info/overwrite-but-separate/sierra.rs index c07b30d2aa0cd..6a2dbb1c31ccc 100644 --- a/tests/rustdoc-html/merge-cross-crate-info/overwrite-but-separate/sierra.rs +++ b/tests/rustdoc-html/merge-cross-crate-info/overwrite-but-separate/sierra.rs @@ -1,8 +1,7 @@ //@ aux-build:tango.rs //@ build-aux-docs -//@ doc-flags:--merge=finalize -//@ doc-flags:--include-parts-dir=info/doc.parts/tango -//@ doc-flags:--include-parts-dir=info/doc.parts/quebec +//@ doc-flags:--read-doc-meta-dir=info/doc.parts/tango +//@ doc-flags:--read-doc-meta-dir=info/doc.parts/quebec //@ doc-flags:--enable-index-page //@ doc-flags:-Zunstable-options diff --git a/tests/rustdoc-html/merge-cross-crate-info/overwrite/auxiliary/quebec.rs b/tests/rustdoc-html/merge-cross-crate-info/overwrite/auxiliary/quebec.rs index 0e28d8e646647..35098e62cede9 100644 --- a/tests/rustdoc-html/merge-cross-crate-info/overwrite/auxiliary/quebec.rs +++ b/tests/rustdoc-html/merge-cross-crate-info/overwrite/auxiliary/quebec.rs @@ -1,4 +1,4 @@ -//@ doc-flags:--merge=none +//@ doc-flags:--write-doc-meta-dir=info/doc.parts/quebec.d //@ doc-flags:--enable-index-page //@ doc-flags:-Zunstable-options diff --git a/tests/rustdoc-html/merge-cross-crate-info/overwrite/auxiliary/tango.rs b/tests/rustdoc-html/merge-cross-crate-info/overwrite/auxiliary/tango.rs index 363b2d5508e63..dcb7965dde86e 100644 --- a/tests/rustdoc-html/merge-cross-crate-info/overwrite/auxiliary/tango.rs +++ b/tests/rustdoc-html/merge-cross-crate-info/overwrite/auxiliary/tango.rs @@ -1,6 +1,6 @@ //@ aux-build:quebec.rs //@ build-aux-docs -//@ doc-flags:--merge=finalize +//@ doc-flags:--read-doc-meta-dir=info/doc.parts //@ doc-flags:--enable-index-page //@ doc-flags:-Zunstable-options diff --git a/tests/rustdoc-html/merge-cross-crate-info/overwrite/sierra.rs b/tests/rustdoc-html/merge-cross-crate-info/overwrite/sierra.rs index cac978f3bb272..57c9732ba8dd7 100644 --- a/tests/rustdoc-html/merge-cross-crate-info/overwrite/sierra.rs +++ b/tests/rustdoc-html/merge-cross-crate-info/overwrite/sierra.rs @@ -1,6 +1,5 @@ //@ aux-build:tango.rs //@ build-aux-docs -//@ doc-flags:--merge=shared //@ doc-flags:--enable-index-page //@ doc-flags:-Zunstable-options @@ -13,7 +12,7 @@ //@ hasraw search.index/name/*.js 'Sierra' //@ !hasraw search.index/name/*.js 'Quebec' -// since tango is documented with --merge=finalize, we overwrite q's +// since tango is documented with "finalize mode", we overwrite q's // cross-crate information extern crate tango; pub struct Sierra; diff --git a/tests/rustdoc-html/merge-cross-crate-info/single-crate-finalize/quebec.rs b/tests/rustdoc-html/merge-cross-crate-info/single-crate-finalize/quebec.rs index 2ab08c112a18c..c157380edf1f9 100644 --- a/tests/rustdoc-html/merge-cross-crate-info/single-crate-finalize/quebec.rs +++ b/tests/rustdoc-html/merge-cross-crate-info/single-crate-finalize/quebec.rs @@ -1,4 +1,3 @@ -//@ doc-flags:--merge=finalize //@ doc-flags:--enable-index-page //@ doc-flags:-Zunstable-options diff --git a/tests/rustdoc-html/merge-cross-crate-info/single-crate-read-write/quebec.rs b/tests/rustdoc-html/merge-cross-crate-info/single-crate-read-write/quebec.rs index 1b9e8a3db08da..4fd510e31227c 100644 --- a/tests/rustdoc-html/merge-cross-crate-info/single-crate-read-write/quebec.rs +++ b/tests/rustdoc-html/merge-cross-crate-info/single-crate-read-write/quebec.rs @@ -1,4 +1,3 @@ -//@ doc-flags:--merge=shared //@ doc-flags:--enable-index-page //@ doc-flags:-Zunstable-options diff --git a/tests/rustdoc-html/merge-cross-crate-info/single-crate-write-anyway/quebec.rs b/tests/rustdoc-html/merge-cross-crate-info/single-crate-write-anyway/quebec.rs index 6b72615eb9dec..fde3635e62250 100644 --- a/tests/rustdoc-html/merge-cross-crate-info/single-crate-write-anyway/quebec.rs +++ b/tests/rustdoc-html/merge-cross-crate-info/single-crate-write-anyway/quebec.rs @@ -1,4 +1,4 @@ -//@ doc-flags:--parts-out-dir=info/doc.parts/quebec +//@ doc-flags:--read-doc-meta-dir=. //@ doc-flags:--enable-index-page //@ doc-flags:-Zunstable-options @@ -8,6 +8,6 @@ //@ has quebec/struct.Quebec.html //@ hasraw search.index/name/*.js 'Quebec' -// we can --parts-out-dir, but that doesn't do anything other than create +// we can --write-doc-meta-dir, but that doesn't do anything other than create // the file pub struct Quebec; diff --git a/tests/rustdoc-html/merge-cross-crate-info/single-merge-none-useless-write/quebec.rs b/tests/rustdoc-html/merge-cross-crate-info/single-merge-none-useless-write/quebec.rs deleted file mode 100644 index bfde21c9ed3c9..0000000000000 --- a/tests/rustdoc-html/merge-cross-crate-info/single-merge-none-useless-write/quebec.rs +++ /dev/null @@ -1,11 +0,0 @@ -//@ doc-flags:--merge=none -//@ doc-flags:--parts-out-dir=info/doc.parts/quebec -//@ doc-flags:--enable-index-page -//@ doc-flags:-Zunstable-options - -//@ !has index.html -//@ has quebec/struct.Quebec.html -//@ !has search.index/name/*.js - -// --merge=none doesn't write anything, despite --parts-out-dir -pub struct Quebec; diff --git a/tests/rustdoc-html/merge-cross-crate-info/transitive-finalize/auxiliary/quebec.rs b/tests/rustdoc-html/merge-cross-crate-info/transitive-finalize/auxiliary/quebec.rs deleted file mode 100644 index 1beca543f814f..0000000000000 --- a/tests/rustdoc-html/merge-cross-crate-info/transitive-finalize/auxiliary/quebec.rs +++ /dev/null @@ -1,5 +0,0 @@ -//@ doc-flags:--merge=finalize -//@ doc-flags:--enable-index-page -//@ doc-flags:-Zunstable-options - -pub struct Quebec; diff --git a/tests/rustdoc-html/merge-cross-crate-info/transitive-finalize/auxiliary/tango.rs b/tests/rustdoc-html/merge-cross-crate-info/transitive-finalize/auxiliary/tango.rs deleted file mode 100644 index 363b2d5508e63..0000000000000 --- a/tests/rustdoc-html/merge-cross-crate-info/transitive-finalize/auxiliary/tango.rs +++ /dev/null @@ -1,8 +0,0 @@ -//@ aux-build:quebec.rs -//@ build-aux-docs -//@ doc-flags:--merge=finalize -//@ doc-flags:--enable-index-page -//@ doc-flags:-Zunstable-options - -extern crate quebec; -pub trait Tango {} diff --git a/tests/rustdoc-html/merge-cross-crate-info/transitive-finalize/sierra.rs b/tests/rustdoc-html/merge-cross-crate-info/transitive-finalize/sierra.rs deleted file mode 100644 index b45895a40a1b6..0000000000000 --- a/tests/rustdoc-html/merge-cross-crate-info/transitive-finalize/sierra.rs +++ /dev/null @@ -1,20 +0,0 @@ -//@ aux-build:tango.rs -//@ build-aux-docs -//@ doc-flags:--merge=finalize -//@ doc-flags:--enable-index-page -//@ doc-flags:-Zunstable-options - -//@ has index.html -//@ has index.html '//h1' 'List of all crates' -//@ has index.html '//ul[@class="all-items"]//a[@href="sierra/index.html"]' 'sierra' -//@ has index.html '//ul[@class="all-items"]//a[@href="tango/index.html"]' 'tango' -//@ has sierra/struct.Sierra.html -//@ has tango/trait.Tango.html -//@ hasraw sierra/struct.Sierra.html 'Tango' -//@ hasraw trait.impl/tango/trait.Tango.js 'struct.Sierra.html' -//@ hasraw search.index/name/*.js 'Sierra' - -// write only overwrites stuff in the output directory -extern crate tango; -pub struct Sierra; -impl tango::Tango for Sierra {} diff --git a/tests/rustdoc-html/merge-cross-crate-info/transitive-merge-none/auxiliary/quebec.rs b/tests/rustdoc-html/merge-cross-crate-info/transitive-merge-none/auxiliary/quebec.rs index 6ab921533b037..442eacb91f480 100644 --- a/tests/rustdoc-html/merge-cross-crate-info/transitive-merge-none/auxiliary/quebec.rs +++ b/tests/rustdoc-html/merge-cross-crate-info/transitive-merge-none/auxiliary/quebec.rs @@ -1,5 +1,4 @@ -//@ doc-flags:--merge=none -//@ doc-flags:--parts-out-dir=info/doc.parts/quebec +//@ doc-flags:--write-doc-meta-dir=info/doc.parts/quebec //@ doc-flags:--enable-index-page //@ doc-flags:-Zunstable-options diff --git a/tests/rustdoc-html/merge-cross-crate-info/transitive-merge-none/auxiliary/tango.rs b/tests/rustdoc-html/merge-cross-crate-info/transitive-merge-none/auxiliary/tango.rs index 9fa99d3be8af4..e10bab806ea0c 100644 --- a/tests/rustdoc-html/merge-cross-crate-info/transitive-merge-none/auxiliary/tango.rs +++ b/tests/rustdoc-html/merge-cross-crate-info/transitive-merge-none/auxiliary/tango.rs @@ -1,7 +1,6 @@ //@ aux-build:quebec.rs //@ build-aux-docs -//@ doc-flags:--merge=none -//@ doc-flags:--parts-out-dir=info/doc.parts/tango +//@ doc-flags:--write-doc-meta-dir=info/doc.parts/tango //@ doc-flags:--enable-index-page //@ doc-flags:-Zunstable-options diff --git a/tests/rustdoc-html/merge-cross-crate-info/transitive-merge-none/sierra.rs b/tests/rustdoc-html/merge-cross-crate-info/transitive-merge-none/sierra.rs index be37137617936..945e8cf84dc7d 100644 --- a/tests/rustdoc-html/merge-cross-crate-info/transitive-merge-none/sierra.rs +++ b/tests/rustdoc-html/merge-cross-crate-info/transitive-merge-none/sierra.rs @@ -1,8 +1,7 @@ //@ aux-build:tango.rs //@ build-aux-docs -//@ doc-flags:--merge=finalize -//@ doc-flags:--include-parts-dir=info/doc.parts/tango -//@ doc-flags:--include-parts-dir=info/doc.parts/quebec +//@ doc-flags:--read-doc-meta-dir=info/doc.parts/tango +//@ doc-flags:--read-doc-meta-dir=info/doc.parts/quebec //@ doc-flags:--enable-index-page //@ doc-flags:-Zunstable-options @@ -21,7 +20,7 @@ //@ hasraw search.index/name/*.js 'Quebec' // We avoid writing any cross-crate information, preferring to include it -// with --include-parts-dir. +// with --read-doc-meta-dir. extern crate tango; pub struct Sierra; impl tango::Tango for Sierra {} diff --git a/tests/rustdoc-html/merge-cross-crate-info/transitive-merge-read-write/auxiliary/quebec.rs b/tests/rustdoc-html/merge-cross-crate-info/transitive-merge-read-write/auxiliary/quebec.rs index 1beca543f814f..c4b3ce498576c 100644 --- a/tests/rustdoc-html/merge-cross-crate-info/transitive-merge-read-write/auxiliary/quebec.rs +++ b/tests/rustdoc-html/merge-cross-crate-info/transitive-merge-read-write/auxiliary/quebec.rs @@ -1,4 +1,3 @@ -//@ doc-flags:--merge=finalize //@ doc-flags:--enable-index-page //@ doc-flags:-Zunstable-options diff --git a/tests/rustdoc-html/merge-cross-crate-info/transitive-merge-read-write/auxiliary/tango.rs b/tests/rustdoc-html/merge-cross-crate-info/transitive-merge-read-write/auxiliary/tango.rs index ff12fe98d82c2..299a673418713 100644 --- a/tests/rustdoc-html/merge-cross-crate-info/transitive-merge-read-write/auxiliary/tango.rs +++ b/tests/rustdoc-html/merge-cross-crate-info/transitive-merge-read-write/auxiliary/tango.rs @@ -1,6 +1,5 @@ //@ aux-build:quebec.rs //@ build-aux-docs -//@ doc-flags:--merge=shared //@ doc-flags:--enable-index-page //@ doc-flags:-Zunstable-options diff --git a/tests/rustdoc-html/merge-cross-crate-info/transitive-merge-read-write/sierra.rs b/tests/rustdoc-html/merge-cross-crate-info/transitive-merge-read-write/sierra.rs index dc10ec3de35b0..67ce91d1e225a 100644 --- a/tests/rustdoc-html/merge-cross-crate-info/transitive-merge-read-write/sierra.rs +++ b/tests/rustdoc-html/merge-cross-crate-info/transitive-merge-read-write/sierra.rs @@ -1,6 +1,5 @@ //@ aux-build:tango.rs //@ build-aux-docs -//@ doc-flags:--merge=shared //@ doc-flags:--enable-index-page //@ doc-flags:-Zunstable-options @@ -19,7 +18,7 @@ //@ hasraw search.index/name/*.js 'Quebec' // We can use read-write to emulate the default behavior of rustdoc, when -// --merge is left out. +// --read-doc-meta-dir is left out. extern crate tango; pub struct Sierra; impl tango::Tango for Sierra {} diff --git a/tests/rustdoc-html/merge-cross-crate-info/transitive-no-info/auxiliary/quebec.rs b/tests/rustdoc-html/merge-cross-crate-info/transitive-no-info/auxiliary/quebec.rs index 0e28d8e646647..442eacb91f480 100644 --- a/tests/rustdoc-html/merge-cross-crate-info/transitive-no-info/auxiliary/quebec.rs +++ b/tests/rustdoc-html/merge-cross-crate-info/transitive-no-info/auxiliary/quebec.rs @@ -1,4 +1,4 @@ -//@ doc-flags:--merge=none +//@ doc-flags:--write-doc-meta-dir=info/doc.parts/quebec //@ doc-flags:--enable-index-page //@ doc-flags:-Zunstable-options diff --git a/tests/rustdoc-html/merge-cross-crate-info/transitive-no-info/auxiliary/tango.rs b/tests/rustdoc-html/merge-cross-crate-info/transitive-no-info/auxiliary/tango.rs index 3827119f696f5..e10bab806ea0c 100644 --- a/tests/rustdoc-html/merge-cross-crate-info/transitive-no-info/auxiliary/tango.rs +++ b/tests/rustdoc-html/merge-cross-crate-info/transitive-no-info/auxiliary/tango.rs @@ -1,6 +1,6 @@ //@ aux-build:quebec.rs //@ build-aux-docs -//@ doc-flags:--merge=none +//@ doc-flags:--write-doc-meta-dir=info/doc.parts/tango //@ doc-flags:--enable-index-page //@ doc-flags:-Zunstable-options diff --git a/tests/rustdoc-html/merge-cross-crate-info/transitive-no-info/sierra.rs b/tests/rustdoc-html/merge-cross-crate-info/transitive-no-info/sierra.rs index 9eaa627419bb4..61c60cf7c9e15 100644 --- a/tests/rustdoc-html/merge-cross-crate-info/transitive-no-info/sierra.rs +++ b/tests/rustdoc-html/merge-cross-crate-info/transitive-no-info/sierra.rs @@ -1,6 +1,6 @@ //@ aux-build:tango.rs //@ build-aux-docs -//@ doc-flags:--merge=none +//@ doc-flags:--write-doc-meta-dir=info/doc.parts/sierra //@ doc-flags:--enable-index-page //@ doc-flags:-Zunstable-options @@ -11,7 +11,8 @@ //@ !has trait.impl/tango/trait.Tango.js //@ !has search.index/name/*.js -// --merge=none on all crates does not generate any cross-crate info +// --write-doc-meta-dir on all crates does not write the search index +// in the actual output folder extern crate tango; pub struct Sierra; impl tango::Tango for Sierra {} diff --git a/tests/rustdoc-html/merge-cross-crate-info/two-separate-out-dir/auxiliary/foxtrot.rs b/tests/rustdoc-html/merge-cross-crate-info/two-separate-out-dir/auxiliary/foxtrot.rs index e492b700dbc2b..713f8812a55a6 100644 --- a/tests/rustdoc-html/merge-cross-crate-info/two-separate-out-dir/auxiliary/foxtrot.rs +++ b/tests/rustdoc-html/merge-cross-crate-info/two-separate-out-dir/auxiliary/foxtrot.rs @@ -1,5 +1,5 @@ //@ unique-doc-out-dir -//@ doc-flags:--parts-out-dir=info/doc.parts/foxtrot +//@ doc-flags:--write-doc-meta-dir=info/doc.parts/foxtrot //@ doc-flags:-Zunstable-options pub trait Foxtrot {} diff --git a/tests/rustdoc-html/merge-cross-crate-info/two-separate-out-dir/echo.rs b/tests/rustdoc-html/merge-cross-crate-info/two-separate-out-dir/echo.rs index d79302e62cdc3..998bb559df8c1 100644 --- a/tests/rustdoc-html/merge-cross-crate-info/two-separate-out-dir/echo.rs +++ b/tests/rustdoc-html/merge-cross-crate-info/two-separate-out-dir/echo.rs @@ -1,6 +1,6 @@ //@ aux-build:foxtrot.rs //@ build-aux-docs -//@ doc-flags:--include-parts-dir=info/doc.parts/foxtrot +//@ doc-flags:--read-doc-meta-dir=info/doc.parts/foxtrot //@ doc-flags:-Zunstable-options //@ has echo/enum.Echo.html diff --git a/tests/rustdoc-js/auxiliary/merged-dep.rs b/tests/rustdoc-js/auxiliary/merged-dep.rs index fadeb6c65270e..0a3216ee5c03f 100644 --- a/tests/rustdoc-js/auxiliary/merged-dep.rs +++ b/tests/rustdoc-js/auxiliary/merged-dep.rs @@ -1,6 +1,5 @@ //@ unique-doc-out-dir -//@ doc-flags:--merge=none -//@ doc-flags:--parts-out-dir=info/doc.parts/merged-dep +//@ doc-flags:--write-doc-meta-dir=info/doc.parts/merged-dep //@ doc-flags:-Zunstable-options pub struct Dep; diff --git a/tests/rustdoc-js/merged-doc.rs b/tests/rustdoc-js/merged-doc.rs index ef7ce4b1fd365..7fc18f2e8bb4a 100644 --- a/tests/rustdoc-js/merged-doc.rs +++ b/tests/rustdoc-js/merged-doc.rs @@ -1,8 +1,7 @@ //@ revisions: merge nomerge //@ aux-build:merged-dep.rs //@ build-aux-docs -//@[merge] doc-flags:--merge=finalize -//@[merge] doc-flags:--include-parts-dir=info/doc.parts/merged-dep +//@[merge] doc-flags:--read-doc-meta-dir=info/doc.parts/merged-dep //@[merge] doc-flags:-Zunstable-options extern crate merged_dep; From b53cbe12c8ad9b1ce6ed87bb66e8f70899574350 Mon Sep 17 00:00:00 2001 From: Manuel Drehwald Date: Wed, 15 Jul 2026 02:29:46 +0800 Subject: [PATCH 21/26] Update Enzyme to handle LLVM23 --- src/tools/enzyme | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/enzyme b/src/tools/enzyme index a8668c7ca3579..6882b799d5aac 160000 --- a/src/tools/enzyme +++ b/src/tools/enzyme @@ -1 +1 @@ -Subproject commit a8668c7ca3579c3304d628bca518bafc0fcc62d1 +Subproject commit 6882b799d5aac80c5da7a4ef4e1b199e49509508 From e176864ce54248b7f483f63bcdc48c16af779a5d Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 17 Jul 2026 09:49:11 +0200 Subject: [PATCH 22/26] fix grammar --- compiler/rustc_mir_transform/src/shim.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_mir_transform/src/shim.rs b/compiler/rustc_mir_transform/src/shim.rs index f4cfda9d50f66..9a75c7007cea3 100644 --- a/compiler/rustc_mir_transform/src/shim.rs +++ b/compiler/rustc_mir_transform/src/shim.rs @@ -1074,7 +1074,7 @@ pub(super) fn build_adt_ctor(tcx: TyCtxt<'_>, ctor_id: DefId) -> Body<'_> { // so this would otherwise not get filled). body.set_mentioned_items(Vec::new()); - // We don't pass any passes here, just to force a phase change to `Optimized`. + // We don't pass any passes here, we just force a phase change to `Optimized`. // Otherwise this bit of MIR will trigger assertions trying to detect MIR with an invalid phase. pm::run_passes_no_validate( tcx, From 353c1943860a92d2c5f4ac6db991e931a55d94c2 Mon Sep 17 00:00:00 2001 From: Yukang Date: Fri, 17 Jul 2026 16:05:45 +0800 Subject: [PATCH 23/26] remove unused check_struct_def --- compiler/rustc_lint/src/late.rs | 1 - compiler/rustc_lint/src/passes.rs | 1 - 2 files changed, 2 deletions(-) diff --git a/compiler/rustc_lint/src/late.rs b/compiler/rustc_lint/src/late.rs index c17fe6f2e510f..c2a3833c716a4 100644 --- a/compiler/rustc_lint/src/late.rs +++ b/compiler/rustc_lint/src/late.rs @@ -198,7 +198,6 @@ impl<'tcx, T: LateLintPass<'tcx>> hir_visit::Visitor<'tcx> for LateContextAndPas } fn visit_variant_data(&mut self, s: &'tcx hir::VariantData<'tcx>) { - lint_callback!(self, check_struct_def, s); hir_visit::walk_struct_def(self, s); } diff --git a/compiler/rustc_lint/src/passes.rs b/compiler/rustc_lint/src/passes.rs index 12cf58907566d..05046b0302700 100644 --- a/compiler/rustc_lint/src/passes.rs +++ b/compiler/rustc_lint/src/passes.rs @@ -37,7 +37,6 @@ macro_rules! late_lint_methods { fn check_trait_item(a: &'tcx rustc_hir::TraitItem<'tcx>); fn check_impl_item(a: &'tcx rustc_hir::ImplItem<'tcx>); fn check_impl_item_post(a: &'tcx rustc_hir::ImplItem<'tcx>); - fn check_struct_def(a: &'tcx rustc_hir::VariantData<'tcx>); fn check_field_def(a: &'tcx rustc_hir::FieldDef<'tcx>); fn check_variant(a: &'tcx rustc_hir::Variant<'tcx>); fn check_path(a: &rustc_hir::Path<'tcx>, b: rustc_hir::HirId); From a7c56bd88869c0bedd0f68970db140a015b590ad Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Thu, 16 Jul 2026 17:45:14 +0300 Subject: [PATCH 24/26] rustc_codegen_ssa: Do not create jobserver helper threads unless necessary --- compiler/rustc_codegen_ssa/src/back/write.rs | 43 ++++++++++++-------- 1 file changed, 27 insertions(+), 16 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index 1419fcaf733f3..baacefc3f2d03 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -1011,12 +1011,17 @@ fn do_thin_lto( // After we've requested tokens then we'll, when we can, // get tokens on `coordinator_receive` which will // get managed in the main loop below. - let coordinator_send2 = coordinator_send.clone(); - let helper = jobserver::client() - .into_helper_thread(move |token| { - drop(coordinator_send2.send(ThinLtoMessage::Token(token))); - }) - .expect("failed to spawn helper thread"); + // Note that using `jobserver::Proxy` is not necessary here, the code below always acquires + // tokens before releasing them, so we can never accidentally release the last token + // permanently held by rustc process. + let jobserver_helper = cgcx.parallel.then(|| { + let coordinator_send2 = coordinator_send.clone(); + jobserver::client() + .into_helper_thread(move |token| { + drop(coordinator_send2.send(ThinLtoMessage::Token(token))); + }) + .expect("failed to spawn helper thread") + }); let mut work_items = vec![]; @@ -1036,7 +1041,7 @@ fn do_thin_lto( let insertion_index = work_items.binary_search_by_key(&cost, |&(_, cost)| cost).unwrap_or_else(|e| e); work_items.insert(insertion_index, (work, cost)); - if cgcx.parallel { + if let Some(helper) = &jobserver_helper { helper.request_token(); } } @@ -1240,12 +1245,18 @@ fn start_executing_work( // After we've requested tokens then we'll, when we can, // get tokens on `coordinator_receive` which will // get managed in the main loop below. - let coordinator_send2 = coordinator_send.clone(); - let helper = jobserver::client() - .into_helper_thread(move |token| { - drop(coordinator_send2.send(Message::Token::(token))); - }) - .expect("failed to spawn helper thread"); + // Note that using `jobserver::Proxy` is not necessary here, the code below always acquires + // tokens before releasing them, so we can never accidentally release the last token + // permanently held by rustc process. + let parallel = !sess.opts.unstable_opts.no_parallel_backend && backend.supports_parallel(); + let jobserver_helper = parallel.then(|| { + let coordinator_send2 = coordinator_send.clone(); + jobserver::client() + .into_helper_thread(move |token| { + drop(coordinator_send2.send(Message::Token::(token))); + }) + .expect("failed to spawn helper thread") + }); let opt_level = tcx.backend_optimization_level(()); let backend_features = tcx.global_backend_features(()).clone(); @@ -1286,7 +1297,7 @@ fn start_executing_work( target_is_like_gpu: tcx.sess.target.is_like_gpu, split_debuginfo: tcx.sess.split_debuginfo(), split_dwarf_kind: tcx.sess.opts.unstable_opts.split_dwarf_kind, - parallel: backend.supports_parallel() && !sess.opts.unstable_opts.no_parallel_backend, + parallel, pointer_size: tcx.data_layout.pointer_size(), }; @@ -1633,7 +1644,7 @@ fn start_executing_work( }; work_items.insert(insertion_index, (llvm_work_item, cost)); - if cgcx.parallel { + if let Some(helper) = &jobserver_helper { helper.request_token(); } assert_eq!(main_thread_state, MainThreadState::Codegenning); @@ -1716,7 +1727,7 @@ fn start_executing_work( drop(codegen_state); drop(tokens); - drop(helper); + drop(jobserver_helper); assert!(work_items.is_empty()); if !needs_fat_lto.is_empty() { From cc4af8e81be9263b58e8d78192bbe49dd0bd873d Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Thu, 16 Jul 2026 23:11:32 +0300 Subject: [PATCH 25/26] rustc_interface: Minor cleanup to jobserver initialization --- compiler/rustc_interface/src/interface.rs | 19 +++++++------------ compiler/rustc_interface/src/tests.rs | 4 +--- 2 files changed, 8 insertions(+), 15 deletions(-) diff --git a/compiler/rustc_interface/src/interface.rs b/compiler/rustc_interface/src/interface.rs index 95aeb1f7a234f..c7ea358f08170 100644 --- a/compiler/rustc_interface/src/interface.rs +++ b/compiler/rustc_interface/src/interface.rs @@ -369,16 +369,6 @@ pub struct Config { pub using_internal_features: &'static std::sync::atomic::AtomicBool, } -/// Initialize jobserver before getting `jobserver::client` and `build_session`. -pub(crate) fn initialize_checked_jobserver(early_dcx: &EarlyDiagCtxt) { - jobserver::initialize_checked(|err| { - early_dcx - .early_struct_warn(err) - .with_note("the build environment is likely misconfigured") - .emit() - }); -} - // JUSTIFICATION: before session exists, only config #[allow(rustc::bad_opt_access)] pub fn run_compiler(config: Config, f: impl FnOnce(&Compiler) -> R + Send) -> R { @@ -389,9 +379,14 @@ pub fn run_compiler(config: Config, f: impl FnOnce(&Compiler) -> R + Se config.opts.unstable_opts.threads.is_some(), ); - // Check jobserver before run_in_thread_pool_with_globals, which call jobserver::acquire_thread + // Initialize jobserver as early as possible. let early_dcx = EarlyDiagCtxt::new(config.opts.error_format); - initialize_checked_jobserver(&early_dcx); + jobserver::initialize_checked(|err| { + early_dcx + .early_struct_warn(err) + .with_note("the build environment is likely misconfigured") + .emit() + }); crate::callbacks::setup_callbacks(); diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index 71d1829f76584..22b643e74e582 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -31,15 +31,13 @@ use rustc_target::spec::{ RelocModel, RelroLevel, SanitizerSet, SplitDebuginfo, StackProtector, TlsModel, }; -use crate::interface::{initialize_checked_jobserver, parse_cfg}; +use crate::interface::parse_cfg; fn sess_and_cfg(args: &[&'static str], f: F) where F: FnOnce(Session, Cfg), { let mut early_dcx = EarlyDiagCtxt::new(ErrorOutputType::default()); - initialize_checked_jobserver(&early_dcx); - let matches = optgroups().parse(args).unwrap(); let sessopts = build_session_options(&mut early_dcx, &matches); let target = rustc_session::config::build_target_config( From eb9dd038d72ec3efdcb7aca256bc62663c28eff4 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Thu, 16 Jul 2026 23:12:56 +0300 Subject: [PATCH 26/26] rustc_data_structures: Expand documentation for rustc jobserver APIs --- .../rustc_data_structures/src/jobserver.rs | 106 ++++++++++++------ 1 file changed, 69 insertions(+), 37 deletions(-) diff --git a/compiler/rustc_data_structures/src/jobserver.rs b/compiler/rustc_data_structures/src/jobserver.rs index 3ed1ea7543f40..6f275d106d640 100644 --- a/compiler/rustc_data_structures/src/jobserver.rs +++ b/compiler/rustc_data_structures/src/jobserver.rs @@ -1,19 +1,16 @@ use std::sync::{Arc, LazyLock, OnceLock}; -pub use jobserver_crate::{Acquired, Client, HelperThread}; -use jobserver_crate::{FromEnv, FromEnvErrorKind}; +pub use jobserver_crate::Acquired; +use jobserver_crate::{Client, FromEnv, FromEnvErrorKind, HelperThread}; use parking_lot::{Condvar, Mutex}; -// We can only call `from_env_ext` once per process - -// We stick this in a global because there could be multiple rustc instances -// in this process, and the jobserver is per-process. +// We stick the jobserver client into a global and initialize it once, because there could be +// multiple compiler instances in this process, and the jobserver is per-process. static GLOBAL_CLIENT: LazyLock> = LazyLock::new(|| { - // Note that this is unsafe because it may misinterpret file descriptors - // on Unix as jobserver file descriptors. We hopefully execute this near - // the beginning of the process though to ensure we don't get false - // positives, or in other words we try to execute this before we open - // any file descriptors ourselves. + // Safety: the checked client construction ensures that the jobserver file descriptors + // (if any) are open and valid. We also try to initialize the jobserver as early as possible + // to avoid unrelated file descriptors with matching values becoming open and valid between + // the process start and the jobserver initialization. let FromEnv { client, var } = unsafe { Client::from_env_ext(true) }; let error = match client { @@ -40,14 +37,17 @@ static GLOBAL_CLIENT: LazyLock> = LazyLock::new(|| { )) }); -// Create a new jobserver if there's no inherited one. +// Creates a new jobserver if there's no inherited one. fn default_client() -> Client { // Pick a "reasonable maximum" capping out at 32 // so we don't take everything down by hogging the process run queue. // The fixed number is used to have deterministic compilation across machines. let client = Client::new(32).expect("failed to create jobserver"); - // Acquire a token for the main thread which we can release later + // Acquire the single token that is always held by the rustc process. + // This is an equivalent of the single token held by a higher level build tool while running + // this instance of rustc. This token is never released - if we are here, then rustc owns the + // jobserver, it is teared down when rustc exits, and there's no one to return the token to. client.acquire_raw().ok(); client @@ -55,47 +55,75 @@ fn default_client() -> Client { static GLOBAL_CLIENT_CHECKED: OnceLock = OnceLock::new(); -pub fn initialize_checked(report_warning: impl FnOnce(&'static str)) { +/// Initializes a jobserver client for the current rustc process. +/// If inheriting jobserver from the environment fails for some reason, an new jobserver owned by +/// the current rustc process will be created. If the inheritance failure reason is non-benign, +/// the passed callback will be used to report the error. +pub fn initialize_checked(report: impl FnOnce(&'static str)) { let client_checked = match &*GLOBAL_CLIENT { Ok(client) => client.clone(), Err(e) => { - report_warning(e); + report(e); default_client() } }; GLOBAL_CLIENT_CHECKED.set(client_checked).ok(); } -const ACCESS_ERROR: &str = "jobserver check should have been called earlier"; - +/// Returns the jobserver client previously initialized by `initialize_checked`. +/// +/// # Assumptions about holding jobserver tokens +/// +/// Rustc process must always hold a single token to avoid being permanently starved and blocked. +/// - If the jobserver is inherited from a higher level build tool, the assumption is that the tool +/// will hold the token and not release it until the rustc process exits. +/// - If the jobserver is owned by the current rustc, the token is acquired by `default_client`. +/// +/// To avoid releasing the last token, users of the client returned by this function must ensure +/// that they never release more tokens than was previously explicitly acquired. +/// Example of a sequence that can accidentally release the last token: +/// `release_raw` -> `wait` -> `acquire_raw`. +/// To avoid situations like this use the `jobserver::Proxy` wrapper instead, +/// it will ensure that the last token is never released. pub fn client() -> Client { - GLOBAL_CLIENT_CHECKED.get().expect(ACCESS_ERROR).clone() + GLOBAL_CLIENT_CHECKED.get().expect("uninitialized jobserver client").clone() } struct ProxyData { - /// The number of tokens assigned to threads. - /// If this is 0, a single token is still assigned to this process, but is unused. + /// The number of tokens assigned to actively working threads, + /// possibly including the single permanently held token. + /// If this number is 0, the single token is still held by the process, + /// but is not currently used for active CPU work. + /// This can happen, for example, if the main thread is waiting for something, + /// in that case some other thread can start using this token to do work. used: u16, - - /// The number of threads requesting a token + /// The number of threads currently requesting a token and waiting. + /// If the proxy releases a token it can immediately give it to one of these threads + /// without going through the real jobserver. pending: u16, } -/// This is a jobserver proxy used to ensure that we hold on to at least one token. +/// A wrapper around jobserver client used for two purposes: +/// - Ensuring that the single token that must be permanently held by the rustc process +/// cannot be accidentally released. +/// - "Token buffering", immediately acquiring freshly released tokens if necessary, +/// without going through the real jobserver. pub struct Proxy { + /// The wrapped jobserver client. client: Client, + /// Helper thread associated with the wrapped client. + helper: OnceLock, + /// The proxy's own data. data: Mutex, - - /// Threads which are waiting on a token will wait on this. + /// Threads which are currently waiting for a token will wait on this condvar. wake_pending: Condvar, - - helper: OnceLock, } impl Proxy { pub fn new() -> Arc { let proxy = Arc::new(Proxy { client: client(), + // Assume that the main thread is actively doing work when it creates the proxy. data: Mutex::new(ProxyData { used: 1, pending: 0 }), wake_pending: Condvar::new(), helper: OnceLock::new(), @@ -105,56 +133,60 @@ impl Proxy { .client .clone() .into_helper_thread(move |token| { + // Reminder: this callback runs when the helper acquires a token. if let Ok(token) = token { let mut data = proxy_.data.lock(); if data.pending > 0 { - // Give the token to a waiting thread + // The token is still needed, give it to one of the waiting threads. token.drop_without_releasing(); assert!(data.used > 0); data.used += 1; data.pending -= 1; proxy_.wake_pending.notify_one(); } else { - // The token is no longer needed, drop it. + // The token is no longer needed, release it by dropping. drop(data); drop(token); } } }) - .expect("failed to create helper thread"); + .expect("failed to spawn helper thread"); proxy.helper.set(helper).unwrap(); proxy } + /// Acquires a token, possibly using some buffered tokens as an optimization. + /// May block and wait until the token is available. pub fn acquire_thread(&self) { let mut data = self.data.lock(); if data.used == 0 { - // There was a free token around. This can - // happen when all threads release their token. + // No threads are doing any active work, but we are still holding the last token. + // Give that token to the current thread. assert_eq!(data.pending, 0); data.used += 1; } else { - // Request a token from the helper thread. We can't directly use `acquire_raw` - // as we also need to be able to wait for the final token in the process which - // does not get a corresponding `release_raw` call. + // Request a token from the helper thread, this is a non-blocking operation. + // Then wait until this or some other request succeeds. self.helper.get().unwrap().request_token(); data.pending += 1; self.wake_pending.wait(&mut data); } } + /// Releases a token, possibly immediately giving it to some other thread as an optimization. + /// Makes sure that the last token is never actually released to the wrapped jobserver. pub fn release_thread(&self) { let mut data = self.data.lock(); if data.pending > 0 { - // Give the token to a waiting thread + // Immediately give the released token to one of the waiting threads. data.pending -= 1; self.wake_pending.notify_one(); } else { data.used -= 1; - // Release the token unless it's the last one in the process + // Release the token to the wrapped jobserver, unless it's the last one in the process. if data.used > 0 { drop(data); self.client.release_raw().ok();