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/23] 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/23] 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 d2b26021379cc2bc66f391a7267c717ff08e15e7 Mon Sep 17 00:00:00 2001 From: kulst <60887784+kulst@users.noreply.github.com> Date: Sat, 10 Jan 2026 14:44:57 +0100 Subject: [PATCH 03/23] Improve error messages for disagreeing *target-modifiers* - Boolean target modifiers are now mentioned without a trailing `=` in the messages. - Wording improved for unset target modifiers. --- compiler/rustc_metadata/src/creader.rs | 2 ++ compiler/rustc_metadata/src/diagnostics.rs | 22 +++++++++++++++---- .../defaults_check.error.stderr | 2 +- ...compatible_fixedx18.error_generated.stderr | 4 ++-- .../no_value_bool.error.stderr | 2 +- .../no_value_bool.error_explicit.stderr | 2 +- ...izer-kcfi-normalize-ints.wrong_flag.stderr | 4 ++-- ...kcfi-normalize-ints.wrong_sanitizer.stderr | 2 +- ...zers-safestack-and-kcfi.missed_both.stderr | 2 +- 9 files changed, 29 insertions(+), 13 deletions(-) diff --git a/compiler/rustc_metadata/src/creader.rs b/compiler/rustc_metadata/src/creader.rs index dd063f5425511..bb91d855feaeb 100644 --- a/compiler/rustc_metadata/src/creader.rs +++ b/compiler/rustc_metadata/src/creader.rs @@ -380,6 +380,7 @@ impl CStore { flag_name, flag_name_prefixed, extern_value: extern_value.to_string(), + has_extern_value: !extern_value.is_empty(), }) } (Some(local_value), None) => { @@ -390,6 +391,7 @@ impl CStore { flag_name, flag_name_prefixed, local_value: local_value.to_string(), + has_local_value: !local_value.is_empty(), }) } (None, None) => panic!("Incorrect target modifiers report_diff(None, None)"), diff --git a/compiler/rustc_metadata/src/diagnostics.rs b/compiler/rustc_metadata/src/diagnostics.rs index 659406f8c84e9..01456377a234f 100644 --- a/compiler/rustc_metadata/src/diagnostics.rs +++ b/compiler/rustc_metadata/src/diagnostics.rs @@ -606,10 +606,16 @@ pub(crate) struct IncompatibleTargetModifiers { "the `{$flag_name_prefixed}` flag modifies the ABI so Rust crates compiled with different values of this flag cannot be used together safely" )] #[note( - "unset `{$flag_name_prefixed}` in this crate is incompatible with `{$flag_name_prefixed}={$extern_value}` in dependency `{$extern_crate}`" + "`{$flag_name_prefixed}` is unset in this crate which is incompatible with {$has_extern_value -> + [false] `{$flag_name_prefixed}` being set + *[other] `{$flag_name_prefixed}={$extern_value}` + } in dependency `{$extern_crate}`" )] #[help( - "set `{$flag_name_prefixed}={$extern_value}` in this crate or unset `{$flag_name_prefixed}` in `{$extern_crate}`" + "set {$has_extern_value -> + [false] `{$flag_name_prefixed}` + *[other] `{$flag_name_prefixed}={$extern_value}` + } in this crate or unset `{$flag_name_prefixed}` in `{$extern_crate}`" )] #[help( "if you are sure this will not cause problems, you may use `-Cunsafe-allow-abi-mismatch={$flag_name}` to silence this error" @@ -622,6 +628,7 @@ pub(crate) struct IncompatibleTargetModifiersLMissed { pub flag_name: String, pub flag_name_prefixed: String, pub extern_value: String, + pub has_extern_value: bool, } #[derive(Diagnostic)] @@ -630,10 +637,16 @@ pub(crate) struct IncompatibleTargetModifiersLMissed { "the `{$flag_name_prefixed}` flag modifies the ABI so Rust crates compiled with different values of this flag cannot be used together safely" )] #[note( - "`{$flag_name_prefixed}={$local_value}` in this crate is incompatible with unset `{$flag_name_prefixed}` in dependency `{$extern_crate}`" + "{$has_local_value -> + [false] `{$flag_name_prefixed}` being set + *[other] `{$flag_name_prefixed}={$local_value}` + } in this crate is incompatible with `{$flag_name_prefixed}` being unset in dependency `{$extern_crate}`" )] #[help( - "unset `{$flag_name_prefixed}` in this crate or set `{$flag_name_prefixed}={$local_value}` in `{$extern_crate}`" + "unset `{$flag_name_prefixed}` in this crate or set {$has_local_value -> + [false] `{$flag_name_prefixed}` + *[other] `{$flag_name_prefixed}={$local_value}` + } in `{$extern_crate}`" )] #[help( "if you are sure this will not cause problems, you may use `-Cunsafe-allow-abi-mismatch={$flag_name}` to silence this error" @@ -646,6 +659,7 @@ pub(crate) struct IncompatibleTargetModifiersRMissed { pub flag_name: String, pub flag_name_prefixed: String, pub local_value: String, + pub has_local_value: bool, } #[derive(Diagnostic)] diff --git a/tests/ui/target_modifiers/defaults_check.error.stderr b/tests/ui/target_modifiers/defaults_check.error.stderr index 644ec079ee3c7..106e64ff29356 100644 --- a/tests/ui/target_modifiers/defaults_check.error.stderr +++ b/tests/ui/target_modifiers/defaults_check.error.stderr @@ -5,7 +5,7 @@ LL | #![feature(no_core)] | ^ | = help: the `-Zreg-struct-return` flag modifies the ABI so Rust crates compiled with different values of this flag cannot be used together safely - = note: `-Zreg-struct-return=true` in this crate is incompatible with unset `-Zreg-struct-return` in dependency `default_reg_struct_return` + = note: `-Zreg-struct-return=true` in this crate is incompatible with `-Zreg-struct-return` being unset in dependency `default_reg_struct_return` = help: unset `-Zreg-struct-return` in this crate or set `-Zreg-struct-return=true` in `default_reg_struct_return` = help: if you are sure this will not cause problems, you may use `-Cunsafe-allow-abi-mismatch=reg-struct-return` to silence this error diff --git a/tests/ui/target_modifiers/incompatible_fixedx18.error_generated.stderr b/tests/ui/target_modifiers/incompatible_fixedx18.error_generated.stderr index a8c13e0ed896d..bcdee625830a7 100644 --- a/tests/ui/target_modifiers/incompatible_fixedx18.error_generated.stderr +++ b/tests/ui/target_modifiers/incompatible_fixedx18.error_generated.stderr @@ -5,8 +5,8 @@ LL | #![feature(no_core)] | ^ | = help: the `-Zfixed-x18` flag modifies the ABI so Rust crates compiled with different values of this flag cannot be used together safely - = note: unset `-Zfixed-x18` in this crate is incompatible with `-Zfixed-x18=` in dependency `fixed_x18` - = help: set `-Zfixed-x18=` in this crate or unset `-Zfixed-x18` in `fixed_x18` + = note: `-Zfixed-x18` is unset in this crate which is incompatible with `-Zfixed-x18` being set in dependency `fixed_x18` + = help: set `-Zfixed-x18` in this crate or unset `-Zfixed-x18` in `fixed_x18` = help: if you are sure this will not cause problems, you may use `-Cunsafe-allow-abi-mismatch=fixed-x18` to silence this error error: aborting due to 1 previous error diff --git a/tests/ui/target_modifiers/no_value_bool.error.stderr b/tests/ui/target_modifiers/no_value_bool.error.stderr index 479f7fb47cb72..c0e3178b89cf2 100644 --- a/tests/ui/target_modifiers/no_value_bool.error.stderr +++ b/tests/ui/target_modifiers/no_value_bool.error.stderr @@ -5,7 +5,7 @@ LL | #![feature(no_core)] | ^ | = help: the `-Zreg-struct-return` flag modifies the ABI so Rust crates compiled with different values of this flag cannot be used together safely - = note: unset `-Zreg-struct-return` in this crate is incompatible with `-Zreg-struct-return=true` in dependency `enabled_reg_struct_return` + = note: `-Zreg-struct-return` is unset in this crate which is incompatible with `-Zreg-struct-return=true` in dependency `enabled_reg_struct_return` = help: set `-Zreg-struct-return=true` in this crate or unset `-Zreg-struct-return` in `enabled_reg_struct_return` = help: if you are sure this will not cause problems, you may use `-Cunsafe-allow-abi-mismatch=reg-struct-return` to silence this error diff --git a/tests/ui/target_modifiers/no_value_bool.error_explicit.stderr b/tests/ui/target_modifiers/no_value_bool.error_explicit.stderr index 479f7fb47cb72..c0e3178b89cf2 100644 --- a/tests/ui/target_modifiers/no_value_bool.error_explicit.stderr +++ b/tests/ui/target_modifiers/no_value_bool.error_explicit.stderr @@ -5,7 +5,7 @@ LL | #![feature(no_core)] | ^ | = help: the `-Zreg-struct-return` flag modifies the ABI so Rust crates compiled with different values of this flag cannot be used together safely - = note: unset `-Zreg-struct-return` in this crate is incompatible with `-Zreg-struct-return=true` in dependency `enabled_reg_struct_return` + = note: `-Zreg-struct-return` is unset in this crate which is incompatible with `-Zreg-struct-return=true` in dependency `enabled_reg_struct_return` = help: set `-Zreg-struct-return=true` in this crate or unset `-Zreg-struct-return` in `enabled_reg_struct_return` = help: if you are sure this will not cause problems, you may use `-Cunsafe-allow-abi-mismatch=reg-struct-return` to silence this error diff --git a/tests/ui/target_modifiers/sanitizer-kcfi-normalize-ints.wrong_flag.stderr b/tests/ui/target_modifiers/sanitizer-kcfi-normalize-ints.wrong_flag.stderr index 1db79b025e9c9..c6abc4b574322 100644 --- a/tests/ui/target_modifiers/sanitizer-kcfi-normalize-ints.wrong_flag.stderr +++ b/tests/ui/target_modifiers/sanitizer-kcfi-normalize-ints.wrong_flag.stderr @@ -5,8 +5,8 @@ LL | #![feature(no_core)] | ^ | = help: the `-Zsanitizer-cfi-normalize-integers` flag modifies the ABI so Rust crates compiled with different values of this flag cannot be used together safely - = note: unset `-Zsanitizer-cfi-normalize-integers` in this crate is incompatible with `-Zsanitizer-cfi-normalize-integers=` in dependency `kcfi_normalize_ints` - = help: set `-Zsanitizer-cfi-normalize-integers=` in this crate or unset `-Zsanitizer-cfi-normalize-integers` in `kcfi_normalize_ints` + = note: `-Zsanitizer-cfi-normalize-integers` is unset in this crate which is incompatible with `-Zsanitizer-cfi-normalize-integers` being set in dependency `kcfi_normalize_ints` + = help: set `-Zsanitizer-cfi-normalize-integers` in this crate or unset `-Zsanitizer-cfi-normalize-integers` in `kcfi_normalize_ints` = help: if you are sure this will not cause problems, you may use `-Cunsafe-allow-abi-mismatch=sanitizer-cfi-normalize-integers` to silence this error error: aborting due to 1 previous error diff --git a/tests/ui/target_modifiers/sanitizer-kcfi-normalize-ints.wrong_sanitizer.stderr b/tests/ui/target_modifiers/sanitizer-kcfi-normalize-ints.wrong_sanitizer.stderr index 5b949c87350b9..79e8ffbf04a5b 100644 --- a/tests/ui/target_modifiers/sanitizer-kcfi-normalize-ints.wrong_sanitizer.stderr +++ b/tests/ui/target_modifiers/sanitizer-kcfi-normalize-ints.wrong_sanitizer.stderr @@ -5,7 +5,7 @@ LL | #![feature(no_core)] | ^ | = help: the `-Zsanitizer` flag modifies the ABI so Rust crates compiled with different values of this flag cannot be used together safely - = note: unset `-Zsanitizer` in this crate is incompatible with `-Zsanitizer=kcfi` in dependency `kcfi_normalize_ints` + = note: `-Zsanitizer` is unset in this crate which is incompatible with `-Zsanitizer=kcfi` in dependency `kcfi_normalize_ints` = help: set `-Zsanitizer=kcfi` in this crate or unset `-Zsanitizer` in `kcfi_normalize_ints` = help: if you are sure this will not cause problems, you may use `-Cunsafe-allow-abi-mismatch=sanitizer` to silence this error diff --git a/tests/ui/target_modifiers/sanitizers-safestack-and-kcfi.missed_both.stderr b/tests/ui/target_modifiers/sanitizers-safestack-and-kcfi.missed_both.stderr index 44a2c24b2324e..adb627925c610 100644 --- a/tests/ui/target_modifiers/sanitizers-safestack-and-kcfi.missed_both.stderr +++ b/tests/ui/target_modifiers/sanitizers-safestack-and-kcfi.missed_both.stderr @@ -5,7 +5,7 @@ LL | #![feature(no_core)] | ^ | = help: the `-Zsanitizer` flag modifies the ABI so Rust crates compiled with different values of this flag cannot be used together safely - = note: unset `-Zsanitizer` in this crate is incompatible with `-Zsanitizer=safestack,kcfi` in dependency `safestack_and_kcfi` + = note: `-Zsanitizer` is unset in this crate which is incompatible with `-Zsanitizer=safestack,kcfi` in dependency `safestack_and_kcfi` = help: set `-Zsanitizer=safestack,kcfi` in this crate or unset `-Zsanitizer` in `safestack_and_kcfi` = help: if you are sure this will not cause problems, you may use `-Cunsafe-allow-abi-mismatch=sanitizer` to silence this error From 078858da826f915e1d28d1332bdef768ebc3fd6e Mon Sep 17 00:00:00 2001 From: kulst Date: Tue, 6 Jan 2026 21:42:06 +0100 Subject: [PATCH 04/23] Treat `-Ctarget-cpu` as a target-modifier when targeting AVR, AMDGCN and NVPTX For AVR, AMDGCN, and NVPTX, crates built with different target CPU values are not generally link-compatible. Add a `requires_consistent_cpu` flag to the target spec and enable it for these targets. When the flag is set, treat `-Ctarget-cpu` as a target modifier and require all linked crates to agree on its value. Reject `-Ctarget-cpu=native` before codegen for targets that set `requires_consistent_cpu` to true. Also do not include `native` in the printed `target-cpus` list for such targets. Add tests covering: - which built-in targets set `requires-consistent-cpu` - cross-crate behavior with and without `requires-consistent-cpu` - that an omitted `-Ctarget-cpu` compares equal to an explicitly specified default CPU - rejection and printing behavior for `native` - precedence of repeated `-Ctarget-cpu` flags in metadata comparison and LLVM IR --- compiler/rustc_codegen_cranelift/src/lib.rs | 4 +- compiler/rustc_codegen_gcc/src/gcc_util.rs | 3 +- compiler/rustc_codegen_llvm/src/llvm_util.rs | 12 +- compiler/rustc_metadata/src/rmeta/decoder.rs | 20 +++- compiler/rustc_session/src/config.rs | 3 + compiler/rustc_session/src/diagnostics.rs | 14 +++ compiler/rustc_session/src/options.rs | 30 ++++- compiler/rustc_session/src/session.rs | 13 +- compiler/rustc_target/src/spec/json.rs | 3 + compiler/rustc_target/src/spec/mod.rs | 14 +++ .../src/spec/targets/amdgcn_amd_amdhsa.rs | 2 + .../rustc_target/src/spec/targets/avr_none.rs | 2 + .../src/spec/targets/nvptx64_nvidia_cuda.rs | 3 + .../src/platform-support/amdgcn-amd-amdhsa.md | 8 +- .../rustc/src/platform-support/avr-none.md | 8 +- .../platform-support/nvptx64-nvidia-cuda.md | 3 +- .../unstable-book/src/compiler-flags/ls.md | 1 + .../empty.rs | 3 + .../rmake.rs | 73 ++++++++++++ .../dependency.rs | 3 + .../target-cpu-as-target-modifier/main.rs | 5 + .../target-cpu-as-target-modifier/rmake.rs | 111 ++++++++++++++++++ tests/run-make/target-cpu-precedence/lib.rs | 37 ++++++ tests/run-make/target-cpu-precedence/rmake.rs | 41 +++++++ .../target-specs/require-explicit-cpu.json | 3 +- tests/ui/target-cpu/explicit-target-cpu.rs | 20 +--- .../auxiliary/target_cpu_default_explicit.rs | 8 ++ .../auxiliary/target_cpu_default_implicit.rs | 8 ++ .../auxiliary/target_cpu_non_default.rs | 8 ++ ...arget_cpu_default.explicit_mismatch.stderr | 13 ++ ...arget_cpu_default.implicit_mismatch.stderr | 13 ++ .../ui/target_modifiers/target_cpu_default.rs | 38 ++++++ 32 files changed, 495 insertions(+), 32 deletions(-) create mode 100644 tests/run-make/requires-consistent-cpu-no-native/empty.rs create mode 100644 tests/run-make/requires-consistent-cpu-no-native/rmake.rs create mode 100644 tests/run-make/target-cpu-as-target-modifier/dependency.rs create mode 100644 tests/run-make/target-cpu-as-target-modifier/main.rs create mode 100644 tests/run-make/target-cpu-as-target-modifier/rmake.rs create mode 100644 tests/run-make/target-cpu-precedence/lib.rs create mode 100644 tests/run-make/target-cpu-precedence/rmake.rs create mode 100644 tests/ui/target_modifiers/auxiliary/target_cpu_default_explicit.rs create mode 100644 tests/ui/target_modifiers/auxiliary/target_cpu_default_implicit.rs create mode 100644 tests/ui/target_modifiers/auxiliary/target_cpu_non_default.rs create mode 100644 tests/ui/target_modifiers/target_cpu_default.explicit_mismatch.stderr create mode 100644 tests/ui/target_modifiers/target_cpu_default.implicit_mismatch.stderr create mode 100644 tests/ui/target_modifiers/target_cpu_default.rs diff --git a/compiler/rustc_codegen_cranelift/src/lib.rs b/compiler/rustc_codegen_cranelift/src/lib.rs index af783f31a2136..f5c852a2bf970 100644 --- a/compiler/rustc_codegen_cranelift/src/lib.rs +++ b/compiler/rustc_codegen_cranelift/src/lib.rs @@ -42,7 +42,7 @@ use rustc_codegen_ssa::{CompiledModules, CrateInfo, TargetConfig, back}; use rustc_log::tracing::info; use rustc_middle::dep_graph::WorkProductMap; use rustc_session::Session; -use rustc_session::config::OutputFilenames; +use rustc_session::config::{NATIVE_CPU, OutputFilenames}; use rustc_span::{Symbol, sym}; use rustc_target::spec::{Arch, CfgAbi, Env, Os}; @@ -341,7 +341,7 @@ fn build_isa(sess: &Session, jit: bool) -> Arc { let flags = settings::Flags::new(flags_builder); let isa_builder = match sess.opts.cg.target_cpu.as_deref() { - Some("native") => cranelift_native::builder_with_options(true).unwrap(), + Some(NATIVE_CPU) => cranelift_native::builder_with_options(true).unwrap(), Some(value) => { let mut builder = cranelift_codegen::isa::lookup(target_triple.clone()).unwrap_or_else(|err| { diff --git a/compiler/rustc_codegen_gcc/src/gcc_util.rs b/compiler/rustc_codegen_gcc/src/gcc_util.rs index 330b5ff6828d5..a95b4da28eb63 100644 --- a/compiler/rustc_codegen_gcc/src/gcc_util.rs +++ b/compiler/rustc_codegen_gcc/src/gcc_util.rs @@ -3,6 +3,7 @@ use gccjit::Context; use rustc_codegen_ssa::target_features; use rustc_data_structures::smallvec::{SmallVec, smallvec}; use rustc_session::Session; +use rustc_session::config::NATIVE_CPU; use rustc_target::spec::Arch; fn gcc_features_by_flags(sess: &Session, features: &mut Vec) { @@ -115,7 +116,7 @@ fn arch_to_gcc(name: &str) -> &str { } fn handle_native(name: &str) -> &str { - if name != "native" { + if name != NATIVE_CPU { return arch_to_gcc(name); } diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs index 73b7f699b606d..87d8676cd8018 100644 --- a/compiler/rustc_codegen_llvm/src/llvm_util.rs +++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs @@ -14,7 +14,7 @@ use rustc_data_structures::small_c_str::SmallCStr; use rustc_fs_util::path_to_c_string; use rustc_middle::bug; use rustc_session::Session; -use rustc_session::config::{PrintKind, PrintRequest}; +use rustc_session::config::{NATIVE_CPU, PrintKind, PrintRequest}; use rustc_target::spec::{ Arch, CfgAbi, Env, MergeFunctions, Os, PanicStrategy, SmallDataThresholdSupport, }; @@ -514,10 +514,12 @@ fn print_target_cpus(sess: &Session, tm: &llvm::TargetMachine, out: &mut String) // Only print the "native" entry when host and target are the same arch, // since otherwise it could be wrong or misleading. - if sess.host.arch == sess.target.arch { + // Also do not print it if `requires_consistent_cpu` is set, because in this case + // "native" would be rejected. + if sess.host.arch == sess.target.arch && !sess.target.requires_consistent_cpu { let host = get_host_cpu_name(); cpus.push_front(Cpu { - cpu_name: "native", + cpu_name: NATIVE_CPU, remark: format!(" - Select the CPU of the current host (currently {host})."), }); } @@ -612,7 +614,7 @@ fn get_host_cpu_name() -> &'static str { /// LLVM. Otherwise, the string is returned as-is. fn handle_native(cpu_name: &str) -> &str { match cpu_name { - "native" => get_host_cpu_name(), + NATIVE_CPU => get_host_cpu_name(), _ => cpu_name, } } @@ -666,7 +668,7 @@ pub(crate) fn global_llvm_features(sess: &Session, only_base_features: bool) -> // -Ctarget-cpu=native match sess.opts.cg.target_cpu { - Some(ref s) if s == "native" => { + Some(ref s) if s == NATIVE_CPU => { // We have already figured out the actual CPU name with `LLVMRustGetHostCPUName` and set // that for LLVM, so the features implied by that CPU name will be available everywhere. // However, that is not sufficient: e.g. `skylake` alone is not sufficient to tell if diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs index bed2be51a3468..efd167b7d7598 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder.rs @@ -749,6 +749,7 @@ impl MetadataBlob { "lang_items".to_owned(), "features".to_owned(), "items".to_owned(), + "target_modifiers".to_owned(), ]; let ls_kinds = if ls_kinds.contains(&"all".to_owned()) { &all_ls_kinds } else { ls_kinds }; @@ -918,11 +919,28 @@ impl MetadataBlob { write!(out, "\n")?; } + "target_modifiers" => { + writeln!(out, "=Target modifiers=")?; + + for modifier in root.decode_target_modifiers(self) { + let extended = modifier.extend(); + + writeln!( + out, + "-{}{}={} [{}]", + extended.prefix, + extended.name, + modifier.value_name, + extended.tech_value, + )?; + } + } _ => { writeln!( out, - "unknown -Zls kind. allowed values are: all, root, lang_items, features, items" + "unknown -Zls kind. allowed values are: all, root, lang_items, features, items, \ + target_modifiers" )?; } } diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index 8ed01315ade53..37488ebbf1e8f 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -46,6 +46,9 @@ mod native_libs; mod print_request; pub mod sigpipe; +/// Special CPU name requesting the CPU of the current host. +pub const NATIVE_CPU: &str = "native"; + /// The different settings that the `-C strip` flag can have. #[derive(Clone, Copy, PartialEq, Hash, Debug)] pub enum Strip { diff --git a/compiler/rustc_session/src/diagnostics.rs b/compiler/rustc_session/src/diagnostics.rs index d1989609cefe2..29f60c7612b4b 100644 --- a/compiler/rustc_session/src/diagnostics.rs +++ b/compiler/rustc_session/src/diagnostics.rs @@ -716,3 +716,17 @@ pub(crate) struct ThinLtoNotSupportedByBackend; #[derive(Diagnostic)] #[diag("`-Zpacked-stack` is only supported on s390x")] pub(crate) struct UnsupportedPackedStack; + +#[derive(Diagnostic)] +#[diag("`-Ctarget-cpu=native` is not allowed for target `{$target_triple}`")] +#[note("this target requires consistent `-Ctarget-cpu` values across all crates")] +#[help( + "specify the target CPU explicitly {$need_explicit_cpu -> + [false] or leave it blank to use the default + *[other] {\"\"} + }" +)] +pub(crate) struct NativeTargetCpuNotAllowed<'a> { + pub(crate) target_triple: &'a TargetTuple, + pub(crate) need_explicit_cpu: bool, +} diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 6f3a505c2c26f..7ea7274cafd8b 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -130,6 +130,28 @@ mod target_modifier_consistency_check { } true } + pub(super) fn target_cpu( + sess: &Session, + l: &TargetModifier, + r: Option<&TargetModifier>, + ) -> bool { + if !sess.target.requires_consistent_cpu { + return true; + } + let l_tech_value = l.extend().tech_value; + let r_tech_value = match r { + Some(r) => r.extend().tech_value, + // If only one of the two compared crates specifies the CPU + // explicitly we compare against the target's default CPU. + None => { + // We reuse the same parsing logic. + CodegenOptionsTargetModifiers::TargetCpu + .reparse(sess.target.cpu.as_ref()) + .tech_value + } + }; + l_tech_value == r_tech_value + } } impl TargetModifier { @@ -152,7 +174,11 @@ impl TargetModifier { } _ => {} }, - _ => {} + OptionsTargetModifiers::CodegenOptions(codegen) => match codegen { + CodegenOptionsTargetModifiers::TargetCpu => { + return target_modifier_consistency_check::target_cpu(sess, self, other); + } + }, }; match other { Some(other) => self.extend().tech_value == other.extend().tech_value, @@ -2273,7 +2299,7 @@ options! { symbol_mangling_version: Option = (None, parse_symbol_mangling_version, [TRACKED], "which mangling version to use for symbol names ('legacy', 'v0' (default), or 'hashed')"), - target_cpu: Option = (None, parse_opt_string, [TRACKED], + target_cpu: Option = (None, parse_opt_string, [TRACKED] { TARGET_MODIFIER: TargetCpu }, "select target processor (`rustc --print target-cpus` for details)"), target_feature: String = (String::new(), parse_target_feature, [TRACKED], "target specific attributes. (`rustc --print target-features` for details). \ diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 733470a7e0471..ab704787191e5 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -38,8 +38,8 @@ use crate::code_stats::CodeStats; pub use crate::code_stats::{DataTypeKind, FieldInfo, FieldKind, SizeKind, VariantInfo}; use crate::config::{ self, Cfg, CheckCfg, CoverageLevel, CoverageOptions, CrateType, DebugInfo, ErrorOutputType, - FunctionReturn, Input, InstrumentCoverage, InstrumentMcount, OptLevel, OutFileName, OutputType, - PointerAuthOption, SwitchWithOptPath, + FunctionReturn, Input, InstrumentCoverage, InstrumentMcount, NATIVE_CPU, OptLevel, OutFileName, + OutputType, PointerAuthOption, SwitchWithOptPath, }; use crate::filesearch::FileSearch; use crate::lint::LintId; @@ -1695,6 +1695,15 @@ fn validate_commandline_args_with_session_available(sess: &Session) { sess.dcx().emit_err(diagnostics::UnsupportedPackedStack); } } + + if let Some(ref cpu_name) = sess.opts.cg.target_cpu { + if cpu_name == NATIVE_CPU && sess.target.requires_consistent_cpu { + sess.dcx().emit_fatal(diagnostics::NativeTargetCpuNotAllowed { + target_triple: &sess.opts.target_triple, + need_explicit_cpu: sess.target.need_explicit_cpu, + }); + } + } } /// Holds data on the current incremental compilation session, if there is one. diff --git a/compiler/rustc_target/src/spec/json.rs b/compiler/rustc_target/src/spec/json.rs index 922b1fb3dff8d..03dbbcb5b481d 100644 --- a/compiler/rustc_target/src/spec/json.rs +++ b/compiler/rustc_target/src/spec/json.rs @@ -116,6 +116,7 @@ impl Target { forward!(asm_args); forward!(cpu); forward!(need_explicit_cpu); + forward!(requires_consistent_cpu); forward!(unsupported_cpus); forward!(features); forward!(dynamic_linking); @@ -321,6 +322,7 @@ impl ToJson for Target { target_option_val!(asm_args); target_option_val!(cpu); target_option_val!(need_explicit_cpu); + target_option_val!(requires_consistent_cpu); target_option_val!(unsupported_cpus); target_option_val!(features); target_option_val!(dynamic_linking); @@ -543,6 +545,7 @@ struct TargetSpecJson { asm_args: Option]>>, cpu: Option>, need_explicit_cpu: Option, + requires_consistent_cpu: Option, unsupported_cpus: Option]>>, features: Option>, dynamic_linking: Option, diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index caac3bbc7c505..a747b0aec7b28 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -1,3 +1,4 @@ +// ignore-tidy-filelength //! [Flexible target specification.](https://github.com/rust-lang/rfcs/pull/131) //! //! Rust targets a wide variety of usecases, and in the interest of flexibility, @@ -2403,6 +2404,10 @@ pub struct TargetOptions { /// Whether a cpu needs to be explicitly set. /// Set to true if there is no default cpu. Defaults to false. pub need_explicit_cpu: bool, + /// Whether `-Ctarget-cpu` is treated as a target modifier. If this is set + /// all crates that are linked together must have been compiled with the + /// same target-cpu. Defaults to false. + pub requires_consistent_cpu: bool, /// A list of CPUs that are provided by LLVM but are considered unsupported by Rust. /// These CPUs are omitted from `--print target-cpus` output and will cause an error /// if used with `-Ctarget-cpu`. @@ -2860,6 +2865,7 @@ impl Default for TargetOptions { asm_args: cvs![], cpu: "generic".into(), need_explicit_cpu: false, + requires_consistent_cpu: false, unsupported_cpus: cvs![], features: "".into(), direct_access_external_data: None, @@ -3636,6 +3642,14 @@ impl Target { } } + // Check that the target cpu constraints make sense. + if self.need_explicit_cpu { + check!( + self.requires_consistent_cpu, + "if `need_explicit_cpu` is set, then `requires_consistent_cpu` must be set" + ); + } + // Check that the given target-features string makes some basic sense. if !self.features.is_empty() { let mut features_enabled = FxHashSet::default(); diff --git a/compiler/rustc_target/src/spec/targets/amdgcn_amd_amdhsa.rs b/compiler/rustc_target/src/spec/targets/amdgcn_amd_amdhsa.rs index d6a2cfc2aab55..9bb36b8147c0b 100644 --- a/compiler/rustc_target/src/spec/targets/amdgcn_amd_amdhsa.rs +++ b/compiler/rustc_target/src/spec/targets/amdgcn_amd_amdhsa.rs @@ -24,6 +24,8 @@ pub(crate) fn target() -> Target { // There are many CPUs, one for each hardware generation. // Require to set one explicitly as there is no good default. need_explicit_cpu: true, + // crates with different `target-cpu`s are not link-compatible for amdgcn + requires_consistent_cpu: true, max_atomic_width: Some(64), diff --git a/compiler/rustc_target/src/spec/targets/avr_none.rs b/compiler/rustc_target/src/spec/targets/avr_none.rs index eaf4e956f55ec..0dcd2428fc703 100644 --- a/compiler/rustc_target/src/spec/targets/avr_none.rs +++ b/compiler/rustc_target/src/spec/targets/avr_none.rs @@ -26,6 +26,8 @@ pub(crate) fn target() -> Target { atomic_cas: false, relocation_model: RelocModel::Static, need_explicit_cpu: true, + // crates with different `target-cpu`s are not link-compatible for AVR + requires_consistent_cpu: true, ..TargetOptions::default() }, } diff --git a/compiler/rustc_target/src/spec/targets/nvptx64_nvidia_cuda.rs b/compiler/rustc_target/src/spec/targets/nvptx64_nvidia_cuda.rs index b74aca48bb9ec..c6f7ee8da5abc 100644 --- a/compiler/rustc_target/src/spec/targets/nvptx64_nvidia_cuda.rs +++ b/compiler/rustc_target/src/spec/targets/nvptx64_nvidia_cuda.rs @@ -29,6 +29,9 @@ pub(crate) fn target() -> Target { "sm_60", "sm_61", "sm_62" ), + // crates with different `target-cpu`s are not link-compatible for NVPTX + requires_consistent_cpu: true, + // FIXME: create tests for the atomics. max_atomic_width: Some(64), diff --git a/src/doc/rustc/src/platform-support/amdgcn-amd-amdhsa.md b/src/doc/rustc/src/platform-support/amdgcn-amd-amdhsa.md index 8934e7085b8d7..e530457348f77 100644 --- a/src/doc/rustc/src/platform-support/amdgcn-amd-amdhsa.md +++ b/src/doc/rustc/src/platform-support/amdgcn-amd-amdhsa.md @@ -61,7 +61,7 @@ Build the library as `cdylib`: crate-type = ["cdylib"] ``` -The target-cpu must be from the list [supported by LLVM] (or printed with `rustc --target amdgcn-amd-amdhsa --print target-cpus`). +The target-cpu[^1] must be from the list [supported by LLVM] (or printed with `rustc --target amdgcn-amd-amdhsa --print target-cpus`). The GPU version on the current system can be found e.g. with [`rocminfo`]. For a GPU series that has xnack support but the target GPU has not, the `-xnack-support` target-feature needs to be enabled. I.e. if the ISA info as printed with [`rocminfo`] says something about `xnack-`, e.g. `gfx1010:xnack-`, add `-Ctarget-feature=-xnack-support` to the rustflags. @@ -78,6 +78,12 @@ rustflags = ["-Ctarget-cpu=gfx1100"] build-std = ["core"] # Optional: "alloc" ``` +[^1]: For this target, crates with different values of `-C target-cpu` are not link-compatible. +Because of this, the compiler ensures that all crates that are linked together +were compiled with the same `target-cpu`. This must be considered when +using `rustc` commands for building. It is less relevant for Cargo, since Cargo +generally uses the same `target-cpu` for all crates in a build. + ## Running Rust programs To run a binary on an AMD GPU, a host runtime is needed. diff --git a/src/doc/rustc/src/platform-support/avr-none.md b/src/doc/rustc/src/platform-support/avr-none.md index 36874387b8048..1862890ca43f3 100644 --- a/src/doc/rustc/src/platform-support/avr-none.md +++ b/src/doc/rustc/src/platform-support/avr-none.md @@ -63,13 +63,19 @@ The final binary will be placed into Note that since AVRs have rather small amounts of registers, ROM and RAM, it's recommended to always use `--release` to avoid running out of space. -Also, please note that specifying `-C target-cpu` is required - here's a list of +Also, please note that specifying `-C target-cpu`[^1] is required - here's a list of the possible variants: https://github.com/llvm/llvm-project/blob/093d4db2f3c874d4683fb01194b00dbb20e5c713/clang/lib/Basic/Targets/AVR.cpp#L32 Note that devices that have no SRAM are not supported, same as when compiling C/C++ programs with avr-gcc or Clang. +[^1]: For this target, crates with different values of `-C target-cpu` are not link-compatible. +Because of this, the compiler ensures that all crates that are linked together +were compiled with the same `target-cpu`. This must be considered when +using `rustc` commands for building. It is less relevant for Cargo, since Cargo +generally uses the same `target-cpu` for all crates in a build. + ## Testing You can use [`simavr`](https://github.com/buserror/simavr) to emulate the diff --git a/src/doc/rustc/src/platform-support/nvptx64-nvidia-cuda.md b/src/doc/rustc/src/platform-support/nvptx64-nvidia-cuda.md index 5c74e7b95d9ab..5ee0ae28cafb2 100644 --- a/src/doc/rustc/src/platform-support/nvptx64-nvidia-cuda.md +++ b/src/doc/rustc/src/platform-support/nvptx64-nvidia-cuda.md @@ -30,7 +30,8 @@ It is often beneficial to specify the target SM architecture, such as `-C target One can use `-C target-feature=+ptx80` to choose a later PTX version without changing the target SM architecture (the default in this case, `ptx78`, requires CUDA driver version 11.8, while `ptx80` would require driver version 12.0). Later PTX versions may allow more efficient code generation. -Although Rust follows LLVM in representing `ptx*` and `sm_*` as target features, they should be thought of as having crate granularity, set via (either via `-Ctarget-cpu` and optionally `-Ctarget-feature`). +For this target, the compiler enforces that all crates built into a binary use the same value for `-C target-cpu`. Therefore, if specifying a value different from the current default (`sm_70`), it is necessary to also build `core` manually with this value. This is possible by invoking cargo with `-Z build-std=core` using a nightly toolchain. +Although Rust follows LLVM in representing `ptx*` and `sm_*` as target features, they should also be thought of as having binary granularity. However, this is not enforced by the compiler. When building crates together, the same value for all crates should be set via `-C target-feature`. While the compiler accepts `#[target_feature(enable = "ptx80", enable = "sm_89")]`, it is not supported, may not behave as intended, and may become erroneous in the future. ## Minimum SM and PTX support by Rust version diff --git a/src/doc/unstable-book/src/compiler-flags/ls.md b/src/doc/unstable-book/src/compiler-flags/ls.md index 812f2eee1d57f..bf94a6c1156ae 100644 --- a/src/doc/unstable-book/src/compiler-flags/ls.md +++ b/src/doc/unstable-book/src/compiler-flags/ls.md @@ -14,6 +14,7 @@ Allowed values are: - `lang_items`: Language items used and missing, if any. - `features`: Library features defined via the `#[stable]` and `#[unstable]` internal attributes. - `items`: All items (such as modules, functions...) in the crate, including attributes like their visibility +- `target_modifiers`: Values of command-line arguments that rustc may require to match across linked crates. - `all`: All of the above ## Example diff --git a/tests/run-make/requires-consistent-cpu-no-native/empty.rs b/tests/run-make/requires-consistent-cpu-no-native/empty.rs new file mode 100644 index 0000000000000..c12c35de302a1 --- /dev/null +++ b/tests/run-make/requires-consistent-cpu-no-native/empty.rs @@ -0,0 +1,3 @@ +#![feature(no_core)] +#![crate_type = "rlib"] +#![no_core] diff --git a/tests/run-make/requires-consistent-cpu-no-native/rmake.rs b/tests/run-make/requires-consistent-cpu-no-native/rmake.rs new file mode 100644 index 0000000000000..6fdb68dd513e2 --- /dev/null +++ b/tests/run-make/requires-consistent-cpu-no-native/rmake.rs @@ -0,0 +1,73 @@ +// Check how `native` interacts with targets that require consistent +// `-Ctarget-cpu` values across crates. +// +// First, the test derives a custom target from the host target and removes +// `requires-consistent-cpu` if present. Since host and target architecture match, +// this target prints `native` in `--print target-cpus`. +// Then, the test derives a second custom target from the host target and sets +// `requires-consistent-cpu` to true. This target must not print `native` in +// `--print target-cpus`. +// Finally, the test verifies that `-Ctarget-cpu=native` is rejected when using +// the second custom target. + +use std::fs; + +use run_make_support::*; +use serde_json::{Value, json}; + +fn main() { + let is_native_cpu_line = |line: &str| line.trim_start().starts_with("native "); + let target_cpus = |target: &str| { + rustc().target(target).arg("-Zunstable-options").print("target-cpus").run().stdout_utf8() + }; + + let host = rustc().print("host-tuple").run().stdout_utf8().trim().to_owned(); + + let host_target_with_cpu_mismatch_allowed = custom_host(&host, false); + let cpus = target_cpus(&host_target_with_cpu_mismatch_allowed); + assert!( + cpus.lines().any(is_native_cpu_line), + "`native` should be printed for the host target without `requires-consistent-cpu`;\n\ + output was:\n{cpus}" + ); + + let host_target_with_requires_consistent_cpu = custom_host(&host, true); + let cpus = target_cpus(&host_target_with_requires_consistent_cpu); + assert!( + !cpus.lines().any(is_native_cpu_line), + "`native` must not be printed for targets with `requires-consistent-cpu`;\n\ + output was:\n{cpus}" + ); + + rustc() + .arg("-Zunstable-options") + .target(&host_target_with_requires_consistent_cpu) + .target_cpu("native") + .input("empty.rs") + .run_fail() + .assert_stderr_contains("`-Ctarget-cpu=native` is not allowed") + .assert_stderr_contains("requires consistent `-Ctarget-cpu` values"); +} + +fn custom_host(host: &str, requires_consistent_cpu: bool) -> String { + let json = rustc() + .arg("-Zunstable-options") + .target(host) + .print("target-spec-json") + .run() + .stdout_utf8(); + + let mut spec: Value = serde_json::from_str(&json).unwrap(); + let spec = spec.as_object_mut().expect("expected target-spec JSON to be an object"); + let filename = if requires_consistent_cpu { + spec.insert("requires-consistent-cpu".to_string(), json!(true)); + format!("{host}-requires-consistent-cpu.json") + } else { + spec.remove("requires-consistent-cpu"); + format!("{host}-cpu-mismatch-allowed.json") + }; + + fs::write(&filename, serde_json::to_string_pretty(&spec).unwrap()).unwrap(); + + filename +} diff --git a/tests/run-make/target-cpu-as-target-modifier/dependency.rs b/tests/run-make/target-cpu-as-target-modifier/dependency.rs new file mode 100644 index 0000000000000..c12c35de302a1 --- /dev/null +++ b/tests/run-make/target-cpu-as-target-modifier/dependency.rs @@ -0,0 +1,3 @@ +#![feature(no_core)] +#![crate_type = "rlib"] +#![no_core] diff --git a/tests/run-make/target-cpu-as-target-modifier/main.rs b/tests/run-make/target-cpu-as-target-modifier/main.rs new file mode 100644 index 0000000000000..bc32fc9c304b7 --- /dev/null +++ b/tests/run-make/target-cpu-as-target-modifier/main.rs @@ -0,0 +1,5 @@ +#![feature(no_core)] +#![crate_type = "rlib"] +#![no_core] + +extern crate dependency; diff --git a/tests/run-make/target-cpu-as-target-modifier/rmake.rs b/tests/run-make/target-cpu-as-target-modifier/rmake.rs new file mode 100644 index 0000000000000..a3be062dd8090 --- /dev/null +++ b/tests/run-make/target-cpu-as-target-modifier/rmake.rs @@ -0,0 +1,111 @@ +// This test verifies that only the expected built-in targets opt in to treating +// `-C target-cpu` as a target modifier. +// +// The test first asks rustc for the full list of supported built-in targets, +// then performs two checks for each target. +// +// 1. Target-spec check +// +// The test prints the target specification as JSON and verifies that +// `requires-consistent-cpu` is set to `true` only for the expected targets. +// All other targets must either omit the field or set it to `false`. +// +// 2. Cross-crate compatibility check +// +// The test builds `dependency.rs` with target CPU `A`, then builds `main.rs` +// against that dependency under several configurations. +// +// The `main.rs` build must succeed when: +// - it is built with the same target CPU `A`; +// - it is built with a different target CPU `B`, but the target does not opt +// in to `requires-consistent-cpu`; +// - it is built with a different target CPU `B`, the target does opt in to +// `requires-consistent-cpu`, and the compiler is invoked with +// `-C unsafe-allow-abi-mismatch=target-cpu`. +// +// The `main.rs` build must fail when it is built with target CPU `B`, the +// target opts in to `requires-consistent-cpu`, and +// `-C unsafe-allow-abi-mismatch=target-cpu` is not used. +// +// The test only verifies the target-modifier compatibility check, so it does +// not need to run code generation and uses `--emit=metadata`. +// +// To avoid depending on whether a target is supported by the selected codegen +// backend, the test also uses `-Z codegen-backend=dummy`. + +use std::collections::BTreeSet; +use std::sync::LazyLock; + +use run_make_support::*; +use serde_json::Value; + +static EXPECTED: LazyLock> = + LazyLock::new(|| BTreeSet::from(["amdgcn-amd-amdhsa", "avr-none", "nvptx64-nvidia-cuda"])); + +use run_make_support::rustc; +fn main() { + verify_target_specs(); + verify_cross_crate_compatibility(); +} + +fn verify_target_specs() { + let requires_consistent_cpu = |spec: &Value| { + spec.get("requires-consistent-cpu").and_then(Value::as_bool).unwrap_or(false) + }; + + let json = rustc().arg("-Zunstable-options").print("all-target-specs-json").run().stdout_utf8(); + + let specs: Value = serde_json::from_str(&json).unwrap(); + + let actual = specs + .as_object() + .expect("expected all-target-specs-json to be a JSON object") + .iter() + .filter_map(|(target, spec)| requires_consistent_cpu(spec).then_some(target.as_str())) + .collect::>(); + + assert_eq!( + actual, *EXPECTED, + "unexpected set of built-in targets with `requires-consistent-cpu = true`", + ); +} + +fn verify_cross_crate_compatibility() { + let target_list = rustc().print("target-list").run().stdout_utf8(); + let targets: Vec<&str> = target_list.lines().collect(); + + for target in targets.iter() { + let compiler = |cpu: &str, input: &str| { + let mut cmd = rustc(); + cmd.target(target) + .target_cpu(cpu) + .input(input) + .panic("abort") + .args(["--emit=metadata", "-Zcodegen-backend=dummy"]); + cmd + }; + let (first_cpu, second_cpu) = ("A", "B"); + + // Build dependency.rs using the first target-cpu + compiler(first_cpu, "dependency.rs").run(); + + if EXPECTED.contains(target) { + // Testing targets where `-Ctarget-cpu` acts as a target modifier: + // Building with the same target cpu must succeed. + compiler(first_cpu, "main.rs").run(); + // Building with a different target cpu must succeed if + // rustc is invoked with `-Cunsafe-allow-abi-mismatch=target-cpu` + compiler(second_cpu, "main.rs").arg("-Cunsafe-allow-abi-mismatch=target-cpu").run(); + // Building with a different target cpu must fail if + // rustc is _not_ invoked with `-Cunsafe-allow-abi-mismatch=target-cpu` + compiler(second_cpu, "main.rs").run_fail().assert_stderr_contains( + "error: mixing `-Ctarget-cpu` will cause \ + an ABI mismatch in crate `main`", + ); + } else { + // Testing targets where `-Ctarget-cpu` does not act as a target modifier: + // Building with a different target cpu must succeed. + compiler(second_cpu, "main.rs").run(); + } + } +} diff --git a/tests/run-make/target-cpu-precedence/lib.rs b/tests/run-make/target-cpu-precedence/lib.rs new file mode 100644 index 0000000000000..3f92f54eb357e --- /dev/null +++ b/tests/run-make/target-cpu-precedence/lib.rs @@ -0,0 +1,37 @@ +#![feature(no_core, lang_items)] +#![no_core] +#![crate_type = "rlib"] + +#[lang = "pointee_sized"] +#[diagnostic::on_unimplemented( + message = "values of type `{Self}` may or may not have a size", + label = "may or may not have a known size" +)] +pub trait PointeeSized {} + +#[lang = "meta_sized"] +#[diagnostic::on_unimplemented( + message = "the size for values of type `{Self}` cannot be known", + label = "doesn't have a known size" +)] +pub trait MetaSized: PointeeSized {} + +#[lang = "sized"] +#[diagnostic::on_unimplemented( + message = "the size for values of type `{Self}` cannot be known at compilation time", + label = "doesn't have a size known at compile-time" +)] +pub trait Sized: MetaSized {} + +// Capture the effective CPU from LLVM IR. This also verifies that the second +// `-Ctarget-cpu` argument took precedence. +// CHECK-LABEL: target triple = "nvptx64-nvidia-cuda" +// CHECK-LABEL: define {{.*}} @foo() {{.*}} #0 +// CHECK-LABEL: attributes #0 = {{.*}} "target-cpu"="sm_80" {{.*}} +#[no_mangle] +pub fn foo() { + () +} +// The value reconstructed from crate metadata must be identical. +// CHECK-LABEL: =Target modifiers= +// CHECK-LABEL: -Ctarget-cpu=sm_80 [Some("sm_80")] diff --git a/tests/run-make/target-cpu-precedence/rmake.rs b/tests/run-make/target-cpu-precedence/rmake.rs new file mode 100644 index 0000000000000..13dfcd72e3891 --- /dev/null +++ b/tests/run-make/target-cpu-precedence/rmake.rs @@ -0,0 +1,41 @@ +// Check that, when rustc is invoked with multiple `-Ctarget-cpu` options, +// the last value is used both for the corresponding target modifier in the +// crate metadata and for the target CPU recorded in the LLVM IR. +//@ needs-llvm-components: nvptx + +use run_make_support::*; + +const TARGET: &str = "nvptx64-nvidia-cuda"; +const FIRST_CPU: &str = "sm_70"; +const LAST_CPU: &str = "sm_80"; + +fn main() { + // Compile lib.rs, emit llvm-ir and metadata + rustc() + .input("lib.rs") + .crate_name("target_cpu_precedence") + .target(TARGET) + .target_cpu(FIRST_CPU) + .target_cpu(LAST_CPU) + .emit("llvm-ir=output.ll,metadata=output.rmeta") + .run(); + + let llvm_ir = rfs::read_to_string("output.ll"); + // Decode the metadata. + let target_modifiers = + rustc().arg("-Zls=target_modifiers").input("output.rmeta").run().stdout_utf8(); + + // Make sure the first target cpu did not survive in either artifact. + assert_not_contains(&llvm_ir, format!(r#""target-cpu"="{FIRST_CPU}""#)); + assert_not_contains(&target_modifiers, format!(r#"target-cpu: Some("{FIRST_CPU}")"#)); + + // Combine LLVM-IR and the metadata output into one file. + // Use FileCheck to verify that both LLVM IR and metadata contain the + // last mentioned target-cpu + let filecheck_input = format!( + "{llvm_ir}\n\ + ; --- target modifiers decoded from output.rmeta ---\n\ + {target_modifiers}" + ); + llvm_filecheck().patterns("lib.rs").stdin_buf(filecheck_input).run(); +} diff --git a/tests/run-make/target-specs/require-explicit-cpu.json b/tests/run-make/target-specs/require-explicit-cpu.json index 4f23b644d8cf6..28de8f64ba597 100644 --- a/tests/run-make/target-specs/require-explicit-cpu.json +++ b/tests/run-make/target-specs/require-explicit-cpu.json @@ -6,5 +6,6 @@ "target-pointer-width": 32, "arch": "x86", "os": "linux", - "need-explicit-cpu": true + "need-explicit-cpu": true, + "requires-consistent-cpu": true } diff --git a/tests/ui/target-cpu/explicit-target-cpu.rs b/tests/ui/target-cpu/explicit-target-cpu.rs index cfec444372728..29f8e9de1f6ea 100644 --- a/tests/ui/target-cpu/explicit-target-cpu.rs +++ b/tests/ui/target-cpu/explicit-target-cpu.rs @@ -21,24 +21,14 @@ //@[avr_cpu] needs-llvm-components: avr //@[avr_cpu] compile-flags: -Ctarget-cpu=atmega328p //@[avr_cpu] build-pass + //@ ignore-backends: gcc #![crate_type = "rlib"] - -// FIXME(#140038): this can't use `minicore` yet because `minicore` doesn't currently propagate the -// `-C target-cpu` for targets that *require* a `target-cpu` being specified. -#![feature(no_core, lang_items)] +// We don't want to link in any other crate as this would make it necessary to specify +// a `-Ctarget-cpu` for them resulting in a *target-modifier* disagreement error instead of the +// error mentioned below. +#![feature(no_core)] #![no_core] -#[lang = "pointee_sized"] -pub trait PointeeSized {} - -#[lang = "meta_sized"] -pub trait MetaSized: PointeeSized {} - -#[lang="sized"] -trait Sized {} - -pub fn foo() {} - //[amdgcn_nocpu,avr_nocpu]~? ERROR target requires explicitly specifying a cpu with `-C target-cpu` diff --git a/tests/ui/target_modifiers/auxiliary/target_cpu_default_explicit.rs b/tests/ui/target_modifiers/auxiliary/target_cpu_default_explicit.rs new file mode 100644 index 0000000000000..3c56f64dfdb3a --- /dev/null +++ b/tests/ui/target_modifiers/auxiliary/target_cpu_default_explicit.rs @@ -0,0 +1,8 @@ +//@ no-prefer-dynamic +//@ compile-flags: --target nvptx64-nvidia-cuda -Ctarget-cpu=sm_70 +//@ needs-llvm-components: nvptx +//@ ignore-backends: gcc + +#![feature(no_core)] +#![crate_type = "rlib"] +#![no_core] diff --git a/tests/ui/target_modifiers/auxiliary/target_cpu_default_implicit.rs b/tests/ui/target_modifiers/auxiliary/target_cpu_default_implicit.rs new file mode 100644 index 0000000000000..6b71ca5eb034d --- /dev/null +++ b/tests/ui/target_modifiers/auxiliary/target_cpu_default_implicit.rs @@ -0,0 +1,8 @@ +//@ no-prefer-dynamic +//@ compile-flags: --target nvptx64-nvidia-cuda +//@ needs-llvm-components: nvptx +//@ ignore-backends: gcc + +#![feature(no_core)] +#![crate_type = "rlib"] +#![no_core] diff --git a/tests/ui/target_modifiers/auxiliary/target_cpu_non_default.rs b/tests/ui/target_modifiers/auxiliary/target_cpu_non_default.rs new file mode 100644 index 0000000000000..a4fa7e2a33af6 --- /dev/null +++ b/tests/ui/target_modifiers/auxiliary/target_cpu_non_default.rs @@ -0,0 +1,8 @@ +//@ no-prefer-dynamic +//@ compile-flags: --target nvptx64-nvidia-cuda -Ctarget-cpu=sm_80 +//@ needs-llvm-components: nvptx +//@ ignore-backends: gcc + +#![feature(no_core)] +#![crate_type = "rlib"] +#![no_core] diff --git a/tests/ui/target_modifiers/target_cpu_default.explicit_mismatch.stderr b/tests/ui/target_modifiers/target_cpu_default.explicit_mismatch.stderr new file mode 100644 index 0000000000000..775569818175b --- /dev/null +++ b/tests/ui/target_modifiers/target_cpu_default.explicit_mismatch.stderr @@ -0,0 +1,13 @@ +error: mixing `-Ctarget-cpu` will cause an ABI mismatch in crate `target_cpu_default` + --> $DIR/target_cpu_default.rs:25:1 + | +LL | #![feature(no_core)] + | ^ + | + = help: the `-Ctarget-cpu` flag modifies the ABI so Rust crates compiled with different values of this flag cannot be used together safely + = note: `-Ctarget-cpu=sm_70` in this crate is incompatible with `-Ctarget-cpu=sm_80` in dependency `target_cpu_non_default` + = help: set `-Ctarget-cpu=sm_80` in this crate or `-Ctarget-cpu=sm_70` in `target_cpu_non_default` + = help: if you are sure this will not cause problems, you may use `-Cunsafe-allow-abi-mismatch=target-cpu` to silence this error + +error: aborting due to 1 previous error + diff --git a/tests/ui/target_modifiers/target_cpu_default.implicit_mismatch.stderr b/tests/ui/target_modifiers/target_cpu_default.implicit_mismatch.stderr new file mode 100644 index 0000000000000..ecfef76992d37 --- /dev/null +++ b/tests/ui/target_modifiers/target_cpu_default.implicit_mismatch.stderr @@ -0,0 +1,13 @@ +error: mixing `-Ctarget-cpu` will cause an ABI mismatch in crate `target_cpu_default` + --> $DIR/target_cpu_default.rs:25:1 + | +LL | #![feature(no_core)] + | ^ + | + = help: the `-Ctarget-cpu` flag modifies the ABI so Rust crates compiled with different values of this flag cannot be used together safely + = note: `-Ctarget-cpu` is unset in this crate which is incompatible with `-Ctarget-cpu=sm_80` in dependency `target_cpu_non_default` + = help: set `-Ctarget-cpu=sm_80` in this crate or unset `-Ctarget-cpu` in `target_cpu_non_default` + = help: if you are sure this will not cause problems, you may use `-Cunsafe-allow-abi-mismatch=target-cpu` to silence this error + +error: aborting due to 1 previous error + diff --git a/tests/ui/target_modifiers/target_cpu_default.rs b/tests/ui/target_modifiers/target_cpu_default.rs new file mode 100644 index 0000000000000..b4f3a5afda2e6 --- /dev/null +++ b/tests/ui/target_modifiers/target_cpu_default.rs @@ -0,0 +1,38 @@ +// Check that an implicit default `-Ctarget-cpu` and an explicit default +// `-Ctarget-cpu` compare equal. +// +// NVPTX requires consistent `-Ctarget-cpu` values across crates, but it does +// not require the CPU to be specified explicitly. Therefore, compiling one crate +// without `-Ctarget-cpu` and another crate with the target's default CPU +// explicitly specified must be accepted. +// +// The mismatch revisions additionally check that an implicit or explicit default +// CPU must not match a different explicitly specified CPU. + +//@ aux-build:target_cpu_default_implicit.rs +//@ aux-build:target_cpu_default_explicit.rs +//@ aux-build:target_cpu_non_default.rs +//@ compile-flags: --target nvptx64-nvidia-cuda +//@ needs-llvm-components: nvptx +//@ ignore-backends: gcc + +//@ revisions: implicit_default explicit_default implicit_mismatch explicit_mismatch +//@[implicit_default] check-pass +//@[explicit_default] compile-flags: -Ctarget-cpu=sm_70 +//@[explicit_default] check-pass +//@[explicit_mismatch] compile-flags: -Ctarget-cpu=sm_70 + +#![feature(no_core)] +//[implicit_mismatch]~^ ERROR mixing `-Ctarget-cpu` will cause an ABI mismatch +//[explicit_mismatch]~^^ ERROR mixing `-Ctarget-cpu` will cause an ABI mismatch +#![crate_type = "rlib"] +#![no_core] + +#[cfg(any(implicit_default, explicit_default))] +extern crate target_cpu_default_implicit; + +#[cfg(any(implicit_default, explicit_default))] +extern crate target_cpu_default_explicit; + +#[cfg(any(implicit_mismatch, explicit_mismatch))] +extern crate target_cpu_non_default; From b72727495c15ce53a0ca5ec0c559e1f9bbe7dd83 Mon Sep 17 00:00:00 2001 From: Connor Tsui Date: Tue, 14 Jul 2026 15:24:18 -0400 Subject: [PATCH 05/23] 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 9c81f660da8aa35f57b5a37628e0a06a4ab64850 Mon Sep 17 00:00:00 2001 From: albab-hasan Date: Thu, 16 Jul 2026 13:00:44 +0600 Subject: [PATCH 06/23] 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/23] 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/23] 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/23] 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/23] 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/23] 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/23] 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/23] 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/23] 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/23] [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/23] 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/23] 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/23] 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/23] 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/23] 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/23] 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/23] 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/23] 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);