From 5cb65792ed2db58df74e13c8331c28dc4dddc571 Mon Sep 17 00:00:00 2001 From: danieljofficial Date: Mon, 4 May 2026 10:04:21 +0100 Subject: [PATCH 01/16] move box related tests into its folder --- .../box-array-match-temporary-drop.rs} | 0 .../box-array-match-temporary-drop.stderr} | 0 .../box-deref-in-match-arm-no-invalid-ir.rs} | 0 .../issue-23491.rs => box/box-dst-node-with-empty-slice.rs} | 0 .../box-fixed-array-coerce-unsized-match.rs} | 0 5 files changed, 0 insertions(+), 0 deletions(-) rename tests/ui/{issues/issue-20055-box-trait.rs => box/box-array-match-temporary-drop.rs} (100%) rename tests/ui/{issues/issue-20055-box-trait.stderr => box/box-array-match-temporary-drop.stderr} (100%) rename tests/ui/{issues/issue-18845.rs => box/box-deref-in-match-arm-no-invalid-ir.rs} (100%) rename tests/ui/{issues/issue-23491.rs => box/box-dst-node-with-empty-slice.rs} (100%) rename tests/ui/{issues/issue-20055-box-unsized-array.rs => box/box-fixed-array-coerce-unsized-match.rs} (100%) diff --git a/tests/ui/issues/issue-20055-box-trait.rs b/tests/ui/box/box-array-match-temporary-drop.rs similarity index 100% rename from tests/ui/issues/issue-20055-box-trait.rs rename to tests/ui/box/box-array-match-temporary-drop.rs diff --git a/tests/ui/issues/issue-20055-box-trait.stderr b/tests/ui/box/box-array-match-temporary-drop.stderr similarity index 100% rename from tests/ui/issues/issue-20055-box-trait.stderr rename to tests/ui/box/box-array-match-temporary-drop.stderr diff --git a/tests/ui/issues/issue-18845.rs b/tests/ui/box/box-deref-in-match-arm-no-invalid-ir.rs similarity index 100% rename from tests/ui/issues/issue-18845.rs rename to tests/ui/box/box-deref-in-match-arm-no-invalid-ir.rs diff --git a/tests/ui/issues/issue-23491.rs b/tests/ui/box/box-dst-node-with-empty-slice.rs similarity index 100% rename from tests/ui/issues/issue-23491.rs rename to tests/ui/box/box-dst-node-with-empty-slice.rs diff --git a/tests/ui/issues/issue-20055-box-unsized-array.rs b/tests/ui/box/box-fixed-array-coerce-unsized-match.rs similarity index 100% rename from tests/ui/issues/issue-20055-box-unsized-array.rs rename to tests/ui/box/box-fixed-array-coerce-unsized-match.rs From 2c2dad1cfeae19186091150f48df10e53d2e0bc8 Mon Sep 17 00:00:00 2001 From: danieljofficial Date: Mon, 4 May 2026 10:23:53 +0100 Subject: [PATCH 02/16] add issue links and bless --- tests/ui/box/box-array-match-temporary-drop.rs | 2 +- tests/ui/box/box-array-match-temporary-drop.stderr | 2 +- tests/ui/box/box-deref-in-match-arm-no-invalid-ir.rs | 2 ++ tests/ui/box/box-dst-node-with-empty-slice.rs | 2 ++ tests/ui/box/box-fixed-array-coerce-unsized-match.rs | 2 +- 5 files changed, 7 insertions(+), 3 deletions(-) diff --git a/tests/ui/box/box-array-match-temporary-drop.rs b/tests/ui/box/box-array-match-temporary-drop.rs index 43f1f43ba2785..d87c4307760f8 100644 --- a/tests/ui/box/box-array-match-temporary-drop.rs +++ b/tests/ui/box/box-array-match-temporary-drop.rs @@ -1,5 +1,5 @@ //@ run-pass -// See Issues #20055 and #21695. +// See Issues https://github.com/rust-lang/rust/issues/20055 and https://github.com/rust-lang/rust/issues/21695. // We are checking here that the temporaries `Box<[i8, k]>`, for `k` // in 1, 2, 3, 4, that are induced by the match expression are diff --git a/tests/ui/box/box-array-match-temporary-drop.stderr b/tests/ui/box/box-array-match-temporary-drop.stderr index b1cbb2a571733..09caa6741afa7 100644 --- a/tests/ui/box/box-array-match-temporary-drop.stderr +++ b/tests/ui/box/box-array-match-temporary-drop.stderr @@ -1,5 +1,5 @@ warning: method `dummy` is never used - --> $DIR/issue-20055-box-trait.rs:11:8 + --> $DIR/box-array-match-temporary-drop.rs:11:8 | LL | trait Boo { | --- method in this trait diff --git a/tests/ui/box/box-deref-in-match-arm-no-invalid-ir.rs b/tests/ui/box/box-deref-in-match-arm-no-invalid-ir.rs index c9dc175b10a8a..8c005a392dca9 100644 --- a/tests/ui/box/box-deref-in-match-arm-no-invalid-ir.rs +++ b/tests/ui/box/box-deref-in-match-arm-no-invalid-ir.rs @@ -1,3 +1,5 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/18845 + //@ run-pass // This used to generate invalid IR in that even if we took the // `false` branch we'd still try to free the Box from the other diff --git a/tests/ui/box/box-dst-node-with-empty-slice.rs b/tests/ui/box/box-dst-node-with-empty-slice.rs index 0a2dfa4257a03..15ee8d4434b5d 100644 --- a/tests/ui/box/box-dst-node-with-empty-slice.rs +++ b/tests/ui/box/box-dst-node-with-empty-slice.rs @@ -1,3 +1,5 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/23491 + //@ run-pass #![allow(unused_variables)] diff --git a/tests/ui/box/box-fixed-array-coerce-unsized-match.rs b/tests/ui/box/box-fixed-array-coerce-unsized-match.rs index 2663bcfb4f727..4251495347183 100644 --- a/tests/ui/box/box-fixed-array-coerce-unsized-match.rs +++ b/tests/ui/box/box-fixed-array-coerce-unsized-match.rs @@ -1,5 +1,5 @@ //@ run-pass -// Issue #2005: Check that boxed fixed-size arrays are properly +// Issue https://github.com/rust-lang/rust/issues/20055: Check that boxed fixed-size arrays are properly // accounted for (namely, only deallocated if they were actually // created) when they appear as temporaries in unused arms of a match // expression. From 10f9f14377a2fdf65a1f07497c48ce264816a04a Mon Sep 17 00:00:00 2001 From: Dnreikronos Date: Mon, 1 Jun 2026 13:47:30 -0300 Subject: [PATCH 03/16] Build a fully-applied trait ref in method-not-found suggestion When a method call fails to resolve, the suggestion machinery probes all traits for a method with the same name and builds a trait reference for the providing trait to detect duplicate trait items coming from multiple crate versions. It passed only the receiver type as the single argument, which is correct only for traits whose sole generic parameter is `Self`. For a trait with further parameters (for example `Borrow`), or when the receiver type is unknown, the argument list no longer matched the trait's generics and tripped the `debug_assert_args_compatible` assertion, producing an ICE. Build the argument list with `GenericArgs::for_item` instead, using the receiver type for `Self` when known and fresh inference variables for the remaining parameters. --- .../rustc_hir_typeck/src/method/suggest.rs | 19 +++++- ...estion-trait-with-extra-generics-no-ice.rs | 24 +++++++ ...on-trait-with-extra-generics-no-ice.stderr | 65 +++++++++++++++++++ 3 files changed, 107 insertions(+), 1 deletion(-) create mode 100644 tests/ui/methods/method-suggestion-trait-with-extra-generics-no-ice.rs create mode 100644 tests/ui/methods/method-suggestion-trait-with-extra-generics-no-ice.stderr diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index c9ec32159f476..48e1f553900b0 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -4796,7 +4796,24 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let hir::Node::Expr(rcvr) = self.tcx.hir_node(hir_id) else { return false; }; - let trait_ref = ty::TraitRef::new(self.tcx, trait_def_id, rcvr_ty.into_iter()); + // The trait may have generic parameters beyond `Self` (e.g. `Borrow`), and + // `rcvr_ty` may even be unknown. We only ever know the receiver type (the `Self` arg), + // so fill `Self` from `rcvr_ty` when available and the remaining parameters with fresh + // inference variables; building a `TraitRef` with a partial arg list would otherwise trip + // `debug_assert_args_compatible` and ICE. See #157189. + let trait_ref = ty::TraitRef::new_from_args( + self.tcx, + trait_def_id, + ty::GenericArgs::for_item(self.tcx, trait_def_id, |param, _| { + if param.index == 0 + && let Some(rcvr_ty) = rcvr_ty + { + rcvr_ty.into() + } else { + self.var_for_def(rcvr.span, param) + } + }), + ); let trait_pred = ty::Binder::dummy(ty::TraitPredicate { trait_ref, polarity: ty::PredicatePolarity::Positive, diff --git a/tests/ui/methods/method-suggestion-trait-with-extra-generics-no-ice.rs b/tests/ui/methods/method-suggestion-trait-with-extra-generics-no-ice.rs new file mode 100644 index 0000000000000..0a98cdef5c923 --- /dev/null +++ b/tests/ui/methods/method-suggestion-trait-with-extra-generics-no-ice.rs @@ -0,0 +1,24 @@ +//! Regression test for #157189. +//! +//! When a method call fails to resolve, the "trait which provides `` is +//! implemented but not in scope" diagnostic probes all traits for a method of the +//! same name. Here `.borrow()` matches `std::borrow::Borrow::borrow`, and `Borrow` +//! has a generic parameter (`Borrowed`) besides `Self`. Building the trait +//! reference for the diagnostic used to pass only the receiver type as the single +//! argument, which mismatched the trait's generics and ICEd in +//! `debug_assert_args_compatible`. It should just report the error. + +trait Foo { + extern "C" fn borrow(&self); +} + +struct Bar; + +fn main() { + let foo: Box usize> = Box::new(Bar); + //~^ ERROR expected a `Fn(bool)` closure, found `Bar` + foo.borrow(); + //~^ ERROR no method named `borrow` found + foo.take() + //~^ ERROR `Box usize>` is not an iterator +} diff --git a/tests/ui/methods/method-suggestion-trait-with-extra-generics-no-ice.stderr b/tests/ui/methods/method-suggestion-trait-with-extra-generics-no-ice.stderr new file mode 100644 index 0000000000000..aa46a41fa3378 --- /dev/null +++ b/tests/ui/methods/method-suggestion-trait-with-extra-generics-no-ice.stderr @@ -0,0 +1,65 @@ +error[E0599]: no method named `borrow` found for struct `Box usize>` in the current scope + --> $DIR/method-suggestion-trait-with-extra-generics-no-ice.rs:20:9 + | +LL | foo.borrow(); + | ^^^^^^ + | + --> $SRC_DIR/core/src/borrow.rs:LL:COL + | + = note: the method is available for `Box usize>` here + | + = help: items from traits can only be used if the trait is in scope +help: use parentheses to call this trait object + | +LL | foo(/* bool */).borrow(); + | ++++++++++++ +help: trait `Borrow` which provides `borrow` is implemented but not in scope; perhaps you want to import it + | +LL + use std::borrow::Borrow; + | +help: there is a method `borrow_mut` with a similar name + | +LL | foo.borrow_mut(); + | ++++ + +error[E0277]: expected a `Fn(bool)` closure, found `Bar` + --> $DIR/method-suggestion-trait-with-extra-generics-no-ice.rs:18:43 + | +LL | let foo: Box usize> = Box::new(Bar); + | ^^^^^^^^^^^^^ expected an `Fn(bool)` closure, found `Bar` + | +help: the trait `Fn(bool)` is not implemented for `Bar` + --> $DIR/method-suggestion-trait-with-extra-generics-no-ice.rs:15:1 + | +LL | struct Bar; + | ^^^^^^^^^^ + = note: required for the cast from `Box` to `Box usize>` + +error[E0599]: `Box usize>` is not an iterator + --> $DIR/method-suggestion-trait-with-extra-generics-no-ice.rs:22:9 + | +LL | foo.take() + | ^^^^ + | | + | this is an associated function, not a method + | `Box usize>` is not an iterator + | + = note: found the following associated functions; to be used as methods, functions must have a `self` parameter + = note: the candidate is defined in an impl for the type `Box` + = note: the following trait bounds were not satisfied: + `dyn Fn(bool) -> usize: Iterator` + which is required by `Box usize>: Iterator` + `Box usize>: Iterator` + which is required by `&mut Box usize>: Iterator` + `dyn Fn(bool) -> usize: Iterator` + which is required by `&mut dyn Fn(bool) -> usize: Iterator` +help: use associated function syntax instead + | +LL - foo.take() +LL + Box:: usize>::take(foo) + | + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0277, E0599. +For more information about an error, try `rustc --explain E0277`. From 4919940fad92a2a503e3fe7fc6a5594fb9771e72 Mon Sep 17 00:00:00 2001 From: Hans Wennborg Date: Wed, 3 Jun 2026 09:46:48 +0200 Subject: [PATCH 04/16] Revert "LLVM 23: Run AssignGUIDPass in some places" This reverts commit cdb73bb67707f78b321fe5579bcb9c128f45c352. --- compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp | 9 --------- tests/codegen-llvm/bpf-allows-unaligned.rs | 2 +- tests/codegen-llvm/branch-protection.rs | 2 +- tests/codegen-llvm/frame-pointer-cli-control.rs | 2 +- tests/codegen-llvm/frame-pointer.rs | 2 +- tests/codegen-llvm/gpu-convergent.rs | 2 +- tests/codegen-llvm/instrument-coverage/testprog.rs | 4 ++-- tests/codegen-llvm/link_section.rs | 2 +- ...pe-metadata-itanium-cxx-abi-normalized-generalized.rs | 6 +++--- .../some-non-zero-from-atomic-optimization.rs | 2 +- .../codegen-llvm/target-feature-negative-implication.rs | 2 +- tests/codegen-llvm/target-feature-overrides.rs | 4 ++-- tests/codegen-llvm/unwind-abis/aapcs-unwind-abi.rs | 4 ++-- tests/codegen-llvm/unwind-abis/c-unwind-abi.rs | 4 ++-- tests/codegen-llvm/unwind-abis/cdecl-unwind-abi.rs | 4 ++-- tests/codegen-llvm/unwind-abis/fastcall-unwind-abi.rs | 4 ++-- tests/codegen-llvm/unwind-abis/stdcall-unwind-abi.rs | 4 ++-- tests/codegen-llvm/unwind-abis/system-unwind-abi.rs | 4 ++-- tests/codegen-llvm/unwind-abis/sysv64-unwind-abi.rs | 4 ++-- tests/codegen-llvm/unwind-abis/thiscall-unwind-abi.rs | 4 ++-- tests/codegen-llvm/unwind-abis/vectorcall-unwind-abi.rs | 4 ++-- tests/codegen-llvm/unwind-abis/win64-unwind-abi.rs | 4 ++-- 22 files changed, 35 insertions(+), 44 deletions(-) diff --git a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp index 8a6caa9b10854..b03a07538ccd8 100644 --- a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp @@ -49,9 +49,6 @@ #include "llvm/Transforms/Instrumentation/RealtimeSanitizer.h" #include "llvm/Transforms/Instrumentation/ThreadSanitizer.h" #include "llvm/Transforms/Scalar/AnnotationRemarks.h" -#if LLVM_VERSION_GE(23, 0) -#include "llvm/Transforms/Utils/AssignGUID.h" -#endif #include "llvm/Transforms/Utils/CanonicalizeAliases.h" #include "llvm/Transforms/Utils/FunctionImportUtils.h" #include "llvm/Transforms/Utils/NameAnonGlobals.h" @@ -918,9 +915,6 @@ extern "C" LLVMRustResult LLVMRustOptimize( if (NeedThinLTOBufferPasses) { MPM.addPass(CanonicalizeAliasesPass()); MPM.addPass(NameAnonGlobalPass()); -#if LLVM_VERSION_GE(23, 0) - MPM.addPass(AssignGUIDPass()); -#endif } // For `-Copt-level=0`, and the pre-link fat/thin LTO stages. if (ThinLTOBufferRef && *ThinLTOBufferRef == nullptr) { @@ -1460,9 +1454,6 @@ extern "C" LLVMRustBuffer *LLVMRustModuleSerialize(LLVMModuleRef M, PB.registerLoopAnalyses(LAM); PB.crossRegisterProxies(LAM, FAM, CGAM, MAM); ModulePassManager MPM; -#if LLVM_VERSION_GE(23, 0) - MPM.addPass(AssignGUIDPass()); -#endif MPM.addPass(ThinLTOBitcodeWriterPass(OS, nullptr)); MPM.run(*unwrap(M), MAM); } else { diff --git a/tests/codegen-llvm/bpf-allows-unaligned.rs b/tests/codegen-llvm/bpf-allows-unaligned.rs index 7e95a56d984c9..c7a70d5b2e502 100644 --- a/tests/codegen-llvm/bpf-allows-unaligned.rs +++ b/tests/codegen-llvm/bpf-allows-unaligned.rs @@ -5,7 +5,7 @@ #[no_mangle] #[target_feature(enable = "allows-misaligned-mem-access")] -// CHECK: define noundef zeroext i8 @foo(i8 noundef returned %arg) unnamed_addr #0 +// CHECK: define noundef zeroext i8 @foo(i8 noundef returned %arg) unnamed_addr #0 { pub unsafe fn foo(arg: u8) -> u8 { arg } diff --git a/tests/codegen-llvm/branch-protection.rs b/tests/codegen-llvm/branch-protection.rs index 11847c256d6ba..ed1cb2cd137ea 100644 --- a/tests/codegen-llvm/branch-protection.rs +++ b/tests/codegen-llvm/branch-protection.rs @@ -22,7 +22,7 @@ extern crate minicore; use minicore::*; // A basic test function. -// CHECK: @test(){{.*}} [[ATTR:#[0-9]+]] +// CHECK: @test(){{.*}} [[ATTR:#[0-9]+]] { #[no_mangle] pub fn test() {} diff --git a/tests/codegen-llvm/frame-pointer-cli-control.rs b/tests/codegen-llvm/frame-pointer-cli-control.rs index 79cdfc70f1ad7..911a5f03cbcda 100644 --- a/tests/codegen-llvm/frame-pointer-cli-control.rs +++ b/tests/codegen-llvm/frame-pointer-cli-control.rs @@ -45,7 +45,7 @@ Specific cases where platforms or tools rely on frame pointers for sound or corr extern crate minicore; -// CHECK: i32 @peach{{.*}}[[PEACH_ATTRS:\#[0-9]+]] +// CHECK: i32 @peach{{.*}}[[PEACH_ATTRS:\#[0-9]+]] { #[no_mangle] pub fn peach(x: u32) -> u32 { x diff --git a/tests/codegen-llvm/frame-pointer.rs b/tests/codegen-llvm/frame-pointer.rs index a52d95a23862d..1d0dd762826b2 100644 --- a/tests/codegen-llvm/frame-pointer.rs +++ b/tests/codegen-llvm/frame-pointer.rs @@ -18,7 +18,7 @@ extern crate minicore; use minicore::*; -// CHECK: define i32 @peach{{.*}}[[PEACH_ATTRS:\#[0-9]+]] +// CHECK: define i32 @peach{{.*}}[[PEACH_ATTRS:\#[0-9]+]] { #[no_mangle] pub fn peach(x: u32) -> u32 { x diff --git a/tests/codegen-llvm/gpu-convergent.rs b/tests/codegen-llvm/gpu-convergent.rs index 376d65a3d4a25..bb9271ab69996 100644 --- a/tests/codegen-llvm/gpu-convergent.rs +++ b/tests/codegen-llvm/gpu-convergent.rs @@ -17,7 +17,7 @@ extern "C" { fn ext(); } -// CHECK: define {{.*}}_kernel void @fun(i32{{.*}}) unnamed_addr #[[ATTR:[0-9]+]] +// CHECK: define {{.*}}_kernel void @fun(i32{{.*}}) unnamed_addr #[[ATTR:[0-9]+]] { // CHECK: declare void @ext() unnamed_addr #[[ATTR]] // CHECK: attributes #[[ATTR]] = {{.*}} convergent #[no_mangle] diff --git a/tests/codegen-llvm/instrument-coverage/testprog.rs b/tests/codegen-llvm/instrument-coverage/testprog.rs index 67c49c438f9f7..ef61ede6de8ee 100644 --- a/tests/codegen-llvm/instrument-coverage/testprog.rs +++ b/tests/codegen-llvm/instrument-coverage/testprog.rs @@ -101,7 +101,7 @@ fn main() { // CHECK-SAME: @__llvm_prf_nm // CHECK-SAME: section "llvm.metadata" -// CHECK: define internal { {{.*}} } @_R{{[a-zA-Z0-9_]+}}testprog14will_be_called() unnamed_addr #{{[0-9]+}} +// CHECK: define internal { {{.*}} } @_R{{[a-zA-Z0-9_]+}}testprog14will_be_called() unnamed_addr #{{[0-9]+}} { // CHECK-NEXT: start: // CHECK-NOT: define internal // CHECK: atomicrmw add ptr @@ -109,7 +109,7 @@ fn main() { // CHECK: declare void @llvm.instrprof.increment(ptr, i64, i32, i32) #[[LLVM_INSTRPROF_INCREMENT_ATTR:[0-9]+]] -// WIN: define linkonce_odr hidden i32 @__llvm_profile_runtime_user() #[[LLVM_PROFILE_RUNTIME_USER_ATTR:[0-9]+]] comdat {{.*}} +// WIN: define linkonce_odr hidden i32 @__llvm_profile_runtime_user() #[[LLVM_PROFILE_RUNTIME_USER_ATTR:[0-9]+]] comdat {{.*}}{ // WIN-NEXT: %1 = load i32, ptr @__llvm_profile_runtime // WIN-NEXT: ret i32 %1 // WIN-NEXT: } diff --git a/tests/codegen-llvm/link_section.rs b/tests/codegen-llvm/link_section.rs index 61bde683c0a41..f196ea86c447d 100644 --- a/tests/codegen-llvm/link_section.rs +++ b/tests/codegen-llvm/link_section.rs @@ -29,7 +29,7 @@ pub static VAR2: E = E::A(666); #[link_section = "__TEST,three"] pub static VAR3: E = E::B(1.); -// CHECK: define {{(dso_local )?}}void @fn1() {{.*}} section "__TEST,four" +// CHECK: define {{(dso_local )?}}void @fn1() {{.*}} section "__TEST,four" { #[no_mangle] #[link_section = "__TEST,four"] pub fn fn1() {} diff --git a/tests/codegen-llvm/sanitizer/cfi/emit-type-metadata-itanium-cxx-abi-normalized-generalized.rs b/tests/codegen-llvm/sanitizer/cfi/emit-type-metadata-itanium-cxx-abi-normalized-generalized.rs index 5b1aa97ab3338..7639ce7b10448 100644 --- a/tests/codegen-llvm/sanitizer/cfi/emit-type-metadata-itanium-cxx-abi-normalized-generalized.rs +++ b/tests/codegen-llvm/sanitizer/cfi/emit-type-metadata-itanium-cxx-abi-normalized-generalized.rs @@ -7,21 +7,21 @@ pub fn foo(f: fn(i32) -> i32, arg: i32) -> i32 { // CHECK-LABEL: define{{.*}}foo - // CHECK-SAME: {{.*}}!type ![[TYPE1:[0-9]+]] + // CHECK-SAME: {{.*}}![[TYPE1:[0-9]+]] // CHECK: call i1 @llvm.type.test(ptr {{%f|%0}}, metadata !"_ZTSFu3i32S_E.normalized.generalized") f(arg) } pub fn bar(f: fn(i32, i32) -> i32, arg1: i32, arg2: i32) -> i32 { // CHECK-LABEL: define{{.*}}bar - // CHECK-SAME: {{.*}}!type ![[TYPE2:[0-9]+]] + // CHECK-SAME: {{.*}}![[TYPE2:[0-9]+]] // CHECK: call i1 @llvm.type.test(ptr {{%f|%0}}, metadata !"_ZTSFu3i32S_S_E.normalized.generalized") f(arg1, arg2) } pub fn baz(f: fn(i32, i32, i32) -> i32, arg1: i32, arg2: i32, arg3: i32) -> i32 { // CHECK-LABEL: define{{.*}}baz - // CHECK-SAME: {{.*}}!type ![[TYPE3:[0-9]+]] + // CHECK-SAME: {{.*}}![[TYPE3:[0-9]+]] // CHECK: call i1 @llvm.type.test(ptr {{%f|%0}}, metadata !"_ZTSFu3i32S_S_S_E.normalized.generalized") f(arg1, arg2, arg3) } diff --git a/tests/codegen-llvm/some-non-zero-from-atomic-optimization.rs b/tests/codegen-llvm/some-non-zero-from-atomic-optimization.rs index 3df2d569f9a40..35317b0dd39cc 100644 --- a/tests/codegen-llvm/some-non-zero-from-atomic-optimization.rs +++ b/tests/codegen-llvm/some-non-zero-from-atomic-optimization.rs @@ -72,7 +72,7 @@ pub unsafe fn some_non_zero_from_atomic_get() -> Option { /// /// The way we check that the LLVM IR is correct is by making sure that neither /// `panic` nor `unreachable` is part of the LLVM IR: -// CHECK-LABEL: define {{.*}} i64 @some_non_zero_from_atomic_get2() {{.*}} +// CHECK-LABEL: define {{.*}} i64 @some_non_zero_from_atomic_get2() {{.*}} { // CHECK-NOT: panic // CHECK-NOT: unreachable #[no_mangle] diff --git a/tests/codegen-llvm/target-feature-negative-implication.rs b/tests/codegen-llvm/target-feature-negative-implication.rs index 376599738e526..a9cdca4283991 100644 --- a/tests/codegen-llvm/target-feature-negative-implication.rs +++ b/tests/codegen-llvm/target-feature-negative-implication.rs @@ -13,7 +13,7 @@ use minicore::*; #[no_mangle] pub unsafe fn banana() { // CHECK-LABEL: @banana() - // CHECK-SAME: [[BANANAATTRS:#[0-9]+]] + // CHECK-SAME: [[BANANAATTRS:#[0-9]+]] { } // CHECK: attributes [[BANANAATTRS]] diff --git a/tests/codegen-llvm/target-feature-overrides.rs b/tests/codegen-llvm/target-feature-overrides.rs index 3bf05c7977ebf..2adc8ee6f53bc 100644 --- a/tests/codegen-llvm/target-feature-overrides.rs +++ b/tests/codegen-llvm/target-feature-overrides.rs @@ -23,7 +23,7 @@ extern "C" { #[no_mangle] pub unsafe fn apple() -> u32 { // CHECK-LABEL: @apple() - // CHECK-SAME: [[APPLEATTRS:#[0-9]+]] + // CHECK-SAME: [[APPLEATTRS:#[0-9]+]] { // CHECK: {{.*}}call{{.*}}@peach peach() } @@ -32,7 +32,7 @@ pub unsafe fn apple() -> u32 { #[no_mangle] pub unsafe fn banana() -> u32 { // CHECK-LABEL: @banana() - // CHECK-SAME: [[BANANAATTRS:#[0-9]+]] + // CHECK-SAME: [[BANANAATTRS:#[0-9]+]] { // COMPAT: {{.*}}call{{.*}}@peach // INCOMPAT: {{.*}}call{{.*}}@apple apple() // Compatible for inline in COMPAT revision and can't be inlined in INCOMPAT diff --git a/tests/codegen-llvm/unwind-abis/aapcs-unwind-abi.rs b/tests/codegen-llvm/unwind-abis/aapcs-unwind-abi.rs index 279780e3a7aeb..ecace722e0dbe 100644 --- a/tests/codegen-llvm/unwind-abis/aapcs-unwind-abi.rs +++ b/tests/codegen-llvm/unwind-abis/aapcs-unwind-abi.rs @@ -16,11 +16,11 @@ pub trait Sized: MetaSized {} // `aapcs-unwind` extern functions. `aapcs-unwind` functions MUST NOT have this attribute. We // disable optimizations above to prevent LLVM from inferring the attribute. -// CHECK: @rust_item_that_cannot_unwind() unnamed_addr #0 +// CHECK: @rust_item_that_cannot_unwind() unnamed_addr #0 { #[no_mangle] pub extern "aapcs" fn rust_item_that_cannot_unwind() {} -// CHECK: @rust_item_that_can_unwind() unnamed_addr #1 +// CHECK: @rust_item_that_can_unwind() unnamed_addr #1 { #[no_mangle] pub extern "aapcs-unwind" fn rust_item_that_can_unwind() {} diff --git a/tests/codegen-llvm/unwind-abis/c-unwind-abi.rs b/tests/codegen-llvm/unwind-abis/c-unwind-abi.rs index 1b3312839e3e8..46c08b5fc4ff4 100644 --- a/tests/codegen-llvm/unwind-abis/c-unwind-abi.rs +++ b/tests/codegen-llvm/unwind-abis/c-unwind-abi.rs @@ -7,11 +7,11 @@ #![crate_type = "lib"] -// CHECK: @rust_item_that_cannot_unwind() unnamed_addr #0 +// CHECK: @rust_item_that_cannot_unwind() unnamed_addr #0 { #[no_mangle] pub extern "C" fn rust_item_that_cannot_unwind() {} -// CHECK: @rust_item_that_can_unwind() unnamed_addr #1 +// CHECK: @rust_item_that_can_unwind() unnamed_addr #1 { #[no_mangle] pub extern "C-unwind" fn rust_item_that_can_unwind() {} diff --git a/tests/codegen-llvm/unwind-abis/cdecl-unwind-abi.rs b/tests/codegen-llvm/unwind-abis/cdecl-unwind-abi.rs index 6f4eafb353ccb..8e643d6ce4947 100644 --- a/tests/codegen-llvm/unwind-abis/cdecl-unwind-abi.rs +++ b/tests/codegen-llvm/unwind-abis/cdecl-unwind-abi.rs @@ -7,11 +7,11 @@ #![crate_type = "lib"] -// CHECK: @rust_item_that_cannot_unwind() unnamed_addr #0 +// CHECK: @rust_item_that_cannot_unwind() unnamed_addr #0 { #[no_mangle] pub extern "cdecl" fn rust_item_that_cannot_unwind() {} -// CHECK: @rust_item_that_can_unwind() unnamed_addr #1 +// CHECK: @rust_item_that_can_unwind() unnamed_addr #1 { #[no_mangle] pub extern "cdecl-unwind" fn rust_item_that_can_unwind() {} diff --git a/tests/codegen-llvm/unwind-abis/fastcall-unwind-abi.rs b/tests/codegen-llvm/unwind-abis/fastcall-unwind-abi.rs index 51c6fd15b9c5c..7df46813ed1dd 100644 --- a/tests/codegen-llvm/unwind-abis/fastcall-unwind-abi.rs +++ b/tests/codegen-llvm/unwind-abis/fastcall-unwind-abi.rs @@ -16,11 +16,11 @@ pub trait Sized: MetaSized {} // `fastcall-unwind` extern functions. `fastcall-unwind` functions MUST NOT have this attribute. We // disable optimizations above to prevent LLVM from inferring the attribute. -// CHECK: @rust_item_that_cannot_unwind() unnamed_addr #0 +// CHECK: @rust_item_that_cannot_unwind() unnamed_addr #0 { #[no_mangle] pub extern "fastcall" fn rust_item_that_cannot_unwind() {} -// CHECK: @rust_item_that_can_unwind() unnamed_addr #1 +// CHECK: @rust_item_that_can_unwind() unnamed_addr #1 { #[no_mangle] pub extern "fastcall-unwind" fn rust_item_that_can_unwind() {} diff --git a/tests/codegen-llvm/unwind-abis/stdcall-unwind-abi.rs b/tests/codegen-llvm/unwind-abis/stdcall-unwind-abi.rs index b5fcea52b4d61..cc06ee125495a 100644 --- a/tests/codegen-llvm/unwind-abis/stdcall-unwind-abi.rs +++ b/tests/codegen-llvm/unwind-abis/stdcall-unwind-abi.rs @@ -16,11 +16,11 @@ pub trait Sized: MetaSized {} // extern functions. `stdcall-unwind` functions MUST NOT have this attribute. We disable // optimizations above to prevent LLVM from inferring the attribute. -// CHECK: @rust_item_that_cannot_unwind() unnamed_addr #0 +// CHECK: @rust_item_that_cannot_unwind() unnamed_addr #0 { #[no_mangle] pub extern "stdcall" fn rust_item_that_cannot_unwind() {} -// CHECK: @rust_item_that_can_unwind() unnamed_addr #1 +// CHECK: @rust_item_that_can_unwind() unnamed_addr #1 { #[no_mangle] pub extern "stdcall-unwind" fn rust_item_that_can_unwind() {} diff --git a/tests/codegen-llvm/unwind-abis/system-unwind-abi.rs b/tests/codegen-llvm/unwind-abis/system-unwind-abi.rs index 15fce95fe285b..5f9102483464b 100644 --- a/tests/codegen-llvm/unwind-abis/system-unwind-abi.rs +++ b/tests/codegen-llvm/unwind-abis/system-unwind-abi.rs @@ -7,11 +7,11 @@ #![crate_type = "lib"] -// CHECK: @rust_item_that_cannot_unwind() unnamed_addr #0 +// CHECK: @rust_item_that_cannot_unwind() unnamed_addr #0 { #[no_mangle] pub extern "system" fn rust_item_that_cannot_unwind() {} -// CHECK: @rust_item_that_can_unwind() unnamed_addr #1 +// CHECK: @rust_item_that_can_unwind() unnamed_addr #1 { #[no_mangle] pub extern "system-unwind" fn rust_item_that_can_unwind() {} diff --git a/tests/codegen-llvm/unwind-abis/sysv64-unwind-abi.rs b/tests/codegen-llvm/unwind-abis/sysv64-unwind-abi.rs index 1293e7c0a5f82..69bfaf80b4be6 100644 --- a/tests/codegen-llvm/unwind-abis/sysv64-unwind-abi.rs +++ b/tests/codegen-llvm/unwind-abis/sysv64-unwind-abi.rs @@ -16,11 +16,11 @@ pub trait Sized: MetaSized {} // `sysv64-unwind` extern functions. `sysv64-unwind` functions MUST NOT have this attribute. We // disable optimizations above to prevent LLVM from inferring the attribute. -// CHECK: @rust_item_that_cannot_unwind() unnamed_addr #0 +// CHECK: @rust_item_that_cannot_unwind() unnamed_addr #0 { #[no_mangle] pub extern "sysv64" fn rust_item_that_cannot_unwind() {} -// CHECK: @rust_item_that_can_unwind() unnamed_addr #1 +// CHECK: @rust_item_that_can_unwind() unnamed_addr #1 { #[no_mangle] pub extern "sysv64-unwind" fn rust_item_that_can_unwind() {} diff --git a/tests/codegen-llvm/unwind-abis/thiscall-unwind-abi.rs b/tests/codegen-llvm/unwind-abis/thiscall-unwind-abi.rs index a9b6c34ee58e2..05f6b8b70e171 100644 --- a/tests/codegen-llvm/unwind-abis/thiscall-unwind-abi.rs +++ b/tests/codegen-llvm/unwind-abis/thiscall-unwind-abi.rs @@ -16,11 +16,11 @@ pub trait Sized: MetaSized {} // `thiscall-unwind` extern functions. `thiscall-unwind` functions MUST NOT have this attribute. We // disable optimizations above to prevent LLVM from inferring the attribute. -// CHECK: @rust_item_that_cannot_unwind() unnamed_addr #0 +// CHECK: @rust_item_that_cannot_unwind() unnamed_addr #0 { #[no_mangle] pub extern "thiscall" fn rust_item_that_cannot_unwind() {} -// CHECK: @rust_item_that_can_unwind() unnamed_addr #1 +// CHECK: @rust_item_that_can_unwind() unnamed_addr #1 { #[no_mangle] pub extern "thiscall-unwind" fn rust_item_that_can_unwind() {} diff --git a/tests/codegen-llvm/unwind-abis/vectorcall-unwind-abi.rs b/tests/codegen-llvm/unwind-abis/vectorcall-unwind-abi.rs index 8cedb55ae1d28..d001a16b32a1c 100644 --- a/tests/codegen-llvm/unwind-abis/vectorcall-unwind-abi.rs +++ b/tests/codegen-llvm/unwind-abis/vectorcall-unwind-abi.rs @@ -16,11 +16,11 @@ pub trait Sized: MetaSized {} // `vectorcall-unwind` extern functions. `vectorcall-unwind` functions MUST NOT have this attribute. // We disable optimizations above to prevent LLVM from inferring the attribute. -// CHECK: @rust_item_that_cannot_unwind() unnamed_addr #0 +// CHECK: @rust_item_that_cannot_unwind() unnamed_addr #0 { #[no_mangle] pub extern "vectorcall" fn rust_item_that_cannot_unwind() {} -// CHECK: @rust_item_that_can_unwind() unnamed_addr #1 +// CHECK: @rust_item_that_can_unwind() unnamed_addr #1 { #[no_mangle] pub extern "vectorcall-unwind" fn rust_item_that_can_unwind() {} diff --git a/tests/codegen-llvm/unwind-abis/win64-unwind-abi.rs b/tests/codegen-llvm/unwind-abis/win64-unwind-abi.rs index 2a3ad330406e6..257f00b54e4d8 100644 --- a/tests/codegen-llvm/unwind-abis/win64-unwind-abi.rs +++ b/tests/codegen-llvm/unwind-abis/win64-unwind-abi.rs @@ -16,11 +16,11 @@ pub trait Sized: MetaSized {} // `win64-unwind` extern functions. `win64-unwind` functions MUST NOT have this attribute. We // disable optimizations above to prevent LLVM from inferring the attribute. -// CHECK: @rust_item_that_cannot_unwind() unnamed_addr #0 +// CHECK: @rust_item_that_cannot_unwind() unnamed_addr #0 { #[no_mangle] pub extern "win64" fn rust_item_that_cannot_unwind() {} -// CHECK: @rust_item_that_can_unwind() unnamed_addr #1 +// CHECK: @rust_item_that_can_unwind() unnamed_addr #1 { #[no_mangle] pub extern "win64-unwind" fn rust_item_that_can_unwind() {} From 20479b727f0067aa56a2b7912a04654ca8c9788f Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 5 Jun 2026 14:58:31 +0200 Subject: [PATCH 05/16] thread spawn hooks: rename `run` to make it clear that it also handles inheriting spawn hooks --- library/std/src/thread/lifecycle.rs | 2 +- library/std/src/thread/spawnhook.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/library/std/src/thread/lifecycle.rs b/library/std/src/thread/lifecycle.rs index af239bee55189..d3a97bbf08fa2 100644 --- a/library/std/src/thread/lifecycle.rs +++ b/library/std/src/thread/lifecycle.rs @@ -66,7 +66,7 @@ where let rust_start = move || { let f = f.into_inner(); let try_result = panic::catch_unwind(panic::AssertUnwindSafe(|| { - crate::sys::backtrace::__rust_begin_short_backtrace(|| hooks.run()); + crate::sys::backtrace::__rust_begin_short_backtrace(|| hooks.inherit_and_run()); crate::sys::backtrace::__rust_begin_short_backtrace(f) })); // SAFETY: `their_packet` as been built just above and moved by the diff --git a/library/std/src/thread/spawnhook.rs b/library/std/src/thread/spawnhook.rs index 254793ac33d08..bb36fb687b4af 100644 --- a/library/std/src/thread/spawnhook.rs +++ b/library/std/src/thread/spawnhook.rs @@ -144,7 +144,7 @@ pub(super) struct ChildSpawnHooks { impl ChildSpawnHooks { // This is run on the newly spawned thread, directly at the start. - pub(super) fn run(self) { + pub(super) fn inherit_and_run(self) { SPAWN_HOOKS.set(self.hooks); for run in self.to_run { run(); From 7602e3fdc180037a94ad8a48408f94ddca1d2baa Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 5 Jun 2026 14:59:02 +0200 Subject: [PATCH 06/16] thread_loca::guard: make the `enable` contract more explicit --- library/std/src/sys/thread_local/guard/windows.rs | 1 + library/std/src/sys/thread_local/mod.rs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/library/std/src/sys/thread_local/guard/windows.rs b/library/std/src/sys/thread_local/guard/windows.rs index 01ac9526910c2..7a9c69ae43784 100644 --- a/library/std/src/sys/thread_local/guard/windows.rs +++ b/library/std/src/sys/thread_local/guard/windows.rs @@ -106,6 +106,7 @@ impl Drop for EnableGuard { } } +/// Set up the current thread to invoke `cleanup` when it finishes. pub fn enable() { let registered = if cfg!(target_thread_local) { #[thread_local] diff --git a/library/std/src/sys/thread_local/mod.rs b/library/std/src/sys/thread_local/mod.rs index e88011aa22dad..d48bb1c8b721e 100644 --- a/library/std/src/sys/thread_local/mod.rs +++ b/library/std/src/sys/thread_local/mod.rs @@ -81,7 +81,7 @@ pub(crate) mod destructors { /// This module provides a way to schedule the execution of the destructor list /// and the [runtime cleanup](crate::rt::thread_cleanup) function. Calling `enable` -/// should ensure that these functions are called at the right times. +/// sets up the current thread to ensure that these functions are called at the right times. pub(crate) mod guard { cfg_select! { all(target_thread_local, target_vendor = "apple") => { From 97b5f453b5648214536cc48c36328225ce18428f Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 5 Jun 2026 15:08:47 +0200 Subject: [PATCH 07/16] windows LazyKey: fix enabling the guard on all relevant threads --- library/std/src/sys/thread_local/key/windows.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/library/std/src/sys/thread_local/key/windows.rs b/library/std/src/sys/thread_local/key/windows.rs index 2ff0fd1196e12..420b628068a3a 100644 --- a/library/std/src/sys/thread_local/key/windows.rs +++ b/library/std/src/sys/thread_local/key/windows.rs @@ -60,6 +60,12 @@ impl LazyKey { #[inline] pub fn force(&'static self) -> Key { + if self.dtor.is_some() { + // Needs to be called on all threads where the key might have a non-null value! + // Otherwise, `run_dtors` might not be called on this thread. + guard::enable(); + } + match self.key.load(Acquire) { 0 => unsafe { self.init() }, key => key - 1, @@ -88,6 +94,8 @@ impl LazyKey { } unsafe { + // Add ourselves to the `DTORS` list, so that when `run_dtors` gets called, + // our dtor is invoked. register_dtor(self); } @@ -144,8 +152,6 @@ static DTORS: Atomic<*mut LazyKey> = AtomicPtr::new(ptr::null_mut()); /// Should only be called once per key, otherwise loops or breaks may occur in /// the linked list. unsafe fn register_dtor(key: &'static LazyKey) { - guard::enable(); - let this = <*const LazyKey>::cast_mut(key); // Use acquire ordering to pass along the changes done by the previously // registered keys when we store the new head with release ordering. From 7d5a8bbef69369b2b265c80f265db99b293983ef Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 5 Jun 2026 16:06:41 +0200 Subject: [PATCH 08/16] disable fiber test on Miri --- library/std/tests/thread_local/tests.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/library/std/tests/thread_local/tests.rs b/library/std/tests/thread_local/tests.rs index 7324b880af67d..633bab38eb4db 100644 --- a/library/std/tests/thread_local/tests.rs +++ b/library/std/tests/thread_local/tests.rs @@ -408,6 +408,7 @@ fn thread_current_in_dtor() { // https://github.com/rust-lang/rust/pull/148799#issuecomment-3731806901 #[cfg(target_os = "windows")] #[test] +#[cfg_attr(miri, ignore)] // Miri does not support fibers fn fiber_does_not_trigger_dtor() { use core::ffi::c_void; use std::ptr; From d04787075691bbb21447db2f9f7366a74dc8a5f1 Mon Sep 17 00:00:00 2001 From: Ohad Ravid Date: Fri, 5 Jun 2026 07:12:11 -0700 Subject: [PATCH 09/16] Test accessing a thread local in a different thread when loading a cdylib --- tests/run-make/dynamic-loading-cdylib/foo.rs | 30 +++++++++++++++---- .../dynamic-loading-cdylib/load_and_unload.rs | 9 ++++++ .../dynamic-loading-cdylib/output_unix.txt | 9 ++++-- .../dynamic-loading-cdylib/output_windows.txt | 9 ++++-- 4 files changed, 47 insertions(+), 10 deletions(-) diff --git a/tests/run-make/dynamic-loading-cdylib/foo.rs b/tests/run-make/dynamic-loading-cdylib/foo.rs index 15ce4825d1b33..5d29ca209edd8 100644 --- a/tests/run-make/dynamic-loading-cdylib/foo.rs +++ b/tests/run-make/dynamic-loading-cdylib/foo.rs @@ -6,18 +6,36 @@ pub extern "C" fn extern_fn_1(a: u32, b: u32) -> u32 { a + b } -struct NotifyOnDrop; +#[derive(Default)] +struct NotifyOnDrop { + last_result: std::cell::Cell, +} + +impl NotifyOnDrop { + fn save_and_print(&self, result: u32) { + self.last_result.set(result); + } +} impl Drop for NotifyOnDrop { fn drop(&mut self) { - println!("drop"); + println!("dropping, last result: {}", self.last_result.get()); } } +thread_local! { + static FOO: NotifyOnDrop = NotifyOnDrop::default(); +} + #[no_mangle] pub extern "C" fn extern_fn_2(a: u32, b: u32) -> u32 { - thread_local!(static FOO: NotifyOnDrop = NotifyOnDrop); - FOO.with(|_foo| {}); - println!("extern_fn_2"); - a * b + let result = a * b; + + FOO.with(|foo| { + foo.save_and_print(result); + }); + + println!("extern_fn_2({a}, {b})"); + + result } diff --git a/tests/run-make/dynamic-loading-cdylib/load_and_unload.rs b/tests/run-make/dynamic-loading-cdylib/load_and_unload.rs index 24409c16e8fea..ec3c558156c7b 100644 --- a/tests/run-make/dynamic-loading-cdylib/load_and_unload.rs +++ b/tests/run-make/dynamic-loading-cdylib/load_and_unload.rs @@ -101,6 +101,15 @@ fn main() { let result = unsafe { extern_fn_2(2, 3) }; println!("result of extern_fn_2(2, 3): {}", result); + println!("spawning thread"); + std::thread::spawn(move || { + let result = unsafe { extern_fn_2(4, 5) }; + println!("result of extern_fn_2(4, 5) in other thread: {}", result); + }) + .join() + .expect("Thread panicked"); + println!("thread joined"); + libloading::unload(foo); println!("unloaded library"); } diff --git a/tests/run-make/dynamic-loading-cdylib/output_unix.txt b/tests/run-make/dynamic-loading-cdylib/output_unix.txt index f79d5a2a63fa6..6e1736ca38d86 100644 --- a/tests/run-make/dynamic-loading-cdylib/output_unix.txt +++ b/tests/run-make/dynamic-loading-cdylib/output_unix.txt @@ -1,7 +1,12 @@ loaded library extern_fn_1 result of extern_fn_1(2, 3): 5 -extern_fn_2 +extern_fn_2(2, 3) result of extern_fn_2(2, 3): 6 +spawning thread +extern_fn_2(4, 5) +result of extern_fn_2(4, 5) in other thread: 20 +dropping, last result: 20 +thread joined unloaded library -drop +dropping, last result: 6 diff --git a/tests/run-make/dynamic-loading-cdylib/output_windows.txt b/tests/run-make/dynamic-loading-cdylib/output_windows.txt index 5ce77ad164359..e87baf4b52110 100644 --- a/tests/run-make/dynamic-loading-cdylib/output_windows.txt +++ b/tests/run-make/dynamic-loading-cdylib/output_windows.txt @@ -1,7 +1,12 @@ loaded library extern_fn_1 result of extern_fn_1(2, 3): 5 -extern_fn_2 +extern_fn_2(2, 3) result of extern_fn_2(2, 3): 6 -drop +spawning thread +extern_fn_2(4, 5) +result of extern_fn_2(4, 5) in other thread: 20 +dropping, last result: 20 +thread joined +dropping, last result: 6 unloaded library From f25f4b459c38678d56b1d0624b4856e9a35c891d Mon Sep 17 00:00:00 2001 From: Jieyou Xu Date: Fri, 5 Jun 2026 22:41:18 +0800 Subject: [PATCH 10/16] compiletest: inject `#![windows_subsystem = "windows"]` to debuginfo tests on Windows So that we don't get a bunch of console windows spawned by the debugger processes. --- src/tools/compiletest/src/runtest.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index 72817ad64521a..79e04b12fbed3 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -1645,6 +1645,11 @@ impl<'test> TestCx<'test> { if self.config.mode == TestMode::CodegenUnits { compiler.args(&["-Z", "human_readable_cgu_names"]); } + + if self.config.mode == TestMode::DebugInfo && cfg!(target_os = "windows") { + // Prevent debugger processes from creating new console windows. + compiler.args(&["-Z", r#"crate-attr=windows_subsystem="windows""#]); + } } if self.config.optimize_tests && compiler_kind == CompilerKind::Rustc { From 36742d886762365ad22152d8ddcf311ed58968cf Mon Sep 17 00:00:00 2001 From: Egor Ivanov Date: Fri, 5 Jun 2026 17:04:12 +0000 Subject: [PATCH 11/16] Convert `QueryRegionConstraint` into a struct --- .../src/type_check/constraint_conversion.rs | 8 ++-- .../src/infer/canonical/query_response.rs | 43 ++++++++++++------- compiler/rustc_middle/src/infer/canonical.rs | 9 ++-- .../src/solve/delegate.rs | 5 ++- .../src/traits/outlives_bounds.rs | 3 +- .../rustc_traits/src/coroutine_witnesses.rs | 3 +- 6 files changed, 45 insertions(+), 26 deletions(-) diff --git a/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs b/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs index bca2de041b657..4d93fa08fe0bd 100644 --- a/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs +++ b/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs @@ -1,7 +1,7 @@ use rustc_data_structures::fx::FxHashSet; use rustc_hir::def_id::LocalDefId; use rustc_infer::infer::SubregionOrigin; -use rustc_infer::infer::canonical::QueryRegionConstraints; +use rustc_infer::infer::canonical::{QueryRegionConstraint, QueryRegionConstraints}; use rustc_infer::infer::outlives::env::RegionBoundPairs; use rustc_infer::infer::outlives::obligations::{TypeOutlives, TypeOutlivesDelegate}; use rustc_infer::infer::region_constraints::{GenericKind, VerifyBound}; @@ -74,9 +74,9 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> { let assumptions = elaborate::elaborate_outlives_assumptions(self.infcx.tcx, assumptions.iter().copied()); - for &(constraint, constraint_category, _) in constraints { + for &QueryRegionConstraint { constraint, category, .. } in constraints { constraint.iter_outlives().for_each(|predicate| { - self.convert(predicate, constraint_category, &assumptions); + self.convert(predicate, category, &assumptions); }); } } @@ -296,7 +296,7 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> { // FIXME(higher_ranked_auto): What should we do with the assumptions here? if let Some(QueryRegionConstraints { constraints, assumptions: _ }) = constraints { next_outlives_predicates.extend(constraints.iter().flat_map( - |(constraint, category, _)| { + |QueryRegionConstraint { constraint, category, .. }| { constraint.iter_outlives().map(|outlives| (outlives, *category)) }, )); diff --git a/compiler/rustc_infer/src/infer/canonical/query_response.rs b/compiler/rustc_infer/src/infer/canonical/query_response.rs index 98763ff742f25..5641523c304c9 100644 --- a/compiler/rustc_infer/src/infer/canonical/query_response.rs +++ b/compiler/rustc_infer/src/infer/canonical/query_response.rs @@ -13,7 +13,7 @@ use std::iter; use rustc_index::{Idx, IndexVec}; use rustc_middle::arena::ArenaAllocatable; use rustc_middle::bug; -use rustc_middle::infer::canonical::CanonicalVarKind; +use rustc_middle::infer::canonical::{CanonicalVarKind, QueryRegionConstraint}; use rustc_middle::ty::{self, BoundVar, GenericArg, GenericArgKind, Ty, TyCtxt, TypeFoldable}; use tracing::{debug, instrument}; @@ -188,7 +188,9 @@ impl<'tcx> InferCtxt<'tcx> { let InferOk { value: result_args, obligations } = self.query_response_instantiation(cause, param_env, original_values, query_response)?; - for (constraint, _category, vis) in &query_response.value.region_constraints.constraints { + for QueryRegionConstraint { constraint, visible_for_leak_check: vis, .. } in + &query_response.value.region_constraints.constraints + { let constraint = instantiate_value(self.tcx, &result_args, *constraint); match constraint { ty::RegionConstraint::Outlives(predicate) => { @@ -285,11 +287,12 @@ impl<'tcx> InferCtxt<'tcx> { (GenericArgKind::Lifetime(v_o), GenericArgKind::Lifetime(v_r)) => { if v_o != v_r { - output_query_region_constraints.constraints.push(( - ty::RegionEqPredicate(v_o, v_r).into(), - constraint_category, - ty::VisibleForLeakCheck::Yes, - )); + let constraint = QueryRegionConstraint { + constraint: ty::RegionEqPredicate(v_o, v_r).into(), + category: constraint_category, + visible_for_leak_check: ty::VisibleForLeakCheck::Yes, + }; + output_query_region_constraints.constraints.push(constraint); } } @@ -321,7 +324,7 @@ impl<'tcx> InferCtxt<'tcx> { let r_c = instantiate_value(self.tcx, &result_args, r_c); // Screen out `'a: 'a` or `'a == 'a` cases. - if r_c.0.is_trivial() { None } else { Some(r_c) } + if r_c.constraint.is_trivial() { None } else { Some(r_c) } }), ); @@ -616,7 +619,7 @@ pub fn make_query_region_constraints<'tcx>( debug!(?constraints); - let constraints: Vec<_> = constraints + let constraints: Vec> = constraints .iter() .map(|(c, origin)| match c.kind { ConstraintKind::VarSubVar @@ -625,22 +628,30 @@ pub fn make_query_region_constraints<'tcx>( | ConstraintKind::RegSubReg => { // Swap regions because we are going from sub (<=) to outlives (>=). let constraint = ty::OutlivesPredicate(c.sup.into(), c.sub).into(); - (constraint, origin.to_constraint_category(), c.visible_for_leak_check) + QueryRegionConstraint { + constraint, + category: origin.to_constraint_category(), + visible_for_leak_check: c.visible_for_leak_check, + } } ConstraintKind::VarEqVar | ConstraintKind::VarEqReg | ConstraintKind::RegEqReg => { let constraint = ty::RegionEqPredicate(c.sup, c.sub).into(); - (constraint, origin.to_constraint_category(), c.visible_for_leak_check) + QueryRegionConstraint { + constraint, + category: origin.to_constraint_category(), + visible_for_leak_check: c.visible_for_leak_check, + } } }) .chain(outlives_obligations.into_iter().map( |TypeOutlivesConstraint { sub_region, sup_type, origin }| { - ( - ty::OutlivesPredicate(sup_type.into(), sub_region).into(), - origin.to_constraint_category(), + QueryRegionConstraint { + constraint: ty::OutlivesPredicate(sup_type.into(), sub_region).into(), + category: origin.to_constraint_category(), // We don't do leak checks for type outlives - ty::VisibleForLeakCheck::Unreachable, - ) + visible_for_leak_check: ty::VisibleForLeakCheck::Unreachable, + } }, )) .collect(); diff --git a/compiler/rustc_middle/src/infer/canonical.rs b/compiler/rustc_middle/src/infer/canonical.rs index ee8ab8a2ff931..8f182d096e759 100644 --- a/compiler/rustc_middle/src/infer/canonical.rs +++ b/compiler/rustc_middle/src/infer/canonical.rs @@ -135,9 +135,12 @@ impl<'tcx, R> QueryResponse<'tcx, R> { } } -// FIXME: Convert this into a struct -pub type QueryRegionConstraint<'tcx> = - (ty::RegionConstraint<'tcx>, ConstraintCategory<'tcx>, ty::VisibleForLeakCheck); +#[derive(Debug, StableHash, Hash, Eq, PartialEq, TypeVisitable, Clone, TypeFoldable, Copy)] +pub struct QueryRegionConstraint<'tcx> { + pub constraint: ty::RegionConstraint<'tcx>, + pub category: ConstraintCategory<'tcx>, + pub visible_for_leak_check: ty::VisibleForLeakCheck, +} #[derive(Default)] pub struct CanonicalParamEnvCache<'tcx> { diff --git a/compiler/rustc_trait_selection/src/solve/delegate.rs b/compiler/rustc_trait_selection/src/solve/delegate.rs index bd22ba6b6bf6d..7c2f5c7f170aa 100644 --- a/compiler/rustc_trait_selection/src/solve/delegate.rs +++ b/compiler/rustc_trait_selection/src/solve/delegate.rs @@ -7,6 +7,7 @@ use rustc_hir::def_id::{CRATE_DEF_ID, DefId}; use rustc_infer::infer::canonical::query_response::make_query_region_constraints; use rustc_infer::infer::canonical::{ Canonical, CanonicalExt as _, CanonicalQueryInput, CanonicalVarKind, CanonicalVarValues, + QueryRegionConstraint, }; use rustc_infer::infer::{InferCtxt, RegionVariableOrigin, SubregionOrigin, TyCtxtInferExt}; use rustc_infer::traits::solve::{FetchEligibleAssocItemResponse, Goal}; @@ -262,7 +263,9 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate< let mut seen = FxHashMap::default(); let mut constraints = vec![]; - for (outlives, _, vis) in region_constraints.constraints { + for QueryRegionConstraint { constraint: outlives, visible_for_leak_check: vis, .. } in + region_constraints.constraints + { match seen.entry(outlives) { Entry::Occupied(occupied) => { let idx = occupied.get(); diff --git a/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs b/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs index 8be26fed0ca42..a171a0de9dd79 100644 --- a/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs +++ b/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs @@ -1,4 +1,5 @@ use rustc_infer::infer::InferOk; +use rustc_infer::infer::canonical::QueryRegionConstraint; use rustc_infer::infer::resolve::OpportunisticRegionResolver; use rustc_infer::traits::query::type_op::ImpliedOutlivesBounds; use rustc_macros::extension; @@ -83,7 +84,7 @@ fn implied_outlives_bounds<'a, 'tcx>( // outlives bound required proving some higher-ranked coroutine obl. let QueryRegionConstraints { constraints, assumptions: _ } = constraints; let cause = ObligationCause::misc(span, body_id); - for &(constraint, _, vis) in &constraints { + for &QueryRegionConstraint { constraint, visible_for_leak_check: vis, .. } in &constraints { match constraint { ty::RegionConstraint::Outlives(predicate) => { infcx.register_outlives_constraint(predicate, vis, &cause) diff --git a/compiler/rustc_traits/src/coroutine_witnesses.rs b/compiler/rustc_traits/src/coroutine_witnesses.rs index 83a77f17b28ce..7fe8303b7459b 100644 --- a/compiler/rustc_traits/src/coroutine_witnesses.rs +++ b/compiler/rustc_traits/src/coroutine_witnesses.rs @@ -1,4 +1,5 @@ use rustc_infer::infer::TyCtxtInferExt; +use rustc_infer::infer::canonical::QueryRegionConstraint; use rustc_infer::infer::canonical::query_response::make_query_region_constraints; use rustc_infer::infer::resolve::OpportunisticRegionResolver; use rustc_infer::traits::{Obligation, ObligationCause}; @@ -80,7 +81,7 @@ fn compute_assumptions<'tcx>( tcx.mk_outlives_from_iter( constraints .into_iter() - .flat_map(|(constraint, _, _)| constraint.iter_outlives()) + .flat_map(|QueryRegionConstraint { constraint, .. }| constraint.iter_outlives()) // FIXME(higher_ranked_auto): We probably should deeply resolve these before // filtering out infers which only correspond to unconstrained infer regions // which we can sometimes get. From 4e59f4c54b480d98dbe3eb37f12feb4d46be3d55 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Wed, 27 May 2026 12:22:03 +0200 Subject: [PATCH 12/16] add `extern "tail"` calling convention --- compiler/rustc_abi/src/canon_abi.rs | 7 +- compiler/rustc_abi/src/extern_abi.rs | 9 ++- compiler/rustc_ast_lowering/src/stability.rs | 3 + .../rustc_ast_passes/src/ast_validation.rs | 1 + .../rustc_codegen_cranelift/src/abi/mod.rs | 7 +- compiler/rustc_codegen_gcc/src/abi.rs | 29 ++++--- compiler/rustc_codegen_gcc/src/context.rs | 8 +- compiler/rustc_codegen_llvm/src/abi.rs | 4 + compiler/rustc_feature/src/unstable.rs | 2 + compiler/rustc_hir_typeck/src/callee.rs | 1 + compiler/rustc_middle/src/ty/layout.rs | 4 +- compiler/rustc_public/src/abi.rs | 1 + compiler/rustc_public/src/ty.rs | 1 + .../src/unstable/convert/internal.rs | 1 + .../src/unstable/convert/stable/abi.rs | 1 + .../src/unstable/convert/stable/ty.rs | 1 + compiler/rustc_span/src/symbol.rs | 1 + compiler/rustc_target/src/spec/abi_map.rs | 1 + tests/codegen-llvm/preserve-none.rs | 2 +- tests/codegen-llvm/tailcc.rs | 28 +++++++ tests/ui/abi/rust-preserve-none-cc.rs | 29 ++++--- tests/ui/abi/rust-tail-cc.rs | 80 +++++++++++++++++++ .../feature-gate-rust-tail-cc.rs | 21 +++++ .../feature-gate-rust-tail-cc.stderr | 73 +++++++++++++++++ .../print-calling-conventions.stdout | 1 + 25 files changed, 286 insertions(+), 30 deletions(-) create mode 100644 tests/codegen-llvm/tailcc.rs create mode 100644 tests/ui/abi/rust-tail-cc.rs create mode 100644 tests/ui/feature-gates/feature-gate-rust-tail-cc.rs create mode 100644 tests/ui/feature-gates/feature-gate-rust-tail-cc.stderr diff --git a/compiler/rustc_abi/src/canon_abi.rs b/compiler/rustc_abi/src/canon_abi.rs index 316cb05ec1806..6b4963ae92461 100644 --- a/compiler/rustc_abi/src/canon_abi.rs +++ b/compiler/rustc_abi/src/canon_abi.rs @@ -28,6 +28,7 @@ pub enum CanonAbi { Rust, RustCold, RustPreserveNone, + RustTail, /// An ABI that rustc does not know how to call or define. Custom, @@ -59,7 +60,10 @@ pub enum CanonAbi { impl CanonAbi { pub fn is_rustic_abi(self) -> bool { match self { - CanonAbi::Rust | CanonAbi::RustCold | CanonAbi::RustPreserveNone => true, + CanonAbi::Rust + | CanonAbi::RustCold + | CanonAbi::RustPreserveNone + | CanonAbi::RustTail => true, CanonAbi::C | CanonAbi::Custom | CanonAbi::Swift @@ -81,6 +85,7 @@ impl fmt::Display for CanonAbi { CanonAbi::Rust => ExternAbi::Rust, CanonAbi::RustCold => ExternAbi::RustCold, CanonAbi::RustPreserveNone => ExternAbi::RustPreserveNone, + CanonAbi::RustTail => ExternAbi::RustTail, CanonAbi::Custom => ExternAbi::Custom, CanonAbi::Swift => ExternAbi::Swift, CanonAbi::Arm(arm_call) => match arm_call { diff --git a/compiler/rustc_abi/src/extern_abi.rs b/compiler/rustc_abi/src/extern_abi.rs index 3def8a8ccf0ba..f30b923eeed17 100644 --- a/compiler/rustc_abi/src/extern_abi.rs +++ b/compiler/rustc_abi/src/extern_abi.rs @@ -49,6 +49,11 @@ pub enum ExternAbi { /// forcing callers to save all registers. RustPreserveNone, + /// Ensures that calls in tail position can always be optimized into a jump. + /// + /// This ABI is not stable, and relies on LLVM implementation details. + RustTail, + /// Unstable impl detail that directly uses Rust types to describe the ABI to LLVM. /// Even normally-compatible Rust types can become ABI-incompatible with this ABI! Unadjusted, @@ -205,6 +210,7 @@ abi_impls! { System { unwind: true } =><= "system-unwind", SysV64 { unwind: false } =><= "sysv64", SysV64 { unwind: true } =><= "sysv64-unwind", + RustTail =><= "tail", Thiscall { unwind: false } =><= "thiscall", Thiscall { unwind: true } =><= "thiscall-unwind", Unadjusted =><= "unadjusted", @@ -280,7 +286,7 @@ impl ExternAbi { /// - are subject to change between compiler versions pub fn is_rustic_abi(self) -> bool { use ExternAbi::*; - matches!(self, Rust | RustCall | RustCold | RustPreserveNone) + matches!(self, Rust | RustCall | RustCold | RustPreserveNone | RustTail) } /// Returns whether the ABI supports C variadics. This only controls whether we allow *imports* @@ -354,6 +360,7 @@ impl ExternAbi { | Self::SysV64 { .. } | Self::Win64 { .. } | Self::RustPreserveNone + | Self::RustTail | Self::Swift => true, } } diff --git a/compiler/rustc_ast_lowering/src/stability.rs b/compiler/rustc_ast_lowering/src/stability.rs index 00b6a353d93f6..b58087e4aa3a6 100644 --- a/compiler/rustc_ast_lowering/src/stability.rs +++ b/compiler/rustc_ast_lowering/src/stability.rs @@ -100,6 +100,9 @@ pub fn extern_abi_stability(abi: ExternAbi) -> Result<(), UnstableAbi> { feature: sym::rust_preserve_none_cc, explain: GateReason::Experimental, }), + ExternAbi::RustTail => { + Err(UnstableAbi { abi, feature: sym::rust_tail_cc, explain: GateReason::Experimental }) + } ExternAbi::RustInvalid => { Err(UnstableAbi { abi, feature: sym::rustc_attrs, explain: GateReason::ImplDetail }) } diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index afaee6e542082..05f0990bf8512 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -424,6 +424,7 @@ impl<'a> AstValidator<'a> { | CanonAbi::Rust | CanonAbi::RustCold | CanonAbi::RustPreserveNone + | CanonAbi::RustTail | CanonAbi::Swift | CanonAbi::Arm(_) | CanonAbi::X86(_) => { /* nothing to check */ } diff --git a/compiler/rustc_codegen_cranelift/src/abi/mod.rs b/compiler/rustc_codegen_cranelift/src/abi/mod.rs index a8b179f169bf6..9644932ae1055 100644 --- a/compiler/rustc_codegen_cranelift/src/abi/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/abi/mod.rs @@ -55,8 +55,9 @@ pub(crate) fn conv_to_call_conv( match c { CanonAbi::Rust | CanonAbi::RustCold | CanonAbi::C => default_call_conv, - // Cranelift doesn't currently have anything for this. - CanonAbi::RustPreserveNone => default_call_conv, + CanonAbi::RustPreserveNone | CanonAbi::RustTail => { + sess.dcx().fatal(format!("call conv {c:?} is LLVM-specific")) + } // Functions with this calling convention can only be called from assembly, but it is // possible to declare an `extern "custom"` block, so the backend still needs a calling @@ -71,7 +72,7 @@ pub(crate) fn conv_to_call_conv( }, CanonAbi::Interrupt(_) | CanonAbi::Arm(_) | CanonAbi::Swift => { - sess.dcx().fatal("call conv {c:?} is not yet implemented") + sess.dcx().fatal(format!("call conv {c:?} is not yet implemented")) } CanonAbi::GpuKernel => { unreachable!("tried to use {c:?} call conv which only exists on an unsupported target") diff --git a/compiler/rustc_codegen_gcc/src/abi.rs b/compiler/rustc_codegen_gcc/src/abi.rs index fb243ff842c83..1b7bb8c907735 100644 --- a/compiler/rustc_codegen_gcc/src/abi.rs +++ b/compiler/rustc_codegen_gcc/src/abi.rs @@ -10,7 +10,7 @@ use rustc_middle::bug; use rustc_middle::ty::Ty; use rustc_middle::ty::layout::LayoutOf; #[cfg(feature = "master")] -use rustc_session::config; +use rustc_session::{Session, config}; use rustc_target::callconv::{ArgAttributes, CastTarget, FnAbi, PassMode}; #[cfg(feature = "master")] use rustc_target::spec::Arch; @@ -230,32 +230,43 @@ impl<'gcc, 'tcx> FnAbiGccExt<'gcc, 'tcx> for FnAbi<'tcx, Ty<'tcx>> { #[cfg(feature = "master")] fn gcc_cconv(&self, cx: &CodegenCx<'gcc, 'tcx>) -> Option> { - conv_to_fn_attribute(self.conv, &cx.tcx.sess.target.arch) + conv_to_fn_attribute(cx.sess(), self.conv) } } #[cfg(feature = "master")] -pub fn conv_to_fn_attribute<'gcc>(conv: CanonAbi, arch: &Arch) -> Option> { +pub fn conv_to_fn_attribute<'gcc>(sess: &Session, conv: CanonAbi) -> Option> { let attribute = match conv { CanonAbi::C | CanonAbi::Rust => return None, - // gcc/gccjit does not have anything for this. - CanonAbi::RustPreserveNone => return None, + CanonAbi::RustPreserveNone => { + // This calling convention is LLVM-specific and unspecified. + sess.dcx() + .fatal("gcc/gccjit backend does not support RustPreserveNone calling convention") + } + CanonAbi::RustTail => { + // This calling convention is LLVM-specific and unspecified. + sess.dcx().fatal("gcc/gccjit backend does not support RustTail calling convention") + } CanonAbi::RustCold => FnAttribute::Cold, // Functions with this calling convention can only be called from assembly, but it is // possible to declare an `extern "custom"` block, so the backend still needs a calling // convention for declaring foreign functions. CanonAbi::Custom => return None, - // gcc/gccjit does not have anything for Swift's calling convention. - CanonAbi::Swift => panic!("gcc/gccjit backend does not support Swift calling convention"), + CanonAbi::Swift => { + // gcc/gccjit does not have anything for Swift's calling convention. + sess.dcx().fatal("gcc/gccjit backend does not support Swift calling convention") + } CanonAbi::Arm(arm_call) => match arm_call { ArmCall::CCmseNonSecureCall => FnAttribute::ArmCmseNonsecureCall, ArmCall::CCmseNonSecureEntry => FnAttribute::ArmCmseNonsecureEntry, ArmCall::Aapcs => FnAttribute::ArmPcs("aapcs"), }, - CanonAbi::GpuKernel => match arch { + CanonAbi::GpuKernel => match &sess.target.arch { &Arch::AmdGpu => FnAttribute::GcnAmdGpuHsaKernel, &Arch::Nvptx64 => FnAttribute::NvptxKernel, - arch => panic!("Arch {arch} does not support GpuKernel calling convention"), + arch => sess + .dcx() + .fatal(format!("Arch {arch} does not support GpuKernel calling convention")), }, // FIXME(antoyo): check if those AVR attributes are mapped correctly. CanonAbi::Interrupt(interrupt_kind) => match interrupt_kind { diff --git a/compiler/rustc_codegen_gcc/src/context.rs b/compiler/rustc_codegen_gcc/src/context.rs index ed313859aeafa..ea71546ea1c0e 100644 --- a/compiler/rustc_codegen_gcc/src/context.rs +++ b/compiler/rustc_codegen_gcc/src/context.rs @@ -486,10 +486,10 @@ impl<'gcc, 'tcx> MiscCodegenMethods<'tcx> for CodegenCx<'gcc, 'tcx> { fn declare_c_main(&self, fn_type: Self::Type) -> Option { let entry_name = self.sess().target.entry_name.as_ref(); if !self.functions.borrow().contains_key(entry_name) { - #[cfg(feature = "master")] - let conv = conv_to_fn_attribute(self.sess().target.entry_abi, &self.sess().target.arch); - #[cfg(not(feature = "master"))] - let conv = None; + let conv = cfg_select! { + feature = "master" => conv_to_fn_attribute(self.sess(), self.sess().target.entry_abi), + _ => None, + }; Some(self.declare_entry_fn(entry_name, fn_type, conv)) } else { // If the symbol already exists, it is an error: for example, the user wrote diff --git a/compiler/rustc_codegen_llvm/src/abi.rs b/compiler/rustc_codegen_llvm/src/abi.rs index 3f6010e55928d..4d18818bbe7bd 100644 --- a/compiler/rustc_codegen_llvm/src/abi.rs +++ b/compiler/rustc_codegen_llvm/src/abi.rs @@ -723,6 +723,10 @@ pub(crate) fn to_llvm_calling_convention(sess: &Session, abi: CanonAbi) -> llvm: Arch::X86_64 | Arch::AArch64 => llvm::PreserveNone, _ => llvm::CCallConv, }, + CanonAbi::RustTail => match &sess.target.arch { + Arch::X86 | Arch::X86_64 | Arch::AArch64 => llvm::Tail, + _ => sess.dcx().fatal("extern \"tail\" is only supported on x86_64 and aarch64"), + }, // Functions with this calling convention can only be called from assembly, but it is // possible to declare an `extern "custom"` block, so the backend still needs a calling // convention for declaring foreign functions. diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index be98544d89ecd..693326e198721 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -709,6 +709,8 @@ declare_features! ( (unstable, rust_cold_cc, "1.63.0", Some(97544)), /// Allows `extern "rust-preserve-none"`. (unstable, rust_preserve_none_cc, "1.95.0", Some(151401)), + /// Allows `extern "tail"`. + (unstable, rust_tail_cc, "CURRENT_RUSTC_VERSION", Some(157427)), /// Target features on s390x. (unstable, s390x_target_feature, "1.82.0", Some(150259)), /// Allows the use of the `sanitize` attribute. diff --git a/compiler/rustc_hir_typeck/src/callee.rs b/compiler/rustc_hir_typeck/src/callee.rs index 7d687099f9715..e70936972e7c7 100644 --- a/compiler/rustc_hir_typeck/src/callee.rs +++ b/compiler/rustc_hir_typeck/src/callee.rs @@ -205,6 +205,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { | CanonAbi::Rust | CanonAbi::RustCold | CanonAbi::RustPreserveNone + | CanonAbi::RustTail | CanonAbi::Swift | CanonAbi::Arm(_) | CanonAbi::X86(_) => {} diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index 682bec55b4a38..a0dd7b5c5cb5e 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -1305,7 +1305,9 @@ pub fn fn_can_unwind(tcx: TyCtxt<'_>, fn_def_id: Option, abi: ExternAbi) | RustInvalid | Swift | Unadjusted => false, - Rust | RustCall | RustCold | RustPreserveNone => tcx.sess.panic_strategy().unwinds(), + Rust | RustCall | RustCold | RustPreserveNone | RustTail => { + tcx.sess.panic_strategy().unwinds() + } } } diff --git a/compiler/rustc_public/src/abi.rs b/compiler/rustc_public/src/abi.rs index 1227fe23713cb..cde885def8a41 100644 --- a/compiler/rustc_public/src/abi.rs +++ b/compiler/rustc_public/src/abi.rs @@ -455,6 +455,7 @@ pub enum CallConvention { PreserveMost, PreserveAll, PreserveNone, + Tail, Custom, diff --git a/compiler/rustc_public/src/ty.rs b/compiler/rustc_public/src/ty.rs index f71ca7213e9ed..9b9a480ab5f82 100644 --- a/compiler/rustc_public/src/ty.rs +++ b/compiler/rustc_public/src/ty.rs @@ -1167,6 +1167,7 @@ pub enum Abi { RiscvInterruptM, RiscvInterruptS, RustPreserveNone, + RustTail, RustInvalid, Custom, Swift, diff --git a/compiler/rustc_public/src/unstable/convert/internal.rs b/compiler/rustc_public/src/unstable/convert/internal.rs index 37f00da9f7103..f18fe84053b32 100644 --- a/compiler/rustc_public/src/unstable/convert/internal.rs +++ b/compiler/rustc_public/src/unstable/convert/internal.rs @@ -618,6 +618,7 @@ impl RustcInternal for Abi { Abi::RiscvInterruptM => rustc_abi::ExternAbi::RiscvInterruptM, Abi::RiscvInterruptS => rustc_abi::ExternAbi::RiscvInterruptS, Abi::RustPreserveNone => rustc_abi::ExternAbi::RustPreserveNone, + Abi::RustTail => rustc_abi::ExternAbi::RustTail, Abi::Custom => rustc_abi::ExternAbi::Custom, Abi::Swift => rustc_abi::ExternAbi::Swift, } diff --git a/compiler/rustc_public/src/unstable/convert/stable/abi.rs b/compiler/rustc_public/src/unstable/convert/stable/abi.rs index f42399241cd28..7e0b04f8a7f61 100644 --- a/compiler/rustc_public/src/unstable/convert/stable/abi.rs +++ b/compiler/rustc_public/src/unstable/convert/stable/abi.rs @@ -125,6 +125,7 @@ impl<'tcx> Stable<'tcx> for CanonAbi { CanonAbi::Rust => CallConvention::Rust, CanonAbi::RustCold => CallConvention::Cold, CanonAbi::RustPreserveNone => CallConvention::PreserveNone, + CanonAbi::RustTail => CallConvention::Tail, CanonAbi::Custom => CallConvention::Custom, CanonAbi::Swift => CallConvention::Swift, CanonAbi::Arm(arm_call) => match arm_call { diff --git a/compiler/rustc_public/src/unstable/convert/stable/ty.rs b/compiler/rustc_public/src/unstable/convert/stable/ty.rs index 925d4a5b40b20..8469c500b24c8 100644 --- a/compiler/rustc_public/src/unstable/convert/stable/ty.rs +++ b/compiler/rustc_public/src/unstable/convert/stable/ty.rs @@ -1046,6 +1046,7 @@ impl<'tcx> Stable<'tcx> for rustc_abi::ExternAbi { ExternAbi::Unadjusted => Abi::Unadjusted, ExternAbi::RustCold => Abi::RustCold, ExternAbi::RustPreserveNone => Abi::RustPreserveNone, + ExternAbi::RustTail => Abi::RustTail, ExternAbi::RustInvalid => Abi::RustInvalid, ExternAbi::RiscvInterruptM => Abi::RiscvInterruptM, ExternAbi::RiscvInterruptS => Abi::RiscvInterruptS, diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index fb433aef68cf8..9313e869278db 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1703,6 +1703,7 @@ symbols! { rust_logo, rust_out, rust_preserve_none_cc, + rust_tail_cc, rustc, rustc_abi, // FIXME(#82232, #143834): temporary name to mitigate `#[align]` nameres ambiguity diff --git a/compiler/rustc_target/src/spec/abi_map.rs b/compiler/rustc_target/src/spec/abi_map.rs index 8c56dcb899718..f7a7ae1eb7555 100644 --- a/compiler/rustc_target/src/spec/abi_map.rs +++ b/compiler/rustc_target/src/spec/abi_map.rs @@ -100,6 +100,7 @@ impl AbiMap { (ExternAbi::RustCold, _) if self.os == OsKind::Windows => CanonAbi::Rust, (ExternAbi::RustCold, _) => CanonAbi::RustCold, (ExternAbi::RustPreserveNone, _) => CanonAbi::RustPreserveNone, + (ExternAbi::RustTail, _) => CanonAbi::RustTail, (ExternAbi::Custom, _) => CanonAbi::Custom, diff --git a/tests/codegen-llvm/preserve-none.rs b/tests/codegen-llvm/preserve-none.rs index b45e49a466bf3..b8c8db25f272c 100644 --- a/tests/codegen-llvm/preserve-none.rs +++ b/tests/codegen-llvm/preserve-none.rs @@ -19,7 +19,7 @@ extern crate minicore; // UNSUPPORTED: define{{( dso_local)?}} void @peach(i16 #[no_mangle] #[inline(never)] -pub extern "rust-preserve-none" fn peach(x: u16) { +pub extern "rust-preserve-none" fn peach(_: u16) { loop {} } diff --git a/tests/codegen-llvm/tailcc.rs b/tests/codegen-llvm/tailcc.rs new file mode 100644 index 0000000000000..15ae69cf0498c --- /dev/null +++ b/tests/codegen-llvm/tailcc.rs @@ -0,0 +1,28 @@ +//@ add-minicore +//@ revisions: I586 X86_64 AARCH64 +//@ [I586] compile-flags: -C no-prepopulate-passes --target=i586-unknown-linux-gnu +//@ [I586] needs-llvm-components: x86 +//@ [X86_64] compile-flags: -C no-prepopulate-passes --target=x86_64-unknown-linux-gnu +//@ [X86_64] needs-llvm-components: x86 +//@ [AARCH64] compile-flags: -C no-prepopulate-passes --target=aarch64-unknown-linux-gnu +//@ [AARCH64] needs-llvm-components: aarch64 + +#![crate_type = "lib"] +#![feature(no_core, rust_tail_cc, explicit_tail_calls)] +#![no_core] + +extern crate minicore; + +// CHECK: define{{( dso_local)?}} tailcc void @peach(i16 +#[no_mangle] +#[inline(never)] +pub extern "tail" fn peach(_: u16) { + loop {} +} + +// CHECK: call tailcc void @peach(i16 +pub fn quince(x: u16) { + if let 12345u16 = x { + peach(54321); + } +} diff --git a/tests/ui/abi/rust-preserve-none-cc.rs b/tests/ui/abi/rust-preserve-none-cc.rs index deacb926971c8..20895e1583f4e 100644 --- a/tests/ui/abi/rust-preserve-none-cc.rs +++ b/tests/ui/abi/rust-preserve-none-cc.rs @@ -1,5 +1,6 @@ //@ run-pass //@ needs-unwind +//@ ignore-backends: gcc #![feature(rust_preserve_none_cc)] @@ -17,9 +18,7 @@ extern "rust-preserve-none" fn oven_explosion() { #[inline(never)] fn bite_into(yummy: u64) -> u64 { - let did_it_actually = std::panic::catch_unwind(move || { - oven_explosion() - }); + let did_it_actually = std::panic::catch_unwind(move || oven_explosion()); assert!(did_it_actually.is_err()); yummy - 25 } @@ -52,16 +51,26 @@ extern "rust-preserve-none" fn lotsa_apples( and_a.rome.iter().sum(), fuji + ambrosia, cosmic_crisp - honeycrisp, - bite_into(and_a.golden_delicious) + bite_into(and_a.golden_delicious), ) } fn main() { - let pie = lotsa_apples(220, 140, 210.54201234, &[180, 210], (), CrateOf { - mcintosh: 150.0, - golden_delicious: 185, - jonagold: None, - rome: [180, 182, 184, 186, 188, 190, 192, 194, 196, 198, 200, 202] - }, 270, 193.1, &[]); + let pie = lotsa_apples( + 220, + 140, + 210.54201234, + &[180, 210], + (), + CrateOf { + mcintosh: 150.0, + golden_delicious: 185, + jonagold: None, + rome: [180, 182, 184, 186, 188, 190, 192, 194, 196, 198, 200, 202], + }, + 270, + 193.1, + &[], + ); assert_eq!(pie, (2292, 403.64201234, 50, 160)); } diff --git a/tests/ui/abi/rust-tail-cc.rs b/tests/ui/abi/rust-tail-cc.rs new file mode 100644 index 0000000000000..aba5d0d695647 --- /dev/null +++ b/tests/ui/abi/rust-tail-cc.rs @@ -0,0 +1,80 @@ +//@ revisions: aarch64 x32 x64 +//@ run-pass +//@[aarch64] only-aarch64 +//@[x32] only-x86 +//@[x64] only-x86_64 +//@ needs-unwind +//@ ignore-backends: gcc + +#![feature(rust_tail_cc)] + +struct CrateOf<'a> { + mcintosh: f64, + golden_delicious: u64, + jonagold: Option<&'a u64>, + rome: [u64; 12], +} + +#[inline(never)] +extern "tail" fn oven_explosion() { + panic!("bad time"); +} + +#[inline(never)] +fn bite_into(yummy: u64) -> u64 { + let did_it_actually = std::panic::catch_unwind(move || oven_explosion()); + assert!(did_it_actually.is_err()); + yummy - 25 +} + +#[inline(never)] +extern "tail" fn lotsa_apples( + honeycrisp: u64, + gala: u32, + fuji: f64, + granny_smith: &[u64], + pink_lady: (), + and_a: CrateOf<'static>, + cosmic_crisp: u64, + ambrosia: f64, + winesap: &[u64], +) -> (u64, f64, u64, u64) { + assert_eq!(honeycrisp, 220); + assert_eq!(gala, 140); + assert_eq!(fuji, 210.54201234); + assert_eq!(granny_smith, &[180, 210]); + assert_eq!(pink_lady, ()); + assert_eq!(and_a.mcintosh, 150.0); + assert_eq!(and_a.golden_delicious, 185); + assert_eq!(and_a.jonagold, None); // my scales can't weight these gargantuans. + assert_eq!(and_a.rome, [180, 182, 184, 186, 188, 190, 192, 194, 196, 198, 200, 202]); + assert_eq!(cosmic_crisp, 270); + assert_eq!(ambrosia, 193.1); + assert_eq!(winesap, &[]); + ( + and_a.rome.iter().sum(), + fuji + ambrosia, + cosmic_crisp - honeycrisp, + bite_into(and_a.golden_delicious), + ) +} + +fn main() { + let pie = lotsa_apples( + 220, + 140, + 210.54201234, + &[180, 210], + (), + CrateOf { + mcintosh: 150.0, + golden_delicious: 185, + jonagold: None, + rome: [180, 182, 184, 186, 188, 190, 192, 194, 196, 198, 200, 202], + }, + 270, + 193.1, + &[], + ); + assert_eq!(pie, (2292, 403.64201234, 50, 160)); +} diff --git a/tests/ui/feature-gates/feature-gate-rust-tail-cc.rs b/tests/ui/feature-gates/feature-gate-rust-tail-cc.rs new file mode 100644 index 0000000000000..b2bbf606cc7a8 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-rust-tail-cc.rs @@ -0,0 +1,21 @@ +#![crate_type = "lib"] + +extern "tail" fn apple() {} //~ ERROR "tail" ABI is experimental + +trait T { + extern "tail" fn banana(); //~ ERROR "tail" ABI is experimental + extern "tail" fn citrus() {} //~ ERROR "tail" ABI is experimental +} + +struct S; +impl T for S { + extern "tail" fn banana() {} //~ ERROR "tail" ABI is experimental +} + +impl S { + extern "tail" fn durian() {} //~ ERROR "tail" ABI is experimental +} + +type Fig = extern "tail" fn(); //~ ERROR "tail" ABI is experimental + +extern "tail" {} //~ ERROR "tail" ABI is experimental diff --git a/tests/ui/feature-gates/feature-gate-rust-tail-cc.stderr b/tests/ui/feature-gates/feature-gate-rust-tail-cc.stderr new file mode 100644 index 0000000000000..e588569e6bd79 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-rust-tail-cc.stderr @@ -0,0 +1,73 @@ +error[E0658]: the extern "tail" ABI is experimental and subject to change + --> $DIR/feature-gate-rust-tail-cc.rs:3:8 + | +LL | extern "tail" fn apple() {} + | ^^^^^^ + | + = note: see issue #157427 for more information + = help: add `#![feature(rust_tail_cc)]` 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]: the extern "tail" ABI is experimental and subject to change + --> $DIR/feature-gate-rust-tail-cc.rs:6:12 + | +LL | extern "tail" fn banana(); + | ^^^^^^ + | + = note: see issue #157427 for more information + = help: add `#![feature(rust_tail_cc)]` 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]: the extern "tail" ABI is experimental and subject to change + --> $DIR/feature-gate-rust-tail-cc.rs:7:12 + | +LL | extern "tail" fn citrus() {} + | ^^^^^^ + | + = note: see issue #157427 for more information + = help: add `#![feature(rust_tail_cc)]` 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]: the extern "tail" ABI is experimental and subject to change + --> $DIR/feature-gate-rust-tail-cc.rs:12:12 + | +LL | extern "tail" fn banana() {} + | ^^^^^^ + | + = note: see issue #157427 for more information + = help: add `#![feature(rust_tail_cc)]` 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]: the extern "tail" ABI is experimental and subject to change + --> $DIR/feature-gate-rust-tail-cc.rs:16:12 + | +LL | extern "tail" fn durian() {} + | ^^^^^^ + | + = note: see issue #157427 for more information + = help: add `#![feature(rust_tail_cc)]` 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]: the extern "tail" ABI is experimental and subject to change + --> $DIR/feature-gate-rust-tail-cc.rs:19:19 + | +LL | type Fig = extern "tail" fn(); + | ^^^^^^ + | + = note: see issue #157427 for more information + = help: add `#![feature(rust_tail_cc)]` 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]: the extern "tail" ABI is experimental and subject to change + --> $DIR/feature-gate-rust-tail-cc.rs:21:8 + | +LL | extern "tail" {} + | ^^^^^^ + | + = note: see issue #157427 for more information + = help: add `#![feature(rust_tail_cc)]` 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 7 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/print-request/print-calling-conventions.stdout b/tests/ui/print-request/print-calling-conventions.stdout index 506bbea23e171..af4f491fc069e 100644 --- a/tests/ui/print-request/print-calling-conventions.stdout +++ b/tests/ui/print-request/print-calling-conventions.stdout @@ -29,6 +29,7 @@ system system-unwind sysv64 sysv64-unwind +tail thiscall thiscall-unwind unadjusted From b58125b1669b9e152be55188b12862e1aa3fc9e6 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 5 Jun 2026 23:59:58 +0200 Subject: [PATCH 13/16] remove solaris implementation for File::lock, it has the wrong semantics --- library/std/src/sys/fs/unix.rs | 66 --------------------------- src/tools/miri/tests/pass/shims/fs.rs | 3 +- 2 files changed, 1 insertion(+), 68 deletions(-) diff --git a/library/std/src/sys/fs/unix.rs b/library/std/src/sys/fs/unix.rs index eefec06f68f54..957c286ee472a 100644 --- a/library/std/src/sys/fs/unix.rs +++ b/library/std/src/sys/fs/unix.rs @@ -1459,15 +1459,6 @@ impl File { return Ok(()); } - #[cfg(target_os = "solaris")] - pub fn lock(&self) -> io::Result<()> { - let mut flock: libc::flock = unsafe { mem::zeroed() }; - flock.l_type = libc::F_WRLCK as libc::c_short; - flock.l_whence = libc::SEEK_SET as libc::c_short; - cvt(unsafe { libc::fcntl(self.as_raw_fd(), libc::F_SETLKW, &flock) })?; - Ok(()) - } - #[cfg(not(any( target_os = "freebsd", target_os = "fuchsia", @@ -1476,7 +1467,6 @@ impl File { target_os = "netbsd", target_os = "openbsd", target_os = "cygwin", - target_os = "solaris", target_os = "illumos", target_os = "aix", target_os = "android", @@ -1504,15 +1494,6 @@ impl File { return Ok(()); } - #[cfg(target_os = "solaris")] - pub fn lock_shared(&self) -> io::Result<()> { - let mut flock: libc::flock = unsafe { mem::zeroed() }; - flock.l_type = libc::F_RDLCK as libc::c_short; - flock.l_whence = libc::SEEK_SET as libc::c_short; - cvt(unsafe { libc::fcntl(self.as_raw_fd(), libc::F_SETLKW, &flock) })?; - Ok(()) - } - #[cfg(not(any( target_os = "freebsd", target_os = "fuchsia", @@ -1521,7 +1502,6 @@ impl File { target_os = "netbsd", target_os = "openbsd", target_os = "cygwin", - target_os = "solaris", target_os = "illumos", target_os = "aix", target_os = "android", @@ -1557,23 +1537,6 @@ impl File { } } - #[cfg(target_os = "solaris")] - pub fn try_lock(&self) -> Result<(), TryLockError> { - let mut flock: libc::flock = unsafe { mem::zeroed() }; - flock.l_type = libc::F_WRLCK as libc::c_short; - flock.l_whence = libc::SEEK_SET as libc::c_short; - let result = cvt(unsafe { libc::fcntl(self.as_raw_fd(), libc::F_SETLK, &flock) }); - if let Err(err) = result { - if err.kind() == io::ErrorKind::WouldBlock { - Err(TryLockError::WouldBlock) - } else { - Err(TryLockError::Error(err)) - } - } else { - Ok(()) - } - } - #[cfg(not(any( target_os = "freebsd", target_os = "fuchsia", @@ -1582,7 +1545,6 @@ impl File { target_os = "netbsd", target_os = "openbsd", target_os = "cygwin", - target_os = "solaris", target_os = "illumos", target_os = "aix", target_os = "android", @@ -1621,23 +1583,6 @@ impl File { } } - #[cfg(target_os = "solaris")] - pub fn try_lock_shared(&self) -> Result<(), TryLockError> { - let mut flock: libc::flock = unsafe { mem::zeroed() }; - flock.l_type = libc::F_RDLCK as libc::c_short; - flock.l_whence = libc::SEEK_SET as libc::c_short; - let result = cvt(unsafe { libc::fcntl(self.as_raw_fd(), libc::F_SETLK, &flock) }); - if let Err(err) = result { - if err.kind() == io::ErrorKind::WouldBlock { - Err(TryLockError::WouldBlock) - } else { - Err(TryLockError::Error(err)) - } - } else { - Ok(()) - } - } - #[cfg(not(any( target_os = "freebsd", target_os = "fuchsia", @@ -1646,7 +1591,6 @@ impl File { target_os = "netbsd", target_os = "openbsd", target_os = "cygwin", - target_os = "solaris", target_os = "illumos", target_os = "aix", target_os = "android", @@ -1677,15 +1621,6 @@ impl File { return Ok(()); } - #[cfg(target_os = "solaris")] - pub fn unlock(&self) -> io::Result<()> { - let mut flock: libc::flock = unsafe { mem::zeroed() }; - flock.l_type = libc::F_UNLCK as libc::c_short; - flock.l_whence = libc::SEEK_SET as libc::c_short; - cvt(unsafe { libc::fcntl(self.as_raw_fd(), libc::F_SETLKW, &flock) })?; - Ok(()) - } - #[cfg(not(any( target_os = "freebsd", target_os = "fuchsia", @@ -1694,7 +1629,6 @@ impl File { target_os = "netbsd", target_os = "openbsd", target_os = "cygwin", - target_os = "solaris", target_os = "illumos", target_os = "aix", target_os = "android", diff --git a/src/tools/miri/tests/pass/shims/fs.rs b/src/tools/miri/tests/pass/shims/fs.rs index cb1088fb08515..f7298a9a2f235 100644 --- a/src/tools/miri/tests/pass/shims/fs.rs +++ b/src/tools/miri/tests/pass/shims/fs.rs @@ -410,8 +410,7 @@ fn test_pread_pwrite() { assert_eq!(&buf1, b" m"); } -// Miri does not support the way this is implemented on Solaris -// (https://github.com/rust-lang/miri/issues/5038). +// Solaris does not support per-handle file locking. #[cfg(not(target_os = "solaris"))] fn test_flock() { let bytes = b"Hello, World!\n"; From 353aa6def05fe859f735a551fa35cc71d18976a0 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 6 Jun 2026 00:07:59 +0200 Subject: [PATCH 14/16] use cfg_select for unix file lock implementations --- library/std/src/sys/fs/unix.rs | 301 ++++++++++++++------------------- 1 file changed, 123 insertions(+), 178 deletions(-) diff --git a/library/std/src/sys/fs/unix.rs b/library/std/src/sys/fs/unix.rs index 957c286ee472a..c9221269440d8 100644 --- a/library/std/src/sys/fs/unix.rs +++ b/library/std/src/sys/fs/unix.rs @@ -1441,201 +1441,146 @@ impl File { } } - #[cfg(any( - target_os = "freebsd", - target_os = "fuchsia", - target_os = "hurd", - target_os = "linux", - target_os = "netbsd", - target_os = "openbsd", - target_os = "cygwin", - target_os = "illumos", - target_os = "aix", - target_os = "android", - target_vendor = "apple", - ))] pub fn lock(&self) -> io::Result<()> { - cvt(unsafe { libc::flock(self.as_raw_fd(), libc::LOCK_EX) })?; - return Ok(()); - } - - #[cfg(not(any( - target_os = "freebsd", - target_os = "fuchsia", - target_os = "hurd", - target_os = "linux", - target_os = "netbsd", - target_os = "openbsd", - target_os = "cygwin", - target_os = "illumos", - target_os = "aix", - target_os = "android", - target_vendor = "apple", - )))] - pub fn lock(&self) -> io::Result<()> { - Err(io::const_error!(io::ErrorKind::Unsupported, "lock() not supported")) - } - - #[cfg(any( - target_os = "freebsd", - target_os = "fuchsia", - target_os = "hurd", - target_os = "linux", - target_os = "netbsd", - target_os = "openbsd", - target_os = "cygwin", - target_os = "illumos", - target_os = "aix", - target_os = "android", - target_vendor = "apple", - ))] - pub fn lock_shared(&self) -> io::Result<()> { - cvt(unsafe { libc::flock(self.as_raw_fd(), libc::LOCK_SH) })?; - return Ok(()); + cfg_select! { + any( + target_os = "freebsd", + target_os = "fuchsia", + target_os = "hurd", + target_os = "linux", + target_os = "netbsd", + target_os = "openbsd", + target_os = "cygwin", + target_os = "illumos", + target_os = "aix", + target_os = "android", + target_vendor = "apple", + ) => { + cvt(unsafe { libc::flock(self.as_raw_fd(), libc::LOCK_EX) })?; + return Ok(()); + } + _ => { + Err(io::const_error!(io::ErrorKind::Unsupported, "lock() not supported")) + } + } } - #[cfg(not(any( - target_os = "freebsd", - target_os = "fuchsia", - target_os = "hurd", - target_os = "linux", - target_os = "netbsd", - target_os = "openbsd", - target_os = "cygwin", - target_os = "illumos", - target_os = "aix", - target_os = "android", - target_vendor = "apple", - )))] pub fn lock_shared(&self) -> io::Result<()> { - Err(io::const_error!(io::ErrorKind::Unsupported, "lock_shared() not supported")) - } - - #[cfg(any( - target_os = "freebsd", - target_os = "fuchsia", - target_os = "hurd", - target_os = "linux", - target_os = "netbsd", - target_os = "openbsd", - target_os = "cygwin", - target_os = "illumos", - target_os = "aix", - target_os = "android", - target_vendor = "apple", - ))] - pub fn try_lock(&self) -> Result<(), TryLockError> { - let result = cvt(unsafe { libc::flock(self.as_raw_fd(), libc::LOCK_EX | libc::LOCK_NB) }); - if let Err(err) = result { - if err.kind() == io::ErrorKind::WouldBlock { - Err(TryLockError::WouldBlock) - } else { - Err(TryLockError::Error(err)) + cfg_select! { + any( + target_os = "freebsd", + target_os = "fuchsia", + target_os = "hurd", + target_os = "linux", + target_os = "netbsd", + target_os = "openbsd", + target_os = "cygwin", + target_os = "illumos", + target_os = "aix", + target_os = "android", + target_vendor = "apple", + ) => { + cvt(unsafe { libc::flock(self.as_raw_fd(), libc::LOCK_SH) })?; + return Ok(()); + } + _ => { + Err(io::const_error!(io::ErrorKind::Unsupported, "lock_shared() not supported")) } - } else { - Ok(()) } } - #[cfg(not(any( - target_os = "freebsd", - target_os = "fuchsia", - target_os = "hurd", - target_os = "linux", - target_os = "netbsd", - target_os = "openbsd", - target_os = "cygwin", - target_os = "illumos", - target_os = "aix", - target_os = "android", - target_vendor = "apple", - )))] pub fn try_lock(&self) -> Result<(), TryLockError> { - Err(TryLockError::Error(io::const_error!( - io::ErrorKind::Unsupported, - "try_lock() not supported" - ))) - } - - #[cfg(any( - target_os = "freebsd", - target_os = "fuchsia", - target_os = "hurd", - target_os = "linux", - target_os = "netbsd", - target_os = "openbsd", - target_os = "cygwin", - target_os = "illumos", - target_os = "aix", - target_os = "android", - target_vendor = "apple", - ))] - pub fn try_lock_shared(&self) -> Result<(), TryLockError> { - let result = cvt(unsafe { libc::flock(self.as_raw_fd(), libc::LOCK_SH | libc::LOCK_NB) }); - if let Err(err) = result { - if err.kind() == io::ErrorKind::WouldBlock { - Err(TryLockError::WouldBlock) - } else { - Err(TryLockError::Error(err)) + cfg_select! { + any( + target_os = "freebsd", + target_os = "fuchsia", + target_os = "hurd", + target_os = "linux", + target_os = "netbsd", + target_os = "openbsd", + target_os = "cygwin", + target_os = "illumos", + target_os = "aix", + target_os = "android", + target_vendor = "apple", + ) => { + let result = cvt(unsafe { libc::flock(self.as_raw_fd(), libc::LOCK_EX | libc::LOCK_NB) }); + if let Err(err) = result { + if err.kind() == io::ErrorKind::WouldBlock { + Err(TryLockError::WouldBlock) + } else { + Err(TryLockError::Error(err)) + } + } else { + Ok(()) + } + } + _ => { + Err(TryLockError::Error(io::const_error!( + io::ErrorKind::Unsupported, + "try_lock() not supported" + ))) } - } else { - Ok(()) } } - #[cfg(not(any( - target_os = "freebsd", - target_os = "fuchsia", - target_os = "hurd", - target_os = "linux", - target_os = "netbsd", - target_os = "openbsd", - target_os = "cygwin", - target_os = "illumos", - target_os = "aix", - target_os = "android", - target_vendor = "apple", - )))] pub fn try_lock_shared(&self) -> Result<(), TryLockError> { - Err(TryLockError::Error(io::const_error!( - io::ErrorKind::Unsupported, - "try_lock_shared() not supported" - ))) - } - - #[cfg(any( - target_os = "freebsd", - target_os = "fuchsia", - target_os = "hurd", - target_os = "linux", - target_os = "netbsd", - target_os = "openbsd", - target_os = "cygwin", - target_os = "illumos", - target_os = "aix", - target_os = "android", - target_vendor = "apple", - ))] - pub fn unlock(&self) -> io::Result<()> { - cvt(unsafe { libc::flock(self.as_raw_fd(), libc::LOCK_UN) })?; - return Ok(()); + cfg_select! { + any( + target_os = "freebsd", + target_os = "fuchsia", + target_os = "hurd", + target_os = "linux", + target_os = "netbsd", + target_os = "openbsd", + target_os = "cygwin", + target_os = "illumos", + target_os = "aix", + target_os = "android", + target_vendor = "apple", + ) => { + let result = cvt(unsafe { libc::flock(self.as_raw_fd(), libc::LOCK_SH | libc::LOCK_NB) }); + if let Err(err) = result { + if err.kind() == io::ErrorKind::WouldBlock { + Err(TryLockError::WouldBlock) + } else { + Err(TryLockError::Error(err)) + } + } else { + Ok(()) + } + } + _ => { + Err(TryLockError::Error(io::const_error!( + io::ErrorKind::Unsupported, + "try_lock_shared() not supported" + ))) + } + } } - #[cfg(not(any( - target_os = "freebsd", - target_os = "fuchsia", - target_os = "hurd", - target_os = "linux", - target_os = "netbsd", - target_os = "openbsd", - target_os = "cygwin", - target_os = "illumos", - target_os = "aix", - target_os = "android", - target_vendor = "apple", - )))] pub fn unlock(&self) -> io::Result<()> { - Err(io::const_error!(io::ErrorKind::Unsupported, "unlock() not supported")) + cfg_select! { + any( + target_os = "freebsd", + target_os = "fuchsia", + target_os = "hurd", + target_os = "linux", + target_os = "netbsd", + target_os = "openbsd", + target_os = "cygwin", + target_os = "illumos", + target_os = "aix", + target_os = "android", + target_vendor = "apple", + ) => { + cvt(unsafe { libc::flock(self.as_raw_fd(), libc::LOCK_UN) })?; + return Ok(()); + } + _ => { + Err(io::const_error!(io::ErrorKind::Unsupported, "unlock() not supported")) + } + } } pub fn truncate(&self, size: u64) -> io::Result<()> { From ce94e80b7c6a93a5b1ecd2d00d1c74d1910279f3 Mon Sep 17 00:00:00 2001 From: Laine Taffin Altman Date: Fri, 5 Jun 2026 20:48:47 -0700 Subject: [PATCH 15/16] Rename `SyncView::{as_pin => as_pin_ref}` --- library/core/src/sync/sync_view.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/core/src/sync/sync_view.rs b/library/core/src/sync/sync_view.rs index 3b02c4c727ca8..63e157bf90f23 100644 --- a/library/core/src/sync/sync_view.rs +++ b/library/core/src/sync/sync_view.rs @@ -188,7 +188,7 @@ impl SyncView { #[rustc_const_unstable(feature = "exclusive_wrapper", issue = "98407")] #[must_use] #[inline] - pub const fn as_pin(self: Pin<&Self>) -> Pin<&T> { + pub const fn as_pin_ref(self: Pin<&Self>) -> Pin<&T> { // SAFETY: `SyncView` can only produce `&T` if itself is unpinned // `Pin::map_unchecked` is not const, so we do this conversion manually unsafe { Pin::new_unchecked(&self.get_ref().inner) } From b06471867e7612ea354e97d33dd0083fb752599a Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 6 Jun 2026 10:59:29 +0200 Subject: [PATCH 16/16] std tests: skip a slow test on Miri --- .../std/src/sys/process/unix/unsupported/wait_status/tests.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/library/std/src/sys/process/unix/unsupported/wait_status/tests.rs b/library/std/src/sys/process/unix/unsupported/wait_status/tests.rs index 0d9232fac5e4e..51af14f0aaace 100644 --- a/library/std/src/sys/process/unix/unsupported/wait_status/tests.rs +++ b/library/std/src/sys/process/unix/unsupported/wait_status/tests.rs @@ -8,6 +8,7 @@ // I.e. we're using Linux as a proxy for "trad unix". #[cfg(target_os = "linux")] #[test] +#[cfg_attr(miri, ignore)] // Miri is too slow fn compare_with_linux() { use super::ExitStatus as Emulated; use crate::os::unix::process::ExitStatusExt as _;