From d218e7fa305b9b43cf94f1ba0e4f6eab03e06992 Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Fri, 6 Mar 2026 10:24:45 -0800 Subject: [PATCH 01/23] `std::any::TypeId`: remove misplaced "and" in `Unique` example --- library/core/src/any.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/core/src/any.rs b/library/core/src/any.rs index 71a529400511c..53c5e28c0be27 100644 --- a/library/core/src/any.rs +++ b/library/core/src/any.rs @@ -666,7 +666,7 @@ impl dyn Any + Send + Sync { /// /// The following is an example program that tries to use `TypeId::of` to /// implement a generic type `Unique` that guarantees unique instances for each `Unique`, -/// that is, and for each type `T` there can be at most one value of type `Unique` at any time. +/// that is, for each type `T` there can be at most one value of type `Unique` at any time. /// /// ``` /// mod unique { From 4e7526144285ecdf92846785d32097d35aab5cef Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Tue, 7 Apr 2026 08:42:09 +0000 Subject: [PATCH 02/23] Add Sized supertrait for CoerceUnsized and DispatchFromDyn --- library/core/src/ops/unsize.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/core/src/ops/unsize.rs b/library/core/src/ops/unsize.rs index f0781ee01fd53..45a6e973ca716 100644 --- a/library/core/src/ops/unsize.rs +++ b/library/core/src/ops/unsize.rs @@ -33,7 +33,7 @@ use crate::marker::{PointeeSized, Unsize}; /// [nomicon-coerce]: ../../nomicon/coercions.html #[unstable(feature = "coerce_unsized", issue = "18598")] #[lang = "coerce_unsized"] -pub trait CoerceUnsized { +pub trait CoerceUnsized: Sized { // Empty. } @@ -116,7 +116,7 @@ impl, U: PointeeSized> CoerceUnsized<*const U> for * /// [^1]: Formerly known as *object safety*. #[unstable(feature = "dispatch_from_dyn", issue = "none")] #[lang = "dispatch_from_dyn"] -pub trait DispatchFromDyn { +pub trait DispatchFromDyn: Sized { // Empty. } From 2d8ae32c58d58eabfa5f9014650111e63cac2927 Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Tue, 7 Apr 2026 08:51:47 +0000 Subject: [PATCH 03/23] Add a test for the ICE --- tests/ui/traits/dyn-coerce-unsized-ice.rs | 16 +++++++++++++ tests/ui/traits/dyn-coerce-unsized-ice.stderr | 23 +++++++++++++++++++ 2 files changed, 39 insertions(+) create mode 100644 tests/ui/traits/dyn-coerce-unsized-ice.rs create mode 100644 tests/ui/traits/dyn-coerce-unsized-ice.stderr diff --git a/tests/ui/traits/dyn-coerce-unsized-ice.rs b/tests/ui/traits/dyn-coerce-unsized-ice.rs new file mode 100644 index 0000000000000..cfb6c57366bd2 --- /dev/null +++ b/tests/ui/traits/dyn-coerce-unsized-ice.rs @@ -0,0 +1,16 @@ +// Regression test for: +// https://github.com/rust-lang/rust/issues/149094#issuecomment-4191071539 +#![feature(coerce_unsized, unsized_fn_params)] +#![expect(internal_features)] + +use std::ops::CoerceUnsized; + +pub trait Trait {} + +pub fn foo(x: dyn CoerceUnsized<*const dyn Trait>) -> *const dyn Trait { +//~^ ERROR: the trait `CoerceUnsized` is not dyn compatible [E0038] + x +//~^ ERROR: the size for values of type `(dyn CoerceUnsized<*const (dyn Trait + 'static)> + 'static)` cannot be known at compilation time [E0277] +} + +fn main() {} diff --git a/tests/ui/traits/dyn-coerce-unsized-ice.stderr b/tests/ui/traits/dyn-coerce-unsized-ice.stderr new file mode 100644 index 0000000000000..1bd3874f0e120 --- /dev/null +++ b/tests/ui/traits/dyn-coerce-unsized-ice.stderr @@ -0,0 +1,23 @@ +error[E0038]: the trait `CoerceUnsized` is not dyn compatible + --> $DIR/dyn-coerce-unsized-ice.rs:10:15 + | +LL | pub fn foo(x: dyn CoerceUnsized<*const dyn Trait>) -> *const dyn Trait { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `CoerceUnsized` is not dyn compatible + | + = note: the trait is not dyn compatible because it requires `Self: Sized` + = note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit + +error[E0277]: the size for values of type `(dyn CoerceUnsized<*const (dyn Trait + 'static)> + 'static)` cannot be known at compilation time + --> $DIR/dyn-coerce-unsized-ice.rs:12:5 + | +LL | x + | ^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `(dyn CoerceUnsized<*const (dyn Trait + 'static)> + 'static)` + = note: required for the cast from `(dyn CoerceUnsized<*const (dyn Trait + 'static)> + 'static)` to `*const (dyn Trait + 'static)` + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0038, E0277. +For more information about an error, try `rustc --explain E0038`. From 46a84f6c4fd229e754a688a9b09239ed901ad555 Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Tue, 7 Apr 2026 08:55:27 +0000 Subject: [PATCH 04/23] Add run-pass test for dyn Receiver --- .../self/arbitrary-self-types-dyn-receiver.rs | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 tests/ui/self/arbitrary-self-types-dyn-receiver.rs diff --git a/tests/ui/self/arbitrary-self-types-dyn-receiver.rs b/tests/ui/self/arbitrary-self-types-dyn-receiver.rs new file mode 100644 index 0000000000000..fe128301d497a --- /dev/null +++ b/tests/ui/self/arbitrary-self-types-dyn-receiver.rs @@ -0,0 +1,21 @@ +//@ run-pass +#![feature(arbitrary_self_types)] + +use std::ops::Receiver; + +trait Trait { + fn foo(self: &dyn Receiver); +} + +struct Thing; +impl Trait for Thing { + fn foo(self: &dyn Receiver) { + println!("huh???"); + } +} + +fn main() { + let x = Box::new(Thing); + let y: &dyn Receiver = &x; + y.foo(); +} From 80bb81359bc045046a05402bb559b4665eb78f37 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Sat, 11 Apr 2026 11:53:05 +1000 Subject: [PATCH 05/23] Inline `field_match_pairs` into its callers --- .../src/builder/matches/match_pair.rs | 28 ++++++------------- 1 file changed, 8 insertions(+), 20 deletions(-) diff --git a/compiler/rustc_mir_build/src/builder/matches/match_pair.rs b/compiler/rustc_mir_build/src/builder/matches/match_pair.rs index bd18a215aea70..6fb425aeba381 100644 --- a/compiler/rustc_mir_build/src/builder/matches/match_pair.rs +++ b/compiler/rustc_mir_build/src/builder/matches/match_pair.rs @@ -13,24 +13,6 @@ use crate::builder::matches::{ }; impl<'a, 'tcx> Builder<'a, 'tcx> { - /// Builds and pushes [`MatchPairTree`] subtrees, one for each pattern in - /// `subpatterns`, representing the fields of a [`PatKind::Variant`] or - /// [`PatKind::Leaf`]. - /// - /// Used internally by [`MatchPairTree::for_pattern`]. - fn field_match_pairs( - &mut self, - match_pairs: &mut Vec>, - extra_data: &mut PatternExtraData<'tcx>, - place: PlaceBuilder<'tcx>, - subpatterns: &[FieldPat<'tcx>], - ) { - for fieldpat in subpatterns { - let place = place.clone_project(PlaceElem::Field(fieldpat.field, fieldpat.pattern.ty)); - MatchPairTree::for_pattern(place, &fieldpat.pattern, self, match_pairs, extra_data); - } - } - /// Builds [`MatchPairTree`] subtrees for the prefix/middle/suffix parts of an /// array pattern or slice pattern, and adds those trees to `match_pairs`. /// @@ -294,7 +276,10 @@ impl<'tcx> MatchPairTree<'tcx> { PatKind::Variant { adt_def, variant_index, args: _, ref subpatterns } => { let downcast_place = place_builder.downcast(adt_def, variant_index); // `(x as Variant)` - cx.field_match_pairs(&mut subpairs, extra_data, downcast_place, subpatterns); + for &FieldPat { field, pattern: ref subpat } in subpatterns { + let subplace = downcast_place.clone_project(PlaceElem::Field(field, subpat.ty)); + MatchPairTree::for_pattern(subplace, subpat, cx, &mut subpairs, extra_data); + } // We treat non-exhaustive enums the same independent of the crate they are // defined in, to avoid differences in the operational semantics between crates. @@ -308,7 +293,10 @@ impl<'tcx> MatchPairTree<'tcx> { } PatKind::Leaf { ref subpatterns } => { - cx.field_match_pairs(&mut subpairs, extra_data, place_builder, subpatterns); + for &FieldPat { field, pattern: ref subpat } in subpatterns { + let subplace = place_builder.clone_project(PlaceElem::Field(field, subpat.ty)); + MatchPairTree::for_pattern(subplace, subpat, cx, &mut subpairs, extra_data); + } None } From 4a5c24f3cb232bb30e21cc2b08861c90d837f56f Mon Sep 17 00:00:00 2001 From: Zalathar Date: Sat, 11 Apr 2026 12:11:51 +1000 Subject: [PATCH 06/23] Move the recursive step out of `prefix_slice_suffix` --- .../src/builder/matches/match_pair.rs | 127 ++++++++---------- 1 file changed, 59 insertions(+), 68 deletions(-) diff --git a/compiler/rustc_mir_build/src/builder/matches/match_pair.rs b/compiler/rustc_mir_build/src/builder/matches/match_pair.rs index 6fb425aeba381..e2d00238e2d59 100644 --- a/compiler/rustc_mir_build/src/builder/matches/match_pair.rs +++ b/compiler/rustc_mir_build/src/builder/matches/match_pair.rs @@ -12,59 +12,58 @@ use crate::builder::matches::{ FlatPat, MatchPairTree, PatConstKind, PatternExtraData, SliceLenOp, TestableCase, }; -impl<'a, 'tcx> Builder<'a, 'tcx> { - /// Builds [`MatchPairTree`] subtrees for the prefix/middle/suffix parts of an - /// array pattern or slice pattern, and adds those trees to `match_pairs`. - /// - /// Used internally by [`MatchPairTree::for_pattern`]. - fn prefix_slice_suffix( - &mut self, - match_pairs: &mut Vec>, - extra_data: &mut PatternExtraData<'tcx>, - place: &PlaceBuilder<'tcx>, - array_len: Option, - prefix: &[Pat<'tcx>], - opt_slice: &Option>>, - suffix: &[Pat<'tcx>], - ) { - let prefix_len = u64::try_from(prefix.len()).unwrap(); - let suffix_len = u64::try_from(suffix.len()).unwrap(); - - // For slice patterns with a `..` followed by 0 or more suffix subpatterns, - // the actual slice index of those subpatterns isn't statically known, so - // we have to index them relative to the end of the slice. - // - // For array patterns, all subpatterns are indexed relative to the start. - let (min_length, is_array) = match array_len { - Some(len) => (len, true), - None => (prefix_len + suffix_len, false), - }; - - for (offset, subpattern) in (0u64..).zip(prefix) { - let elem = ProjectionElem::ConstantIndex { offset, min_length, from_end: false }; - let place = place.clone_project(elem); - MatchPairTree::for_pattern(place, subpattern, self, match_pairs, extra_data) - } +/// For an array or slice pattern's subpatterns (prefix/slice/suffix), returns a list +/// of those subpatterns, each paired with a suitably-projected [`PlaceBuilder`]. +fn prefix_slice_suffix<'a, 'tcx>( + place: &PlaceBuilder<'tcx>, + array_len: Option, // Some for array patterns; None for slice patterns + prefix: &'a [Pat<'tcx>], + opt_slice: &'a Option>>, + suffix: &'a [Pat<'tcx>], +) -> Vec<(PlaceBuilder<'tcx>, &'a Pat<'tcx>)> { + let prefix_len = u64::try_from(prefix.len()).unwrap(); + let suffix_len = u64::try_from(suffix.len()).unwrap(); + + let mut output_pairs = + Vec::with_capacity(prefix.len() + usize::from(opt_slice.is_some()) + suffix.len()); + + // For slice patterns with a `..` followed by 0 or more suffix subpatterns, + // the actual slice index of those subpatterns isn't statically known, so + // we have to index them relative to the end of the slice. + // + // For array patterns, all subpatterns are indexed relative to the start. + let (min_length, is_array) = match array_len { + Some(len) => (len, true), + None => (prefix_len + suffix_len, false), + }; + + for (offset, prefix_subpat) in (0u64..).zip(prefix) { + let elem = ProjectionElem::ConstantIndex { offset, min_length, from_end: false }; + let subplace = place.clone_project(elem); + output_pairs.push((subplace, prefix_subpat)); + } - if let Some(subslice_pat) = opt_slice { - let subslice = place.clone_project(PlaceElem::Subslice { - from: prefix_len, - to: if is_array { min_length - suffix_len } else { suffix_len }, - from_end: !is_array, - }); - MatchPairTree::for_pattern(subslice, subslice_pat, self, match_pairs, extra_data); - } + if let Some(slice_subpat) = opt_slice { + let elem = PlaceElem::Subslice { + from: prefix_len, + to: if is_array { min_length - suffix_len } else { suffix_len }, + from_end: !is_array, + }; + let subplace = place.clone_project(elem); + output_pairs.push((subplace, slice_subpat)); + } - for (end_offset, subpattern) in (1u64..).zip(suffix.iter().rev()) { - let elem = ProjectionElem::ConstantIndex { - offset: if is_array { min_length - end_offset } else { end_offset }, - min_length, - from_end: !is_array, - }; - let place = place.clone_project(elem); - MatchPairTree::for_pattern(place, subpattern, self, match_pairs, extra_data) - } + for (offset_from_end, suffix_subpat) in (1u64..).zip(suffix.iter().rev()) { + let elem = ProjectionElem::ConstantIndex { + offset: if is_array { min_length - offset_from_end } else { offset_from_end }, + min_length, + from_end: !is_array, + }; + let subplace = place.clone_project(elem); + output_pairs.push((subplace, suffix_subpat)); } + + output_pairs } impl<'tcx> MatchPairTree<'tcx> { @@ -221,15 +220,11 @@ impl<'tcx> MatchPairTree<'tcx> { _ => None, }; if let Some(array_len) = array_len { - cx.prefix_slice_suffix( - &mut subpairs, - extra_data, - &place_builder, - Some(array_len), - prefix, - slice, - suffix, - ); + for (subplace, subpat) in + prefix_slice_suffix(&place_builder, Some(array_len), prefix, slice, suffix) + { + MatchPairTree::for_pattern(subplace, subpat, cx, &mut subpairs, extra_data); + } } else { // If the array length couldn't be determined, ignore the // subpatterns and delayed-assert that compilation will fail. @@ -245,15 +240,11 @@ impl<'tcx> MatchPairTree<'tcx> { None } PatKind::Slice { ref prefix, ref slice, ref suffix } => { - cx.prefix_slice_suffix( - &mut subpairs, - extra_data, - &place_builder, - None, - prefix, - slice, - suffix, - ); + for (subplace, subpat) in + prefix_slice_suffix(&place_builder, None, prefix, slice, suffix) + { + MatchPairTree::for_pattern(subplace, subpat, cx, &mut subpairs, extra_data); + } if prefix.is_empty() && slice.is_some() && suffix.is_empty() { // This pattern is shaped like `[..]`. It can match a slice From b17a3e2f2dd8f9121e13bbaf0428df6be5162062 Mon Sep 17 00:00:00 2001 From: lapla Date: Wed, 15 Apr 2026 00:33:36 +0900 Subject: [PATCH 07/23] Fix misleading "borrowed data escapes outside of function" diagnostic --- .../rustc_borrowck/src/diagnostics/region_errors.rs | 2 ++ .../fn-ptr-lifetime-mismatch-with-impl-trait-arg.rs | 8 ++++++++ ...fn-ptr-lifetime-mismatch-with-impl-trait-arg.stderr | 10 ++++++++++ 3 files changed, 20 insertions(+) create mode 100644 tests/ui/borrowck/fn-ptr-lifetime-mismatch-with-impl-trait-arg.rs create mode 100644 tests/ui/borrowck/fn-ptr-lifetime-mismatch-with-impl-trait-arg.stderr diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs index f6a20e41742bb..934eadd215ad5 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs @@ -686,6 +686,8 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { || (*category == ConstraintCategory::Assignment && self.regioncx.universal_regions().defining_ty.is_fn_def()) || self.regioncx.universal_regions().defining_ty.is_const() + || (fr_name_and_span.is_none() + && self.regioncx.universal_regions().defining_ty.is_fn_def()) { return self.report_general_error(errci); } diff --git a/tests/ui/borrowck/fn-ptr-lifetime-mismatch-with-impl-trait-arg.rs b/tests/ui/borrowck/fn-ptr-lifetime-mismatch-with-impl-trait-arg.rs new file mode 100644 index 0000000000000..730e5cdc278fd --- /dev/null +++ b/tests/ui/borrowck/fn-ptr-lifetime-mismatch-with-impl-trait-arg.rs @@ -0,0 +1,8 @@ +// Regression test for https://github.com/rust-lang/rust/issues/154350 + +fn func<'a>(f: impl FnOnce(fn(&'a i32)), x: fn(&'static i32)) { + f(x); + //~^ ERROR lifetime may not live long enough +} + +fn main() {} diff --git a/tests/ui/borrowck/fn-ptr-lifetime-mismatch-with-impl-trait-arg.stderr b/tests/ui/borrowck/fn-ptr-lifetime-mismatch-with-impl-trait-arg.stderr new file mode 100644 index 0000000000000..92c613acdd83a --- /dev/null +++ b/tests/ui/borrowck/fn-ptr-lifetime-mismatch-with-impl-trait-arg.stderr @@ -0,0 +1,10 @@ +error: lifetime may not live long enough + --> $DIR/fn-ptr-lifetime-mismatch-with-impl-trait-arg.rs:4:5 + | +LL | fn func<'a>(f: impl FnOnce(fn(&'a i32)), x: fn(&'static i32)) { + | -- lifetime `'a` defined here +LL | f(x); + | ^^^^ argument requires that `'a` must outlive `'static` + +error: aborting due to 1 previous error + From 0e522d6c621fb37dc0be327f854bbb2578c262bd Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Fri, 17 Oct 2025 15:28:21 +0200 Subject: [PATCH 08/23] naked functions: respect `function_sections` on windows For `gnu` function_sections is off by default. --- .../rustc_codegen_ssa/src/mir/naked_asm.rs | 9 ++- .../codegen-llvm/naked-fn/naked-functions.rs | 66 +++++++++++-------- 2 files changed, 47 insertions(+), 28 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs b/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs index 97cfc648b7cb0..e9dc28dca0142 100644 --- a/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs +++ b/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs @@ -128,6 +128,8 @@ fn prefix_and_suffix<'tcx>( let is_arm = tcx.sess.target.arch == Arch::Arm; let is_thumb = tcx.sess.unstable_target_features.contains(&sym::thumb_mode); + let function_sections = + tcx.sess.opts.unstable_opts.function_sections.unwrap_or(tcx.sess.target.function_sections); // If we're compiling the compiler-builtins crate, e.g., the equivalent of // compiler-rt, then we want to implicitly compile everything with hidden @@ -278,8 +280,11 @@ fn prefix_and_suffix<'tcx>( writeln!(begin, ".type 32").unwrap(); writeln!(begin, ".endef").unwrap(); - let section = link_section.unwrap_or_else(|| format!(".text.{asm_name}")); - writeln!(begin, ".pushsection {},\"xr\"", section).unwrap(); + if let Some(section) = &link_section { + writeln!(begin, ".pushsection {section},\"xr\"").unwrap() + } else if function_sections { + writeln!(begin, ".pushsection .text${asm_name},\"xr\"").unwrap() + } write_linkage(&mut begin).unwrap(); writeln!(begin, ".balign {align_bytes}").unwrap(); writeln!(begin, "{asm_name}:").unwrap(); diff --git a/tests/codegen-llvm/naked-fn/naked-functions.rs b/tests/codegen-llvm/naked-fn/naked-functions.rs index b5c84ede8f063..3243ef8135047 100644 --- a/tests/codegen-llvm/naked-fn/naked-functions.rs +++ b/tests/codegen-llvm/naked-fn/naked-functions.rs @@ -1,12 +1,14 @@ //@ add-minicore -//@ revisions: linux win_x86 win_i686 macos thumb +//@ revisions: linux win_x86_msvc win_x86_gnu win_i686_gnu macos thumb // //@[linux] compile-flags: --target x86_64-unknown-linux-gnu //@[linux] needs-llvm-components: x86 -//@[win_x86] compile-flags: --target x86_64-pc-windows-gnu -//@[win_x86] needs-llvm-components: x86 -//@[win_i686] compile-flags: --target i686-pc-windows-gnu -//@[win_i686] needs-llvm-components: x86 +//@[win_x86_gnu] compile-flags: --target x86_64-pc-windows-gnu +//@[win_x86_gnu] needs-llvm-components: x86 +//@[win_x86_msvc] compile-flags: --target x86_64-pc-windows-msvc +//@[win_x86_msvc] needs-llvm-components: x86 +//@[win_i686_gnu] compile-flags: --target i686-pc-windows-gnu +//@[win_i686_gnu] needs-llvm-components: x86 //@[macos] compile-flags: --target aarch64-apple-darwin //@[macos] needs-llvm-components: aarch64 //@[thumb] compile-flags: --target thumbv7em-none-eabi @@ -23,6 +25,11 @@ use minicore::*; // // linux: .pushsection .text.naked_empty,\22ax\22, @progbits // macos: .pushsection __TEXT,__text,regular,pure_instructions +// +// win_x86_msvc: .pushsection .text$naked_empty,\22xr\22 +// win_x86_gnu-NOT: .pushsection +// win_i686_gnu-NOT: .pushsection +// // thumb: .pushsection .text.naked_empty,\22ax\22, %progbits // // linux, macos, thumb: .balign 4 @@ -35,12 +42,12 @@ use minicore::*; // // linux: .type naked_empty, @function // -// win_x86: .def naked_empty -// win_i686: .def _naked_empty +// win_x86_msvc,win_x86_gnu: .def naked_empty +// win_i686_gnu: .def _naked_empty // -// win_x86,win_i686: .scl 2 -// win_x86,win_i686: .type 32 -// win_x86,win_i686: .endef +// win_x86_msvc,win_x86_gnu,win_i686_gnu: .scl 2 +// win_x86_msvc,win_x86_gnu,win_i686_gnu: .type 32 +// win_x86_msvc,win_x86_gnu,win_i686_gnu: .endef // // win_x86: .pushsection .text.naked_empty,\22xr\22 // win_i686: .pushsection .text._naked_empty,\22xr\22 @@ -59,7 +66,8 @@ use minicore::*; // linux,macos,win_x86,win_x86: ret // thumb: bx lr // -// linux,macos,thumb: .popsection +// linux,windows,win_x86_msvc,thumb: .popsection +// win_x86_gnu-NOT,win_i686_gnu-NOT: .popsection // // thumb: .thumb // @@ -82,6 +90,11 @@ pub extern "C" fn naked_empty() { // // linux: .pushsection .text.naked_with_args_and_return,\22ax\22, @progbits // macos: .pushsection __TEXT,__text,regular,pure_instructions +// +// win_x86_msvc: .pushsection .text$naked_with_args_and_return,\22xr\22 +// win_x86_gnu-NOT: .pushsection +// win_i686_gnu-NOT: .pushsection +// // thumb: .pushsection .text.naked_with_args_and_return,\22ax\22, %progbits // // linux, macos, thumb: .balign 4 @@ -94,12 +107,12 @@ pub extern "C" fn naked_empty() { // // linux: .type naked_with_args_and_return, @function // -// win_x86: .def naked_with_args_and_return -// win_i686: .def _naked_with_args_and_return +// win_x86_msvc,win_x86_gnu: .def naked_with_args_and_return +// win_i686_gnu: .def _naked_with_args_and_return // -// win_x86,win_i686: .scl 2 -// win_x86,win_i686: .type 32 -// win_x86,win_i686: .endef +// win_x86_msvc,win_x86_gnu,win_i686_gnu: .scl 2 +// win_x86_msvc,win_x86_gnu,win_i686_gnu: .type 32 +// win_x86_msvc,win_x86_gnu,win_i686_gnu: .endef // // win_x86: .pushsection .text.naked_with_args_and_return,\22xr\22 // win_i686: .pushsection .text._naked_with_args_and_return,\22xr\22 @@ -115,14 +128,15 @@ pub extern "C" fn naked_empty() { // // CHECK-LABEL: naked_with_args_and_return: // -// linux, win_x86,win_i686: lea rax, [rdi + rsi] +// linux,win_x86_msvc,win_x86_gnu,win_i686_gnu: lea rax, [rdi + rsi] // macos: add x0, x0, x1 // thumb: adds r0, r0, r1 // // linux,macos,win_x86,win_i686: ret // thumb: bx lr // -// linux,macos,thumb: .popsection +// linux,windows,win_x86_msvc,thumb: .popsection +// win_x86_gnu-NOT,win_i686_gnu-NOT: .popsection // // thumb: .thumb // @@ -146,7 +160,7 @@ pub extern "C" fn naked_with_args_and_return(a: isize, b: isize) -> isize { // linux: .pushsection .text.some_different_name,\22ax\22, @progbits // macos: .pushsection .text.some_different_name,regular,pure_instructions -// win_x86,win_i686: .pushsection .text.some_different_name,\22xr\22 +// win_x86_msvc,win_x86_gnu,win_i686_gnu: .pushsection .text.some_different_name,\22xr\22 // thumb: .pushsection .text.some_different_name,\22ax\22, %progbits // CHECK-LABEL: test_link_section: #[no_mangle] @@ -163,15 +177,15 @@ pub extern "C" fn test_link_section() { } } -// win_x86: .def fastcall_cc -// win_i686: .def @fastcall_cc@4 +// win_x86_msvc,win_x86_gnu: .def fastcall_cc +// win_i686_gnu: .def @fastcall_cc@4 // -// win_x86,win_i686: .scl 2 -// win_x86,win_i686: .type 32 -// win_x86,win_i686: .endef +// win_x86_msvc,win_x86_gnu,win_i686_gnu: .scl 2 +// win_x86_msvc,win_x86_gnu,win_i686_gnu: .type 32 +// win_x86_msvc,win_x86_gnu,win_i686_gnu: .endef // -// win_x86-LABEL: fastcall_cc: -// win_i686-LABEL: @fastcall_cc@4: +// win_x86_msvc-LABEL,win_x86_gnu-LABEL: fastcall_cc: +// win_i686_gnu-LABEL: @fastcall_cc@4: #[cfg(target_os = "windows")] #[no_mangle] #[unsafe(naked)] From 872301bfddf1c874a195acac437d8934f913f092 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Fri, 17 Oct 2025 15:54:41 +0200 Subject: [PATCH 09/23] naked functions: respect `function_sections` on linux/macos --- .../rustc_codegen_ssa/src/mir/naked_asm.rs | 22 +++++++++++++------ .../codegen-llvm/naked-fn/naked-functions.rs | 4 ++-- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs b/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs index e9dc28dca0142..4589ddbc3e2e7 100644 --- a/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs +++ b/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs @@ -220,8 +220,6 @@ fn prefix_and_suffix<'tcx>( let mut end = String::new(); match asm_binary_format { BinaryFormat::Elf => { - let section = link_section.unwrap_or_else(|| format!(".text.{asm_name}")); - let progbits = match is_arm { true => "%progbits", false => "@progbits", @@ -232,7 +230,11 @@ fn prefix_and_suffix<'tcx>( false => "@function", }; - writeln!(begin, ".pushsection {section},\"ax\", {progbits}").unwrap(); + if let Some(section) = &link_section { + writeln!(begin, ".pushsection {section},\"ax\", {progbits}").unwrap(); + } else if function_sections { + writeln!(begin, ".pushsection .text.{asm_name},\"ax\", {progbits}").unwrap(); + } writeln!(begin, ".balign {align_bytes}").unwrap(); write_linkage(&mut begin).unwrap(); match visibility { @@ -251,14 +253,18 @@ fn prefix_and_suffix<'tcx>( // pattern match on assembly generated by LLVM. writeln!(end, ".Lfunc_end_{asm_name}:").unwrap(); writeln!(end, ".size {asm_name}, . - {asm_name}").unwrap(); - writeln!(end, ".popsection").unwrap(); + if link_section.is_some() || function_sections { + writeln!(end, ".popsection").unwrap(); + } if !arch_suffix.is_empty() { writeln!(end, "{}", arch_suffix).unwrap(); } } BinaryFormat::MachO => { - let section = link_section.unwrap_or_else(|| "__TEXT,__text".to_string()); - writeln!(begin, ".pushsection {},regular,pure_instructions", section).unwrap(); + // NOTE: LLVM ignores `-Zfunction-sections` on macos. + if let Some(section) = &link_section { + writeln!(begin, ".pushsection {section},regular,pure_instructions").unwrap(); + } writeln!(begin, ".balign {align_bytes}").unwrap(); write_linkage(&mut begin).unwrap(); match visibility { @@ -269,7 +275,9 @@ fn prefix_and_suffix<'tcx>( writeln!(end).unwrap(); writeln!(end, ".Lfunc_end_{asm_name}:").unwrap(); - writeln!(end, ".popsection").unwrap(); + if link_section.is_some() { + writeln!(end, ".popsection").unwrap(); + } if !arch_suffix.is_empty() { writeln!(end, "{}", arch_suffix).unwrap(); } diff --git a/tests/codegen-llvm/naked-fn/naked-functions.rs b/tests/codegen-llvm/naked-fn/naked-functions.rs index 3243ef8135047..e83a62498bc57 100644 --- a/tests/codegen-llvm/naked-fn/naked-functions.rs +++ b/tests/codegen-llvm/naked-fn/naked-functions.rs @@ -24,7 +24,7 @@ use minicore::*; // linux,win_x86,win_i686: .intel_syntax // // linux: .pushsection .text.naked_empty,\22ax\22, @progbits -// macos: .pushsection __TEXT,__text,regular,pure_instructions +// macos-NOT: .pushsection // // win_x86_msvc: .pushsection .text$naked_empty,\22xr\22 // win_x86_gnu-NOT: .pushsection @@ -89,7 +89,7 @@ pub extern "C" fn naked_empty() { // linux,win_x86,win_i686: .intel_syntax // // linux: .pushsection .text.naked_with_args_and_return,\22ax\22, @progbits -// macos: .pushsection __TEXT,__text,regular,pure_instructions +// macos-NOT: .pushsection // // win_x86_msvc: .pushsection .text$naked_with_args_and_return,\22xr\22 // win_x86_gnu-NOT: .pushsection From 25e1647869f2e9fa41adcd9f39d41688cb4c3d50 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Fri, 17 Oct 2025 16:38:35 +0200 Subject: [PATCH 10/23] naked functions: add run-make test for DCE --- .../naked-dead-code-elimination/main.rs | 24 +++++++++++++++++++ .../naked-dead-code-elimination/rmake.rs | 10 ++++++++ 2 files changed, 34 insertions(+) create mode 100644 tests/run-make/naked-dead-code-elimination/main.rs create mode 100644 tests/run-make/naked-dead-code-elimination/rmake.rs diff --git a/tests/run-make/naked-dead-code-elimination/main.rs b/tests/run-make/naked-dead-code-elimination/main.rs new file mode 100644 index 0000000000000..d0a518630179f --- /dev/null +++ b/tests/run-make/naked-dead-code-elimination/main.rs @@ -0,0 +1,24 @@ +use std::arch::naked_asm; + +#[unsafe(naked)] +#[unsafe(no_mangle)] +extern "C" fn used() { + naked_asm!("ret") +} + +#[unsafe(naked)] +#[unsafe(no_mangle)] +extern "C" fn unused() { + naked_asm!("ret") +} + +#[unsafe(naked)] +#[unsafe(link_section = "foobar")] +#[unsafe(no_mangle)] +extern "C" fn unused_link_section() { + naked_asm!("ret") +} + +fn main() { + used(); +} diff --git a/tests/run-make/naked-dead-code-elimination/rmake.rs b/tests/run-make/naked-dead-code-elimination/rmake.rs new file mode 100644 index 0000000000000..1e1256f55b0fa --- /dev/null +++ b/tests/run-make/naked-dead-code-elimination/rmake.rs @@ -0,0 +1,10 @@ +//@ needs-asm-support + +use run_make_support::symbols::object_contains_any_symbol; +use run_make_support::{bin_name, rustc}; + +fn main() { + rustc().input("main.rs").opt().run(); + let mut unused = vec!["unused", "unused_link_section"]; + assert!(!object_contains_any_symbol(bin_name("main"), &unused)); +} From bc4aad37ca10e0f4a055f297ce4323c830c2afd5 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Sat, 17 Jan 2026 13:30:47 +0100 Subject: [PATCH 11/23] naked-functions: properly document the -Zfunction-sections windows status --- .../rustc_codegen_ssa/src/mir/naked_asm.rs | 32 +++++++++++--- .../codegen-llvm/naked-fn/naked-functions.rs | 42 ++++++++++++------- 2 files changed, 54 insertions(+), 20 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs b/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs index 4589ddbc3e2e7..941267e4eceae 100644 --- a/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs +++ b/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs @@ -8,7 +8,7 @@ use rustc_middle::ty::{Instance, Ty, TyCtxt, TypeVisitableExt}; use rustc_middle::{bug, ty}; use rustc_span::sym; use rustc_target::callconv::{ArgAbi, FnAbi, PassMode}; -use rustc_target::spec::{Arch, BinaryFormat}; +use rustc_target::spec::{Arch, BinaryFormat, Env}; use crate::common; use crate::mir::AsmCodegenMethods; @@ -234,6 +234,8 @@ fn prefix_and_suffix<'tcx>( writeln!(begin, ".pushsection {section},\"ax\", {progbits}").unwrap(); } else if function_sections { writeln!(begin, ".pushsection .text.{asm_name},\"ax\", {progbits}").unwrap(); + } else { + writeln!(begin, ".text").unwrap(); } writeln!(begin, ".balign {align_bytes}").unwrap(); write_linkage(&mut begin).unwrap(); @@ -261,7 +263,9 @@ fn prefix_and_suffix<'tcx>( } } BinaryFormat::MachO => { - // NOTE: LLVM ignores `-Zfunction-sections` on macos. + // NOTE: LLVM ignores `-Zfunction-sections` on macos. Instead the Mach-O symbol + // subsection splitting feature is used, which can be enabled with the + // `.subsections_via_symbols` global directive. LLVM already enables this directive. if let Some(section) = &link_section { writeln!(begin, ".pushsection {section},regular,pure_instructions").unwrap(); } @@ -289,9 +293,27 @@ fn prefix_and_suffix<'tcx>( writeln!(begin, ".endef").unwrap(); if let Some(section) = &link_section { - writeln!(begin, ".pushsection {section},\"xr\"").unwrap() - } else if function_sections { - writeln!(begin, ".pushsection .text${asm_name},\"xr\"").unwrap() + writeln!(begin, ".section {section},\"xr\"").unwrap() + } else if !function_sections { + // Function sections are enabled by default on MSVC, but disabled by default on GNU. + writeln!(begin, ".text").unwrap(); + } else { + // LLVM uses an extension to the section directive to support defining multiple + // sections with the same name and comdat. It adds `unique,` at the end of the + // `.section` directive. We have no way of generating that unique ID here, so don't + // emit it. + // + // See https://llvm.org/docs/Extensions.html#id2. + match &tcx.sess.target.options.env { + Env::Gnu => { + writeln!(begin, ".section .text${asm_name},\"xr\",one_only,{asm_name}") + .unwrap(); + } + Env::Msvc => { + writeln!(begin, ".section .text,\"xr\",one_only,{asm_name}").unwrap(); + } + other => bug!("invalid coff env {other:?}"), + } } write_linkage(&mut begin).unwrap(); writeln!(begin, ".balign {align_bytes}").unwrap(); diff --git a/tests/codegen-llvm/naked-fn/naked-functions.rs b/tests/codegen-llvm/naked-fn/naked-functions.rs index e83a62498bc57..a782ab5310e34 100644 --- a/tests/codegen-llvm/naked-fn/naked-functions.rs +++ b/tests/codegen-llvm/naked-fn/naked-functions.rs @@ -1,10 +1,17 @@ +// ignore-tidy-linelength +// //@ add-minicore -//@ revisions: linux win_x86_msvc win_x86_gnu win_i686_gnu macos thumb +//@ revisions: linux linux_no_function_sections macos thumb +//@ revisions: win_x86_msvc win_x86_gnu win_i686_gnu win_x86_gnu_function_sections // //@[linux] compile-flags: --target x86_64-unknown-linux-gnu //@[linux] needs-llvm-components: x86 +//@[linux_no_function_sections] compile-flags: --target x86_64-unknown-linux-gnu -Zfunction-sections=false +//@[linux_no_function_sections] needs-llvm-components: x86 //@[win_x86_gnu] compile-flags: --target x86_64-pc-windows-gnu //@[win_x86_gnu] needs-llvm-components: x86 +//@[win_x86_gnu_function_sections] compile-flags: --target x86_64-pc-windows-gnu -Zfunction-sections +//@[win_x86_gnu_function_sections] needs-llvm-components: x86 //@[win_x86_msvc] compile-flags: --target x86_64-pc-windows-msvc //@[win_x86_msvc] needs-llvm-components: x86 //@[win_i686_gnu] compile-flags: --target i686-pc-windows-gnu @@ -21,20 +28,22 @@ extern crate minicore; use minicore::*; -// linux,win_x86,win_i686: .intel_syntax +// linux,win_x86_gnu,win_i686_gnu: .intel_syntax // // linux: .pushsection .text.naked_empty,\22ax\22, @progbits +// linux_no_function_sections: .text // macos-NOT: .pushsection // -// win_x86_msvc: .pushsection .text$naked_empty,\22xr\22 -// win_x86_gnu-NOT: .pushsection -// win_i686_gnu-NOT: .pushsection +// win_x86_msvc: .section .text,\22xr\22,one_only,naked_empty +// win_x86_gnu_function_sections: .section .text$naked_empty,\22xr\22,one_only,naked_empty +// win_x86_gnu-NOT: .section +// win_i686_gnu-NOT: .section // // thumb: .pushsection .text.naked_empty,\22ax\22, %progbits // // linux, macos, thumb: .balign 4 // -// linux,thumb: .globl naked_empty +// linux,win_x86_gnu,thumb: .globl naked_empty // macos: .globl _naked_empty // // CHECK-NOT: .private_extern @@ -63,7 +72,7 @@ use minicore::*; // // CHECK-LABEL: naked_empty: // -// linux,macos,win_x86,win_x86: ret +// linux,macos,win_x86_msvc,win_x86_gnu,win_i686_gnu: ret // thumb: bx lr // // linux,windows,win_x86_msvc,thumb: .popsection @@ -86,20 +95,22 @@ pub extern "C" fn naked_empty() { } } -// linux,win_x86,win_i686: .intel_syntax +// linux,win_x86_gnu,win_i686_gnu,win_x86_msvc: .intel_syntax // // linux: .pushsection .text.naked_with_args_and_return,\22ax\22, @progbits +// linux_no_function_sections: .text // macos-NOT: .pushsection // -// win_x86_msvc: .pushsection .text$naked_with_args_and_return,\22xr\22 -// win_x86_gnu-NOT: .pushsection -// win_i686_gnu-NOT: .pushsection +// win_x86_msvc: .section .text,\22xr\22,one_only,naked_with_args_and_return +// win_x86_gnu_function_sections: .section .text$naked_with_args_and_return,\22xr\22,one_only,naked_with_args_and_return +// win_x86_gnu-NOT: .section +// win_i686_gnu-NOT: .section // // thumb: .pushsection .text.naked_with_args_and_return,\22ax\22, %progbits // // linux, macos, thumb: .balign 4 // -// linux,thumb: .globl naked_with_args_and_return +// linux,win_x86_gnu,win_x86_msvc,win_i686_gnu,thumb: .globl naked_with_args_and_return // macos: .globl _naked_with_args_and_return // // CHECK-NOT: .private_extern @@ -132,7 +143,7 @@ pub extern "C" fn naked_empty() { // macos: add x0, x0, x1 // thumb: adds r0, r0, r1 // -// linux,macos,win_x86,win_i686: ret +// linux,macos,win_x86_msvc,win_x86_gnu,win_i686_gnu: ret // thumb: bx lr // // linux,windows,win_x86_msvc,thumb: .popsection @@ -158,9 +169,10 @@ pub extern "C" fn naked_with_args_and_return(a: isize, b: isize) -> isize { } } -// linux: .pushsection .text.some_different_name,\22ax\22, @progbits +// linux,linux_no_function_sections: .pushsection .text.some_different_name,\22ax\22, @progbits // macos: .pushsection .text.some_different_name,regular,pure_instructions -// win_x86_msvc,win_x86_gnu,win_i686_gnu: .pushsection .text.some_different_name,\22xr\22 +// win_x86_msvc,win_x86_gnu,win_i686_gnu: .section .text.some_different_name,\22xr\22 +// win_x86_gnu_function_sections: .section .text.some_different_name,\22xr\22 // thumb: .pushsection .text.some_different_name,\22ax\22, %progbits // CHECK-LABEL: test_link_section: #[no_mangle] From 7787bd915b4a076ab35b83b42f66f8a45be237cd Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Mon, 30 Mar 2026 00:44:56 +0200 Subject: [PATCH 12/23] fix macho section specifier & windows test --- .../rustc_codegen_ssa/src/mir/naked_asm.rs | 3 ++- .../src/external_deps/rustc.rs | 10 ++++++++++ .../naked-dead-code-elimination/main.rs | 20 ++++++++++++++++++- .../naked-dead-code-elimination/rmake.rs | 18 ++++++++++++++--- 4 files changed, 46 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs b/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs index 941267e4eceae..c94376c7673da 100644 --- a/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs +++ b/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs @@ -295,7 +295,8 @@ fn prefix_and_suffix<'tcx>( if let Some(section) = &link_section { writeln!(begin, ".section {section},\"xr\"").unwrap() } else if !function_sections { - // Function sections are enabled by default on MSVC, but disabled by default on GNU. + // Function sections are enabled by default on MSVC and windows-gnullvm, + // but disabled by default on GNU. writeln!(begin, ".text").unwrap(); } else { // LLVM uses an extension to the section directive to support defining multiple diff --git a/src/tools/run-make-support/src/external_deps/rustc.rs b/src/tools/run-make-support/src/external_deps/rustc.rs index 2fa680a8e2334..e8645e00e1852 100644 --- a/src/tools/run-make-support/src/external_deps/rustc.rs +++ b/src/tools/run-make-support/src/external_deps/rustc.rs @@ -449,6 +449,16 @@ impl Rustc { self.cmd.arg("-Zcodegen-source-order"); self } + + /// Specify `-Z function-sections={yes, no}`. + pub fn function_sections(&mut self, enable: bool) -> &mut Self { + let flag = match enable { + true => "-Zfunction-sections=yes", + false => "-Zfunction-sections=no", + }; + self.cmd.arg(flag); + self + } } /// Query the sysroot path corresponding `rustc --print=sysroot`. diff --git a/tests/run-make/naked-dead-code-elimination/main.rs b/tests/run-make/naked-dead-code-elimination/main.rs index d0a518630179f..83da62d314587 100644 --- a/tests/run-make/naked-dead-code-elimination/main.rs +++ b/tests/run-make/naked-dead-code-elimination/main.rs @@ -1,3 +1,4 @@ +#![feature(cfg_target_object_format)] use std::arch::naked_asm; #[unsafe(naked)] @@ -6,6 +7,11 @@ extern "C" fn used() { naked_asm!("ret") } +#[unsafe(no_mangle)] +extern "C" fn unused_clothed() -> i32 { + 42 +} + #[unsafe(naked)] #[unsafe(no_mangle)] extern "C" fn unused() { @@ -13,12 +19,24 @@ extern "C" fn unused() { } #[unsafe(naked)] -#[unsafe(link_section = "foobar")] +#[unsafe(link_section = cfg_select!( + target_object_format = "mach-o" => "__TEXT,foobar", + _ => ".foobar", +))] #[unsafe(no_mangle)] extern "C" fn unused_link_section() { naked_asm!("ret") } +#[unsafe(link_section = cfg_select!( + target_object_format = "mach-o" => "__TEXT,baz", + _ => ".baz", +))] +#[unsafe(no_mangle)] +extern "C" fn unused_link_section_clothed() -> i32 { + 43 +} + fn main() { used(); } diff --git a/tests/run-make/naked-dead-code-elimination/rmake.rs b/tests/run-make/naked-dead-code-elimination/rmake.rs index 1e1256f55b0fa..a29212084b127 100644 --- a/tests/run-make/naked-dead-code-elimination/rmake.rs +++ b/tests/run-make/naked-dead-code-elimination/rmake.rs @@ -4,7 +4,19 @@ use run_make_support::symbols::object_contains_any_symbol; use run_make_support::{bin_name, rustc}; fn main() { - rustc().input("main.rs").opt().run(); - let mut unused = vec!["unused", "unused_link_section"]; - assert!(!object_contains_any_symbol(bin_name("main"), &unused)); + rustc().input("main.rs").opt().function_sections(true).run(); + + let bin = bin_name("main"); + + // Check that the naked symbol is eliminated when the "clothed" one is. + + assert_eq!( + object_contains_any_symbol(&bin, &["unused_clothed"]), + object_contains_any_symbol(&bin, &["unused"]) + ); + + assert_eq!( + object_contains_any_symbol(&bin, &["unused_link_section_clothed"]), + object_contains_any_symbol(&bin, &["unused_link_section"]) + ); } From cc1ebb5e202459bba0760ccf870a3281dc99c4be Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Thu, 16 Apr 2026 10:51:52 +0200 Subject: [PATCH 13/23] add uefi to windows link section test --- tests/assembly-llvm/naked-functions/link-section-windows.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/assembly-llvm/naked-functions/link-section-windows.rs b/tests/assembly-llvm/naked-functions/link-section-windows.rs index 5823498973a33..35782a7bf92c4 100644 --- a/tests/assembly-llvm/naked-functions/link-section-windows.rs +++ b/tests/assembly-llvm/naked-functions/link-section-windows.rs @@ -1,4 +1,4 @@ -//@ revisions: windows-x86-gnu windows-x86-msvc +//@ revisions: windows-x86-gnu windows-x86-msvc x86-uefi //@ add-minicore //@ assembly-output: emit-asm // @@ -7,6 +7,9 @@ // //@[windows-x86-msvc] compile-flags: --target x86_64-pc-windows-msvc //@[windows-x86-msvc] needs-llvm-components: x86 +// +//@[x86-uefi] compile-flags: --target x86_64-unknown-uefi +//@[x86-uefi] needs-llvm-components: x86 #![crate_type = "lib"] #![feature(no_core)] From 41afd5f8d6e7ac25f2543fbd2f654ebb8d75f253 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Thu, 16 Apr 2026 11:36:55 +0200 Subject: [PATCH 14/23] handle `uefi` and test assembly versus regular functions --- .../rustc_codegen_ssa/src/mir/naked_asm.rs | 12 ++- .../naked-functions/function-sections.rs | 97 +++++++++++++++++++ 2 files changed, 107 insertions(+), 2 deletions(-) create mode 100644 tests/assembly-llvm/naked-functions/function-sections.rs diff --git a/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs b/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs index c94376c7673da..bdefacefd20b9 100644 --- a/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs +++ b/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs @@ -8,7 +8,7 @@ use rustc_middle::ty::{Instance, Ty, TyCtxt, TypeVisitableExt}; use rustc_middle::{bug, ty}; use rustc_span::sym; use rustc_target::callconv::{ArgAbi, FnAbi, PassMode}; -use rustc_target::spec::{Arch, BinaryFormat, Env}; +use rustc_target::spec::{Arch, BinaryFormat, Env, Os}; use crate::common; use crate::mir::AsmCodegenMethods; @@ -268,6 +268,8 @@ fn prefix_and_suffix<'tcx>( // `.subsections_via_symbols` global directive. LLVM already enables this directive. if let Some(section) = &link_section { writeln!(begin, ".pushsection {section},regular,pure_instructions").unwrap(); + } else { + writeln!(begin, ".section __TEXT,__text,regular,pure_instructions").unwrap(); } writeln!(begin, ".balign {align_bytes}").unwrap(); write_linkage(&mut begin).unwrap(); @@ -313,7 +315,13 @@ fn prefix_and_suffix<'tcx>( Env::Msvc => { writeln!(begin, ".section .text,\"xr\",one_only,{asm_name}").unwrap(); } - other => bug!("invalid coff env {other:?}"), + Env::Unspecified => match &tcx.sess.target.options.os { + Os::Uefi => { + writeln!(begin, ".section .text,\"xr\",one_only,{asm_name}").unwrap(); + } + _ => bug!("unexpected coff target {}", tcx.sess.target.llvm_target), + }, + other => bug!("unexpected coff env {other:?}"), } } write_linkage(&mut begin).unwrap(); diff --git a/tests/assembly-llvm/naked-functions/function-sections.rs b/tests/assembly-llvm/naked-functions/function-sections.rs new file mode 100644 index 0000000000000..751812bd768b2 --- /dev/null +++ b/tests/assembly-llvm/naked-functions/function-sections.rs @@ -0,0 +1,97 @@ +//@ add-minicore +//@ assembly-output: emit-asm +// +//@ revisions: linux-x86-gnu-fs-true linux-x86-gnu-fs-false +//@[linux-x86-gnu-fs-true] compile-flags: --target x86_64-unknown-linux-gnu -Zfunction-sections=true +//@[linux-x86-gnu-fs-true] needs-llvm-components: x86 +//@[linux-x86-gnu-fs-false] compile-flags: --target x86_64-unknown-linux-gnu -Zfunction-sections=false +//@[linux-x86-gnu-fs-false] needs-llvm-components: x86 +// +//@ revisions: macos-aarch64-fs-true macos-aarch64-fs-false +//@[macos-aarch64-fs-true] compile-flags: --target aarch64-apple-darwin -Zfunction-sections=true +//@[macos-aarch64-fs-true] needs-llvm-components: aarch64 +//@[macos-aarch64-fs-false] compile-flags: --target aarch64-apple-darwin -Zfunction-sections=false +//@[macos-aarch64-fs-false] needs-llvm-components: aarch64 +// +//@ revisions: windows-x86-gnu-fs-true windows-x86-gnu-fs-false +//@[windows-x86-gnu-fs-true] compile-flags: --target x86_64-pc-windows-gnu -Zfunction-sections=true +//@[windows-x86-gnu-fs-true] needs-llvm-components: x86 +//@[windows-x86-gnu-fs-false] compile-flags: --target x86_64-pc-windows-gnu -Zfunction-sections=false +//@[windows-x86-gnu-fs-false] needs-llvm-components: x86 +// +//@ revisions: windows-x86-msvc-fs-true windows-x86-msvc-fs-false +//@[windows-x86-msvc-fs-true] compile-flags: --target x86_64-pc-windows-msvc -Zfunction-sections=true +//@[windows-x86-msvc-fs-true] needs-llvm-components: x86 +//@[windows-x86-msvc-fs-false] compile-flags: --target x86_64-pc-windows-msvc -Zfunction-sections=false +//@[windows-x86-msvc-fs-false] needs-llvm-components: x86 +// +//@ revisions: x86-uefi-fs-true x86-uefi-fs-false +//@[x86-uefi-fs-true] compile-flags: --target x86_64-unknown-uefi -Zfunction-sections=true +//@[x86-uefi-fs-true] needs-llvm-components: x86 +//@[x86-uefi-fs-false] compile-flags: --target x86_64-unknown-uefi -Zfunction-sections=false +//@[x86-uefi-fs-false] needs-llvm-components: x86 + +#![crate_type = "lib"] +#![feature(no_core)] +#![no_core] + +// Tests that naked and non-naked functions emit the same directives when (not) using +// -Zfunction-sections. This setting is ignored on macos, off by default on windows gnu, +// and on by default in the remaining revisions tested here. + +extern crate minicore; +use minicore::*; + +#[unsafe(naked)] +#[unsafe(no_mangle)] +extern "C" fn naked_ret() { + // linux-x86-gnu-fs-true: .section .text.naked_ret,"ax",@progbits + // linux-x86-gnu-fs-false: .text + // + // macos-aarch64-fs-true: .section __TEXT,__text,regular,pure_instructions + // macos-aarch64-fs-false: .section __TEXT,__text,regular,pure_instructions + // + // NOTE: the regular function below adds `unique,0` at the end, but we have no way of generating + // the unique ID to use there, so don't emit that part. + // + // windows-x86-gnu-fs-true: .section .text$naked_ret,"xr",one_only,naked_ret + // windows-x86-msvc-fs-true: .section .text,"xr",one_only,naked_ret + // x86-uefi-fs-true: .section .text,"xr",one_only,naked_ret + // + // windows-x86-gnu-fs-false: .text + // windows-x86-msvc-fs-false: .text + // x86-uefi-fs-false: .text + // + // CHECK-LABEL: naked_ret: + naked_asm!("ret") +} + +// Use a different section here so that `regular_ret` has to explicitly specify the section. +#[link_section = cfg_select!( + target_os = "macos" => "__FOO,bar", + _ => ".bar", +)] +#[unsafe(no_mangle)] +extern "C" fn omarker() -> i32 { + // CHECK-LABEL: omarker: + 32 +} + +#[unsafe(no_mangle)] +extern "C" fn regular_ret() { + // linux-x86-gnu-fs-true: .section .text.regular_ret,"ax",@progbits + // linux-x86-gnu-fs-false: .text + // + // macos-aarch64-fs-true: .section __TEXT,__text,regular,pure_instructions + // macos-aarch64-fs-false: .section __TEXT,__text,regular,pure_instructions + // + // windows-x86-gnu-fs-true: .section .text$regular_ret,"xr",one_only,regular_ret,unique,0 + // windows-x86-msvc-fs-true: .section .text,"xr",one_only,regular_ret,unique,0 + // x86-uefi-fs-true: .section .text,"xr",one_only,regular_ret,unique,0 + // + // windows-x86-gnu-fs-false: .text + // windows-x86-msvc-fs-false: .text + // x86-uefi-fs-false: .text + // + // CHECK-LABEL: regular_ret: +} From a66b7810c34987d61131fb18bafd6e815252d22c Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Fri, 11 Apr 2025 12:23:00 -0700 Subject: [PATCH 15/23] `impl Default for RepeatN` This creates an empty iterator, like `repeat_n(value, 0)` but without needing any such value at hand. There's precedent in many other iterators that the `Default` is empty, like `slice::Iter`. I found myself wanting this for rayon's `RepeatN` as it lowers to a sequential iterator [here][1]. Since rayon is also optimizing to avoid extra clones, it may end up with parallel splits that have count 0 and no item value. Calling `std::iter::repeat_n(x, 0)` just drops that value, but there's no way to construct the same result without a value yet. This would be straightforward with an empty `Default`. [1]: https://github.com/rayon-rs/rayon/blob/ae07384e3e0b238cea89f0c14891f351c65a5cee/src/iter/repeat.rs#L201-L202 --- library/core/src/iter/sources/repeat_n.rs | 9 +++++++++ library/coretests/tests/iter/sources.rs | 16 ++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/library/core/src/iter/sources/repeat_n.rs b/library/core/src/iter/sources/repeat_n.rs index c29ab24a08357..4cbaf41852142 100644 --- a/library/core/src/iter/sources/repeat_n.rs +++ b/library/core/src/iter/sources/repeat_n.rs @@ -102,6 +102,15 @@ impl fmt::Debug for RepeatN { } } +/// Creates an empty iterator, like [`repeat_n(value, 0)`][`repeat_n`] +/// but without needing any such value at hand. +#[stable(feature = "iter_repeat_n_default", since = "CURRENT_RUSTC_VERSION")] +impl Default for RepeatN { + fn default() -> Self { + RepeatN { inner: None } + } +} + #[stable(feature = "iter_repeat_n", since = "1.82.0")] impl Iterator for RepeatN { type Item = A; diff --git a/library/coretests/tests/iter/sources.rs b/library/coretests/tests/iter/sources.rs index 420f3088e6ee4..c1df278b54064 100644 --- a/library/coretests/tests/iter/sources.rs +++ b/library/coretests/tests/iter/sources.rs @@ -192,3 +192,19 @@ fn test_repeat_n_soundness() { let _z = y; assert_eq!(0, *x); } + +#[test] +fn test_repeat_n_default() { + #[derive(Clone)] + pub struct PanicOnDrop; + + impl Drop for PanicOnDrop { + fn drop(&mut self) { + unreachable!() + } + } + + // The default is an empty iterator, so there's never any item to drop. + let iter = RepeatN::::default(); + assert_eq!(iter.count(), 0); +} From 2db9de37823a97aaa3e1d1037fc8fd1c7c09ca6f Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Thu, 16 Apr 2026 21:14:22 +0200 Subject: [PATCH 16/23] add `ignore-cross-compile` to run-make test --- .../naked-dead-code-elimination/main.rs | 14 +++++++++---- .../naked-dead-code-elimination/rmake.rs | 21 +++++++++++++++++-- 2 files changed, 29 insertions(+), 6 deletions(-) diff --git a/tests/run-make/naked-dead-code-elimination/main.rs b/tests/run-make/naked-dead-code-elimination/main.rs index 83da62d314587..5df2691e5b443 100644 --- a/tests/run-make/naked-dead-code-elimination/main.rs +++ b/tests/run-make/naked-dead-code-elimination/main.rs @@ -7,6 +7,16 @@ extern "C" fn used() { naked_asm!("ret") } +#[unsafe(no_mangle)] +extern "C" fn used_clothed() -> i32 { + 41 +} + +pub fn main() { + std::hint::black_box(used()); + std::hint::black_box(used_clothed()); +} + #[unsafe(no_mangle)] extern "C" fn unused_clothed() -> i32 { 42 @@ -36,7 +46,3 @@ extern "C" fn unused_link_section() { extern "C" fn unused_link_section_clothed() -> i32 { 43 } - -fn main() { - used(); -} diff --git a/tests/run-make/naked-dead-code-elimination/rmake.rs b/tests/run-make/naked-dead-code-elimination/rmake.rs index a29212084b127..1be22de367c99 100644 --- a/tests/run-make/naked-dead-code-elimination/rmake.rs +++ b/tests/run-make/naked-dead-code-elimination/rmake.rs @@ -1,13 +1,30 @@ +//@ ignore-cross-compile //@ needs-asm-support use run_make_support::symbols::object_contains_any_symbol; use run_make_support::{bin_name, rustc}; fn main() { - rustc().input("main.rs").opt().function_sections(true).run(); - let bin = bin_name("main"); + rustc().input("main.rs").opt().function_sections(false).run(); + + // Check that the naked symbol is eliminated when the "clothed" one is. + + assert_eq!( + object_contains_any_symbol(&bin, &["unused_clothed"]), + object_contains_any_symbol(&bin, &["unused"]) + ); + + assert_eq!( + object_contains_any_symbol(&bin, &["unused_link_section_clothed"]), + object_contains_any_symbol(&bin, &["unused_link_section"]) + ); + + // --- + + rustc().input("main.rs").opt().function_sections(true).run(); + // Check that the naked symbol is eliminated when the "clothed" one is. assert_eq!( From 9b36d40819e81807cb64f0e8651284f2f139c4db Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Fri, 17 Apr 2026 12:03:58 +0200 Subject: [PATCH 17/23] ptr: update text in intro text to one in with_addr doc The one where this was copied from has since been updated. --- library/core/src/ptr/mod.rs | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs index ddeb1ccc72af7..0d68fad10f733 100644 --- a/library/core/src/ptr/mod.rs +++ b/library/core/src/ptr/mod.rs @@ -262,16 +262,15 @@ //! [`with_addr`] method: //! //! ```text -//! /// Creates a new pointer with the given address. -//! /// -//! /// This performs the same operation as an `addr as ptr` cast, but copies -//! /// the *provenance* of `self` to the new pointer. -//! /// This allows us to dynamically preserve and propagate this important -//! /// information in a way that is otherwise impossible with a unary cast. -//! /// -//! /// This is equivalent to using `wrapping_offset` to offset `self` to the -//! /// given address, and therefore has all the same capabilities and restrictions. -//! pub fn with_addr(self, addr: usize) -> Self; +//! /// Creates a new pointer with the given address and the provenance of `self`. +//! /// +//! /// This is similar to a `addr as *const T` cast, +//! /// but copies the provenance of `self` to the new pointer. +//! /// This avoids the inherent ambiguity of the unary cast. +//! /// +//! /// This is equivalent to using `wrapping_offset` to offset `self` to the given address, +//! /// and therefore has all the same capabilities and restrictions. +//! pub fn with_addr(self, addr: usize) -> Self; //! ``` //! //! So you're still able to drop down to the address representation and do whatever From b967de6255686bcb11cf16f6ae55e5ab578cbad8 Mon Sep 17 00:00:00 2001 From: Tony Wu Date: Fri, 17 Apr 2026 14:19:14 +0800 Subject: [PATCH 18/23] rustdoc: fix issues with redundant_explicit_links --- .../passes/lint/redundant_explicit_links.rs | 24 ++++++++---- ...edundant_explicit_links-some-skipped.fixed | 17 ++++++++ .../redundant_explicit_links-some-skipped.rs | 17 ++++++++ ...dundant_explicit_links-some-skipped.stderr | 39 +++++++++++++++++++ .../redundant_explicit_links-with-title.rs | 15 +++++++ 5 files changed, 104 insertions(+), 8 deletions(-) create mode 100644 tests/rustdoc-ui/lints/redundant_explicit_links-some-skipped.fixed create mode 100644 tests/rustdoc-ui/lints/redundant_explicit_links-some-skipped.rs create mode 100644 tests/rustdoc-ui/lints/redundant_explicit_links-some-skipped.stderr create mode 100644 tests/rustdoc-ui/lints/redundant_explicit_links-with-title.rs diff --git a/src/librustdoc/passes/lint/redundant_explicit_links.rs b/src/librustdoc/passes/lint/redundant_explicit_links.rs index 13bc4c079aa78..8da21f100c6a3 100644 --- a/src/librustdoc/passes/lint/redundant_explicit_links.rs +++ b/src/librustdoc/passes/lint/redundant_explicit_links.rs @@ -90,12 +90,23 @@ fn check_redundant_explicit_link<'md>( .into_offset_iter(); while let Some((event, link_range)) = offset_iter.next() { - if let Event::Start(Tag::Link { link_type, dest_url, .. }) = event { + if let Event::Start(Tag::Link { link_type, dest_url, title, .. }) = event { + if !title.is_empty() { + // Skips if the link specifies a title, e.g. `[Option](Option "title")`, + // in which case the explicit link cannot be removed without also + // removing the title. + continue; + } + let link_data = collect_link_data(&mut offset_iter); - if let Some(resolvable_link) = link_data.resolvable_link.as_ref() - && &link_data.display_link.replace('`', "") != resolvable_link - { + let Some(resolvable_link) = link_data.resolvable_link.as_ref() else { + // collect_link_data didn't return a resolvable_link + // most likely due to the displayed link containing inline markup + continue; + }; + + if &link_data.display_link.replace('`', "") != resolvable_link { // Skips if display link does not match to actual // resolvable link, usually happens if display link // has several segments, e.g. @@ -103,10 +114,7 @@ fn check_redundant_explicit_link<'md>( continue; } - let explicit_link = dest_url.to_string(); - let display_link = link_data.resolvable_link.clone()?; - - if explicit_link.ends_with(&display_link) || display_link.ends_with(&explicit_link) { + if dest_url.ends_with(resolvable_link) || resolvable_link.ends_with(&*dest_url) { match link_type { LinkType::Inline | LinkType::ReferenceUnknown => { check_inline_or_reference_unknown_redundancy( diff --git a/tests/rustdoc-ui/lints/redundant_explicit_links-some-skipped.fixed b/tests/rustdoc-ui/lints/redundant_explicit_links-some-skipped.fixed new file mode 100644 index 0000000000000..75e2398e64c5a --- /dev/null +++ b/tests/rustdoc-ui/lints/redundant_explicit_links-some-skipped.fixed @@ -0,0 +1,17 @@ +//@ run-rustfix + +// There was a logic error in `redundant_explicit_links` that caused the lint +// to skip all remaining links once it skipped a link containing inline markups. +// This test asserts that the lint continues after skipping such links. + +#![deny(rustdoc::redundant_explicit_links)] + +/// [Option] +///~^ ERROR redundant explicit link target +/// +/// [**u8**](u8) +/// This link should not lint. +/// +/// [Result] +///~^ ERROR redundant explicit link target +pub fn func() {} diff --git a/tests/rustdoc-ui/lints/redundant_explicit_links-some-skipped.rs b/tests/rustdoc-ui/lints/redundant_explicit_links-some-skipped.rs new file mode 100644 index 0000000000000..0c39ad8f18013 --- /dev/null +++ b/tests/rustdoc-ui/lints/redundant_explicit_links-some-skipped.rs @@ -0,0 +1,17 @@ +//@ run-rustfix + +// There was a logic error in `redundant_explicit_links` that caused the lint +// to skip all remaining links once it skipped a link containing inline markups. +// This test asserts that the lint continues after skipping such links. + +#![deny(rustdoc::redundant_explicit_links)] + +/// [Option][Option] +///~^ ERROR redundant explicit link target +/// +/// [**u8**](u8) +/// This link should not lint. +/// +/// [Result][Result] +///~^ ERROR redundant explicit link target +pub fn func() {} diff --git a/tests/rustdoc-ui/lints/redundant_explicit_links-some-skipped.stderr b/tests/rustdoc-ui/lints/redundant_explicit_links-some-skipped.stderr new file mode 100644 index 0000000000000..61f4ee584da8b --- /dev/null +++ b/tests/rustdoc-ui/lints/redundant_explicit_links-some-skipped.stderr @@ -0,0 +1,39 @@ +error: redundant explicit link target + --> $DIR/redundant_explicit_links-some-skipped.rs:9:14 + | +LL | /// [Option][Option] + | ------ ^^^^^^ 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-some-skipped.rs:7:9 + | +LL | #![deny(rustdoc::redundant_explicit_links)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: remove explicit link target + | +LL - /// [Option][Option] +LL + /// [Option] + | + +error: redundant explicit link target + --> $DIR/redundant_explicit_links-some-skipped.rs:15:14 + | +LL | /// [Result][Result] + | ------ ^^^^^^ 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 +help: remove explicit link target + | +LL - /// [Result][Result] +LL + /// [Result] + | + +error: aborting due to 2 previous errors + diff --git a/tests/rustdoc-ui/lints/redundant_explicit_links-with-title.rs b/tests/rustdoc-ui/lints/redundant_explicit_links-with-title.rs new file mode 100644 index 0000000000000..01b84563ce556 --- /dev/null +++ b/tests/rustdoc-ui/lints/redundant_explicit_links-with-title.rs @@ -0,0 +1,15 @@ +//@ check-pass + +#![deny(rustdoc::redundant_explicit_links)] + +/// [drop](drop "This function is not magic") +/// +/// This link should not lint, because it specifies a link title, and it is +/// not possible to remove the explicit link without also removing the title. +/// +/// [Vec][vec] +/// +/// [vec]: std::vec::Vec "A contiguous growable array type" +/// +/// This also applies to reference-style links. +pub fn func() {} From d9c717783ed80069701229347138f56f2f58f1ff Mon Sep 17 00:00:00 2001 From: aisr Date: Fri, 17 Apr 2026 23:44:35 +0800 Subject: [PATCH 19/23] remove unnecessary safety conditions related to unchecked uint arithmetic --- library/core/src/num/uint_macros.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/library/core/src/num/uint_macros.rs b/library/core/src/num/uint_macros.rs index b925400e19227..5be1b837798f3 100644 --- a/library/core/src/num/uint_macros.rs +++ b/library/core/src/num/uint_macros.rs @@ -866,7 +866,7 @@ macro_rules! uint_impl { /// # Safety /// /// This results in undefined behavior when - #[doc = concat!("`self + rhs > ", stringify!($SelfT), "::MAX` or `self + rhs < ", stringify!($SelfT), "::MIN`,")] + #[doc = concat!("`self + rhs > ", stringify!($SelfT), "::MAX`,")] /// i.e. when [`checked_add`] would return `None`. /// /// [`unwrap_unchecked`]: option/enum.Option.html#method.unwrap_unchecked @@ -1045,7 +1045,7 @@ macro_rules! uint_impl { /// # Safety /// /// This results in undefined behavior when - #[doc = concat!("`self - rhs > ", stringify!($SelfT), "::MAX` or `self - rhs < ", stringify!($SelfT), "::MIN`,")] + #[doc = concat!("`self - rhs < ", stringify!($SelfT), "::MIN`,")] /// i.e. when [`checked_sub`] would return `None`. /// /// [`unwrap_unchecked`]: option/enum.Option.html#method.unwrap_unchecked @@ -1254,7 +1254,7 @@ macro_rules! uint_impl { /// # Safety /// /// This results in undefined behavior when - #[doc = concat!("`self * rhs > ", stringify!($SelfT), "::MAX` or `self * rhs < ", stringify!($SelfT), "::MIN`,")] + #[doc = concat!("`self * rhs > ", stringify!($SelfT), "::MAX`,")] /// i.e. when [`checked_mul`] would return `None`. /// /// [`unwrap_unchecked`]: option/enum.Option.html#method.unwrap_unchecked From 10cc6c4a39c087be65533247d30a01ea03304a0c Mon Sep 17 00:00:00 2001 From: Olivier Amacker Date: Fri, 17 Apr 2026 19:33:21 +0200 Subject: [PATCH 20/23] docs: Fix typo in std/src/thread/scoped.rs --- library/std/src/thread/scoped.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/std/src/thread/scoped.rs b/library/std/src/thread/scoped.rs index 929f7fdc6dcac..d8e018bde7471 100644 --- a/library/std/src/thread/scoped.rs +++ b/library/std/src/thread/scoped.rs @@ -177,7 +177,7 @@ impl<'scope, 'env> Scope<'scope, 'env> { /// Spawns a new thread within a scope, returning a [`ScopedJoinHandle`] for it. /// /// Unlike non-scoped threads, threads spawned with this function may - /// borrow non-`'static` data from the outside the scope. See [`scope`] for + /// borrow non-`'static` data from outside the scope. See [`scope`] for /// details. /// /// The join handle provides a [`join`] method that can be used to join the spawned From 6525e06475404a18b6d239050d00a143e3d9a0f5 Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Fri, 17 Apr 2026 15:54:12 -0700 Subject: [PATCH 21/23] `std::error::Request`: add missing period in docs All but one of the bullet points ended with a period; add the missing period. --- library/core/src/error.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/core/src/error.rs b/library/core/src/error.rs index 011d6ac4a1c78..5bc020334c582 100644 --- a/library/core/src/error.rs +++ b/library/core/src/error.rs @@ -514,7 +514,7 @@ where /// /// * A Producer initializes the value of one of its fields of a specific type. (or is otherwise /// prepared to generate a value requested). eg, `backtrace::Backtrace` or -/// `std::backtrace::Backtrace` +/// `std::backtrace::Backtrace`. /// * A Consumer requests an object of a specific type (say `std::backtrace::Backtrace`). In the /// case of a `dyn Error` trait object (the Producer), there are functions called `request_ref` and /// `request_value` to simplify obtaining an `Option` for a given type. From 1d1aa9a7fb5f9d75838ff0a38b35bcd77808d9ce Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Fri, 17 Apr 2026 16:16:01 -0700 Subject: [PATCH 22/23] `std::error::Request`: more documentation cleanup --- library/core/src/error.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/library/core/src/error.rs b/library/core/src/error.rs index 5bc020334c582..0f01c09c8d91c 100644 --- a/library/core/src/error.rs +++ b/library/core/src/error.rs @@ -494,7 +494,7 @@ where /// `Request` supports generic, type-driven access to data. Its use is currently restricted to the /// standard library in cases where trait authors wish to allow trait implementors to share generic /// information across trait boundaries. The motivating and prototypical use case is -/// `core::error::Error` which would otherwise require a method per concrete type (eg. +/// `core::error::Error` which would otherwise require a method per concrete type (e.g. /// `std::backtrace::Backtrace` instance that implementors want to expose to users). /// /// # Data flow @@ -502,18 +502,18 @@ where /// To describe the intended data flow for Request objects, let's consider two conceptual users /// separated by API boundaries: /// -/// * Consumer - the consumer requests objects using a Request instance; eg a crate that offers +/// * Consumer - the consumer requests objects using a Request instance; e.g. a crate that offers /// fancy `Error`/`Result` reporting to users wants to request a Backtrace from a given `dyn Error`. /// -/// * Producer - the producer provides objects when requested via Request; eg. a library with an +/// * Producer - the producer provides objects when requested via Request; e.g. a library with an /// an `Error` implementation that automatically captures backtraces at the time instances are /// created. /// -/// The consumer only needs to know where to submit their request and are expected to handle the +/// The consumer only needs to know where to submit their request and is expected to handle the /// request not being fulfilled by the use of `Option` in the responses offered by the producer. /// /// * A Producer initializes the value of one of its fields of a specific type. (or is otherwise -/// prepared to generate a value requested). eg, `backtrace::Backtrace` or +/// prepared to generate a value requested). e.g., `backtrace::Backtrace` or /// `std::backtrace::Backtrace`. /// * A Consumer requests an object of a specific type (say `std::backtrace::Backtrace`). In the /// case of a `dyn Error` trait object (the Producer), there are functions called `request_ref` and @@ -521,10 +521,10 @@ where /// * The Producer, when requested, populates the given Request object which is given as a mutable /// reference. /// * The Consumer extracts a value or reference to the requested type from the `Request` object -/// wrapped in an `Option`; in the case of `dyn Error` the aforementioned `request_ref` and ` -/// request_value` methods mean that `dyn Error` users don't have to deal with the `Request` type at +/// wrapped in an `Option`; in the case of `dyn Error` the aforementioned `request_ref` and +/// `request_value` methods mean that `dyn Error` users don't have to deal with the `Request` type at /// all (but `Error` implementors do). The `None` case of the `Option` suggests only that the -/// Producer cannot currently offer an instance of the requested type, not it can't or never will. +/// Producer cannot currently offer an instance of the requested type, not that it can't or never will. /// /// # Examples /// From a779e054a91ac3bd8f5f1e8a898a2bb1affe3126 Mon Sep 17 00:00:00 2001 From: lapla Date: Fri, 17 Apr 2026 19:22:10 +0900 Subject: [PATCH 23/23] Fix ICE in borrowck mutability suggestion with multi-byte ref sigil --- .../src/diagnostics/mutability_errors.rs | 17 +++++++++--- tests/crashes/139089.rs | 2 -- ...k-assign-to-andmut-in-aliasable-loc.stderr | 2 +- ...orrow-mut-base-ptr-in-aliasable-loc.stderr | 2 +- tests/ui/borrowck/mutability-errors.stderr | 20 ++++++++------ ...tability-suggestion-fullwidth-ampersand.rs | 9 +++++++ ...lity-suggestion-fullwidth-ampersand.stderr | 27 +++++++++++++++++++ 7 files changed, 64 insertions(+), 15 deletions(-) delete mode 100644 tests/crashes/139089.rs create mode 100644 tests/ui/span/mutability-suggestion-fullwidth-ampersand.rs create mode 100644 tests/ui/span/mutability-suggestion-fullwidth-ampersand.stderr diff --git a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs index a0e53248c9047..1d90b5dbcd10d 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs @@ -1410,9 +1410,20 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { (span, " mut".to_owned(), true) // If there is already a binding, we modify it to be `mut`. } else if binding_exists { - // Shrink the span to just after the `&` in `&variable`. - let span = span.with_lo(span.lo() + BytePos(1)).shrink_to_lo(); - (span, "mut ".to_owned(), true) + // Replace the sigil with the mutable version. We may be dealing + // with parser recovery here and cannot assume the user actually + // typed `&` or `*const`, so we compute the prefix from the snippet. + let Ok(src) = self.infcx.tcx.sess.source_map().span_to_snippet(span) else { + return; + }; + let (prefix_len, replacement) = if local_decl.ty.is_ref() { + (src.chars().next().map_or(0, char::len_utf8), "&mut ") + } else { + (src.find("const").map_or(1, |i| i + "const".len()), "*mut ") + }; + let ws_len = src[prefix_len..].len() - src[prefix_len..].trim_start().len(); + let span = span.with_hi(span.lo() + BytePos((prefix_len + ws_len) as u32)); + (span, replacement.to_owned(), true) } else { // Otherwise, suggest that the user annotates the binding; We provide the // type of the local. diff --git a/tests/crashes/139089.rs b/tests/crashes/139089.rs deleted file mode 100644 index 3326aa6ad9846..0000000000000 --- a/tests/crashes/139089.rs +++ /dev/null @@ -1,2 +0,0 @@ -//@ known-bug: #139089 -pub fn foo3(x: &Vec) { x.push(0); } diff --git a/tests/ui/borrowck/borrowck-assign-to-andmut-in-aliasable-loc.stderr b/tests/ui/borrowck/borrowck-assign-to-andmut-in-aliasable-loc.stderr index 62d456c5510a2..84c689d81c9ce 100644 --- a/tests/ui/borrowck/borrowck-assign-to-andmut-in-aliasable-loc.stderr +++ b/tests/ui/borrowck/borrowck-assign-to-andmut-in-aliasable-loc.stderr @@ -17,7 +17,7 @@ LL | *s.pointer += 1; | help: consider changing this to be a mutable reference | -LL | fn c(s: &mut &mut S) { +LL | fn c(s: &mut &mut S) { | +++ error: aborting due to 2 previous errors diff --git a/tests/ui/borrowck/borrowck-borrow-mut-base-ptr-in-aliasable-loc.stderr b/tests/ui/borrowck/borrowck-borrow-mut-base-ptr-in-aliasable-loc.stderr index 7e516fe89b40c..11607159eb013 100644 --- a/tests/ui/borrowck/borrowck-borrow-mut-base-ptr-in-aliasable-loc.stderr +++ b/tests/ui/borrowck/borrowck-borrow-mut-base-ptr-in-aliasable-loc.stderr @@ -27,7 +27,7 @@ LL | let x: &mut isize = &mut **t0; | help: consider changing this to be a mutable reference | -LL | fn foo4(t0: &mut &mut isize) { +LL | fn foo4(t0: &mut &mut isize) { | +++ error: aborting due to 3 previous errors diff --git a/tests/ui/borrowck/mutability-errors.stderr b/tests/ui/borrowck/mutability-errors.stderr index 18d8e6eb1a6e7..34adf33a99c2d 100644 --- a/tests/ui/borrowck/mutability-errors.stderr +++ b/tests/ui/borrowck/mutability-errors.stderr @@ -74,8 +74,9 @@ LL | *x = (1,); | help: consider changing this to be a mutable pointer | -LL | unsafe fn named_ptr(x: *mut const (i32,)) { - | +++ +LL - unsafe fn named_ptr(x: *const (i32,)) { +LL + unsafe fn named_ptr(x: *mut (i32,)) { + | error[E0594]: cannot assign to `x.0`, which is behind a `*const` pointer --> $DIR/mutability-errors.rs:24:5 @@ -85,8 +86,9 @@ LL | (*x).0 = 1; | help: consider changing this to be a mutable pointer | -LL | unsafe fn named_ptr(x: *mut const (i32,)) { - | +++ +LL - unsafe fn named_ptr(x: *const (i32,)) { +LL + unsafe fn named_ptr(x: *mut (i32,)) { + | error[E0596]: cannot borrow `*x` as mutable, as it is behind a `*const` pointer --> $DIR/mutability-errors.rs:25:5 @@ -96,8 +98,9 @@ LL | &mut *x; | help: consider changing this to be a mutable pointer | -LL | unsafe fn named_ptr(x: *mut const (i32,)) { - | +++ +LL - unsafe fn named_ptr(x: *const (i32,)) { +LL + unsafe fn named_ptr(x: *mut (i32,)) { + | error[E0596]: cannot borrow `x.0` as mutable, as it is behind a `*const` pointer --> $DIR/mutability-errors.rs:26:5 @@ -107,8 +110,9 @@ LL | &mut (*x).0; | help: consider changing this to be a mutable pointer | -LL | unsafe fn named_ptr(x: *mut const (i32,)) { - | +++ +LL - unsafe fn named_ptr(x: *const (i32,)) { +LL + unsafe fn named_ptr(x: *mut (i32,)) { + | error[E0594]: cannot assign to data in a `*const` pointer --> $DIR/mutability-errors.rs:30:5 diff --git a/tests/ui/span/mutability-suggestion-fullwidth-ampersand.rs b/tests/ui/span/mutability-suggestion-fullwidth-ampersand.rs new file mode 100644 index 0000000000000..a9bdd381307e7 --- /dev/null +++ b/tests/ui/span/mutability-suggestion-fullwidth-ampersand.rs @@ -0,0 +1,9 @@ +// Regression test for https://github.com/rust-lang/rust/issues/139089 + +fn foo(x: &Vec) { + //~^ ERROR unknown start of token + x.push(0); + //~^ ERROR cannot borrow `*x` as mutable +} + +fn main() {} diff --git a/tests/ui/span/mutability-suggestion-fullwidth-ampersand.stderr b/tests/ui/span/mutability-suggestion-fullwidth-ampersand.stderr new file mode 100644 index 0000000000000..e6712f5da9a83 --- /dev/null +++ b/tests/ui/span/mutability-suggestion-fullwidth-ampersand.stderr @@ -0,0 +1,27 @@ +error: unknown start of token: \u{ff06} + --> $DIR/mutability-suggestion-fullwidth-ampersand.rs:3:11 + | +LL | fn foo(x: &Vec) { + | ^^ + | +help: Unicode character '&' (Fullwidth Ampersand) looks like '&' (Ampersand), but it is not + | +LL - fn foo(x: &Vec) { +LL + fn foo(x: &Vec) { + | + +error[E0596]: cannot borrow `*x` as mutable, as it is behind a `&` reference + --> $DIR/mutability-suggestion-fullwidth-ampersand.rs:5:5 + | +LL | x.push(0); + | ^ `x` is a `&` reference, so it cannot be borrowed as mutable + | +help: consider changing this to be a mutable reference + | +LL - fn foo(x: &Vec) { +LL + fn foo(x: &mut Vec) { + | + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0596`.