From 67e8a19a252f7711365eab10e2c1d05b4d8e3f85 Mon Sep 17 00:00:00 2001 From: Jonathan Keller Date: Wed, 19 Aug 2026 15:49:44 -0700 Subject: [PATCH 01/22] [bootstrap] Don't reverse the order of dylib search path entries --- src/bootstrap/src/utils/helpers.rs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/bootstrap/src/utils/helpers.rs b/src/bootstrap/src/utils/helpers.rs index 8e881f1bf7734..c21de29bf98f2 100644 --- a/src/bootstrap/src/utils/helpers.rs +++ b/src/bootstrap/src/utils/helpers.rs @@ -141,11 +141,8 @@ pub fn libdir(target: TargetSelection) -> &'static str { /// Adds a list of lookup paths to `cmd`'s dynamic library lookup path. /// If the dylib_path_var is already set for this cmd, the old value will be overwritten! pub fn add_dylib_path(path: Vec, cmd: &mut BootstrapCommand) { - let mut list = dylib_path(); - for path in path { - list.insert(0, path); - } - cmd.env(dylib_path_var(), t!(env::join_paths(list))); + let paths = path.into_iter().chain(dylib_path()); + cmd.env(dylib_path_var(), t!(env::join_paths(paths))); } pub struct TimeIt(bool, Instant); From 37aaf5c30436c34ca2426b5356bebadded2b1f4c Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Thu, 6 Aug 2026 00:01:00 +0200 Subject: [PATCH 02/22] forward global target features to module-level assembly --- compiler/rustc_codegen_llvm/src/asm.rs | 12 ++++- compiler/rustc_codegen_llvm/src/back/write.rs | 4 +- compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 18 ++++--- compiler/rustc_codegen_llvm/src/llvm/mod.rs | 19 +++++-- .../rustc_llvm/llvm-wrapper/RustWrapper.cpp | 14 ++++++ tests/ui/asm/global-target-feature.rs | 49 +++++++++++++++++++ tests/ui/asm/inline-syntax.arm.stderr | 1 + tests/ui/asm/inline-syntax.rs | 2 +- 8 files changed, 104 insertions(+), 15 deletions(-) create mode 100644 tests/ui/asm/global-target-feature.rs diff --git a/compiler/rustc_codegen_llvm/src/asm.rs b/compiler/rustc_codegen_llvm/src/asm.rs index e715f9cd16c9d..549769547da78 100644 --- a/compiler/rustc_codegen_llvm/src/asm.rs +++ b/compiler/rustc_codegen_llvm/src/asm.rs @@ -16,12 +16,12 @@ use rustc_target::spec::HasTargetSpec; use smallvec::SmallVec; use tracing::debug; -use crate::attributes; use crate::builder::Builder; use crate::common::Funclet; use crate::context::CodegenCx; use crate::llvm::{self, ToLlvmBool, Type, Value}; use crate::type_of::LayoutLlvmExt; +use crate::{attributes, llvm_util}; impl<'ll, 'tcx> AsmBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { fn codegen_inline_asm( @@ -499,7 +499,15 @@ impl<'tcx> AsmCodegenMethods<'tcx> for CodegenCx<'_, 'tcx> { template_str.push_str("\n.att_syntax\n"); } - llvm::append_module_inline_asm(self.llmod, template_str.as_bytes()); + let target_features = self.tcx.global_backend_features(()).join(","); + let target_cpu = llvm_util::target_cpu(self.tcx.sess); + + llvm::append_module_inline_asm( + self.llmod, + template_str.as_bytes(), + &target_features, + target_cpu, + ); } fn mangled_name(&self, instance: Instance<'tcx>) -> String { diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index 66b51d9184a79..b8952ffc6bf81 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -1303,9 +1303,9 @@ fn embed_bitcode( // We need custom section flags, so emit module-level inline assembly. let section_flags = if cgcx.is_pe_coff { "n" } else { "e" }; let asm = create_section_with_flags_asm(".llvmbc", section_flags, bitcode); - llvm::append_module_inline_asm(llmod, &asm); + llvm::append_module_inline_asm(llmod, &asm, "", ""); let asm = create_section_with_flags_asm(".llvmcmd", section_flags, &[]); - llvm::append_module_inline_asm(llmod, &asm); + llvm::append_module_inline_asm(llmod, &asm, "", ""); } } diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 1a60b59a93525..0db9698bf9545 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -907,13 +907,6 @@ unsafe extern "C" { pub(crate) fn LLVMGetDataLayoutStr(M: &Module) -> *const c_char; pub(crate) fn LLVMSetDataLayout(M: &Module, Triple: *const c_char); - /// Append inline assembly to a module. See `Module::appendModuleInlineAsm`. - pub(crate) fn LLVMAppendModuleInlineAsm( - M: &Module, - Asm: *const c_uchar, // See "PTR_LEN_STR". - Len: size_t, - ); - /// Create the specified uniqued inline asm string. See `InlineAsm::get()`. pub(crate) fn LLVMGetInlineAsm<'ll>( Ty: &'ll Type, @@ -2119,6 +2112,17 @@ unsafe extern "C" { ConstraintsLen: size_t, ) -> bool; + /// Append inline assembly to a module. See `Module::appendModuleInlineAsm`. + pub(crate) fn LLVMRustAppendModuleInlineAsm( + M: &Module, + Asm: *const c_uchar, // See "PTR_LEN_STR". + AsmLen: size_t, + TargetFeatures: *const c_uchar, // See "PTR_LEN_STR". + TargetFeaturesLen: size_t, + TargetCpu: *const c_uchar, // See "PTR_LEN_STR". + TargetCpuLen: size_t, + ); + /// A list of pointer-length strings is passed as two pointer-length slices, /// one slice containing pointers and one slice containing their corresponding /// lengths. The implementation will check that both slices have the same length. diff --git a/compiler/rustc_codegen_llvm/src/llvm/mod.rs b/compiler/rustc_codegen_llvm/src/llvm/mod.rs index eb7a529c0b198..5452f4abc5c33 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/mod.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/mod.rs @@ -474,11 +474,24 @@ pub(crate) fn set_dso_local<'ll>(v: &'ll Value) { } } -/// Safe wrapper for `LLVMAppendModuleInlineAsm`, which delegates to +/// Safe wrapper for `LLVMRustAppendModuleInlineAsm`, which delegates to /// `Module::appendModuleInlineAsm`. -pub(crate) fn append_module_inline_asm<'ll>(llmod: &'ll Module, asm: &[u8]) { +pub(crate) fn append_module_inline_asm<'ll>( + llmod: &'ll Module, + asm: &[u8], + target_features: &str, + target_cpu: &str, +) { unsafe { - LLVMAppendModuleInlineAsm(llmod, asm.as_ptr(), asm.len()); + LLVMRustAppendModuleInlineAsm( + llmod, + asm.as_ptr(), + asm.len(), + target_features.as_ptr(), + target_features.len(), + target_cpu.as_ptr(), + target_cpu.len(), + ); } } diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index 983a506bd4ac6..c928282596cdd 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -661,6 +661,20 @@ extern "C" bool LLVMRustInlineAsmVerify(LLVMTypeRef Ty, char *Constraints, unwrap(Ty), StringRef(Constraints, ConstraintsLen))); } +extern "C" void LLVMRustAppendModuleInlineAsm( + LLVMModuleRef M, const char *Asm, size_t AsmLen, const char *TargetFeatures, + size_t TargetFeaturesLen, const char *TargetCPU, size_t TargetCPULen) { +#if LLVM_VERSION_GE(23, 0) + Module::GlobalAsmProperties Props; + Props.TargetFeatures = std::string(TargetFeatures, TargetFeaturesLen); + Props.TargetCPU = std::string(TargetCPU, TargetCPULen); + unwrap(M)->appendModuleInlineAsm( + Module::GlobalAsmFragment(std::string(Asm, AsmLen), Props)); +#else + unwrap(M)->appendModuleInlineAsm(StringRef(Asm, AsmLen)); +#endif +} + template DIT *unwrapDIPtr(LLVMMetadataRef Ref) { return (DIT *)(Ref ? unwrap(Ref) : nullptr); } diff --git a/tests/ui/asm/global-target-feature.rs b/tests/ui/asm/global-target-feature.rs new file mode 100644 index 0000000000000..d84b4d51e6582 --- /dev/null +++ b/tests/ui/asm/global-target-feature.rs @@ -0,0 +1,49 @@ +//@ build-pass +//@ add-minicore +//@ min-llvm-version: 23 +//@ ignore-backends: gcc +// +//@ revisions: riscv opt-0-bitcode-no opt-0 opt-s-bitcode-no +// +//@[riscv] compile-flags: --target riscv64gc-unknown-linux-gnu -Clto=thin +//@[riscv] needs-llvm-components: riscv +// +//@[opt-0-bitcode-no] compile-flags: --target armv7r-none-eabihf -Copt-level=0 -Cembed-bitcode=no +//@[opt-0-bitcode-no] needs-llvm-components: arm +// +//@[opt-0] compile-flags: --target armv7r-none-eabihf -Copt-level=0 +//@[opt-0] needs-llvm-components: arm +// +//@[opt-s-bitcode-no] compile-flags: --target armv7r-none-eabihf -Copt-level=s -Cembed-bitcode=no +//@[opt-s-bitcode-no] needs-llvm-components: arm + +// Regression test for +// +// - https://github.com/llvm/llvm-project/issues/61991 +// - https://github.com/rust-lang/rust/issues/80608 +// - https://github.com/rust-lang/rust/issues/127269 +// +// Since LLVM 23 target features are taken into account for module-level assembly. + +#![feature(no_core)] +#![no_core] +#![crate_type = "lib"] + +extern crate minicore; +use minicore::*; + +#[cfg(target_arch = "riscv64")] +global_asm!("fld f0, 0(sp)"); + +#[cfg(target_arch = "arm")] +global_asm!( + r#" +.section .text.startup +.global _start +.code 32 +.align 0 + +_start: + vmsr fpexc, r0 +"# +); diff --git a/tests/ui/asm/inline-syntax.arm.stderr b/tests/ui/asm/inline-syntax.arm.stderr index 5b4eb3cc1409c..5b193d26c8776 100644 --- a/tests/ui/asm/inline-syntax.arm.stderr +++ b/tests/ui/asm/inline-syntax.arm.stderr @@ -13,6 +13,7 @@ note: instantiated into assembly here | LL | .intel_syntax noprefix | ^ + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error: unknown directive --> $DIR/inline-syntax.rs:21:15 diff --git a/tests/ui/asm/inline-syntax.rs b/tests/ui/asm/inline-syntax.rs index b48841aabfe7b..63395c1096c09 100644 --- a/tests/ui/asm/inline-syntax.rs +++ b/tests/ui/asm/inline-syntax.rs @@ -3,10 +3,10 @@ //@[x86_64] compile-flags: --target x86_64-unknown-linux-gnu //@[x86_64] check-pass //@[x86_64] needs-llvm-components: x86 -// LLVM 19+ has full support for 64-bit cookies. //@[arm] compile-flags: --target armv7-unknown-linux-gnueabihf //@[arm] build-fail //@[arm] needs-llvm-components: arm +//@[arm] min-llvm-version: 23 //@ ignore-backends: gcc #![feature(no_core)] From 8ceed4d81b26a8d34608338cf43b7d73396eb0b2 Mon Sep 17 00:00:00 2001 From: zep Date: Wed, 26 Aug 2026 15:06:25 +0530 Subject: [PATCH 03/22] Document PartialOrd behavior for Option where T: PartialOrd --- library/core/src/option.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/library/core/src/option.rs b/library/core/src/option.rs index 201019037148d..354d728ce5030 100644 --- a/library/core/src/option.rs +++ b/library/core/src/option.rs @@ -2456,6 +2456,16 @@ const impl PartialEq for Option { #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_unstable(feature = "const_cmp", issue = "143800")] const impl PartialOrd for Option { + /// If T implements PartialOrd then Option will derive its PartialOrd implementation. + /// With this order, None compares as less than any Some, and two Some compare the same way + /// as their contained values would in T. If T also implements Ord, then so does Option. + /// + /// # Examples + /// + /// ``` + /// assert!(None < Some(0)); + /// assert!(Some(0) < Some(1)); + /// ``` #[inline] fn partial_cmp(&self, other: &Self) -> Option { match (self, other) { From 3fec803fd7b155e397ec801ccb97798361e7eef3 Mon Sep 17 00:00:00 2001 From: zep Date: Wed, 26 Aug 2026 16:30:02 +0530 Subject: [PATCH 04/22] Formatting changes for HTML tag errors --- library/core/src/option.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/library/core/src/option.rs b/library/core/src/option.rs index 354d728ce5030..61925c3dbc628 100644 --- a/library/core/src/option.rs +++ b/library/core/src/option.rs @@ -2456,9 +2456,9 @@ const impl PartialEq for Option { #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_unstable(feature = "const_cmp", issue = "143800")] const impl PartialOrd for Option { - /// If T implements PartialOrd then Option will derive its PartialOrd implementation. - /// With this order, None compares as less than any Some, and two Some compare the same way - /// as their contained values would in T. If T also implements Ord, then so does Option. + /// If `T` implements [`PartialOrd`], then [`Option`] will derive its [`PartialOrd`] implementation. + /// With this order, `None` compares as less than any `Some`, and two `Some` values compare the + /// same way as their contained values would in `T`. If `T` also implements [`Ord`], then so does [`Option`]. /// /// # Examples /// From bb75ebf894248aa9984c3406be319a353795034f Mon Sep 17 00:00:00 2001 From: WANG Rui Date: Thu, 27 Aug 2026 19:08:53 +0800 Subject: [PATCH 05/22] loongarch: support passing `u128`/`i128` to inline assembly --- compiler/rustc_target/src/asm/loongarch.rs | 4 +- .../language-features/asm-experimental-reg.md | 4 +- .../bad-reg.loongarch32_ilp32d.stderr | 98 +++++++++++----- .../bad-reg.loongarch32_ilp32s.stderr | 110 ++++++++++++------ .../bad-reg.loongarch64_lp64d.stderr | 18 +-- .../bad-reg.loongarch64_lp64s.stderr | 110 ++++++++++++------ tests/ui/asm/loongarch/bad-reg.rs | 9 +- 7 files changed, 240 insertions(+), 113 deletions(-) diff --git a/compiler/rustc_target/src/asm/loongarch.rs b/compiler/rustc_target/src/asm/loongarch.rs index 4aa69dac2d7db..3603d54400536 100644 --- a/compiler/rustc_target/src/asm/loongarch.rs +++ b/compiler/rustc_target/src/asm/loongarch.rs @@ -53,7 +53,7 @@ impl LoongArchInlineAsmRegClass { (Self::vreg, _) => { if allow_experimental_reg { types! { - lsx: F16, F32, F64, + lsx: I128, F16, F32, F64, VecI8(16), VecI16(8), VecI32(4), VecI64(2), VecF32(4), VecF64(2); } } else { @@ -63,7 +63,7 @@ impl LoongArchInlineAsmRegClass { (Self::xreg, _) => { if allow_experimental_reg { types! { - lasx: F16, F32, F64, + lasx: I128, F16, F32, F64, VecI8(16), VecI16(8), VecI32(4), VecI64(2), VecF32(4), VecF64(2), VecI8(32), VecI16(16), VecI32(8), VecI64(4), VecF32(8), VecF64(4); } diff --git a/src/doc/unstable-book/src/language-features/asm-experimental-reg.md b/src/doc/unstable-book/src/language-features/asm-experimental-reg.md index db72c44a2dc92..854d66f96756c 100644 --- a/src/doc/unstable-book/src/language-features/asm-experimental-reg.md +++ b/src/doc/unstable-book/src/language-features/asm-experimental-reg.md @@ -19,8 +19,8 @@ This tracks support for additional registers in architectures where inline assem | Architecture | Register class | Target feature | Allowed types | | ------------ | -------------- | -------------- | ------------- | -| LoongArch | `vreg` | `lsx` | `f32`, `f64`,
`i8x16`, `i16x8`, `i32x4`, `i64x2`, `f32x4`, `f64x2` | -| LoongArch | `xreg` | `lasx` | `f32`, `f64`,
`i8x16`, `i16x8`, `i32x4`, `i64x2`, `f32x4`, `f64x2`,
`i8x32`, `i16x16`, `i32x8`, `i64x4`, `f32x8`, `f64x4` | +| LoongArch | `vreg` | `lsx` | `i128`, `f32`, `f64`,
`i8x16`, `i16x8`, `i32x4`, `i64x2`, `f32x4`, `f64x2` | +| LoongArch | `xreg` | `lasx` | `i128`, `f32`, `f64`,
`i8x16`, `i16x8`, `i32x4`, `i64x2`, `f32x4`, `f64x2`,
`i8x32`, `i16x16`, `i32x8`, `i64x4`, `f32x8`, `f64x4` | ## Register aliases diff --git a/tests/ui/asm/loongarch/bad-reg.loongarch32_ilp32d.stderr b/tests/ui/asm/loongarch/bad-reg.loongarch32_ilp32d.stderr index 28ac455c06441..dca21ad47ea10 100644 --- a/tests/ui/asm/loongarch/bad-reg.loongarch32_ilp32d.stderr +++ b/tests/ui/asm/loongarch/bad-reg.loongarch32_ilp32d.stderr @@ -1,41 +1,51 @@ error: invalid register `$r0`: constant zero cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:28:18 + --> $DIR/bad-reg.rs:29:18 | LL | asm!("", out("$r0") _); | ^^^^^^^^^^^^ error: invalid register `$tp`: reserved for TLS - --> $DIR/bad-reg.rs:30:18 + --> $DIR/bad-reg.rs:31:18 | LL | asm!("", out("$tp") _); | ^^^^^^^^^^^^ error: invalid register `$sp`: the stack pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:32:18 + --> $DIR/bad-reg.rs:33:18 | LL | asm!("", out("$sp") _); | ^^^^^^^^^^^^ error: invalid register `$r21`: reserved by the ABI - --> $DIR/bad-reg.rs:34:18 + --> $DIR/bad-reg.rs:35:18 | LL | asm!("", out("$r21") _); | ^^^^^^^^^^^^^ error: invalid register `$fp`: the frame pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:36:18 + --> $DIR/bad-reg.rs:37:18 | LL | asm!("", out("$fp") _); | ^^^^^^^^^^^^ error: invalid register `$r31`: $r31 is used internally by LLVM and cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:38:18 + --> $DIR/bad-reg.rs:39:18 | LL | asm!("", out("$r31") _); | ^^^^^^^^^^^^^ error[E0658]: register class `vreg` can only be used as a clobber in stable - --> $DIR/bad-reg.rs:52:26 + --> $DIR/bad-reg.rs:53:26 + | +LL | asm!("/* {} */", in(vreg) q); + | ^^^^^^^^^^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: register class `vreg` can only be used as a clobber in stable + --> $DIR/bad-reg.rs:56:26 | LL | asm!("/* {} */", in(vreg) f); | ^^^^^^^^^^ @@ -45,7 +55,7 @@ LL | asm!("/* {} */", in(vreg) f); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: register class `vreg` can only be used as a clobber in stable - --> $DIR/bad-reg.rs:55:26 + --> $DIR/bad-reg.rs:59:26 | LL | asm!("/* {} */", out(vreg) _); | ^^^^^^^^^^^ @@ -55,7 +65,7 @@ LL | asm!("/* {} */", out(vreg) _); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: register class `vreg` can only be used as a clobber in stable - --> $DIR/bad-reg.rs:57:26 + --> $DIR/bad-reg.rs:61:26 | LL | asm!("/* {} */", in(vreg) d); | ^^^^^^^^^^ @@ -65,7 +75,7 @@ LL | asm!("/* {} */", in(vreg) d); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: register class `vreg` can only be used as a clobber in stable - --> $DIR/bad-reg.rs:60:26 + --> $DIR/bad-reg.rs:64:26 | LL | asm!("/* {} */", out(vreg) d); | ^^^^^^^^^^^ @@ -75,7 +85,17 @@ LL | asm!("/* {} */", out(vreg) d); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: register class `xreg` can only be used as a clobber in stable - --> $DIR/bad-reg.rs:65:26 + --> $DIR/bad-reg.rs:69:26 + | +LL | asm!("/* {} */", in(xreg) q); + | ^^^^^^^^^^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: register class `xreg` can only be used as a clobber in stable + --> $DIR/bad-reg.rs:72:26 | LL | asm!("/* {} */", in(xreg) f); | ^^^^^^^^^^ @@ -85,7 +105,7 @@ LL | asm!("/* {} */", in(xreg) f); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: register class `xreg` can only be used as a clobber in stable - --> $DIR/bad-reg.rs:68:26 + --> $DIR/bad-reg.rs:75:26 | LL | asm!("/* {} */", out(xreg) _); | ^^^^^^^^^^^ @@ -95,7 +115,7 @@ LL | asm!("/* {} */", out(xreg) _); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: register class `xreg` can only be used as a clobber in stable - --> $DIR/bad-reg.rs:70:26 + --> $DIR/bad-reg.rs:77:26 | LL | asm!("/* {} */", in(xreg) d); | ^^^^^^^^^^ @@ -105,7 +125,7 @@ LL | asm!("/* {} */", in(xreg) d); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: register class `xreg` can only be used as a clobber in stable - --> $DIR/bad-reg.rs:73:26 + --> $DIR/bad-reg.rs:80:26 | LL | asm!("/* {} */", out(xreg) d); | ^^^^^^^^^^^ @@ -115,7 +135,7 @@ LL | asm!("/* {} */", out(xreg) d); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: register class `vreg` can only be used as a clobber in stable - --> $DIR/bad-reg.rs:77:31 + --> $DIR/bad-reg.rs:84:31 | LL | asm!("", in("$f0") f, in("$vr0") d); | ^^^^^^^^^^^^ @@ -125,7 +145,7 @@ LL | asm!("", in("$f0") f, in("$vr0") d); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: register class `xreg` can only be used as a clobber in stable - --> $DIR/bad-reg.rs:82:31 + --> $DIR/bad-reg.rs:89:31 | LL | asm!("", in("$f0") f, in("$xr0") d); | ^^^^^^^^^^^^ @@ -135,7 +155,7 @@ LL | asm!("", in("$f0") f, in("$xr0") d); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: register class `vreg` can only be used as a clobber in stable - --> $DIR/bad-reg.rs:87:18 + --> $DIR/bad-reg.rs:94:18 | LL | asm!("", in("$vr0") f, in("$xr0") d); | ^^^^^^^^^^^^ @@ -145,7 +165,7 @@ LL | asm!("", in("$vr0") f, in("$xr0") d); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: register class `xreg` can only be used as a clobber in stable - --> $DIR/bad-reg.rs:87:32 + --> $DIR/bad-reg.rs:94:32 | LL | asm!("", in("$vr0") f, in("$xr0") d); | ^^^^^^^^^^^^ @@ -154,8 +174,18 @@ LL | asm!("", in("$vr0") f, in("$xr0") d); = help: add `#![feature(asm_experimental_reg)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date +error[E0658]: type `u128` cannot be used with this register class in stable + --> $DIR/bad-reg.rs:53:35 + | +LL | asm!("/* {} */", in(vreg) q); + | ^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + error[E0658]: type `f32` cannot be used with this register class in stable - --> $DIR/bad-reg.rs:52:35 + --> $DIR/bad-reg.rs:56:35 | LL | asm!("/* {} */", in(vreg) f); | ^ @@ -165,7 +195,7 @@ LL | asm!("/* {} */", in(vreg) f); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: type `f64` cannot be used with this register class in stable - --> $DIR/bad-reg.rs:57:35 + --> $DIR/bad-reg.rs:61:35 | LL | asm!("/* {} */", in(vreg) d); | ^ @@ -175,7 +205,7 @@ LL | asm!("/* {} */", in(vreg) d); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: type `f64` cannot be used with this register class in stable - --> $DIR/bad-reg.rs:60:36 + --> $DIR/bad-reg.rs:64:36 | LL | asm!("/* {} */", out(vreg) d); | ^ @@ -184,8 +214,18 @@ LL | asm!("/* {} */", out(vreg) d); = help: add `#![feature(asm_experimental_reg)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date +error[E0658]: type `u128` cannot be used with this register class in stable + --> $DIR/bad-reg.rs:69:35 + | +LL | asm!("/* {} */", in(xreg) q); + | ^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + error[E0658]: type `f32` cannot be used with this register class in stable - --> $DIR/bad-reg.rs:65:35 + --> $DIR/bad-reg.rs:72:35 | LL | asm!("/* {} */", in(xreg) f); | ^ @@ -195,7 +235,7 @@ LL | asm!("/* {} */", in(xreg) f); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: type `f64` cannot be used with this register class in stable - --> $DIR/bad-reg.rs:70:35 + --> $DIR/bad-reg.rs:77:35 | LL | asm!("/* {} */", in(xreg) d); | ^ @@ -205,7 +245,7 @@ LL | asm!("/* {} */", in(xreg) d); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: type `f64` cannot be used with this register class in stable - --> $DIR/bad-reg.rs:73:36 + --> $DIR/bad-reg.rs:80:36 | LL | asm!("/* {} */", out(xreg) d); | ^ @@ -215,7 +255,7 @@ LL | asm!("/* {} */", out(xreg) d); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: type `f64` cannot be used with this register class in stable - --> $DIR/bad-reg.rs:77:42 + --> $DIR/bad-reg.rs:84:42 | LL | asm!("", in("$f0") f, in("$vr0") d); | ^ @@ -225,7 +265,7 @@ LL | asm!("", in("$f0") f, in("$vr0") d); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: type `f64` cannot be used with this register class in stable - --> $DIR/bad-reg.rs:82:42 + --> $DIR/bad-reg.rs:89:42 | LL | asm!("", in("$f0") f, in("$xr0") d); | ^ @@ -235,7 +275,7 @@ LL | asm!("", in("$f0") f, in("$xr0") d); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: type `f32` cannot be used with this register class in stable - --> $DIR/bad-reg.rs:87:29 + --> $DIR/bad-reg.rs:94:29 | LL | asm!("", in("$vr0") f, in("$xr0") d); | ^ @@ -245,7 +285,7 @@ LL | asm!("", in("$vr0") f, in("$xr0") d); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: type `f64` cannot be used with this register class in stable - --> $DIR/bad-reg.rs:87:43 + --> $DIR/bad-reg.rs:94:43 | LL | asm!("", in("$vr0") f, in("$xr0") d); | ^ @@ -254,6 +294,6 @@ LL | asm!("", in("$vr0") f, in("$xr0") d); = help: add `#![feature(asm_experimental_reg)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error: aborting due to 28 previous errors +error: aborting due to 32 previous errors For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/asm/loongarch/bad-reg.loongarch32_ilp32s.stderr b/tests/ui/asm/loongarch/bad-reg.loongarch32_ilp32s.stderr index 1ee1b86989c4d..9ffab83db95cb 100644 --- a/tests/ui/asm/loongarch/bad-reg.loongarch32_ilp32s.stderr +++ b/tests/ui/asm/loongarch/bad-reg.loongarch32_ilp32s.stderr @@ -1,41 +1,51 @@ error: invalid register `$r0`: constant zero cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:28:18 + --> $DIR/bad-reg.rs:29:18 | LL | asm!("", out("$r0") _); | ^^^^^^^^^^^^ error: invalid register `$tp`: reserved for TLS - --> $DIR/bad-reg.rs:30:18 + --> $DIR/bad-reg.rs:31:18 | LL | asm!("", out("$tp") _); | ^^^^^^^^^^^^ error: invalid register `$sp`: the stack pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:32:18 + --> $DIR/bad-reg.rs:33:18 | LL | asm!("", out("$sp") _); | ^^^^^^^^^^^^ error: invalid register `$r21`: reserved by the ABI - --> $DIR/bad-reg.rs:34:18 + --> $DIR/bad-reg.rs:35:18 | LL | asm!("", out("$r21") _); | ^^^^^^^^^^^^^ error: invalid register `$fp`: the frame pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:36:18 + --> $DIR/bad-reg.rs:37:18 | LL | asm!("", out("$fp") _); | ^^^^^^^^^^^^ error: invalid register `$r31`: $r31 is used internally by LLVM and cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:38:18 + --> $DIR/bad-reg.rs:39:18 | LL | asm!("", out("$r31") _); | ^^^^^^^^^^^^^ error[E0658]: register class `vreg` can only be used as a clobber in stable - --> $DIR/bad-reg.rs:52:26 + --> $DIR/bad-reg.rs:53:26 + | +LL | asm!("/* {} */", in(vreg) q); + | ^^^^^^^^^^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: register class `vreg` can only be used as a clobber in stable + --> $DIR/bad-reg.rs:56:26 | LL | asm!("/* {} */", in(vreg) f); | ^^^^^^^^^^ @@ -45,7 +55,7 @@ LL | asm!("/* {} */", in(vreg) f); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: register class `vreg` can only be used as a clobber in stable - --> $DIR/bad-reg.rs:55:26 + --> $DIR/bad-reg.rs:59:26 | LL | asm!("/* {} */", out(vreg) _); | ^^^^^^^^^^^ @@ -55,7 +65,7 @@ LL | asm!("/* {} */", out(vreg) _); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: register class `vreg` can only be used as a clobber in stable - --> $DIR/bad-reg.rs:57:26 + --> $DIR/bad-reg.rs:61:26 | LL | asm!("/* {} */", in(vreg) d); | ^^^^^^^^^^ @@ -65,7 +75,7 @@ LL | asm!("/* {} */", in(vreg) d); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: register class `vreg` can only be used as a clobber in stable - --> $DIR/bad-reg.rs:60:26 + --> $DIR/bad-reg.rs:64:26 | LL | asm!("/* {} */", out(vreg) d); | ^^^^^^^^^^^ @@ -75,7 +85,17 @@ LL | asm!("/* {} */", out(vreg) d); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: register class `xreg` can only be used as a clobber in stable - --> $DIR/bad-reg.rs:65:26 + --> $DIR/bad-reg.rs:69:26 + | +LL | asm!("/* {} */", in(xreg) q); + | ^^^^^^^^^^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: register class `xreg` can only be used as a clobber in stable + --> $DIR/bad-reg.rs:72:26 | LL | asm!("/* {} */", in(xreg) f); | ^^^^^^^^^^ @@ -85,7 +105,7 @@ LL | asm!("/* {} */", in(xreg) f); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: register class `xreg` can only be used as a clobber in stable - --> $DIR/bad-reg.rs:68:26 + --> $DIR/bad-reg.rs:75:26 | LL | asm!("/* {} */", out(xreg) _); | ^^^^^^^^^^^ @@ -95,7 +115,7 @@ LL | asm!("/* {} */", out(xreg) _); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: register class `xreg` can only be used as a clobber in stable - --> $DIR/bad-reg.rs:70:26 + --> $DIR/bad-reg.rs:77:26 | LL | asm!("/* {} */", in(xreg) d); | ^^^^^^^^^^ @@ -105,7 +125,7 @@ LL | asm!("/* {} */", in(xreg) d); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: register class `xreg` can only be used as a clobber in stable - --> $DIR/bad-reg.rs:73:26 + --> $DIR/bad-reg.rs:80:26 | LL | asm!("/* {} */", out(xreg) d); | ^^^^^^^^^^^ @@ -115,7 +135,7 @@ LL | asm!("/* {} */", out(xreg) d); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: register class `vreg` can only be used as a clobber in stable - --> $DIR/bad-reg.rs:77:31 + --> $DIR/bad-reg.rs:84:31 | LL | asm!("", in("$f0") f, in("$vr0") d); | ^^^^^^^^^^^^ @@ -125,7 +145,7 @@ LL | asm!("", in("$f0") f, in("$vr0") d); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: register class `xreg` can only be used as a clobber in stable - --> $DIR/bad-reg.rs:82:31 + --> $DIR/bad-reg.rs:89:31 | LL | asm!("", in("$f0") f, in("$xr0") d); | ^^^^^^^^^^^^ @@ -135,7 +155,7 @@ LL | asm!("", in("$f0") f, in("$xr0") d); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: register class `vreg` can only be used as a clobber in stable - --> $DIR/bad-reg.rs:87:18 + --> $DIR/bad-reg.rs:94:18 | LL | asm!("", in("$vr0") f, in("$xr0") d); | ^^^^^^^^^^^^ @@ -145,7 +165,7 @@ LL | asm!("", in("$vr0") f, in("$xr0") d); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: register class `xreg` can only be used as a clobber in stable - --> $DIR/bad-reg.rs:87:32 + --> $DIR/bad-reg.rs:94:32 | LL | asm!("", in("$vr0") f, in("$xr0") d); | ^^^^^^^^^^^^ @@ -155,31 +175,41 @@ LL | asm!("", in("$vr0") f, in("$xr0") d); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:42:26 + --> $DIR/bad-reg.rs:43:26 | LL | asm!("/* {} */", in(freg) f); | ^^^^^^^^^^ error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:44:26 + --> $DIR/bad-reg.rs:45:26 | LL | asm!("/* {} */", out(freg) _); | ^^^^^^^^^^^ error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:46:26 + --> $DIR/bad-reg.rs:47:26 | LL | asm!("/* {} */", in(freg) d); | ^^^^^^^^^^ error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:48:26 + --> $DIR/bad-reg.rs:49:26 | LL | asm!("/* {} */", out(freg) d); | ^^^^^^^^^^^ +error[E0658]: type `u128` cannot be used with this register class in stable + --> $DIR/bad-reg.rs:53:35 + | +LL | asm!("/* {} */", in(vreg) q); + | ^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + error[E0658]: type `f32` cannot be used with this register class in stable - --> $DIR/bad-reg.rs:52:35 + --> $DIR/bad-reg.rs:56:35 | LL | asm!("/* {} */", in(vreg) f); | ^ @@ -189,7 +219,7 @@ LL | asm!("/* {} */", in(vreg) f); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: type `f64` cannot be used with this register class in stable - --> $DIR/bad-reg.rs:57:35 + --> $DIR/bad-reg.rs:61:35 | LL | asm!("/* {} */", in(vreg) d); | ^ @@ -199,7 +229,7 @@ LL | asm!("/* {} */", in(vreg) d); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: type `f64` cannot be used with this register class in stable - --> $DIR/bad-reg.rs:60:36 + --> $DIR/bad-reg.rs:64:36 | LL | asm!("/* {} */", out(vreg) d); | ^ @@ -208,8 +238,18 @@ LL | asm!("/* {} */", out(vreg) d); = help: add `#![feature(asm_experimental_reg)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date +error[E0658]: type `u128` cannot be used with this register class in stable + --> $DIR/bad-reg.rs:69:35 + | +LL | asm!("/* {} */", in(xreg) q); + | ^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + error[E0658]: type `f32` cannot be used with this register class in stable - --> $DIR/bad-reg.rs:65:35 + --> $DIR/bad-reg.rs:72:35 | LL | asm!("/* {} */", in(xreg) f); | ^ @@ -219,7 +259,7 @@ LL | asm!("/* {} */", in(xreg) f); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: type `f64` cannot be used with this register class in stable - --> $DIR/bad-reg.rs:70:35 + --> $DIR/bad-reg.rs:77:35 | LL | asm!("/* {} */", in(xreg) d); | ^ @@ -229,7 +269,7 @@ LL | asm!("/* {} */", in(xreg) d); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: type `f64` cannot be used with this register class in stable - --> $DIR/bad-reg.rs:73:36 + --> $DIR/bad-reg.rs:80:36 | LL | asm!("/* {} */", out(xreg) d); | ^ @@ -239,13 +279,13 @@ LL | asm!("/* {} */", out(xreg) d); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:77:18 + --> $DIR/bad-reg.rs:84:18 | LL | asm!("", in("$f0") f, in("$vr0") d); | ^^^^^^^^^^^ error[E0658]: type `f64` cannot be used with this register class in stable - --> $DIR/bad-reg.rs:77:42 + --> $DIR/bad-reg.rs:84:42 | LL | asm!("", in("$f0") f, in("$vr0") d); | ^ @@ -255,13 +295,13 @@ LL | asm!("", in("$f0") f, in("$vr0") d); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:82:18 + --> $DIR/bad-reg.rs:89:18 | LL | asm!("", in("$f0") f, in("$xr0") d); | ^^^^^^^^^^^ error[E0658]: type `f64` cannot be used with this register class in stable - --> $DIR/bad-reg.rs:82:42 + --> $DIR/bad-reg.rs:89:42 | LL | asm!("", in("$f0") f, in("$xr0") d); | ^ @@ -271,7 +311,7 @@ LL | asm!("", in("$f0") f, in("$xr0") d); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: type `f32` cannot be used with this register class in stable - --> $DIR/bad-reg.rs:87:29 + --> $DIR/bad-reg.rs:94:29 | LL | asm!("", in("$vr0") f, in("$xr0") d); | ^ @@ -281,7 +321,7 @@ LL | asm!("", in("$vr0") f, in("$xr0") d); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: type `f64` cannot be used with this register class in stable - --> $DIR/bad-reg.rs:87:43 + --> $DIR/bad-reg.rs:94:43 | LL | asm!("", in("$vr0") f, in("$xr0") d); | ^ @@ -290,6 +330,6 @@ LL | asm!("", in("$vr0") f, in("$xr0") d); = help: add `#![feature(asm_experimental_reg)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error: aborting due to 34 previous errors +error: aborting due to 38 previous errors For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/asm/loongarch/bad-reg.loongarch64_lp64d.stderr b/tests/ui/asm/loongarch/bad-reg.loongarch64_lp64d.stderr index 97462d6dc5f0b..067dd354f3093 100644 --- a/tests/ui/asm/loongarch/bad-reg.loongarch64_lp64d.stderr +++ b/tests/ui/asm/loongarch/bad-reg.loongarch64_lp64d.stderr @@ -1,41 +1,41 @@ error: invalid register `$r0`: constant zero cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:28:18 + --> $DIR/bad-reg.rs:29:18 | LL | asm!("", out("$r0") _); | ^^^^^^^^^^^^ error: invalid register `$tp`: reserved for TLS - --> $DIR/bad-reg.rs:30:18 + --> $DIR/bad-reg.rs:31:18 | LL | asm!("", out("$tp") _); | ^^^^^^^^^^^^ error: invalid register `$sp`: the stack pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:32:18 + --> $DIR/bad-reg.rs:33:18 | LL | asm!("", out("$sp") _); | ^^^^^^^^^^^^ error: invalid register `$r21`: reserved by the ABI - --> $DIR/bad-reg.rs:34:18 + --> $DIR/bad-reg.rs:35:18 | LL | asm!("", out("$r21") _); | ^^^^^^^^^^^^^ error: invalid register `$fp`: the frame pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:36:18 + --> $DIR/bad-reg.rs:37:18 | LL | asm!("", out("$fp") _); | ^^^^^^^^^^^^ error: invalid register `$r31`: $r31 is used internally by LLVM and cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:38:18 + --> $DIR/bad-reg.rs:39:18 | LL | asm!("", out("$r31") _); | ^^^^^^^^^^^^^ error: register `$vr0` conflicts with register `$f0` - --> $DIR/bad-reg.rs:77:31 + --> $DIR/bad-reg.rs:84:31 | LL | asm!("", in("$f0") f, in("$vr0") d); | ----------- ^^^^^^^^^^^^ register `$vr0` @@ -43,7 +43,7 @@ LL | asm!("", in("$f0") f, in("$vr0") d); | register `$f0` error: register `$xr0` conflicts with register `$f0` - --> $DIR/bad-reg.rs:82:31 + --> $DIR/bad-reg.rs:89:31 | LL | asm!("", in("$f0") f, in("$xr0") d); | ----------- ^^^^^^^^^^^^ register `$xr0` @@ -51,7 +51,7 @@ LL | asm!("", in("$f0") f, in("$xr0") d); | register `$f0` error: register `$xr0` conflicts with register `$vr0` - --> $DIR/bad-reg.rs:87:32 + --> $DIR/bad-reg.rs:94:32 | LL | asm!("", in("$vr0") f, in("$xr0") d); | ------------ ^^^^^^^^^^^^ register `$xr0` diff --git a/tests/ui/asm/loongarch/bad-reg.loongarch64_lp64s.stderr b/tests/ui/asm/loongarch/bad-reg.loongarch64_lp64s.stderr index 1ee1b86989c4d..9ffab83db95cb 100644 --- a/tests/ui/asm/loongarch/bad-reg.loongarch64_lp64s.stderr +++ b/tests/ui/asm/loongarch/bad-reg.loongarch64_lp64s.stderr @@ -1,41 +1,51 @@ error: invalid register `$r0`: constant zero cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:28:18 + --> $DIR/bad-reg.rs:29:18 | LL | asm!("", out("$r0") _); | ^^^^^^^^^^^^ error: invalid register `$tp`: reserved for TLS - --> $DIR/bad-reg.rs:30:18 + --> $DIR/bad-reg.rs:31:18 | LL | asm!("", out("$tp") _); | ^^^^^^^^^^^^ error: invalid register `$sp`: the stack pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:32:18 + --> $DIR/bad-reg.rs:33:18 | LL | asm!("", out("$sp") _); | ^^^^^^^^^^^^ error: invalid register `$r21`: reserved by the ABI - --> $DIR/bad-reg.rs:34:18 + --> $DIR/bad-reg.rs:35:18 | LL | asm!("", out("$r21") _); | ^^^^^^^^^^^^^ error: invalid register `$fp`: the frame pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:36:18 + --> $DIR/bad-reg.rs:37:18 | LL | asm!("", out("$fp") _); | ^^^^^^^^^^^^ error: invalid register `$r31`: $r31 is used internally by LLVM and cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:38:18 + --> $DIR/bad-reg.rs:39:18 | LL | asm!("", out("$r31") _); | ^^^^^^^^^^^^^ error[E0658]: register class `vreg` can only be used as a clobber in stable - --> $DIR/bad-reg.rs:52:26 + --> $DIR/bad-reg.rs:53:26 + | +LL | asm!("/* {} */", in(vreg) q); + | ^^^^^^^^^^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: register class `vreg` can only be used as a clobber in stable + --> $DIR/bad-reg.rs:56:26 | LL | asm!("/* {} */", in(vreg) f); | ^^^^^^^^^^ @@ -45,7 +55,7 @@ LL | asm!("/* {} */", in(vreg) f); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: register class `vreg` can only be used as a clobber in stable - --> $DIR/bad-reg.rs:55:26 + --> $DIR/bad-reg.rs:59:26 | LL | asm!("/* {} */", out(vreg) _); | ^^^^^^^^^^^ @@ -55,7 +65,7 @@ LL | asm!("/* {} */", out(vreg) _); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: register class `vreg` can only be used as a clobber in stable - --> $DIR/bad-reg.rs:57:26 + --> $DIR/bad-reg.rs:61:26 | LL | asm!("/* {} */", in(vreg) d); | ^^^^^^^^^^ @@ -65,7 +75,7 @@ LL | asm!("/* {} */", in(vreg) d); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: register class `vreg` can only be used as a clobber in stable - --> $DIR/bad-reg.rs:60:26 + --> $DIR/bad-reg.rs:64:26 | LL | asm!("/* {} */", out(vreg) d); | ^^^^^^^^^^^ @@ -75,7 +85,17 @@ LL | asm!("/* {} */", out(vreg) d); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: register class `xreg` can only be used as a clobber in stable - --> $DIR/bad-reg.rs:65:26 + --> $DIR/bad-reg.rs:69:26 + | +LL | asm!("/* {} */", in(xreg) q); + | ^^^^^^^^^^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: register class `xreg` can only be used as a clobber in stable + --> $DIR/bad-reg.rs:72:26 | LL | asm!("/* {} */", in(xreg) f); | ^^^^^^^^^^ @@ -85,7 +105,7 @@ LL | asm!("/* {} */", in(xreg) f); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: register class `xreg` can only be used as a clobber in stable - --> $DIR/bad-reg.rs:68:26 + --> $DIR/bad-reg.rs:75:26 | LL | asm!("/* {} */", out(xreg) _); | ^^^^^^^^^^^ @@ -95,7 +115,7 @@ LL | asm!("/* {} */", out(xreg) _); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: register class `xreg` can only be used as a clobber in stable - --> $DIR/bad-reg.rs:70:26 + --> $DIR/bad-reg.rs:77:26 | LL | asm!("/* {} */", in(xreg) d); | ^^^^^^^^^^ @@ -105,7 +125,7 @@ LL | asm!("/* {} */", in(xreg) d); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: register class `xreg` can only be used as a clobber in stable - --> $DIR/bad-reg.rs:73:26 + --> $DIR/bad-reg.rs:80:26 | LL | asm!("/* {} */", out(xreg) d); | ^^^^^^^^^^^ @@ -115,7 +135,7 @@ LL | asm!("/* {} */", out(xreg) d); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: register class `vreg` can only be used as a clobber in stable - --> $DIR/bad-reg.rs:77:31 + --> $DIR/bad-reg.rs:84:31 | LL | asm!("", in("$f0") f, in("$vr0") d); | ^^^^^^^^^^^^ @@ -125,7 +145,7 @@ LL | asm!("", in("$f0") f, in("$vr0") d); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: register class `xreg` can only be used as a clobber in stable - --> $DIR/bad-reg.rs:82:31 + --> $DIR/bad-reg.rs:89:31 | LL | asm!("", in("$f0") f, in("$xr0") d); | ^^^^^^^^^^^^ @@ -135,7 +155,7 @@ LL | asm!("", in("$f0") f, in("$xr0") d); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: register class `vreg` can only be used as a clobber in stable - --> $DIR/bad-reg.rs:87:18 + --> $DIR/bad-reg.rs:94:18 | LL | asm!("", in("$vr0") f, in("$xr0") d); | ^^^^^^^^^^^^ @@ -145,7 +165,7 @@ LL | asm!("", in("$vr0") f, in("$xr0") d); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: register class `xreg` can only be used as a clobber in stable - --> $DIR/bad-reg.rs:87:32 + --> $DIR/bad-reg.rs:94:32 | LL | asm!("", in("$vr0") f, in("$xr0") d); | ^^^^^^^^^^^^ @@ -155,31 +175,41 @@ LL | asm!("", in("$vr0") f, in("$xr0") d); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:42:26 + --> $DIR/bad-reg.rs:43:26 | LL | asm!("/* {} */", in(freg) f); | ^^^^^^^^^^ error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:44:26 + --> $DIR/bad-reg.rs:45:26 | LL | asm!("/* {} */", out(freg) _); | ^^^^^^^^^^^ error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:46:26 + --> $DIR/bad-reg.rs:47:26 | LL | asm!("/* {} */", in(freg) d); | ^^^^^^^^^^ error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:48:26 + --> $DIR/bad-reg.rs:49:26 | LL | asm!("/* {} */", out(freg) d); | ^^^^^^^^^^^ +error[E0658]: type `u128` cannot be used with this register class in stable + --> $DIR/bad-reg.rs:53:35 + | +LL | asm!("/* {} */", in(vreg) q); + | ^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + error[E0658]: type `f32` cannot be used with this register class in stable - --> $DIR/bad-reg.rs:52:35 + --> $DIR/bad-reg.rs:56:35 | LL | asm!("/* {} */", in(vreg) f); | ^ @@ -189,7 +219,7 @@ LL | asm!("/* {} */", in(vreg) f); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: type `f64` cannot be used with this register class in stable - --> $DIR/bad-reg.rs:57:35 + --> $DIR/bad-reg.rs:61:35 | LL | asm!("/* {} */", in(vreg) d); | ^ @@ -199,7 +229,7 @@ LL | asm!("/* {} */", in(vreg) d); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: type `f64` cannot be used with this register class in stable - --> $DIR/bad-reg.rs:60:36 + --> $DIR/bad-reg.rs:64:36 | LL | asm!("/* {} */", out(vreg) d); | ^ @@ -208,8 +238,18 @@ LL | asm!("/* {} */", out(vreg) d); = help: add `#![feature(asm_experimental_reg)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date +error[E0658]: type `u128` cannot be used with this register class in stable + --> $DIR/bad-reg.rs:69:35 + | +LL | asm!("/* {} */", in(xreg) q); + | ^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + error[E0658]: type `f32` cannot be used with this register class in stable - --> $DIR/bad-reg.rs:65:35 + --> $DIR/bad-reg.rs:72:35 | LL | asm!("/* {} */", in(xreg) f); | ^ @@ -219,7 +259,7 @@ LL | asm!("/* {} */", in(xreg) f); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: type `f64` cannot be used with this register class in stable - --> $DIR/bad-reg.rs:70:35 + --> $DIR/bad-reg.rs:77:35 | LL | asm!("/* {} */", in(xreg) d); | ^ @@ -229,7 +269,7 @@ LL | asm!("/* {} */", in(xreg) d); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: type `f64` cannot be used with this register class in stable - --> $DIR/bad-reg.rs:73:36 + --> $DIR/bad-reg.rs:80:36 | LL | asm!("/* {} */", out(xreg) d); | ^ @@ -239,13 +279,13 @@ LL | asm!("/* {} */", out(xreg) d); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:77:18 + --> $DIR/bad-reg.rs:84:18 | LL | asm!("", in("$f0") f, in("$vr0") d); | ^^^^^^^^^^^ error[E0658]: type `f64` cannot be used with this register class in stable - --> $DIR/bad-reg.rs:77:42 + --> $DIR/bad-reg.rs:84:42 | LL | asm!("", in("$f0") f, in("$vr0") d); | ^ @@ -255,13 +295,13 @@ LL | asm!("", in("$f0") f, in("$vr0") d); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:82:18 + --> $DIR/bad-reg.rs:89:18 | LL | asm!("", in("$f0") f, in("$xr0") d); | ^^^^^^^^^^^ error[E0658]: type `f64` cannot be used with this register class in stable - --> $DIR/bad-reg.rs:82:42 + --> $DIR/bad-reg.rs:89:42 | LL | asm!("", in("$f0") f, in("$xr0") d); | ^ @@ -271,7 +311,7 @@ LL | asm!("", in("$f0") f, in("$xr0") d); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: type `f32` cannot be used with this register class in stable - --> $DIR/bad-reg.rs:87:29 + --> $DIR/bad-reg.rs:94:29 | LL | asm!("", in("$vr0") f, in("$xr0") d); | ^ @@ -281,7 +321,7 @@ LL | asm!("", in("$vr0") f, in("$xr0") d); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: type `f64` cannot be used with this register class in stable - --> $DIR/bad-reg.rs:87:43 + --> $DIR/bad-reg.rs:94:43 | LL | asm!("", in("$vr0") f, in("$xr0") d); | ^ @@ -290,6 +330,6 @@ LL | asm!("", in("$vr0") f, in("$xr0") d); = help: add `#![feature(asm_experimental_reg)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error: aborting due to 34 previous errors +error: aborting due to 38 previous errors For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/asm/loongarch/bad-reg.rs b/tests/ui/asm/loongarch/bad-reg.rs index 6bda28eb10fde..ac2eeb7d78a70 100644 --- a/tests/ui/asm/loongarch/bad-reg.rs +++ b/tests/ui/asm/loongarch/bad-reg.rs @@ -8,6 +8,7 @@ //@[loongarch64_lp64d] needs-llvm-components: loongarch //@[loongarch64_lp64s] compile-flags: --target loongarch64-unknown-none-softfloat //@[loongarch64_lp64s] needs-llvm-components: loongarch +//@ min-llvm-version: 23 //@ ignore-backends: gcc #![cfg_attr(loongarch64_lp64d, feature(asm_experimental_reg))] @@ -20,7 +21,7 @@ extern crate minicore; use minicore::*; fn f() { - let mut x = 0; + let mut q = 0_u128; let mut f = 0.0_f32; let mut d = 0.0_f64; unsafe { @@ -49,6 +50,9 @@ fn f() { //[loongarch32_ilp32s,loongarch64_lp64s]~^ ERROR register class `freg` requires at least one of the following target features: d, f asm!("", out("$vr0") _); // ok + asm!("/* {} */", in(vreg) q); + //[loongarch32_ilp32s,loongarch32_ilp32d,loongarch64_lp64s]~^ ERROR register class `vreg` can only be used as a clobber in stable + //[loongarch32_ilp32s,loongarch32_ilp32d,loongarch64_lp64s]~| ERROR type `u128` cannot be used with this register class in stable asm!("/* {} */", in(vreg) f); //[loongarch32_ilp32s,loongarch32_ilp32d,loongarch64_lp64s]~^ ERROR register class `vreg` can only be used as a clobber in stable //[loongarch32_ilp32s,loongarch32_ilp32d,loongarch64_lp64s]~| ERROR type `f32` cannot be used with this register class in stable @@ -62,6 +66,9 @@ fn f() { //[loongarch32_ilp32s,loongarch32_ilp32d,loongarch64_lp64s]~| ERROR type `f64` cannot be used with this register class in stable asm!("", out("$xr0") _); // ok + asm!("/* {} */", in(xreg) q); + //[loongarch32_ilp32s,loongarch32_ilp32d,loongarch64_lp64s]~^ ERROR register class `xreg` can only be used as a clobber in stable + //[loongarch32_ilp32s,loongarch32_ilp32d,loongarch64_lp64s]~| ERROR type `u128` cannot be used with this register class in stable asm!("/* {} */", in(xreg) f); //[loongarch32_ilp32s,loongarch32_ilp32d,loongarch64_lp64s]~^ ERROR register class `xreg` can only be used as a clobber in stable //[loongarch32_ilp32s,loongarch32_ilp32d,loongarch64_lp64s]~| ERROR type `f32` cannot be used with this register class in stable From 97fe424048d97b383aa981fd41879f0e58c87b74 Mon Sep 17 00:00:00 2001 From: zep Date: Thu, 27 Aug 2026 20:12:35 +0530 Subject: [PATCH 06/22] Change the doc comments to link to the relevant doc section --- library/core/src/option.rs | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/library/core/src/option.rs b/library/core/src/option.rs index 61925c3dbc628..7e6e31931813c 100644 --- a/library/core/src/option.rs +++ b/library/core/src/option.rs @@ -2456,16 +2456,7 @@ const impl PartialEq for Option { #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_unstable(feature = "const_cmp", issue = "143800")] const impl PartialOrd for Option { - /// If `T` implements [`PartialOrd`], then [`Option`] will derive its [`PartialOrd`] implementation. - /// With this order, `None` compares as less than any `Some`, and two `Some` values compare the - /// same way as their contained values would in `T`. If `T` also implements [`Ord`], then so does [`Option`]. - /// - /// # Examples - /// - /// ``` - /// assert!(None < Some(0)); - /// assert!(Some(0) < Some(1)); - /// ``` + /// See [the documentation](https://doc.rust-lang.org/std/option/#comparison-operators) for details. #[inline] fn partial_cmp(&self, other: &Self) -> Option { match (self, other) { From a1fb7b3cb832f99ccd4a56bb237e9a65a9bf4b26 Mon Sep 17 00:00:00 2001 From: qaijuang <237468078+qaijuang@users.noreply.github.com> Date: Thu, 27 Aug 2026 11:25:08 -0400 Subject: [PATCH 07/22] add test --- .../lints/redundant-explicit-links-ice.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 tests/rustdoc-ui/lints/redundant-explicit-links-ice.rs diff --git a/tests/rustdoc-ui/lints/redundant-explicit-links-ice.rs b/tests/rustdoc-ui/lints/redundant-explicit-links-ice.rs new file mode 100644 index 0000000000000..a129baecdb79f --- /dev/null +++ b/tests/rustdoc-ui/lints/redundant-explicit-links-ice.rs @@ -0,0 +1,13 @@ +// Regression test for . + +//@ run-rustfix + +#![deny(rustdoc::redundant_explicit_links)] + +//! [queue](macro.queue.html) +//~^ ERROR redundant explicit link target + +#[macro_export] +macro_rules! queue { + () => {}; +} From 0f08915b3ef24ac1909c7c94700389238e9ee1ba Mon Sep 17 00:00:00 2001 From: qaijuang <237468078+qaijuang@users.noreply.github.com> Date: Thu, 27 Aug 2026 12:23:00 -0400 Subject: [PATCH 08/22] do not load macro metadata for local definitions in rustdoc --- src/librustdoc/clean/inline.rs | 20 +++++++++------- .../lints/redundant-explicit-links-ice.fixed | 13 +++++++++++ .../lints/redundant-explicit-links-ice.stderr | 23 +++++++++++++++++++ 3 files changed, 48 insertions(+), 8 deletions(-) create mode 100644 tests/rustdoc-ui/lints/redundant-explicit-links-ice.fixed create mode 100644 tests/rustdoc-ui/lints/redundant-explicit-links-ice.stderr diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index cb7ddd0ec58e7..d7fbe64c30771 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -250,17 +250,21 @@ pub(crate) fn get_item_path(tcx: TyCtxt<'_>, def_id: DefId, kind: ItemType) -> V if let ItemType::Macro = kind { // Check to see if it is a macro 2.0 or built-in macro // More information in . - if matches!( - CStore::from_tcx(tcx).load_macro_untracked(tcx, def_id), - LoadedMacro::MacroDef { def, .. } if !def.macro_rules - ) { - once(crate_name).chain(relative).collect() + let is_macro_2_0_or_builtin = if let Some(local_def_id) = def_id.as_local() { + let (_, macro_def, _) = tcx.hir_expect_item(local_def_id).expect_macro(); + !macro_def.macro_rules } else { - vec![crate_name, *relative.last().expect("relative was empty")] + matches!( + CStore::from_tcx(tcx).load_macro_untracked(tcx, def_id), + LoadedMacro::MacroDef { def, .. } if !def.macro_rules + ) + }; + if !is_macro_2_0_or_builtin { + return vec![crate_name, *relative.last().expect("relative was empty")]; } - } else { - once(crate_name).chain(relative).collect() } + + once(crate_name).chain(relative).collect() } /// Record an external fully qualified name in the external_paths cache. diff --git a/tests/rustdoc-ui/lints/redundant-explicit-links-ice.fixed b/tests/rustdoc-ui/lints/redundant-explicit-links-ice.fixed new file mode 100644 index 0000000000000..bfd09e970db06 --- /dev/null +++ b/tests/rustdoc-ui/lints/redundant-explicit-links-ice.fixed @@ -0,0 +1,13 @@ +// Regression test for . + +//@ run-rustfix + +#![deny(rustdoc::redundant_explicit_links)] + +//! [queue] +//~^ ERROR redundant explicit link target + +#[macro_export] +macro_rules! queue { + () => {}; +} diff --git a/tests/rustdoc-ui/lints/redundant-explicit-links-ice.stderr b/tests/rustdoc-ui/lints/redundant-explicit-links-ice.stderr new file mode 100644 index 0000000000000..2caf08e26936f --- /dev/null +++ b/tests/rustdoc-ui/lints/redundant-explicit-links-ice.stderr @@ -0,0 +1,23 @@ +error: redundant explicit link target + --> $DIR/redundant-explicit-links-ice.rs:7:13 + | +LL | //! [queue](macro.queue.html) + | ----- ^^^^^^^^^^^^^^^^ explicit target is redundant + | | + | because label contains path that resolves to same destination + | + = note: when a link's destination is not specified, + the label is used to resolve intra-doc links +note: the lint level is defined here + --> $DIR/redundant-explicit-links-ice.rs:5:9 + | +LL | #![deny(rustdoc::redundant_explicit_links)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: remove explicit link target + | +LL - //! [queue](macro.queue.html) +LL + //! [queue] + | + +error: aborting due to 1 previous error + From 42629e39dcec43948166b074c8143d6bb48526d3 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 27 Aug 2026 21:22:34 +0200 Subject: [PATCH 09/22] better deal with internal features being injected into doctests --- compiler/rustc_arena/src/lib.rs | 2 +- compiler/rustc_ast/src/lib.rs | 2 +- compiler/rustc_graphviz/src/lib.rs | 2 +- compiler/rustc_parse_format/src/lib.rs | 2 +- compiler/rustc_public/src/lib.rs | 2 +- compiler/rustc_public_bridge/src/lib.rs | 2 +- compiler/rustc_serialize/src/lib.rs | 2 +- src/bootstrap/src/bin/rustdoc.rs | 5 ++++- 8 files changed, 11 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_arena/src/lib.rs b/compiler/rustc_arena/src/lib.rs index c33765f03d77d..dfc48b0bd1cd6 100644 --- a/compiler/rustc_arena/src/lib.rs +++ b/compiler/rustc_arena/src/lib.rs @@ -13,7 +13,7 @@ #![cfg_attr(bootstrap, feature(never_type))] #![cfg_attr(test, feature(test))] #![deny(unsafe_op_in_unsafe_fn)] -#![doc(test(no_crate_inject, attr(deny(warnings), allow(internal_features))))] +#![doc(test(no_crate_inject, attr(deny(warnings))))] #![feature(decl_macro)] #![feature(dropck_eyepatch)] #![feature(rustc_attrs)] diff --git a/compiler/rustc_ast/src/lib.rs b/compiler/rustc_ast/src/lib.rs index 3b01eb6eefa7d..46d8e11cc0931 100644 --- a/compiler/rustc_ast/src/lib.rs +++ b/compiler/rustc_ast/src/lib.rs @@ -5,7 +5,7 @@ //! This API is completely unstable and subject to change. // tidy-alphabetical-start -#![doc(test(attr(deny(warnings), allow(internal_features))))] +#![doc(test(attr(deny(warnings))))] #![feature(associated_type_defaults)] #![feature(deref_patterns)] #![feature(iter_order_by)] diff --git a/compiler/rustc_graphviz/src/lib.rs b/compiler/rustc_graphviz/src/lib.rs index cd1e573ea28df..2aaf9cea97e44 100644 --- a/compiler/rustc_graphviz/src/lib.rs +++ b/compiler/rustc_graphviz/src/lib.rs @@ -270,7 +270,7 @@ //! * [DOT language](https://www.graphviz.org/doc/info/lang.html) // tidy-alphabetical-start -#![doc(test(attr(allow(unused_variables), deny(warnings), allow(internal_features))))] +#![doc(test(attr(allow(unused_variables), deny(warnings))))] // tidy-alphabetical-end use std::borrow::Cow; diff --git a/compiler/rustc_parse_format/src/lib.rs b/compiler/rustc_parse_format/src/lib.rs index 256bc8c3fe30e..90e04fe388ad6 100644 --- a/compiler/rustc_parse_format/src/lib.rs +++ b/compiler/rustc_parse_format/src/lib.rs @@ -8,7 +8,7 @@ // We want to be able to build this crate with a stable compiler, // so no `#![feature]` attributes should be added. #![deny(unstable_features)] -#![doc(test(attr(deny(warnings), allow(internal_features))))] +#![doc(test(attr(deny(warnings))))] // tidy-alphabetical-end use std::ops::Range; diff --git a/compiler/rustc_public/src/lib.rs b/compiler/rustc_public/src/lib.rs index 4adc0e139a68e..ac2d1eb7a7a42 100644 --- a/compiler/rustc_public/src/lib.rs +++ b/compiler/rustc_public/src/lib.rs @@ -43,7 +43,7 @@ //! For more information, see . #![allow(rustc::usage_of_ty_tykind)] -#![doc(test(attr(allow(unused_variables), deny(warnings), allow(internal_features))))] +#![doc(test(attr(allow(unused_variables), deny(warnings))))] #![feature(sized_hierarchy)] use std::fmt::Debug; diff --git a/compiler/rustc_public_bridge/src/lib.rs b/compiler/rustc_public_bridge/src/lib.rs index d598f88a00d27..c8859b0e349ea 100644 --- a/compiler/rustc_public_bridge/src/lib.rs +++ b/compiler/rustc_public_bridge/src/lib.rs @@ -13,7 +13,7 @@ // tidy-alphabetical-start #![allow(rustc::usage_of_ty_tykind)] -#![doc(test(attr(allow(unused_variables), deny(warnings), allow(internal_features))))] +#![doc(test(attr(allow(unused_variables), deny(warnings))))] #![feature(trait_alias)] // tidy-alphabetical-end diff --git a/compiler/rustc_serialize/src/lib.rs b/compiler/rustc_serialize/src/lib.rs index 39333ab00b57f..ae7c3854d00b4 100644 --- a/compiler/rustc_serialize/src/lib.rs +++ b/compiler/rustc_serialize/src/lib.rs @@ -4,7 +4,7 @@ #![allow(internal_features)] #![allow(rustc::internal)] #![cfg_attr(bootstrap, feature(never_type))] -#![doc(test(attr(allow(unused_variables), deny(warnings), allow(internal_features))))] +#![doc(test(attr(allow(unused_variables), deny(warnings))))] #![feature(core_intrinsics)] #![feature(min_specialization)] #![feature(nonzero_internals)] diff --git a/src/bootstrap/src/bin/rustdoc.rs b/src/bootstrap/src/bin/rustdoc.rs index eba1e9ef1c5cf..da80d7cd8c599 100644 --- a/src/bootstrap/src/bin/rustdoc.rs +++ b/src/bootstrap/src/bin/rustdoc.rs @@ -65,7 +65,10 @@ fn main() { if let Some(crate_name) = parse_value_from_args(&args, "--crate-name") { // Add rust logo and set html root for all rustc crates. if crate_name.starts_with("rustc_") { - cmd.arg("-Ainternal_features") + // We use `-Zcrate-attr=allow` instead of `-A` to force rustdoc to forward this flag to + // the actual doctests. Otherwise those tests all receive the + // `feature(rustdoc_internals)` without receiving the `-A` which leads to errors. + cmd.arg("-Zcrate-attr=allow(internal_features)") .arg("-Zcrate-attr=doc(rust_logo)") .arg("-Zcrate-attr=doc(html_root_url = \"https://doc.rust-lang.org/nightly/nightly-rustc/\")"); From a0ec5117dedb2802ee391df172c6e35e654c6715 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 27 Aug 2026 20:54:52 +0200 Subject: [PATCH 10/22] fix rustc_lint_defs doctest issues --- compiler/rustc_lint_defs/src/builtin.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index def2662e15837..93e2cd971a1b9 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -798,7 +798,7 @@ declare_lint! { /// /// ### Example /// - /// ```rust + /// ```rust,compile_fail /// #![deny(dead_code_pub_in_binary)] /// /// pub fn unused_pub_fn() {} @@ -1107,9 +1107,9 @@ declare_lint! { /// /// ### Example /// - /// ```rust + /// ```rust,compile_fail /// #![deny(warnings)] - /// fn foo() {} + /// struct non_standard_name; /// ``` /// /// {{produces}} From cdd3fc01eaee52874310b9dede3cf510db6dc0e1 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Thu, 27 Aug 2026 21:47:21 +0100 Subject: [PATCH 11/22] std: uefi: fix File::seek returning the EOF sentinel seek(SeekFrom::End(0)) special-cased the offset to the UEFI 0xFFFFFFFFFFFFFFFF "set position to end of file" sentinel, then returned that value as the new stream position. so seek reported u64::MAX instead of the file size, and the default Seek::stream_len did too. compute the offset from the file size in every End case instead. --- library/std/src/sys/fs/uefi.rs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/library/std/src/sys/fs/uefi.rs b/library/std/src/sys/fs/uefi.rs index 1a0da329ce1a3..08473e245cc8e 100644 --- a/library/std/src/sys/fs/uefi.rs +++ b/library/std/src/sys/fs/uefi.rs @@ -360,12 +360,7 @@ impl File { let off = match pos { SeekFrom::Start(p) => p, SeekFrom::End(p) => { - // Seeking to position 0xFFFFFFFFFFFFFFFF causes the current position to be set to the end of the file. - if p == 0 { - 0xFFFFFFFFFFFFFFFF - } else { - self.file_attr()?.size().checked_add_signed(p).ok_or(NEG_OFF_ERR)? - } + self.file_attr()?.size().checked_add_signed(p).ok_or(NEG_OFF_ERR)? } SeekFrom::Current(p) => self.tell()?.checked_add_signed(p).ok_or(NEG_OFF_ERR)?, }; From 7047ae88811f6ef9bf54da93810f3daa1b73f9cf Mon Sep 17 00:00:00 2001 From: Yukang Date: Fri, 28 Aug 2026 08:07:48 +0800 Subject: [PATCH 12/22] Add regression test for empty contract attributes with lifetime lints --- .../lint/single-use-lifetimes-issue-146834.rs | 18 ++++++ .../single-use-lifetimes-issue-146834.stderr | 63 +++++++++++++++++++ 2 files changed, 81 insertions(+) create mode 100644 tests/ui/lint/single-use-lifetimes-issue-146834.rs create mode 100644 tests/ui/lint/single-use-lifetimes-issue-146834.stderr diff --git a/tests/ui/lint/single-use-lifetimes-issue-146834.rs b/tests/ui/lint/single-use-lifetimes-issue-146834.rs new file mode 100644 index 0000000000000..aee16ae5731c1 --- /dev/null +++ b/tests/ui/lint/single-use-lifetimes-issue-146834.rs @@ -0,0 +1,18 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/146834. + +//@ compile-flags: -Wsingle-use-lifetimes +//@ edition: 2024 + +#![expect(incomplete_features)] +#![feature(contracts)] + +#[core::contracts::ensures] +//~^ ERROR expected an `Fn(&_)` closure, found `()` +fn f<'a, 'b>(a: &'a i32, b: &'b i32) -> (&i32, &i32) { + //~^ ERROR missing lifetime specifiers + //~| WARN lifetime parameter `'a` only used once + //~| WARN lifetime parameter `'b` only used once + loop {} +} + +fn main() {} diff --git a/tests/ui/lint/single-use-lifetimes-issue-146834.stderr b/tests/ui/lint/single-use-lifetimes-issue-146834.stderr new file mode 100644 index 0000000000000..259a908e1b818 --- /dev/null +++ b/tests/ui/lint/single-use-lifetimes-issue-146834.stderr @@ -0,0 +1,63 @@ +error[E0106]: missing lifetime specifiers + --> $DIR/single-use-lifetimes-issue-146834.rs:11:42 + | +LL | fn f<'a, 'b>(a: &'a i32, b: &'b i32) -> (&i32, &i32) { + | ------- ------- ^ ^ expected named lifetime parameter + | | + | expected named lifetime parameter + | + = help: this function's return type contains a borrowed value with an elided lifetime, but the lifetime cannot be derived from the arguments +note: these named lifetimes are available to use + --> $DIR/single-use-lifetimes-issue-146834.rs:11:6 + | +LL | fn f<'a, 'b>(a: &'a i32, b: &'b i32) -> (&i32, &i32) { + | ^^ ^^ +help: consider using one of the available lifetimes here + | +LL | fn f<'a, 'b>(a: &'a i32, b: &'b i32) -> (&'lifetime i32, &'lifetime i32) { + | +++++++++ +++++++++ + +warning: lifetime parameter `'a` only used once + --> $DIR/single-use-lifetimes-issue-146834.rs:11:6 + | +LL | fn f<'a, 'b>(a: &'a i32, b: &'b i32) -> (&i32, &i32) { + | ^^ -- ...is used only here + | | + | this lifetime... + | + = note: requested on the command line with `-W single-use-lifetimes` +help: elide the single-use lifetime + | +LL - fn f<'a, 'b>(a: &'a i32, b: &'b i32) -> (&i32, &i32) { +LL + fn f<'b>(a: &i32, b: &'b i32) -> (&i32, &i32) { + | + +warning: lifetime parameter `'b` only used once + --> $DIR/single-use-lifetimes-issue-146834.rs:11:10 + | +LL | fn f<'a, 'b>(a: &'a i32, b: &'b i32) -> (&i32, &i32) { + | ^^ this lifetime... -- ...is used only here + | +help: elide the single-use lifetime + | +LL - fn f<'a, 'b>(a: &'a i32, b: &'b i32) -> (&i32, &i32) { +LL + fn f<'a>(a: &'a i32, b: &i32) -> (&i32, &i32) { + | + +error[E0277]: expected an `Fn(&_)` closure, found `()` + --> $DIR/single-use-lifetimes-issue-146834.rs:9:1 + | +LL | #[core::contracts::ensures] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | expected an `Fn(&_)` closure, found `()` + | required by a bound introduced by this call + | + = help: the trait `for<'a> Fn(&'a _)` is not implemented for `()` +note: required by a bound in `build_check_ensures` + --> $SRC_DIR/core/src/contracts.rs:LL:COL + +error: aborting due to 2 previous errors; 2 warnings emitted + +Some errors have detailed explanations: E0106, E0277. +For more information about an error, try `rustc --explain E0106`. From 12f970e8d77d7abed5053b0c9f90c625466d787e Mon Sep 17 00:00:00 2001 From: Yukang Date: Fri, 28 Aug 2026 08:11:01 +0800 Subject: [PATCH 13/22] Reject contract attributes without arguments --- .../rustc_builtin_macros/src/contracts.rs | 15 +++++++++++++ tests/ui/contracts/empty-ensures.rs | 2 +- tests/ui/contracts/empty-ensures.stderr | 10 +-------- tests/ui/contracts/empty-requires.rs | 4 +--- tests/ui/contracts/empty-requires.stderr | 7 +++--- .../lint/single-use-lifetimes-issue-146834.rs | 2 +- .../single-use-lifetimes-issue-146834.stderr | 22 ++++++------------- ...-tokenstream-for-contracts-issue-140683.rs | 2 +- ...enstream-for-contracts-issue-140683.stderr | 20 +++++------------ 9 files changed, 36 insertions(+), 48 deletions(-) diff --git a/compiler/rustc_builtin_macros/src/contracts.rs b/compiler/rustc_builtin_macros/src/contracts.rs index e47c1b1363df0..20001400857a6 100644 --- a/compiler/rustc_builtin_macros/src/contracts.rs +++ b/compiler/rustc_builtin_macros/src/contracts.rs @@ -137,6 +137,21 @@ fn expand_contract_clause_tts( annotated: TokenStream, clause_keyword: rustc_span::Symbol, ) -> Result { + if annotation.is_empty() { + let (name, example) = if clause_keyword == kw::ContractRequires { + ("requires", "condition") + } else { + ("ensures", "|result: &T| condition") + }; + ecx.sess.dcx().span_err( + attr_span, + format!("`{name}` attribute requires an argument, e.g., `#[{name}({example})]`"), + ); + // Returning `Err` would replace it with a dummy fragment and cause cascading name-resolution errors. + // Instead, we return the original token stream so that there is no later noises. + return Ok(annotated); + } + let feature_span = ecx.with_def_site_ctxt(attr_span); expand_contract_clause(ecx, attr_span, annotated, |new_tts| { new_tts.push(TokenTree::Token( diff --git a/tests/ui/contracts/empty-ensures.rs b/tests/ui/contracts/empty-ensures.rs index 79e57df6eb984..242e903b7cc96 100644 --- a/tests/ui/contracts/empty-ensures.rs +++ b/tests/ui/contracts/empty-ensures.rs @@ -6,7 +6,7 @@ extern crate core; use core::contracts::ensures; #[ensures()] -//~^ ERROR expected an `Fn(&_)` closure, found `()` [E0277] +//~^ ERROR `ensures` attribute requires an argument fn foo(x: u32) -> u32 { x * 2 } diff --git a/tests/ui/contracts/empty-ensures.stderr b/tests/ui/contracts/empty-ensures.stderr index b87f709eeb7a4..369ba431b62ce 100644 --- a/tests/ui/contracts/empty-ensures.stderr +++ b/tests/ui/contracts/empty-ensures.stderr @@ -1,16 +1,8 @@ -error[E0277]: expected an `Fn(&_)` closure, found `()` +error: `ensures` attribute requires an argument, e.g., `#[ensures(|result: &T| condition)]` --> $DIR/empty-ensures.rs:8:1 | LL | #[ensures()] | ^^^^^^^^^^^^ - | | - | expected an `Fn(&_)` closure, found `()` - | required by a bound introduced by this call - | - = help: the trait `for<'a> Fn(&'a _)` is not implemented for `()` -note: required by a bound in `build_check_ensures` - --> $SRC_DIR/core/src/contracts.rs:LL:COL error: aborting due to 1 previous error -For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/contracts/empty-requires.rs b/tests/ui/contracts/empty-requires.rs index dedcc10d52cb0..eae86607acf31 100644 --- a/tests/ui/contracts/empty-requires.rs +++ b/tests/ui/contracts/empty-requires.rs @@ -1,4 +1,3 @@ -//@ dont-require-annotations: NOTE //@ compile-flags: -Zcontract-checks=yes #![expect(incomplete_features)] #![feature(contracts)] @@ -7,8 +6,7 @@ extern crate core; use core::contracts::requires; #[requires()] -//~^ ERROR mismatched types [E0308] -//~| NOTE expected `bool`, found `()` +//~^ ERROR `requires` attribute requires an argument fn foo(x: u32) -> u32 { x * 2 } diff --git a/tests/ui/contracts/empty-requires.stderr b/tests/ui/contracts/empty-requires.stderr index 702b8a23c55e3..c8fa644702259 100644 --- a/tests/ui/contracts/empty-requires.stderr +++ b/tests/ui/contracts/empty-requires.stderr @@ -1,9 +1,8 @@ -error[E0308]: mismatched types - --> $DIR/empty-requires.rs:9:1 +error: `requires` attribute requires an argument, e.g., `#[requires(condition)]` + --> $DIR/empty-requires.rs:8:1 | LL | #[requires()] - | ^^^^^^^^^^^^^ expected `bool`, found `()` + | ^^^^^^^^^^^^^ error: aborting due to 1 previous error -For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/lint/single-use-lifetimes-issue-146834.rs b/tests/ui/lint/single-use-lifetimes-issue-146834.rs index aee16ae5731c1..8c03e40fa8070 100644 --- a/tests/ui/lint/single-use-lifetimes-issue-146834.rs +++ b/tests/ui/lint/single-use-lifetimes-issue-146834.rs @@ -7,7 +7,7 @@ #![feature(contracts)] #[core::contracts::ensures] -//~^ ERROR expected an `Fn(&_)` closure, found `()` +//~^ ERROR `ensures` attribute requires an argument fn f<'a, 'b>(a: &'a i32, b: &'b i32) -> (&i32, &i32) { //~^ ERROR missing lifetime specifiers //~| WARN lifetime parameter `'a` only used once diff --git a/tests/ui/lint/single-use-lifetimes-issue-146834.stderr b/tests/ui/lint/single-use-lifetimes-issue-146834.stderr index 259a908e1b818..d7b84680fd081 100644 --- a/tests/ui/lint/single-use-lifetimes-issue-146834.stderr +++ b/tests/ui/lint/single-use-lifetimes-issue-146834.stderr @@ -1,3 +1,9 @@ +error: `ensures` attribute requires an argument, e.g., `#[ensures(|result: &T| condition)]` + --> $DIR/single-use-lifetimes-issue-146834.rs:9:1 + | +LL | #[core::contracts::ensures] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + error[E0106]: missing lifetime specifiers --> $DIR/single-use-lifetimes-issue-146834.rs:11:42 | @@ -44,20 +50,6 @@ LL - fn f<'a, 'b>(a: &'a i32, b: &'b i32) -> (&i32, &i32) { LL + fn f<'a>(a: &'a i32, b: &i32) -> (&i32, &i32) { | -error[E0277]: expected an `Fn(&_)` closure, found `()` - --> $DIR/single-use-lifetimes-issue-146834.rs:9:1 - | -LL | #[core::contracts::ensures] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | | - | expected an `Fn(&_)` closure, found `()` - | required by a bound introduced by this call - | - = help: the trait `for<'a> Fn(&'a _)` is not implemented for `()` -note: required by a bound in `build_check_ensures` - --> $SRC_DIR/core/src/contracts.rs:LL:COL - error: aborting due to 2 previous errors; 2 warnings emitted -Some errors have detailed explanations: E0106, E0277. -For more information about an error, try `rustc --explain E0106`. +For more information about this error, try `rustc --explain E0106`. diff --git a/tests/ui/macros/ice-in-tokenstream-for-contracts-issue-140683.rs b/tests/ui/macros/ice-in-tokenstream-for-contracts-issue-140683.rs index 2b1bacf7e0c31..db85d496991f7 100644 --- a/tests/ui/macros/ice-in-tokenstream-for-contracts-issue-140683.rs +++ b/tests/ui/macros/ice-in-tokenstream-for-contracts-issue-140683.rs @@ -4,7 +4,7 @@ struct T; impl T { - #[core::contracts::ensures] //~ ERROR expected an `Fn(&_)` closure, found `()` + #[core::contracts::ensures] //~ ERROR `ensures` attribute requires an argument fn b() {(loop)} //~^ ERROR expected `{`, found `)` //~| ERROR expected `{`, found `)` diff --git a/tests/ui/macros/ice-in-tokenstream-for-contracts-issue-140683.stderr b/tests/ui/macros/ice-in-tokenstream-for-contracts-issue-140683.stderr index 56dbdae14189b..ab6459bdc919a 100644 --- a/tests/ui/macros/ice-in-tokenstream-for-contracts-issue-140683.stderr +++ b/tests/ui/macros/ice-in-tokenstream-for-contracts-issue-140683.stderr @@ -6,6 +6,12 @@ LL | fn b() {(loop)} | | | while parsing this `loop` expression +error: `ensures` attribute requires an argument, e.g., `#[ensures(|result: &T| condition)]` + --> $DIR/ice-in-tokenstream-for-contracts-issue-140683.rs:7:5 + | +LL | #[core::contracts::ensures] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + error: expected `{`, found `)` --> $DIR/ice-in-tokenstream-for-contracts-issue-140683.rs:8:18 | @@ -16,19 +22,5 @@ LL | fn b() {(loop)} | = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error[E0277]: expected an `Fn(&_)` closure, found `()` - --> $DIR/ice-in-tokenstream-for-contracts-issue-140683.rs:7:5 - | -LL | #[core::contracts::ensures] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | | - | expected an `Fn(&_)` closure, found `()` - | required by a bound introduced by this call - | - = help: the trait `for<'a> Fn(&'a _)` is not implemented for `()` -note: required by a bound in `build_check_ensures` - --> $SRC_DIR/core/src/contracts.rs:LL:COL - error: aborting due to 3 previous errors -For more information about this error, try `rustc --explain E0277`. From 9c6ebca4725d647107bbcc43fddee99195af3dd4 Mon Sep 17 00:00:00 2001 From: Yukang Date: Fri, 28 Aug 2026 09:22:08 +0800 Subject: [PATCH 14/22] Add regression test for the Polonius help default --- tests/run-make/rustc-help/polonius-help.stdout | 1 + tests/run-make/rustc-help/rmake.rs | 6 ++++++ 2 files changed, 7 insertions(+) create mode 100644 tests/run-make/rustc-help/polonius-help.stdout diff --git a/tests/run-make/rustc-help/polonius-help.stdout b/tests/run-make/rustc-help/polonius-help.stdout new file mode 100644 index 0000000000000..9f56fb6de9481 --- /dev/null +++ b/tests/run-make/rustc-help/polonius-help.stdout @@ -0,0 +1 @@ + -Z polonius=val -- enable polonius-based borrow-checker (default: no) diff --git a/tests/run-make/rustc-help/rmake.rs b/tests/run-make/rustc-help/rmake.rs index 17811ef18449f..a5e733fb8d9fc 100644 --- a/tests/run-make/rustc-help/rmake.rs +++ b/tests/run-make/rustc-help/rmake.rs @@ -22,6 +22,12 @@ fn main() { // Check that all help options can be invoked at once let codegen_help = bare_rustc().arg("-Chelp").run().stdout_utf8(); let unstable_help = bare_rustc().arg("-Zhelp").run().stdout_utf8(); + let polonius_help = + format!("{}\n", unstable_help.lines().find(|line| line.contains("polonius=val")).unwrap()); + diff() + .expected_file("polonius-help.stdout") + .actual_text("rustc -Zhelp (polonius)", &polonius_help) + .run(); let lints_help = bare_rustc().arg("-Whelp").run().stdout_utf8(); let expected_all = format!("{help}{codegen_help}{unstable_help}{lints_help}"); let all_help = bare_rustc().args(["--help", "-Chelp", "-Zhelp", "-Whelp"]).run().stdout_utf8(); From d471202631fd097101bfa12fbc9680d2b4a93093 Mon Sep 17 00:00:00 2001 From: khyperia <953151+khyperia@users.noreply.github.com> Date: Fri, 28 Aug 2026 06:41:43 +0200 Subject: [PATCH 15/22] fix ICE in generic_const_parameter_types with inherents --- .../src/normalize_projection_ty.rs | 12 ---------- .../inherent-type-const.rs | 22 +++++++++++++++++++ 2 files changed, 22 insertions(+), 12 deletions(-) create mode 100644 tests/ui/const-generics/generic_const_parameter_types/inherent-type-const.rs diff --git a/compiler/rustc_traits/src/normalize_projection_ty.rs b/compiler/rustc_traits/src/normalize_projection_ty.rs index 3710d41dba0d9..03dff745210d6 100644 --- a/compiler/rustc_traits/src/normalize_projection_ty.rs +++ b/compiler/rustc_traits/src/normalize_projection_ty.rs @@ -59,12 +59,6 @@ fn normalize_canonicalized_projection<'tcx>( 0, &mut obligations, ); - obligations.extend(const_arg_has_type_obligation( - tcx, - param_env, - normalized_term, - goal, - )); ocx.register_obligations(obligations); // #112047: With projections and opaques, we are able to create opaques that // are recursive (given some generic parameters of the opaque's type variables). @@ -147,12 +141,6 @@ fn normalize_canonicalized_inherent_projection<'tcx>( 0, &mut obligations, ); - obligations.extend(const_arg_has_type_obligation( - tcx, - param_env, - normalized_term, - goal, - )); ocx.register_obligations(obligations); Ok(NormalizationResult { normalized_term }) diff --git a/tests/ui/const-generics/generic_const_parameter_types/inherent-type-const.rs b/tests/ui/const-generics/generic_const_parameter_types/inherent-type-const.rs new file mode 100644 index 0000000000000..86eab5ec70eb2 --- /dev/null +++ b/tests/ui/const-generics/generic_const_parameter_types/inherent-type-const.rs @@ -0,0 +1,22 @@ +//@ check-pass +#![feature( + min_generic_const_args, + generic_const_parameter_types, + inherent_associated_types, + min_adt_const_params, + const_param_ty_trait +)] + +struct ThreeTypes(T1, T2, T3); + +impl ThreeTypes { + type const INHERENT: [T3; 0] = []; +} + +struct Struct; + +fn f() -> Struct<{ core::direct_const_arg!(ThreeTypes::::INHERENT) }> { + Struct +} + +fn main() {} From f31b77f5c6585941431eed7a7fff2dabfbf2ef34 Mon Sep 17 00:00:00 2001 From: Yukang Date: Fri, 28 Aug 2026 09:32:05 +0800 Subject: [PATCH 16/22] Report the configured Polonius default in -Z help --- compiler/rustc_session/src/config.rs | 5 ++++- compiler/rustc_session/src/options.rs | 10 ++++++++-- tests/run-make/rustc-help/polonius-help.stdout | 2 +- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index a5053c408b155..95f6348cfbdbb 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -3647,11 +3647,14 @@ pub enum Polonius { impl Default for Polonius { fn default() -> Self { - if option_env!("CFG_DEFAULT_POLONIUS_NEXT").is_some() { Self::Next } else { Self::Off } + Self::DEFAULT } } impl Polonius { + pub(crate) const DEFAULT: Self = + if option_env!("CFG_DEFAULT_POLONIUS_NEXT").is_some() { Self::Next } else { Self::Off }; + /// Returns whether the legacy version of polonius is enabled pub fn is_legacy_enabled(&self) -> bool { matches!(self, Polonius::Legacy) diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 90459090ced87..bab087249593a 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -510,7 +510,7 @@ macro_rules! options { $( { TARGET_MODIFIER: $tmod_variant:ident } )? $( { MITIGATION: $mitigation_variant:ident } )? , - $desc:literal + $desc:expr $(, removed: $removed:ident )? ), )* @@ -2350,6 +2350,12 @@ options! { // - src/doc/rustc/src/codegen-options/index.md } +const POLONIUS_HELP: &str = match Polonius::DEFAULT { + Polonius::Off => "enable polonius-based borrow-checker (default: no)", + Polonius::Next => "enable polonius-based borrow-checker (default: next)", + Polonius::Legacy => panic!("Polonius::Legacy is not a valid default value"), +}; + options! { UnstableOptions, UnstableOptionsTargetModifiers, Z_OPTIONS, dbopts, "Z", "unstable", @@ -2750,7 +2756,7 @@ options! { `vt-ptr-type-discrimination - incorporate type discrimination in authenticated vtable pointers Example: `-Zpointer-authentication=+calls,-init-fini`."), polonius: Polonius = (Polonius::default(), parse_polonius, [TRACKED], - "enable polonius-based borrow-checker (default: no)"), + POLONIUS_HELP), pre_link_arg: (/* redirected to pre_link_args */) = ((), parse_string_push, [UNTRACKED], "a single extra argument to prepend the linker invocation (can be used several times)"), pre_link_args: Vec = (Vec::new(), parse_list, [UNTRACKED], diff --git a/tests/run-make/rustc-help/polonius-help.stdout b/tests/run-make/rustc-help/polonius-help.stdout index 9f56fb6de9481..b1dfd15c09958 100644 --- a/tests/run-make/rustc-help/polonius-help.stdout +++ b/tests/run-make/rustc-help/polonius-help.stdout @@ -1 +1 @@ - -Z polonius=val -- enable polonius-based borrow-checker (default: no) + -Z polonius=val -- enable polonius-based borrow-checker (default: next) From 66bbc147f55c6b4cea5876ae266fc45ed652b154 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Fri, 28 Aug 2026 12:45:04 +0200 Subject: [PATCH 17/22] Add rustdoc-html regression test for generated macro --- tests/rustdoc-html/auxiliary/generated_macro.rs | 17 +++++++++++++++++ tests/rustdoc-html/generated_macro.rs | 16 ++++++++++++++++ 2 files changed, 33 insertions(+) create mode 100644 tests/rustdoc-html/auxiliary/generated_macro.rs create mode 100644 tests/rustdoc-html/generated_macro.rs diff --git a/tests/rustdoc-html/auxiliary/generated_macro.rs b/tests/rustdoc-html/auxiliary/generated_macro.rs new file mode 100644 index 0000000000000..47a2eae5b3c2c --- /dev/null +++ b/tests/rustdoc-html/auxiliary/generated_macro.rs @@ -0,0 +1,17 @@ +//@ no-prefer-dynamic + +#![crate_type = "proc-macro"] + +use std::str::FromStr; + +extern crate proc_macro; + +#[proc_macro_derive(MyDeriveMacro)] +pub fn derive_my_derive_macro(item: proc_macro::TokenStream) -> proc_macro::TokenStream { + proc_macro::TokenStream::from_str(" + #[macro_export] + macro_rules! my_generated_macro { + ($my_macro_parameter: expr) => {}; + } + ").unwrap() +} diff --git a/tests/rustdoc-html/generated_macro.rs b/tests/rustdoc-html/generated_macro.rs new file mode 100644 index 0000000000000..3986930f8e631 --- /dev/null +++ b/tests/rustdoc-html/generated_macro.rs @@ -0,0 +1,16 @@ +// This test ensures that the macro generated by the proc macro has the correct +// "item-decl" code block. +// Regression test for . + +//@ aux-build:generated_macro.rs + +#![crate_name = "foo"] + +extern crate generated_macro; + +//@ has 'foo/macro.my_generated_macro.html' +//@ matches - '//*[@class="rust item-decl"]/code' \ +// 'macro_rules! my_generated_macro \{\s+\(\$my_macro_parameter:expr\) => \{ ... \};\s+\}' + +#[derive(generated_macro::MyDeriveMacro)] +struct MyStruct {} From 448757dcdd3bc85f968be595fed9594a6b9530f3 Mon Sep 17 00:00:00 2001 From: Jieyou Xu Date: Fri, 28 Aug 2026 19:09:25 +0800 Subject: [PATCH 18/22] Retroactively add relnotes for `bool::{ok_or,ok_or_else}` --- RELEASES.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/RELEASES.md b/RELEASES.md index 940fa7c6072a9..f2d74f2e7f1f0 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -67,6 +67,8 @@ Stabilized APIs - [`Atomic::get_mut_slice`](https://doc.rust-lang.org/stable/core/sync/atomic/struct.Atomic.html#method.get_mut_slice) - [`Atomic::from_mut_slice`](https://doc.rust-lang.org/stable/core/sync/atomic/struct.Atomic.html#method.from_mut_slice) - [`std::range::legacy`](https://doc.rust-lang.org/stable/std/range/legacy/index.html) +- [`bool::ok_or`](https://doc.rust-lang.org/stable/std/primitive.bool.html#method.ok_or) +- [`bool::ok_or_else`](https://doc.rust-lang.org/stable/std/primitive.bool.html#method.ok_or_else) From d3b2959c63920fe2ab0572e4e242e51af5d97ac3 Mon Sep 17 00:00:00 2001 From: zep Date: Fri, 28 Aug 2026 21:32:58 +0530 Subject: [PATCH 19/22] Clarify None and Some comparisons in docs --- library/core/src/option.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/library/core/src/option.rs b/library/core/src/option.rs index 7e6e31931813c..40be9131931a4 100644 --- a/library/core/src/option.rs +++ b/library/core/src/option.rs @@ -2457,6 +2457,7 @@ const impl PartialEq for Option { #[rustc_const_unstable(feature = "const_cmp", issue = "143800")] const impl PartialOrd for Option { /// See [the documentation](https://doc.rust-lang.org/std/option/#comparison-operators) for details. + /// [`None`] always compares less than any [`Some`] #[inline] fn partial_cmp(&self, other: &Self) -> Option { match (self, other) { From 9b2b381ae4c837efe7db5899c0306003047bab33 Mon Sep 17 00:00:00 2001 From: Pavel Grigorenko Date: Mon, 24 Aug 2026 21:08:28 +0300 Subject: [PATCH 20/22] Use `drop_guard` in some places in {core,alloc,std} --- library/alloc/src/boxed/thin.rs | 41 +-- .../alloc/src/collections/binary_heap/mod.rs | 14 +- library/alloc/src/collections/btree/map.rs | 18 +- library/alloc/src/collections/btree/mem.rs | 10 +- library/alloc/src/collections/btree/node.rs | 16 +- library/alloc/src/collections/linked_list.rs | 22 +- .../alloc/src/collections/vec_deque/drain.rs | 241 +++++++++--------- .../src/collections/vec_deque/into_iter.rs | 54 ++-- .../alloc/src/collections/vec_deque/mod.rs | 30 +-- library/alloc/src/rc.rs | 40 +-- library/alloc/src/slice.rs | 35 ++- library/alloc/src/string.rs | 33 +-- library/alloc/src/sync.rs | 55 ++-- library/alloc/src/vec/drain.rs | 41 ++- library/alloc/src/vec/into_iter.rs | 18 +- library/alloctests/lib.rs | 1 + library/std/src/sys/fs/unix.rs | 36 +-- library/std/src/sys/pal/unix/sync/condvar.rs | 21 +- library/std/src/sys/process/unix/unix.rs | 70 ++--- library/std/src/sys/process/windows/tests.rs | 12 +- 20 files changed, 309 insertions(+), 499 deletions(-) diff --git a/library/alloc/src/boxed/thin.rs b/library/alloc/src/boxed/thin.rs index 22c3d89e3ccdb..02b3af1411733 100644 --- a/library/alloc/src/boxed/thin.rs +++ b/library/alloc/src/boxed/thin.rs @@ -11,7 +11,7 @@ use core::marker::PhantomData; use core::marker::Unsize; #[cfg(not(no_global_oom_handling))] use core::mem; -use core::mem::SizedTypeProperties; +use core::mem::{DropGuard, SizedTypeProperties}; use core::ops::{Deref, DerefMut}; use core::ptr::{self, NonNull, Pointee}; @@ -360,38 +360,23 @@ impl WithHeader { // - Assumes that either `value` can be dereferenced, or is the // `NonNull::dangling()` we use when both `T` and `H` are ZSTs. unsafe fn drop(&self, value: *mut T) { - struct DropGuard { - ptr: NonNull, - value_layout: Layout, - _marker: PhantomData, - } - - impl Drop for DropGuard { - fn drop(&mut self) { - // All ZST are allocated statically. - if self.value_layout.size() == 0 { - return; - } + unsafe { + // `_guard` will deallocate the memory when dropped, even if `drop_in_place` unwinds. + let _guard = + DropGuard::new((self.0, Layout::for_value_raw(value)), |(ptr, value_layout)| { + // All ZST are allocated statically. + if value_layout.size() == 0 { + return; + } - unsafe { // SAFETY: Layout must have been computable if we're in drop let (layout, value_offset) = - WithHeader::::alloc_layout(self.value_layout).unwrap_unchecked(); + WithHeader::::alloc_layout(value_layout).unwrap_unchecked(); // Since we only allocate for non-ZSTs, the layout size cannot be zero. - debug_assert!(layout.size() != 0); - alloc::dealloc(self.ptr.as_ptr().sub(value_offset), layout); - } - } - } - - unsafe { - // `_guard` will deallocate the memory when dropped, even if `drop_in_place` unwinds. - let _guard = DropGuard { - ptr: self.0, - value_layout: Layout::for_value_raw(value), - _marker: PhantomData::, - }; + debug_assert_ne!(layout.size(), 0); + alloc::dealloc(ptr.as_ptr().sub(value_offset), layout); + }); // We only drop the value because the Pointee trait requires that the metadata is copy // aka trivially droppable. diff --git a/library/alloc/src/collections/binary_heap/mod.rs b/library/alloc/src/collections/binary_heap/mod.rs index 98192e1fb5b01..e84f9e44771ff 100644 --- a/library/alloc/src/collections/binary_heap/mod.rs +++ b/library/alloc/src/collections/binary_heap/mod.rs @@ -145,7 +145,7 @@ use core::alloc::Allocator; use core::iter::{FusedIterator, InPlaceIterable, SourceIter, TrustedFused, TrustedLen}; -use core::mem::{self, ManuallyDrop, swap}; +use core::mem::{DropGuard, ManuallyDrop, swap}; use core::num::NonZero; use core::ops::{Deref, DerefMut}; use core::{fmt, ptr}; @@ -1907,18 +1907,10 @@ impl<'a, T: Ord, A: Allocator> DrainSorted<'a, T, A> { impl<'a, T: Ord, A: Allocator> Drop for DrainSorted<'a, T, A> { /// Removes heap elements in heap order. fn drop(&mut self) { - struct DropGuard<'r, 'a, T: Ord, A: Allocator>(&'r mut DrainSorted<'a, T, A>); - - impl<'r, 'a, T: Ord, A: Allocator> Drop for DropGuard<'r, 'a, T, A> { - fn drop(&mut self) { - while self.0.inner.pop().is_some() {} - } - } - while let Some(item) = self.inner.pop() { - let guard = DropGuard(self); + let guard = DropGuard::new(&mut *self, |this| while this.inner.pop().is_some() {}); drop(item); - mem::forget(guard); + DropGuard::dismiss(guard); } } } diff --git a/library/alloc/src/collections/btree/map.rs b/library/alloc/src/collections/btree/map.rs index 80317ab2f17ed..8da9fa6df347d 100644 --- a/library/alloc/src/collections/btree/map.rs +++ b/library/alloc/src/collections/btree/map.rs @@ -5,7 +5,7 @@ use core::fmt::{self, Debug}; use core::hash::{Hash, Hasher}; use core::iter::{FusedIterator, TrustedLen}; use core::marker::PhantomData; -use core::mem::{self, ManuallyDrop}; +use core::mem::{self, DropGuard, ManuallyDrop}; use core::ops::{Bound, Index, RangeBounds}; use core::ptr; @@ -1904,24 +1904,18 @@ impl IntoIterator for BTreeMap { #[stable(feature = "btree_drop", since = "1.7.0")] impl Drop for IntoIter { fn drop(&mut self) { - struct DropGuard<'a, K, V, A: AllocatorClone>(&'a mut IntoIter); - - impl<'a, K, V, A: AllocatorClone> Drop for DropGuard<'a, K, V, A> { - fn drop(&mut self) { + while let Some(kv) = self.dying_next() { + let guard = DropGuard::new(&mut *self, |this| { // Continue the same loop we perform below. This only runs when unwinding, so we // don't have to care about panics this time (they'll abort). - while let Some(kv) = self.0.dying_next() { + while let Some(kv) = this.dying_next() { // SAFETY: we consume the dying handle immediately. unsafe { kv.drop_key_val() }; } - } - } - - while let Some(kv) = self.dying_next() { - let guard = DropGuard(self); + }); // SAFETY: we don't touch the tree before consuming the dying handle. unsafe { kv.drop_key_val() }; - mem::forget(guard); + DropGuard::dismiss(guard); } } } diff --git a/library/alloc/src/collections/btree/mem.rs b/library/alloc/src/collections/btree/mem.rs index 4643c4133d55d..34f1023ec3db8 100644 --- a/library/alloc/src/collections/btree/mem.rs +++ b/library/alloc/src/collections/btree/mem.rs @@ -16,18 +16,12 @@ pub(super) fn take_mut(v: &mut T, change: impl FnOnce(T) -> T) { /// If a panic occurs in the `change` closure, the entire process will be aborted. #[inline] pub(super) fn replace(v: &mut T, change: impl FnOnce(T) -> (T, R)) -> R { - struct PanicGuard; - impl Drop for PanicGuard { - fn drop(&mut self) { - intrinsics::abort() - } - } - let guard = PanicGuard; + let guard = mem::DropGuard::new((), |()| intrinsics::abort()); let value = unsafe { ptr::read(v) }; let (new_value, ret) = change(value); unsafe { ptr::write(v, new_value); } - mem::forget(guard); + mem::DropGuard::dismiss(guard); ret } diff --git a/library/alloc/src/collections/btree/node.rs b/library/alloc/src/collections/btree/node.rs index 8088fec38ed6a..5491b68a7edd0 100644 --- a/library/alloc/src/collections/btree/node.rs +++ b/library/alloc/src/collections/btree/node.rs @@ -32,7 +32,7 @@ // an edge both identifies a position and contains a pointer to a child node. use core::marker::PhantomData; -use core::mem::{self, MaybeUninit}; +use core::mem::{self, DropGuard, MaybeUninit}; use core::num::NonZero; use core::ptr::{self, NonNull}; use core::slice::SliceIndex; @@ -1192,23 +1192,13 @@ impl Handle, marker::KV> /// The node that the handle refers to must not yet have been deallocated. #[inline] pub(super) unsafe fn drop_key_val(mut self) { - // Run the destructor of the value even if the destructor of the key panics. - struct Dropper<'a, T>(&'a mut MaybeUninit); - impl Drop for Dropper<'_, T> { - #[inline] - fn drop(&mut self) { - unsafe { - self.0.assume_init_drop(); - } - } - } - debug_assert!(self.idx < self.node.len()); let leaf = self.node.as_leaf_dying(); unsafe { let key = leaf.keys.get_unchecked_mut(self.idx); let val = leaf.vals.get_unchecked_mut(self.idx); - let _guard = Dropper(val); + // Run the destructor of the value even if the destructor of the key panics. + let _guard = DropGuard::new(val, |val| val.assume_init_drop()); key.assume_init_drop(); // dropping the guard will drop the value } diff --git a/library/alloc/src/collections/linked_list.rs b/library/alloc/src/collections/linked_list.rs index a0542d2b5737c..dba57a12dd6ac 100644 --- a/library/alloc/src/collections/linked_list.rs +++ b/library/alloc/src/collections/linked_list.rs @@ -17,6 +17,7 @@ use core::cmp::Ordering; use core::hash::{Hash, Hasher}; use core::iter::{FusedIterator, TrustedLen}; use core::marker::PhantomData; +use core::mem::DropGuard; use core::ptr::NonNull; use core::{fmt, mem}; @@ -1177,20 +1178,15 @@ impl LinkedList { #[stable(feature = "rust1", since = "1.0.0")] unsafe impl<#[may_dangle] T, A: Allocator> Drop for LinkedList { fn drop(&mut self) { - struct DropGuard<'a, T, A: Allocator>(&'a mut LinkedList); - - impl<'a, T, A: Allocator> Drop for DropGuard<'a, T, A> { - fn drop(&mut self) { - // Continue the same loop we do below. This only runs when a destructor has - // panicked. If another one panics this will abort. - while self.0.pop_front_node().is_some() {} - } - } - // Wrap self so that if a destructor panics, we can try to keep looping - let guard = DropGuard(self); - while guard.0.pop_front_node().is_some() {} - mem::forget(guard); + let mut guard = DropGuard::new(self, |this| { + // Continue the same loop we do below. This only runs when a destructor has + // panicked. If another one panics this will abort. + while this.pop_front_node().is_some() {} + }); + + while guard.pop_front_node().is_some() {} + DropGuard::dismiss(guard); } } diff --git a/library/alloc/src/collections/vec_deque/drain.rs b/library/alloc/src/collections/vec_deque/drain.rs index da4b803c64d56..ff8f5c97886bb 100644 --- a/library/alloc/src/collections/vec_deque/drain.rs +++ b/library/alloc/src/collections/vec_deque/drain.rs @@ -1,6 +1,6 @@ use core::iter::FusedIterator; use core::marker::PhantomData; -use core::mem::{self, SizedTypeProperties}; +use core::mem::{self, DropGuard, SizedTypeProperties}; use core::ptr::NonNull; use core::{fmt, ptr}; @@ -93,140 +93,133 @@ unsafe impl Send for Drain<'_, T, A> {} #[stable(feature = "drain", since = "1.6.0")] impl Drop for Drain<'_, T, A> { fn drop(&mut self) { - struct DropGuard<'r, 'a, T, A: Allocator>(&'r mut Drain<'a, T, A>); - - let guard = DropGuard(self); - - if mem::needs_drop::() && guard.0.remaining != 0 { - unsafe { - // SAFETY: We just checked that `self.remaining != 0`. - let (front, back) = guard.0.as_slices(); - // since idx is a logical index, we don't need to worry about wrapping. - guard.0.idx += front.len(); - guard.0.remaining -= front.len(); - ptr::drop_in_place(front); - guard.0.remaining = 0; - ptr::drop_in_place(back); - } - } - // Dropping `guard` handles moving the remaining elements into place. - impl<'r, 'a, T, A: Allocator> Drop for DropGuard<'r, 'a, T, A> { - #[inline] - fn drop(&mut self) { - if mem::needs_drop::() && self.0.remaining != 0 { - unsafe { - // SAFETY: We just checked that `self.remaining != 0`. - let (front, back) = self.0.as_slices(); - ptr::drop_in_place(front); - ptr::drop_in_place(back); - } + let mut guard = DropGuard::new(self, |this| { + if mem::needs_drop::() && this.remaining != 0 { + unsafe { + // SAFETY: We just checked that `self.remaining != 0`. + let (front, back) = this.as_slices(); + ptr::drop_in_place(front); + ptr::drop_in_place(back); } + } - let source_deque = unsafe { self.0.deque.as_mut() }; + let source_deque = unsafe { this.deque.as_mut() }; - let drain_len = self.0.drain_len; - let head_len = source_deque.len; // #elements in front of the drain - let tail_len = self.0.tail_len; // #elements behind the drain - let new_len = head_len + tail_len; + let drain_len = this.drain_len; + let head_len = source_deque.len; // #elements in front of the drain + let tail_len = this.tail_len; // #elements behind the drain + let new_len = head_len + tail_len; - if T::IS_ZST { - // no need to copy around any memory if T is a ZST - source_deque.len = new_len; - return; - } + if T::IS_ZST { + // no need to copy around any memory if T is a ZST + source_deque.len = new_len; + return; + } - // Next, we will fill the hole left by the drain with as few writes as possible. - // The code below handles the following control flow and reduces the amount of - // branches under the assumption that `head_len == 0 || tail_len == 0`, i.e. - // draining at the front or at the back of the dequeue is especially common. - // - // H = "head index" = `deque.head` - // h = elements in front of the drain - // d = elements in the drain - // t = elements behind the drain - // - // Note that the buffer may wrap at any point and the wrapping is handled by - // `wrap_copy` and `to_physical_idx`. - // - // Case 1: if `head_len == 0 && tail_len == 0` - // Everything was drained, reset the head index back to 0. - // H - // [ . . . . . d d d d . . . . . ] - // H - // [ . . . . . . . . . . . . . . ] - // - // Case 2: else if `tail_len == 0` - // Don't move data or the head index. - // H - // [ . . . h h h h d d d d . . . ] - // H - // [ . . . h h h h . . . . . . . ] - // - // Case 3: else if `head_len == 0` - // Don't move data, but move the head index. - // H - // [ . . . d d d d t t t t . . . ] - // H - // [ . . . . . . . t t t t . . . ] - // - // Case 4: else if `tail_len <= head_len` - // Move data, but not the head index. - // H - // [ . . h h h h d d d d t t . . ] - // H - // [ . . h h h h t t . . . . . . ] - // - // Case 5: else - // Move data and the head index. - // H - // [ . . h h d d d d t t t t . . ] - // H - // [ . . . . . . h h t t t t . . ] + // Next, we will fill the hole left by the drain with as few writes as possible. + // The code below handles the following control flow and reduces the amount of + // branches under the assumption that `head_len == 0 || tail_len == 0`, i.e. + // draining at the front or at the back of the dequeue is especially common. + // + // H = "head index" = `deque.head` + // h = elements in front of the drain + // d = elements in the drain + // t = elements behind the drain + // + // Note that the buffer may wrap at any point and the wrapping is handled by + // `wrap_copy` and `to_physical_idx`. + // + // Case 1: if `head_len == 0 && tail_len == 0` + // Everything was drained, reset the head index back to 0. + // H + // [ . . . . . d d d d . . . . . ] + // H + // [ . . . . . . . . . . . . . . ] + // + // Case 2: else if `tail_len == 0` + // Don't move data or the head index. + // H + // [ . . . h h h h d d d d . . . ] + // H + // [ . . . h h h h . . . . . . . ] + // + // Case 3: else if `head_len == 0` + // Don't move data, but move the head index. + // H + // [ . . . d d d d t t t t . . . ] + // H + // [ . . . . . . . t t t t . . . ] + // + // Case 4: else if `tail_len <= head_len` + // Move data, but not the head index. + // H + // [ . . h h h h d d d d t t . . ] + // H + // [ . . h h h h t t . . . . . . ] + // + // Case 5: else + // Move data and the head index. + // H + // [ . . h h d d d d t t t t . . ] + // H + // [ . . . . . . h h t t t t . . ] - // When draining at the front (`.drain(..n)`) or at the back (`.drain(n..)`), - // we don't need to copy any data. The number of elements copied would be 0. - if head_len != 0 && tail_len != 0 { - join_head_and_tail_wrapping(source_deque, drain_len, head_len, tail_len); - // Marking this function as cold helps LLVM to eliminate it entirely if - // this branch is never taken. - // We use `#[cold]` instead of `#[inline(never)]`, because inlining this - // function into the general case (`.drain(n..m)`) is fine. - // See `tests/codegen-llvm/vecdeque-drain.rs` for a test. - #[cold] - fn join_head_and_tail_wrapping( - source_deque: &mut VecDeque, - drain_len: usize, - head_len: usize, - tail_len: usize, - ) { - // Pick whether to move the head or the tail here. - let (src, dst, len); - if head_len < tail_len { - src = source_deque.head; - dst = source_deque.to_wrapped_index(drain_len); - len = head_len; - } else { - src = source_deque.to_wrapped_index(head_len + drain_len); - dst = source_deque.to_wrapped_index(head_len); - len = tail_len; - }; + // When draining at the front (`.drain(..n)`) or at the back (`.drain(n..)`), + // we don't need to copy any data. The number of elements copied would be 0. + if head_len != 0 && tail_len != 0 { + join_head_and_tail_wrapping(source_deque, drain_len, head_len, tail_len); + // Marking this function as cold helps LLVM to eliminate it entirely if + // this branch is never taken. + // We use `#[cold]` instead of `#[inline(never)]`, because inlining this + // function into the general case (`.drain(n..m)`) is fine. + // See `tests/codegen-llvm/vecdeque-drain.rs` for a test. + #[cold] + fn join_head_and_tail_wrapping( + source_deque: &mut VecDeque, + drain_len: usize, + head_len: usize, + tail_len: usize, + ) { + // Pick whether to move the head or the tail here. + let (src, dst, len); + if head_len < tail_len { + src = source_deque.head; + dst = source_deque.to_wrapped_index(drain_len); + len = head_len; + } else { + src = source_deque.to_wrapped_index(head_len + drain_len); + dst = source_deque.to_wrapped_index(head_len); + len = tail_len; + }; - unsafe { - source_deque.wrap_copy(src, dst, len); - } + unsafe { + source_deque.wrap_copy(src, dst, len); } } + } - if new_len == 0 { - // Special case: If the entire deque was drained, reset the head back to 0, - // like `.clear()` does. - source_deque.head = WrappedIndex::zero(); - } else if head_len < tail_len { - // If we moved the head above, then we need to adjust the head index here. - source_deque.head = source_deque.to_wrapped_index(drain_len); - } - source_deque.len = new_len; + if new_len == 0 { + // Special case: If the entire deque was drained, reset the head back to 0, + // like `.clear()` does. + source_deque.head = WrappedIndex::zero(); + } else if head_len < tail_len { + // If we moved the head above, then we need to adjust the head index here. + source_deque.head = source_deque.to_wrapped_index(drain_len); + } + source_deque.len = new_len; + }); + + if mem::needs_drop::() && guard.remaining != 0 { + unsafe { + // SAFETY: We just checked that `self.remaining != 0`. + let (front, back) = guard.as_slices(); + // since idx is a logical index, we don't need to worry about wrapping. + guard.idx += front.len(); + guard.remaining -= front.len(); + ptr::drop_in_place(front); + guard.remaining = 0; + ptr::drop_in_place(back); } } } diff --git a/library/alloc/src/collections/vec_deque/into_iter.rs b/library/alloc/src/collections/vec_deque/into_iter.rs index e18b85dd4b694..7c83fff6c4ab1 100644 --- a/library/alloc/src/collections/vec_deque/into_iter.rs +++ b/library/alloc/src/collections/vec_deque/into_iter.rs @@ -1,5 +1,5 @@ use core::iter::{FusedIterator, TrustedLen}; -use core::mem::MaybeUninit; +use core::mem::{DropGuard, MaybeUninit}; use core::num::NonZero; use core::ops::Try; use core::{array, fmt, ptr}; @@ -78,28 +78,20 @@ impl Iterator for IntoIter { F: FnMut(B, Self::Item) -> R, R: Try, { - struct Guard<'a, T, A: Allocator> { - deque: &'a mut VecDeque, - // `consumed <= deque.len` always holds. - consumed: usize, - } - - impl<'a, T, A: Allocator> Drop for Guard<'a, T, A> { - fn drop(&mut self) { - self.deque.len -= self.consumed; - self.deque.head = self.deque.to_wrapped_index(self.consumed); - } - } - - let mut guard = Guard { deque: &mut self.inner, consumed: 0 }; + // `consumed <= deque.len` always holds. + let mut guard = DropGuard::new((&mut self.inner, 0), |(deque, consumed)| { + deque.len -= consumed; + deque.head = deque.to_wrapped_index(consumed); + }); - let (head, tail) = guard.deque.as_slices(); + let (deque, consumed) = &mut *guard; + let (head, tail) = deque.as_slices(); init = head .iter() .map(|elem| { - guard.consumed += 1; - // SAFETY: Because we incremented `guard.consumed`, the + *consumed += 1; + // SAFETY: Because we incremented `consumed`, the // deque effectively forgot the element, so we can take // ownership unsafe { ptr::read(elem) } @@ -108,7 +100,7 @@ impl Iterator for IntoIter { tail.iter() .map(|elem| { - guard.consumed += 1; + *consumed += 1; // SAFETY: Same as above. unsafe { ptr::read(elem) } }) @@ -201,26 +193,18 @@ impl DoubleEndedIterator for IntoIter { F: FnMut(B, Self::Item) -> R, R: Try, { - struct Guard<'a, T, A: Allocator> { - deque: &'a mut VecDeque, - // `consumed <= deque.len` always holds. - consumed: usize, - } - - impl<'a, T, A: Allocator> Drop for Guard<'a, T, A> { - fn drop(&mut self) { - self.deque.len -= self.consumed; - } - } - - let mut guard = Guard { deque: &mut self.inner, consumed: 0 }; + // `consumed <= deque.len` always holds. + let mut guard = DropGuard::new((&mut self.inner, 0), |(deque, consumed)| { + deque.len -= consumed; + }); - let (head, tail) = guard.deque.as_slices(); + let (deque, consumed) = &mut *guard; + let (head, tail) = deque.as_slices(); init = tail .iter() .map(|elem| { - guard.consumed += 1; + *consumed += 1; // SAFETY: See `try_fold`'s safety comment. unsafe { ptr::read(elem) } }) @@ -228,7 +212,7 @@ impl DoubleEndedIterator for IntoIter { head.iter() .map(|elem| { - guard.consumed += 1; + *consumed += 1; // SAFETY: Same as above. unsafe { ptr::read(elem) } }) diff --git a/library/alloc/src/collections/vec_deque/mod.rs b/library/alloc/src/collections/vec_deque/mod.rs index b007e3054ee6a..96c7403080578 100644 --- a/library/alloc/src/collections/vec_deque/mod.rs +++ b/library/alloc/src/collections/vec_deque/mod.rs @@ -17,7 +17,7 @@ use core::iter::{ByRefSized, repeat_n, repeat_with}; // failures in linkchecker even though rustdoc built the docs just fine. #[allow(unused_imports)] use core::mem; -use core::mem::{ManuallyDrop, SizedTypeProperties}; +use core::mem::{DropGuard, ManuallyDrop, SizedTypeProperties}; use core::ops::{Index, IndexMut, Range, RangeBounds}; use core::{fmt, ptr, slice}; @@ -632,35 +632,23 @@ impl VecDeque { mut iter: impl Iterator, len: usize, ) -> usize { - struct Guard<'a, T, A: Allocator> { - deque: &'a mut VecDeque, - written: usize, - } - - impl<'a, T, A: Allocator> Drop for Guard<'a, T, A> { - fn drop(&mut self) { - self.deque.len += self.written; - } - } - let head_room = self.capacity() - dst.as_index(); - let mut guard = Guard { deque: self, written: 0 }; + let mut guard = DropGuard::new((self, 0), |(deque, written)| { + deque.len += written; + }); + let (deque, written) = &mut *guard; if head_room >= len { - unsafe { guard.deque.write_iter(dst, iter, &mut guard.written) }; + unsafe { deque.write_iter(dst, iter, written) }; } else { unsafe { - guard.deque.write_iter( - dst, - ByRefSized(&mut iter).take(head_room), - &mut guard.written, - ); - guard.deque.write_iter(WrappedIndex::zero(), iter, &mut guard.written) + deque.write_iter(dst, ByRefSized(&mut iter).take(head_room), written); + deque.write_iter(WrappedIndex::zero(), iter, written) }; } - guard.written + *written } /// Frobs the head and tail sections around to handle the fact that we diff --git a/library/alloc/src/rc.rs b/library/alloc/src/rc.rs index 37714859ede38..0950a542f8428 100644 --- a/library/alloc/src/rc.rs +++ b/library/alloc/src/rc.rs @@ -2396,45 +2396,31 @@ impl Rc<[T]> { /// Behavior is undefined should the size be wrong. #[cfg(not(no_global_oom_handling))] unsafe fn from_iter_exact(iter: impl Iterator, len: usize) -> Rc<[T]> { - // Panic guard while cloning T elements. - // In the event of a panic, elements that have been written - // into the new RcInner will be dropped, then the memory freed. - struct Guard { - mem: NonNull, - elems: *mut T, - layout: Layout, - n_elems: usize, - } - - impl Drop for Guard { - fn drop(&mut self) { - unsafe { - let slice = from_raw_parts_mut(self.elems, self.n_elems); - ptr::drop_in_place(slice); - - Global.deallocate(self.mem, self.layout); - } - } - } + use core::mem::DropGuard; unsafe { let ptr = Self::allocate_for_slice(len); - - let mem = ptr as *mut _ as *mut u8; let layout = Layout::for_value_raw(ptr); // Pointer to first element - let elems = (&raw mut (*ptr).value) as *mut T; + let elems = (&raw mut (*ptr).value).as_mut_ptr(); - let mut guard = Guard { mem: NonNull::new_unchecked(mem), elems, layout, n_elems: 0 }; + // Panic guard while cloning T elements. + // In the event of a panic, elements that have been written + // into the new RcInner will be dropped, then the memory freed. + let mut guard = DropGuard::new(0, |n_elems| { + let slice = from_raw_parts_mut(elems, n_elems); + ptr::drop_in_place(slice); + Global.deallocate(NonNull::new_unchecked(ptr.cast()), layout); + }); for (i, item) in iter.enumerate() { ptr::write(elems.add(i), item); - guard.n_elems += 1; + *guard += 1; } - // All clear. Forget the guard so it doesn't free the new RcInner. - mem::forget(guard); + // All clear. Dismiss the guard so it doesn't free the new RcInner. + DropGuard::dismiss(guard); Self::from_ptr(ptr) } diff --git a/library/alloc/src/slice.rs b/library/alloc/src/slice.rs index e6b540f093ba5..ff7dc0f4ea17d 100644 --- a/library/alloc/src/slice.rs +++ b/library/alloc/src/slice.rs @@ -408,35 +408,30 @@ impl [T] { impl ConvertVec for T { #[inline] default fn to_vec(s: &[Self], alloc: A) -> Vec { - struct DropGuard<'a, T, A: Allocator> { - vec: &'a mut Vec, - num_init: usize, - } - impl<'a, T, A: Allocator> Drop for DropGuard<'a, T, A> { - #[inline] - fn drop(&mut self) { + use core::mem::DropGuard; + + let mut guard = DropGuard::new( + (0, Vec::with_capacity_in(s.len(), alloc)), + |(num_init, mut vec)| { // SAFETY: // items were marked initialized in the loop below - unsafe { - self.vec.set_len(self.num_init); - } - } - } - let mut vec = Vec::with_capacity_in(s.len(), alloc); - let mut guard = DropGuard { vec: &mut vec, num_init: 0 }; - let slots = guard.vec.spare_capacity_mut(); + unsafe { vec.set_len(num_init) } + }, + ); + let (num_init, vec) = &mut *guard; + + let slots = vec.spare_capacity_mut(); // .take(slots.len()) is necessary for LLVM to remove bounds checks // and has better codegen than zip. for (i, b) in s.iter().enumerate().take(slots.len()) { - guard.num_init = i; + *num_init = i; slots[i].write(b.clone()); } - core::mem::forget(guard); + + let (_, mut vec) = DropGuard::dismiss(guard); // SAFETY: // the vec was allocated and initialized above to at least this length. - unsafe { - vec.set_len(s.len()); - } + unsafe { vec.set_len(s.len()) }; vec } } diff --git a/library/alloc/src/string.rs b/library/alloc/src/string.rs index 83dad22eccb61..ce47976fdc650 100644 --- a/library/alloc/src/string.rs +++ b/library/alloc/src/string.rs @@ -46,6 +46,7 @@ use core::error::Error; use core::iter::FusedIterator; #[cfg(not(no_global_oom_handling))] use core::iter::from_fn; +use core::mem::DropGuard; #[cfg(not(no_global_oom_handling))] use core::num::Saturating; #[cfg(not(no_global_oom_handling))] @@ -1680,20 +1681,6 @@ impl String { return; } - struct PanicGuard<'a> { - s: &'a mut String, - write: usize, - } - - impl Drop for PanicGuard<'_> { - fn drop(&mut self) { - debug_assert!(self.write <= self.s.len()); - debug_assert!(str::from_utf8(&self.s.vec[..self.write]).is_ok()); - // SAFETY: Restore the string length to the number of bytes written so far. - unsafe { self.s.vec.set_len(self.write) } - } - } - // Fast path: find the first character that should be removed or return early. let mut chars = self.char_indices(); let (mut read, write) = loop { @@ -1705,26 +1692,32 @@ impl String { drop(chars); // Slow path: at least one character is going to be removed. - let mut g = PanicGuard { s: self, write }; + let mut guard = DropGuard::new((self, write), |(s, write)| { + debug_assert!(write <= s.len()); + debug_assert!(str::from_utf8(&s.vec[..write]).is_ok()); + // SAFETY: Restore the string length to the number of bytes written so far. + unsafe { s.vec.set_len(write) } + }); + let (s, write) = &mut *guard; while read < len { // SAFETY: `read` is within bound because `read` < `len`, so taking // a slice with `len` is safe. - let ch = unsafe { g.s.get_unchecked(read..len).chars().next().unwrap_unchecked() }; + let ch = unsafe { s.get_unchecked(read..len).chars().next().unwrap_unchecked() }; let ch_len = ch.len_utf8(); if f(ch) { // SAFETY: `read` is on a char boundary, as guaranteed above; `g.write` is // within bounds because it is always behind `read`. unsafe { - let ptr = g.s.vec.as_mut_ptr(); - ptr::copy(ptr.add(read), ptr.add(g.write), ch_len); + let ptr = s.vec.as_mut_ptr(); + ptr::copy(ptr.add(read), ptr.add(*write), ch_len); } - g.write += ch_len; + *write += ch_len; } read += ch_len; } // All bytes processed; commit the final length by dropping the guard. - drop(g); + drop(guard); } /// Inserts a character into this `String` at byte position `idx`. diff --git a/library/alloc/src/sync.rs b/library/alloc/src/sync.rs index cca6f881e1740..072a5f0d9e3e4 100644 --- a/library/alloc/src/sync.rs +++ b/library/alloc/src/sync.rs @@ -19,6 +19,8 @@ use core::intrinsics::abort; #[cfg(not(no_global_oom_handling))] use core::iter; use core::marker::{PhantomData, Unsize}; +#[cfg(not(no_global_oom_handling))] +use core::mem::DropGuard; use core::mem::{self, Alignment, ManuallyDrop}; use core::num::NonZeroUsize; use core::ops::{CoerceUnsized, Deref, DerefMut, DerefPure, DispatchFromDyn, LegacyReceiver}; @@ -2360,45 +2362,30 @@ impl Arc<[T]> { /// Behavior is undefined should the size be wrong. #[cfg(not(no_global_oom_handling))] unsafe fn from_iter_exact(iter: impl Iterator, len: usize) -> Arc<[T]> { - // Panic guard while cloning T elements. - // In the event of a panic, elements that have been written - // into the new ArcInner will be dropped, then the memory freed. - struct Guard { - mem: NonNull, - elems: *mut T, - layout: Layout, - n_elems: usize, - } - - impl Drop for Guard { - fn drop(&mut self) { - unsafe { - let slice = from_raw_parts_mut(self.elems, self.n_elems); - ptr::drop_in_place(slice); - - Global.deallocate(self.mem, self.layout); - } - } - } - unsafe { let ptr = Self::allocate_for_slice(len); - - let mem = ptr as *mut _ as *mut u8; let layout = Layout::for_value_raw(ptr); // Pointer to first element - let elems = (&raw mut (*ptr).data) as *mut T; + let elems = (&raw mut (*ptr).data).as_mut_ptr(); + + // Panic guard while cloning T elements. + // In the event of a panic, elements that have been written + // into the new ArcInner will be dropped, then the memory freed. + let mut guard = DropGuard::new(0, |n_elems| { + let slice = from_raw_parts_mut(elems, n_elems); + ptr::drop_in_place(slice); - let mut guard = Guard { mem: NonNull::new_unchecked(mem), elems, layout, n_elems: 0 }; + Global.deallocate(NonNull::new_unchecked(ptr.cast()), layout); + }); for (i, item) in iter.enumerate() { ptr::write(elems.add(i), item); - guard.n_elems += 1; + *guard += 1; } - // All clear. Forget the guard so it doesn't free the new ArcInner. - mem::forget(guard); + // All clear. Dismiss the guard so it doesn't free the new ArcInner. + DropGuard::dismiss(guard); Self::from_ptr(ptr) } @@ -2616,15 +2603,7 @@ impl Arc { // If we unwind before the Arc is overwritten, we expose a strong // count of 0, resulting in a UAF (#155746, #157203). // Until the new Arc is written, the old Arc must remain valid - struct Guard<'a, T: ?Sized> { - inner: &'a ArcInner, - } - impl<'a, T: ?Sized> Drop for Guard<'a, T> { - fn drop(&mut self) { - self.inner.strong.store(1, Release); - } - } - let guard = Guard { inner: this.inner() }; + let guard = DropGuard::new(this.inner(), |inner| inner.strong.store(1, Release)); // Can just steal the data, all that's left is Weaks // Note that this can panic in two ways: @@ -2644,7 +2623,7 @@ impl Arc { ); // We are now safe from panics. - mem::forget(guard); + DropGuard::dismiss(guard); // Materialize our own implicit weak pointer, so that it can clean // up the ArcInner as needed. diff --git a/library/alloc/src/vec/drain.rs b/library/alloc/src/vec/drain.rs index d12dea20b33cb..327fa00233863 100644 --- a/library/alloc/src/vec/drain.rs +++ b/library/alloc/src/vec/drain.rs @@ -1,5 +1,5 @@ use core::iter::{FusedIterator, TrustedLen}; -use core::mem::{self, ManuallyDrop, SizedTypeProperties}; +use core::mem::{self, DropGuard, ManuallyDrop, SizedTypeProperties}; use core::ptr::{self, NonNull}; use core::{fmt, slice}; @@ -172,28 +172,6 @@ impl DoubleEndedIterator for Drain<'_, T, A> { #[stable(feature = "drain", since = "1.6.0")] impl Drop for Drain<'_, T, A> { fn drop(&mut self) { - /// Moves back the un-`Drain`ed elements to restore the original `Vec`. - struct DropGuard<'r, 'a, T, A: Allocator>(&'r mut Drain<'a, T, A>); - - impl<'r, 'a, T, A: Allocator> Drop for DropGuard<'r, 'a, T, A> { - fn drop(&mut self) { - if self.0.tail_len > 0 { - unsafe { - let source_vec = self.0.vec.as_mut(); - // memmove back untouched tail, update to new length - let start = source_vec.len(); - let tail = self.0.tail_start; - if tail != start { - let src = source_vec.as_ptr().add(tail); - let dst = source_vec.as_mut_ptr().add(start); - ptr::copy(src, dst, self.0.tail_len); - } - source_vec.set_len(start + self.0.tail_len); - } - } - } - } - let iter = mem::take(&mut self.iter); let drop_len = iter.len(); @@ -213,7 +191,22 @@ impl Drop for Drain<'_, T, A> { } // ensure elements are moved back into their appropriate places, even when drop_in_place panics - let _guard = DropGuard(self); + let _guard = DropGuard::new(self, |this| { + if this.tail_len > 0 { + unsafe { + let source_vec = this.vec.as_mut(); + // memmove back untouched tail, update to new length + let start = source_vec.len(); + let tail = this.tail_start; + if tail != start { + let src = source_vec.as_ptr().add(tail); + let dst = source_vec.as_mut_ptr().add(start); + ptr::copy(src, dst, this.tail_len); + } + source_vec.set_len(start + this.tail_len); + } + } + }); if drop_len == 0 { return; diff --git a/library/alloc/src/vec/into_iter.rs b/library/alloc/src/vec/into_iter.rs index 4b25634326e16..7c486aed84d62 100644 --- a/library/alloc/src/vec/into_iter.rs +++ b/library/alloc/src/vec/into_iter.rs @@ -3,7 +3,7 @@ use core::iter::{ TrustedRandomAccessNoCoerce, }; use core::marker::PhantomData; -use core::mem::{ManuallyDrop, MaybeUninit, SizedTypeProperties}; +use core::mem::{DropGuard, ManuallyDrop, MaybeUninit, SizedTypeProperties}; use core::num::NonZero; #[cfg(not(no_global_oom_handling))] use core::ops::Deref; @@ -581,21 +581,9 @@ impl Clone for IntoIter { #[stable(feature = "rust1", since = "1.0.0")] unsafe impl<#[may_dangle] T, A: Allocator> Drop for IntoIter { fn drop(&mut self) { - struct DropGuard<'a, T, A: Allocator>(&'a mut IntoIter); - - impl Drop for DropGuard<'_, T, A> { - fn drop(&mut self) { - unsafe { - self.0.dealloc_only(); - } - } - } - - let guard = DropGuard(self); + let mut guard = DropGuard::new(self, |this| unsafe { this.dealloc_only() }); // destroy the remaining elements - unsafe { - ptr::drop_in_place(guard.0.as_raw_mut_slice()); - } + unsafe { ptr::drop_in_place(guard.as_raw_mut_slice()) } // now `guard` will be dropped and do the rest } } diff --git a/library/alloctests/lib.rs b/library/alloctests/lib.rs index eca8444812521..77e56d3ac54f4 100644 --- a/library/alloctests/lib.rs +++ b/library/alloctests/lib.rs @@ -28,6 +28,7 @@ #![feature(const_try)] #![feature(copied_into_inner)] #![feature(core_intrinsics)] +#![feature(drop_guard)] #![feature(exact_size_is_empty)] #![feature(extend_one)] #![feature(extend_one_unchecked)] diff --git a/library/std/src/sys/fs/unix.rs b/library/std/src/sys/fs/unix.rs index b33ebadebe4ad..aff608fa56aa3 100644 --- a/library/std/src/sys/fs/unix.rs +++ b/library/std/src/sys/fs/unix.rs @@ -2239,19 +2239,6 @@ pub fn copy(from: &Path, to: &Path) -> io::Result { #[cfg(target_vendor = "apple")] pub fn copy(from: &Path, to: &Path) -> io::Result { const COPYFILE_ALL: libc::copyfile_flags_t = libc::COPYFILE_METADATA | libc::COPYFILE_DATA; - - struct FreeOnDrop(libc::copyfile_state_t); - impl Drop for FreeOnDrop { - fn drop(&mut self) { - // The code below ensures that `FreeOnDrop` is never a null pointer - unsafe { - // `copyfile_state_free` returns -1 if the `to` or `from` files - // cannot be closed. However, this is not considered an error. - libc::copyfile_state_free(self.0); - } - } - } - let (reader, reader_metadata) = open_from(from)?; let clonefile_result = run_path_with_cstr(to, &|to| { @@ -2272,24 +2259,29 @@ pub fn copy(from: &Path, to: &Path) -> io::Result { // Fall back to using `fcopyfile` if `fclonefileat` does not succeed. let (writer, writer_metadata) = open_to_and_set_permissions(to, &reader_metadata)?; - // We ensure that `FreeOnDrop` never contains a null pointer so it is + let state = unsafe { libc::copyfile_state_alloc() }; + // We ensure that the guard never contains a null pointer so it is // always safe to call `copyfile_state_free` - let state = unsafe { - let state = libc::copyfile_state_alloc(); - if state.is_null() { - return Err(crate::io::Error::last_os_error()); + if state.is_null() { + return Err(crate::io::Error::last_os_error()); + } + let state = crate::mem::DropGuard::new(state, |state| { + // SAFETY: just checked it's not null + unsafe { + // `copyfile_state_free` returns -1 if the `to` or `from` files + // cannot be closed. However, this is not considered an error. + libc::copyfile_state_free(state); } - FreeOnDrop(state) - }; + }); let flags = if writer_metadata.is_file() { COPYFILE_ALL } else { libc::COPYFILE_DATA }; - cvt(unsafe { libc::fcopyfile(reader.as_raw_fd(), writer.as_raw_fd(), state.0, flags) })?; + cvt(unsafe { libc::fcopyfile(reader.as_raw_fd(), writer.as_raw_fd(), *state, flags) })?; let mut bytes_copied: libc::off_t = 0; cvt(unsafe { libc::copyfile_state_get( - state.0, + *state, libc::COPYFILE_STATE_COPIED as u32, (&raw mut bytes_copied) as *mut libc::c_void, ) diff --git a/library/std/src/sys/pal/unix/sync/condvar.rs b/library/std/src/sys/pal/unix/sync/condvar.rs index 7c9dcdc8b7375..9b1fd3f1027f8 100644 --- a/library/std/src/sys/pal/unix/sync/condvar.rs +++ b/library/std/src/sys/pal/unix/sync/condvar.rs @@ -152,27 +152,22 @@ impl Condvar { /// May only be called once per instance of `Self`. pub unsafe fn init(self: Pin<&mut Self>) { use crate::pin::pin; - - struct AttrGuard<'a>(Pin<&'a COpaque>); - impl Drop for AttrGuard<'_> { - fn drop(&mut self) { - unsafe { - let result = libc::pthread_condattr_destroy(self.0.get()); - assert_eq!(result, 0); - } - } - } + let a = b; unsafe { let attr = pin!(COpaque::::uninit()); + // FIXME(pin-ergonomics): remove the next line. let attr = attr.into_ref(); let r = libc::pthread_condattr_init(attr.get()); assert_eq!(r, 0); - let attr = AttrGuard(attr); - let r = libc::pthread_condattr_setclock(attr.0.get(), Self::CLOCK); + let attr = DropGuard::new(attr, |attr| { + let result = libc::pthread_condattr_destroy(attr.get()); + assert_eq!(result, 0); + }); + let r = libc::pthread_condattr_setclock(attr.get(), Self::CLOCK); assert_eq!(r, 0); - let r = libc::pthread_cond_init(self.as_ref().raw(), attr.0.get()); + let r = libc::pthread_cond_init(self.as_ref().raw(), attr.get()); assert_eq!(r, 0); } } diff --git a/library/std/src/sys/process/unix/unix.rs b/library/std/src/sys/process/unix/unix.rs index aa47fcb3360c1..ba04471631be9 100644 --- a/library/std/src/sys/process/unix/unix.rs +++ b/library/std/src/sys/process/unix/unix.rs @@ -394,19 +394,11 @@ impl Command { // want to be sure to restore the global environment back to what it // once was, ensuring that our temporary override, when free'd, doesn't // corrupt our process's environment. - let mut _reset = None; + let _reset; if let Some(envp) = maybe_envp { - struct Reset(*const *const libc::c_char); - - impl Drop for Reset { - fn drop(&mut self) { - unsafe { - *sys::env::environ() = self.0; - } - } - } - - _reset = Some(Reset(*sys::env::environ())); + _reset = core::mem::DropGuard::new(*sys::env::environ(), |prev| { + *sys::env::environ() = prev; + }); *sys::env::environ() = envp.as_ptr(); } @@ -461,8 +453,8 @@ impl Command { #[cfg(target_os = "linux")] use core::sync::atomic::{Atomic, AtomicU8, Ordering}; - use crate::mem::MaybeUninit; - use crate::pin::{Pin, pin}; + use crate::mem::{DropGuard, MaybeUninit}; + use crate::pin::pin; use crate::sys::helpers::COpaque; use crate::sys::{self, cvt_nz, on_broken_pipe_used}; @@ -679,68 +671,52 @@ impl Command { let pgroup = self.get_pgroup(); - struct PosixSpawnFileActions<'a>(Pin<&'a COpaque>); - - impl Drop for PosixSpawnFileActions<'_> { - fn drop(&mut self) { - unsafe { - libc::posix_spawn_file_actions_destroy(self.0.get()); - } - } - } - - struct PosixSpawnattr<'a>(Pin<&'a COpaque>); - - impl Drop for PosixSpawnattr<'_> { - fn drop(&mut self) { - unsafe { - libc::posix_spawnattr_destroy(self.0.get()); - } - } - } - unsafe { let attrs = pin!(COpaque::uninit()); // FIXME(pin-ergonomics): remove the next line. let attrs = attrs.into_ref(); cvt_nz(libc::posix_spawnattr_init(attrs.get()))?; - let attrs = PosixSpawnattr(attrs); + let attrs = DropGuard::new(attrs, |attrs| { + libc::posix_spawnattr_destroy(attrs.get()); + }); let mut flags = 0; let file_actions = pin!(COpaque::uninit()); let file_actions = file_actions.into_ref(); cvt_nz(libc::posix_spawn_file_actions_init(file_actions.get()))?; - let file_actions = PosixSpawnFileActions(file_actions); + let file_actions = DropGuard::new(file_actions, |file_actions| { + libc::posix_spawn_file_actions_destroy(file_actions.get()); + }); if let Some(fd) = stdio.stdin.fd() { cvt_nz(libc::posix_spawn_file_actions_adddup2( - file_actions.0.get(), + file_actions.get(), fd, libc::STDIN_FILENO, ))?; } if let Some(fd) = stdio.stdout.fd() { cvt_nz(libc::posix_spawn_file_actions_adddup2( - file_actions.0.get(), + file_actions.get(), fd, libc::STDOUT_FILENO, ))?; } if let Some(fd) = stdio.stderr.fd() { cvt_nz(libc::posix_spawn_file_actions_adddup2( - file_actions.0.get(), + file_actions.get(), fd, libc::STDERR_FILENO, ))?; } if let Some((f, cwd)) = addchdir { - cvt_nz(f(file_actions.0.get(), cwd.as_ptr()))?; + cvt_nz(f(file_actions.get(), cwd.as_ptr()))?; } if let Some(pgroup) = pgroup { flags |= libc::POSIX_SPAWN_SETPGROUP; - cvt_nz(libc::posix_spawnattr_setpgroup(attrs.0.get(), pgroup))?; + cvt_nz(libc::posix_spawnattr_setpgroup(attrs.get(), pgroup))?; } // Inherit the signal mask from this process rather than resetting it (i.e. do not call @@ -758,7 +734,7 @@ impl Command { { cvt(sigaddset(default_set.as_mut_ptr(), libc::SIGLOST))?; } - cvt_nz(libc::posix_spawnattr_setsigdefault(attrs.0.get(), default_set.as_ptr()))?; + cvt_nz(libc::posix_spawnattr_setsigdefault(attrs.get(), default_set.as_ptr()))?; flags |= libc::POSIX_SPAWN_SETSIGDEF; } @@ -773,7 +749,7 @@ impl Command { } } - cvt_nz(libc::posix_spawnattr_setflags(attrs.0.get(), flags as _))?; + cvt_nz(libc::posix_spawnattr_setflags(attrs.get(), flags as _))?; // Make sure we synchronize access to the global `environ` resource let _env_lock = sys::env::env_read_lock(); @@ -790,8 +766,8 @@ impl Command { let spawn_res = pidfd_spawnp.get().unwrap()( &mut pidfd, self.get_program_cstr().as_ptr(), - file_actions.0.get(), - attrs.0.get(), + file_actions.get(), + attrs.get(), self.get_argv().as_ptr() as *const _, envp as *const _, ); @@ -832,8 +808,8 @@ impl Command { let spawn_res = spawn_fn( &mut p.pid, self.get_program_cstr().as_ptr(), - file_actions.0.get(), - attrs.0.get(), + file_actions.get(), + attrs.get(), self.get_argv().as_ptr() as *const _, envp as *const _, ); diff --git a/library/std/src/sys/process/windows/tests.rs b/library/std/src/sys/process/windows/tests.rs index bc5e0d5c7fc97..4d13f4d9e3b9f 100644 --- a/library/std/src/sys/process/windows/tests.rs +++ b/library/std/src/sys/process/windows/tests.rs @@ -1,6 +1,7 @@ use super::child_pipe::{Pipes, child_pipe}; use super::{Arg, make_command_line}; use crate::ffi::{OsStr, OsString}; +use crate::mem::DropGuard; use crate::os::windows::io::AsHandle; use crate::process::{Command, Stdio}; use crate::time::Duration; @@ -36,14 +37,9 @@ fn test_thread_handle() { assert!(p.is_ok()); // Ensure the process is killed in the event something goes wrong. - struct DropGuard(crate::process::Child); - impl Drop for DropGuard { - fn drop(&mut self) { - let _ = self.0.kill(); - } - } - let mut p = DropGuard(p.unwrap()); - let p = &mut p.0; + let mut p = DropGuard::new(p.unwrap(), |mut p| { + let _: Result<(), crate::io::Error> = p.kill(); + }); unsafe extern "system" { unsafe fn ResumeThread(hHandle: BorrowedHandle<'_>) -> u32; From d20d512c3e37859e5bfc3a2177c6bcb7c9067d25 Mon Sep 17 00:00:00 2001 From: joboet Date: Fri, 28 Aug 2026 19:38:03 +0200 Subject: [PATCH 21/22] std: optimize IO error formatting --- library/std/src/io/error.rs | 9 +++------ library/std/src/io/error/tests.rs | 4 ++-- library/std/src/sys/io/error/generic.rs | 6 ++++-- library/std/src/sys/io/error/hermit.rs | 7 ++++--- library/std/src/sys/io/error/motor.rs | 11 ++++++----- library/std/src/sys/io/error/sgx.rs | 11 ++++++----- library/std/src/sys/io/error/solid.rs | 6 +++--- library/std/src/sys/io/error/uefi.rs | 8 ++++---- library/std/src/sys/io/error/unix.rs | 10 +++++----- library/std/src/sys/io/error/windows.rs | 18 ++++++------------ library/std/src/sys/io/error/windows/tests.rs | 2 +- library/std/src/sys/io/error/xous.rs | 6 ++++-- library/std/src/sys/io/mod.rs | 2 +- library/std/src/sys/process/uefi.rs | 8 +++----- 14 files changed, 52 insertions(+), 56 deletions(-) diff --git a/library/std/src/io/error.rs b/library/std/src/io/error.rs index 6dbe37ca9fba8..f1208e090f040 100644 --- a/library/std/src/io/error.rs +++ b/library/std/src/io/error.rs @@ -7,7 +7,7 @@ mod tests; )] use crate::{ io::{Error, OsFunctions, RawOsError}, - sys::io::{decode_error_kind, errno, error_string, is_interrupted}, + sys::io::{decode_error_kind, errno, format_error, is_interrupted}, }; // Because std is linked in during testing, these incoherent implementations would @@ -74,11 +74,8 @@ impl Error { #[must_use] #[inline] pub fn from_raw_os_error(code: RawOsError) -> Error { - const FUNCTIONS: &'static OsFunctions = &OsFunctions { - format_os_error: |code, fmt| fmt.write_str(&error_string(code)), - decode_error_kind, - is_interrupted, - }; + const FUNCTIONS: &'static OsFunctions = + &OsFunctions { format_os_error: format_error, decode_error_kind, is_interrupted }; // SAFETY: `FUNCTIONS` is a constant and not created at runtime. unsafe { Error::from_raw_os_error_with_functions(code, FUNCTIONS) } diff --git a/library/std/src/io/error/tests.rs b/library/std/src/io/error/tests.rs index a3a2f5830ae91..7f75d7b4c980c 100644 --- a/library/std/src/io/error/tests.rs +++ b/library/std/src/io/error/tests.rs @@ -1,5 +1,5 @@ use crate::io::{Error, ErrorKind, const_error}; -use crate::sys::io::{decode_error_kind, error_string}; +use crate::sys::io::{decode_error_kind, format_error}; use crate::{assert_matches, error, fmt}; #[test] @@ -10,7 +10,7 @@ fn test_size() { #[test] fn test_debug_error() { let code = 6; - let msg = error_string(code); + let msg = fmt::from_fn(|f| format_error(code, f)).to_string(); let kind = decode_error_kind(code); let err = Error::new(ErrorKind::InvalidInput, Error::from_raw_os_error(code)); let expected = format!( diff --git a/library/std/src/sys/io/error/generic.rs b/library/std/src/sys/io/error/generic.rs index fc70fbaba7e8c..1ff0e9303041d 100644 --- a/library/std/src/sys/io/error/generic.rs +++ b/library/std/src/sys/io/error/generic.rs @@ -1,3 +1,5 @@ +use crate::fmt; + pub fn errno() -> i32 { 0 } @@ -10,6 +12,6 @@ pub fn decode_error_kind(_code: i32) -> crate::io::ErrorKind { crate::io::ErrorKind::Uncategorized } -pub fn error_string(_errno: i32) -> String { - "operation successful".to_string() +pub fn format_error(_errno: i32, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("operation successful") } diff --git a/library/std/src/sys/io/error/hermit.rs b/library/std/src/sys/io/error/hermit.rs index 5f42144bb7cfb..28735c4c8275c 100644 --- a/library/std/src/sys/io/error/hermit.rs +++ b/library/std/src/sys/io/error/hermit.rs @@ -1,4 +1,4 @@ -use crate::io; +use crate::{fmt, io}; pub fn errno() -> i32 { unsafe { hermit_abi::get_errno() } @@ -30,6 +30,7 @@ pub fn decode_error_kind(errno: i32) -> io::ErrorKind { } } -pub fn error_string(errno: i32) -> String { - hermit_abi::error_string(errno).to_string() +pub fn format_error(errno: i32, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let description = hermit_abi::error_string(errno); + f.write_str(description) } diff --git a/library/std/src/sys/io/error/motor.rs b/library/std/src/sys/io/error/motor.rs index 06417417e8554..3afbd3fa9b7f1 100644 --- a/library/std/src/sys/io/error/motor.rs +++ b/library/std/src/sys/io/error/motor.rs @@ -1,4 +1,4 @@ -use crate::io; +use crate::{fmt, io}; pub fn errno() -> io::RawOsError { // Not used in Motor OS because it is ambiguous: Motor OS @@ -57,11 +57,12 @@ pub fn decode_error_kind(code: io::RawOsError) -> io::ErrorKind { } } -pub fn error_string(errno: io::RawOsError) -> String { - let error: moto_rt::Error = match errno { +pub fn format_error(errno: io::RawOsError, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let error = match errno { x if x < 0 => moto_rt::Error::Unknown, x if x > u16::MAX.into() => moto_rt::Error::Unknown, - x => (x as moto_rt::ErrorCode).into(), /* u16 */ + x => moto_rt::Error::from(x as moto_rt::ErrorCode), /* u16 */ }; - format!("{}", error) + + write!(f, "{error}") } diff --git a/library/std/src/sys/io/error/sgx.rs b/library/std/src/sys/io/error/sgx.rs index b7b4030422e12..2d0827aad39ab 100644 --- a/library/std/src/sys/io/error/sgx.rs +++ b/library/std/src/sys/io/error/sgx.rs @@ -1,6 +1,6 @@ use fortanix_sgx_abi::{Error, RESULT_SUCCESS}; -use crate::io; +use crate::{fmt, io}; pub fn errno() -> i32 { RESULT_SUCCESS @@ -54,12 +54,13 @@ pub fn decode_error_kind(code: i32) -> io::ErrorKind { } } -pub fn error_string(errno: i32) -> String { +pub fn format_error(errno: i32, f: &mut fmt::Formatter<'_>) -> fmt::Result { if errno == RESULT_SUCCESS { - "operation successful".into() + f.write_str("operation successful") } else if ((Error::UserRangeStart as _)..=(Error::UserRangeEnd as _)).contains(&errno) { - format!("user-specified error {errno:08x}") + write!(f, "user-specified error {errno:08x}") } else { - format!("{}", decode_error_kind(errno)) + let kind = decode_error_kind(errno); + write!(f, "{kind}") } } diff --git a/library/std/src/sys/io/error/solid.rs b/library/std/src/sys/io/error/solid.rs index 8e9503272abbc..ced354cb4c6e2 100644 --- a/library/std/src/sys/io/error/solid.rs +++ b/library/std/src/sys/io/error/solid.rs @@ -1,5 +1,5 @@ -use crate::io; use crate::sys::pal::error; +use crate::{fmt, io}; pub fn errno() -> i32 { 0 @@ -14,6 +14,6 @@ pub fn decode_error_kind(code: i32) -> io::ErrorKind { error::decode_error_kind(code) } -pub fn error_string(errno: i32) -> String { - if let Some(name) = error::error_name(errno) { name.to_owned() } else { format!("{errno}") } +pub fn format_error(errno: i32, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if let Some(name) = error::error_name(errno) { f.write_str(name) } else { write!(f, "{errno}") } } diff --git a/library/std/src/sys/io/error/uefi.rs b/library/std/src/sys/io/error/uefi.rs index bedea240d523b..a8793f2799be9 100644 --- a/library/std/src/sys/io/error/uefi.rs +++ b/library/std/src/sys/io/error/uefi.rs @@ -1,6 +1,6 @@ use r_efi::efi::Status; -use crate::io; +use crate::{fmt, io}; pub fn errno() -> io::RawOsError { 0 @@ -54,7 +54,7 @@ pub fn decode_error_kind(code: io::RawOsError) -> io::ErrorKind { } } -pub fn error_string(errno: io::RawOsError) -> String { +pub fn format_error(errno: io::RawOsError, f: &mut fmt::Formatter<'_>) -> fmt::Result { // Keep the List in Alphabetical Order // The Messages are taken from UEFI Specification Appendix D - Status Codes #[rustfmt::skip] @@ -98,7 +98,7 @@ pub fn error_string(errno: io::RawOsError) -> String { Status::VOLUME_FULL => "There is no more space on the file system.", Status::VOLUME_CORRUPTED => "An inconstancy was detected on the file system causing the operating to fail.", Status::WRITE_PROTECTED => "The device cannot be written to.", - _ => return format!("Status: {errno}"), + _ => return write!(f, "Status: {errno}"), }; - msg.to_owned() + f.write_str(msg) } diff --git a/library/std/src/sys/io/error/unix.rs b/library/std/src/sys/io/error/unix.rs index 12acde7311e4c..5f60abf9d9528 100644 --- a/library/std/src/sys/io/error/unix.rs +++ b/library/std/src/sys/io/error/unix.rs @@ -1,7 +1,7 @@ use crate::ffi::c_int; #[cfg(not(target_os = "teeos"))] use crate::ffi::{CStr, c_char}; -use crate::io; +use crate::{fmt, io}; unsafe extern "C" { #[cfg(not(any( @@ -195,7 +195,7 @@ pub fn decode_error_kind(errno: i32) -> io::ErrorKind { /// Gets a detailed string description for the given error number. #[cfg(any(target_family = "unix", target_os = "wasi"))] -pub fn error_string(errno: i32) -> String { +pub fn format_error(errno: i32, f: &mut fmt::Formatter<'_>) -> fmt::Result { const TMPBUF_SZ: usize = if cfg!(target_os = "wasi") { 1024 } else { 128 }; unsafe extern "C" { @@ -226,11 +226,11 @@ pub fn error_string(errno: i32) -> String { let p = p as *const _; // We can't always expect a UTF-8 environment. When we don't get that luxury, // it's better to give a low-quality error message than none at all. - String::from_utf8_lossy(CStr::from_ptr(p).to_bytes()).into() + write!(f, "{}", CStr::from_ptr(p).display()) } } #[cfg(target_os = "teeos")] -pub fn error_string(_errno: i32) -> String { - "error string unimplemented".to_string() +pub fn format_error(_errno: i32, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("error string unimplemented") } diff --git a/library/std/src/sys/io/error/windows.rs b/library/std/src/sys/io/error/windows.rs index 0ca3aee389b3e..6056f1774c146 100644 --- a/library/std/src/sys/io/error/windows.rs +++ b/library/std/src/sys/io/error/windows.rs @@ -1,5 +1,5 @@ use crate::sys::pal::{api, c}; -use crate::{io, ptr}; +use crate::{fmt, io, ptr}; #[cfg(test)] mod tests; @@ -95,7 +95,7 @@ pub fn decode_error_kind(errno: i32) -> io::ErrorKind { } /// Gets a detailed string description for the given error number. -pub fn error_string(mut errnum: i32) -> String { +pub fn format_error(mut errnum: i32, f: &mut fmt::Formatter<'_>) -> fmt::Result { let mut buf = [0 as c::WCHAR; 2048]; unsafe { @@ -131,21 +131,15 @@ pub fn error_string(mut errnum: i32) -> String { if res == 0 { // Sometimes FormatMessageW can fail e.g., system doesn't like 0 as langId, let fm_err = errno(); - return format!("OS Error {errnum} (FormatMessageW() returned error {fm_err})"); + return write!(f, "OS Error {errnum} (FormatMessageW() returned error {fm_err})"); } match String::from_utf16(&buf[..res]) { - Ok(mut msg) => { + Ok(msg) => { // Trim trailing CRLF inserted by FormatMessageW - let len = msg.trim_ascii_end().len(); - msg.truncate(len); - msg + f.write_str(msg.trim_ascii_end()) } - Err(..) => format!( - "OS Error {} (FormatMessageW() returned \ - invalid UTF-16)", - errnum - ), + Err(..) => write!(f, "OS Error {} (FormatMessageW() returned invalid UTF-16)", errnum), } } } diff --git a/library/std/src/sys/io/error/windows/tests.rs b/library/std/src/sys/io/error/windows/tests.rs index 7fc545ad00666..088472eceab71 100644 --- a/library/std/src/sys/io/error/windows/tests.rs +++ b/library/std/src/sys/io/error/windows/tests.rs @@ -1,7 +1,7 @@ use crate::io::Error; use crate::sys::pal::c; -// tests `error_string` above +// tests `format_error` #[test] fn ntstatus_error() { const STATUS_UNSUCCESSFUL: u32 = 0xc000_0001; diff --git a/library/std/src/sys/io/error/xous.rs b/library/std/src/sys/io/error/xous.rs index 2e9ea8e4f0928..bdecf437d0126 100644 --- a/library/std/src/sys/io/error/xous.rs +++ b/library/std/src/sys/io/error/xous.rs @@ -1,3 +1,4 @@ +use crate::fmt; use crate::os::xous::ffi::Error as XousError; pub fn errno() -> i32 { @@ -12,6 +13,7 @@ pub fn decode_error_kind(_code: i32) -> crate::io::ErrorKind { crate::io::ErrorKind::Uncategorized } -pub fn error_string(errno: i32) -> String { - Into::::into(errno).to_string() +pub fn format_error(errno: i32, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let error = XousError::from(errno); + write!(f, "{error}") } diff --git a/library/std/src/sys/io/mod.rs b/library/std/src/sys/io/mod.rs index 33182e4eb5539..b9c48e3fe6ba5 100644 --- a/library/std/src/sys/io/mod.rs +++ b/library/std/src/sys/io/mod.rs @@ -51,5 +51,5 @@ pub use error::errno_location; target_os = "wasi", ))] pub use error::set_errno; -pub use error::{decode_error_kind, errno, error_string, is_interrupted}; +pub use error::{decode_error_kind, errno, format_error, is_interrupted}; pub use is_terminal::is_terminal; diff --git a/library/std/src/sys/process/uefi.rs b/library/std/src/sys/process/uefi.rs index 0f5c7c9a58c40..8c233a0d2024e 100644 --- a/library/std/src/sys/process/uefi.rs +++ b/library/std/src/sys/process/uefi.rs @@ -8,7 +8,7 @@ use crate::num::{NonZero, NonZeroI32}; use crate::path::{Path, PathBuf}; use crate::process::StdioPipes; use crate::sys::fs::File; -use crate::sys::io::error_string; +use crate::sys::io::format_error; use crate::sys::pal::helpers; use crate::sys::unsupported; use crate::{fmt, io}; @@ -264,8 +264,7 @@ impl ExitStatus { impl fmt::Display for ExitStatus { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let err_str = error_string(self.0.as_usize()); - write!(f, "{}", err_str) + format_error(self.0.as_usize(), f) } } @@ -280,8 +279,7 @@ pub struct ExitStatusError(r_efi::efi::Status); impl fmt::Debug for ExitStatusError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let err_str = error_string(self.0.as_usize()); - write!(f, "{}", err_str) + format_error(self.0.as_usize(), f) } } From aee52d782c7bd49237c3b2af967254f7ebf22f18 Mon Sep 17 00:00:00 2001 From: malezjaa Date: Fri, 28 Aug 2026 20:27:31 +0200 Subject: [PATCH 22/22] implement [u8]::split_ascii_whitespace --- library/core/src/internal_macros.rs | 4 +- library/core/src/slice/ascii.rs | 140 ++++++++++++++++++++++++++++ library/core/src/slice/mod.rs | 2 + library/core/src/str/mod.rs | 7 +- library/coretests/tests/lib.rs | 1 + library/coretests/tests/slice.rs | 27 ++++++ 6 files changed, 175 insertions(+), 6 deletions(-) diff --git a/library/core/src/internal_macros.rs b/library/core/src/internal_macros.rs index 0d0ff23fe2946..360ad1099b335 100644 --- a/library/core/src/internal_macros.rs +++ b/library/core/src/internal_macros.rs @@ -72,13 +72,13 @@ macro_rules! forward_ref_op_assign { macro_rules! impl_fn_for_zst { ($( $( #[$attr: meta] )* - struct $Name: ident impl$( <$( $lifetime : lifetime ),+> )? Fn = + $vis:vis struct $Name: ident impl$( <$( $lifetime : lifetime ),+> )? Fn = |$( $arg: ident: $ArgTy: ty ),*| -> $ReturnTy: ty $body: block; )+) => { $( $( #[$attr] )* - struct $Name; + $vis struct $Name; impl $( <$( $lifetime ),+> )? Fn<($( $ArgTy, )*)> for $Name { #[inline] diff --git a/library/core/src/slice/ascii.rs b/library/core/src/slice/ascii.rs index 2b6037b2ee53e..3f17d3591af1c 100644 --- a/library/core/src/slice/ascii.rs +++ b/library/core/src/slice/ascii.rs @@ -6,6 +6,12 @@ use crate::fmt::{self, Write}; #[cfg(not(all(target_arch = "loongarch64", target_feature = "lsx")))] use crate::intrinsics::const_eval_select; use crate::{ascii, iter, ops}; +#[unstable(feature = "u8_split_ascii_whitespace", issue = "147878")] +use crate::{ + iter::{Filter, FusedIterator}, + slice::Split, + str::{BytesIsNotEmpty, IsAsciiWhitespace}, +}; impl [u8] { /// Checks if all bytes in this slice are within the ASCII range. @@ -314,6 +320,140 @@ impl [u8] { pub const fn trim_ascii(&self) -> &[u8] { self.trim_ascii_start().trim_ascii_end() } + + /// Splits a byte slice by ASCII whitespace. + /// + /// The returned iterator yields byte slices that are subslices of the + /// original byte slice, separated by any amount of ASCII whitespace. + /// + /// This uses the same definition as [`u8::is_ascii_whitespace`]. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(u8_split_ascii_whitespace)] + /// + /// let mut iter = b"A few words".split_ascii_whitespace(); + /// + /// assert_eq!(Some(&b"A"[..]), iter.next()); + /// assert_eq!(Some(&b"few"[..]), iter.next()); + /// assert_eq!(Some(&b"words"[..]), iter.next()); + /// + /// assert_eq!(None, iter.next()); + /// ``` + /// + /// Various kinds of ASCII whitespace are considered + /// (see [`u8::is_ascii_whitespace`]): + /// + /// ``` + /// #![feature(u8_split_ascii_whitespace)] + /// + /// let mut iter = b" Mary had\ta little \n\t lamb".split_ascii_whitespace(); + /// + /// assert_eq!(Some(&b"Mary"[..]), iter.next()); + /// assert_eq!(Some(&b"had"[..]), iter.next()); + /// assert_eq!(Some(&b"a"[..]), iter.next()); + /// assert_eq!(Some(&b"little"[..]), iter.next()); + /// assert_eq!(Some(&b"lamb"[..]), iter.next()); + /// + /// assert_eq!(None, iter.next()); + /// ``` + /// + /// If the byte slice is empty or contains only ASCII whitespace, the iterator + /// yields no byte slices: + /// + /// ``` + /// #![feature(u8_split_ascii_whitespace)] + /// + /// assert_eq!(b"".split_ascii_whitespace().next(), None); + /// assert_eq!(b" ".split_ascii_whitespace().next(), None); + /// ``` + #[must_use = "this returns the split byte slice as an iterator, without modifying the original"] + #[unstable(feature = "u8_split_ascii_whitespace", issue = "147878")] + #[inline] + pub fn split_ascii_whitespace(&self) -> SplitAsciiWhitespace<'_> { + let inner = self.split(IsAsciiWhitespace).filter(BytesIsNotEmpty); + SplitAsciiWhitespace { inner } + } +} + +/// An iterator over the non-ASCII-whitespace subslices of a byte slice, +/// separated by any amount of ASCII whitespace. +/// +/// This struct is created by the [`split_ascii_whitespace`] method on [`[u8]`][byteslice]. +/// See its documentation for more. +/// +/// [`split_ascii_whitespace`]: slice::split_ascii_whitespace +/// [byteslice]: prim@slice +#[unstable(feature = "u8_split_ascii_whitespace", issue = "147878")] +#[derive(Clone, Debug)] +pub struct SplitAsciiWhitespace<'a> { + pub(crate) inner: Filter, BytesIsNotEmpty>, +} + +#[unstable(feature = "u8_split_ascii_whitespace", issue = "147878")] +impl<'a> Iterator for SplitAsciiWhitespace<'a> { + type Item = &'a [u8]; + + #[inline] + fn next(&mut self) -> Option<&'a [u8]> { + self.inner.next() + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + self.inner.size_hint() + } + + #[inline] + fn last(mut self) -> Option<&'a [u8]> { + self.next_back() + } +} + +#[unstable(feature = "u8_split_ascii_whitespace", issue = "147878")] +impl<'a> DoubleEndedIterator for SplitAsciiWhitespace<'a> { + #[inline] + fn next_back(&mut self) -> Option<&'a [u8]> { + self.inner.next_back() + } +} + +#[unstable(feature = "u8_split_ascii_whitespace", issue = "147878")] +impl FusedIterator for SplitAsciiWhitespace<'_> {} + +impl<'a> SplitAsciiWhitespace<'a> { + /// Returns remainder of the split slice. + /// + /// If the iterator is empty, returns `None`. + /// + /// # Examples + /// + /// ``` + /// #![feature(u8_split_ascii_whitespace)] + /// + /// let mut split = b"Mary had a little lamb".split_ascii_whitespace(); + /// assert_eq!(split.remainder(), Some(b"Mary had a little lamb".as_slice())); + /// + /// split.next(); + /// assert_eq!(split.remainder(), Some(b"had a little lamb".as_slice())); + /// + /// split.by_ref().for_each(drop); + /// assert_eq!(split.remainder(), None); + /// ``` + #[inline] + #[must_use] + // This is also blocked on: https://github.com/rust-lang/rust/issues/77998 + #[unstable(feature = "u8_split_ascii_whitespace", issue = "147878")] + pub fn remainder(&self) -> Option<&'a [u8]> { + if self.inner.iter.finished { + return None; + } + + Some(self.inner.iter.v) + } } impl_fn_for_zst! { diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs index f73f0c5390629..f787b7994ba9d 100644 --- a/library/core/src/slice/mod.rs +++ b/library/core/src/slice/mod.rs @@ -45,6 +45,8 @@ mod specialize; #[stable(feature = "inherent_ascii_escape", since = "1.60.0")] pub use ascii::EscapeAscii; +#[unstable(feature = "u8_split_ascii_whitespace", issue = "147878")] +pub use ascii::SplitAsciiWhitespace; #[unstable(feature = "str_internals", issue = "none")] #[doc(hidden)] pub use ascii::is_ascii_simple; diff --git a/library/core/src/str/mod.rs b/library/core/src/str/mod.rs index 79f4f29da2d43..f0dd0430230e2 100644 --- a/library/core/src/str/mod.rs +++ b/library/core/src/str/mod.rs @@ -1263,8 +1263,7 @@ impl str { #[stable(feature = "split_ascii_whitespace", since = "1.34.0")] #[inline] pub fn split_ascii_whitespace(&self) -> SplitAsciiWhitespace<'_> { - let inner = - self.as_bytes().split(IsAsciiWhitespace).filter(BytesIsNotEmpty).map(UnsafeBytesToStr); + let inner = self.as_bytes().split_ascii_whitespace().inner.map(UnsafeBytesToStr); SplitAsciiWhitespace { inner } } @@ -3351,7 +3350,7 @@ impl_fn_for_zst! { }; #[derive(Clone)] - struct IsAsciiWhitespace impl Fn = |byte: &u8| -> bool { + pub(crate) struct IsAsciiWhitespace impl Fn = |byte: &u8| -> bool { byte.is_ascii_whitespace() }; @@ -3361,7 +3360,7 @@ impl_fn_for_zst! { }; #[derive(Clone)] - struct BytesIsNotEmpty impl<'a, 'b> Fn = |s: &'a &'b [u8]| -> bool { + pub(crate) struct BytesIsNotEmpty impl<'a, 'b> Fn = |s: &'a &'b [u8]| -> bool { !s.is_empty() }; diff --git a/library/coretests/tests/lib.rs b/library/coretests/tests/lib.rs index c653fdcdf4927..b2e090e64d928 100644 --- a/library/coretests/tests/lib.rs +++ b/library/coretests/tests/lib.rs @@ -123,6 +123,7 @@ #![feature(try_from_int_error_kind)] #![feature(try_trait_v2)] #![feature(type_info)] +#![feature(u8_split_ascii_whitespace)] #![feature(uint_carryless_mul)] #![feature(uint_gather_scatter_bits)] #![feature(unicode_internals)] diff --git a/library/coretests/tests/slice.rs b/library/coretests/tests/slice.rs index 9b0db0e733c57..e570bff645af7 100644 --- a/library/coretests/tests/slice.rs +++ b/library/coretests/tests/slice.rs @@ -2610,3 +2610,30 @@ fn test_shift_right() { case([1, 2, 3, 4], [5], [1], [2, 3, 4, 5]); case([1, 2, 3, 4, 5], [], [], [1, 2, 3, 4, 5]); } + +#[test] +fn test_split_ascii_whitespace_non_ascii() { + let bytes = b"\xff \x80 \xc2\xa0"; + + assert_eq!( + bytes.split_ascii_whitespace().collect::>(), + vec![&b"\xff"[..], &b"\x80"[..], &b"\xc2\xa0"[..]], + ); +} + +#[test] +fn test_split_ascii_whitespace_remainder() { + let bytes = b" Mary \t had "; + let mut split = bytes.split_ascii_whitespace(); + + assert_eq!(split.remainder(), Some(&bytes[..])); + + assert_eq!(split.next(), Some(&b"Mary"[..])); + assert_eq!(split.remainder(), Some(&b"\t had "[..])); + + assert_eq!(split.next(), Some(&b"had"[..])); + assert_eq!(split.remainder(), Some(&b" "[..])); + + assert_eq!(split.next(), None); + assert_eq!(split.remainder(), None); +}