From 02dac5cd78ff36663facc7863e6b4ed4b8df92b2 Mon Sep 17 00:00:00 2001 From: xizheyin Date: Tue, 17 Jun 2025 23:42:57 +0800 Subject: [PATCH 01/24] add test suggest-clone-in-macro-issue-139253.rs Signed-off-by: xizheyin --- .../suggest-clone-in-macro-issue-139253.rs | 19 +++++++++ ...suggest-clone-in-macro-issue-139253.stderr | 41 +++++++++++++++++++ 2 files changed, 60 insertions(+) create mode 100644 tests/ui/typeck/suggestions/suggest-clone-in-macro-issue-139253.rs create mode 100644 tests/ui/typeck/suggestions/suggest-clone-in-macro-issue-139253.stderr diff --git a/tests/ui/typeck/suggestions/suggest-clone-in-macro-issue-139253.rs b/tests/ui/typeck/suggestions/suggest-clone-in-macro-issue-139253.rs new file mode 100644 index 0000000000000..3b3ea0586304b --- /dev/null +++ b/tests/ui/typeck/suggestions/suggest-clone-in-macro-issue-139253.rs @@ -0,0 +1,19 @@ +#[derive(Debug, Clone)] +struct Struct { field: S } + +#[derive(Debug, Clone)] +struct S; + +macro_rules! expand { + ($ident:ident) => { Struct { $ident } } +} + +fn test1() { + let field = &S; + let a: Struct = dbg!(expand!(field)); //~ ERROR mismatched types [E0308] + let b: Struct = dbg!(Struct { field }); //~ ERROR mismatched types [E0308] + let c: S = dbg!(field); //~ ERROR mismatched types [E0308] + let c: S = dbg!(dbg!(field)); //~ ERROR mismatched types [E0308] +} + +fn main() {} diff --git a/tests/ui/typeck/suggestions/suggest-clone-in-macro-issue-139253.stderr b/tests/ui/typeck/suggestions/suggest-clone-in-macro-issue-139253.stderr new file mode 100644 index 0000000000000..e615cecb36544 --- /dev/null +++ b/tests/ui/typeck/suggestions/suggest-clone-in-macro-issue-139253.stderr @@ -0,0 +1,41 @@ +error[E0308]: mismatched types + --> $DIR/suggest-clone-in-macro-issue-139253.rs:13:34 + | +LL | let a: Struct = dbg!(expand!(field)); + | ^^^^^ expected `S`, found `&S` + | +help: consider using clone here + | +LL | let a: Struct = dbg!(expand!(field: field.clone())); + | +++++++++++++++ + +error[E0308]: mismatched types + --> $DIR/suggest-clone-in-macro-issue-139253.rs:14:35 + | +LL | let b: Struct = dbg!(Struct { field }); + | ^^^^^ expected `S`, found `&S` + | +help: consider using clone here + | +LL | let b: Struct = dbg!(Struct { field: field.clone() }); + | +++++++++++++++ + +error[E0308]: mismatched types + --> $DIR/suggest-clone-in-macro-issue-139253.rs:15:16 + | +LL | let c: S = dbg!(field); + | ^^^^^^^^^^^ expected `S`, found `&S` + | + = note: this error originates in the macro `dbg` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0308]: mismatched types + --> $DIR/suggest-clone-in-macro-issue-139253.rs:16:16 + | +LL | let c: S = dbg!(dbg!(field)); + | ^^^^^^^^^^^^^^^^^ expected `S`, found `&S` + | + = note: this error originates in the macro `dbg` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0308`. From fd9c1b962ae29ff928c2c6310a227b56b9db19bd Mon Sep 17 00:00:00 2001 From: xizheyin Date: Tue, 17 Jun 2025 23:48:03 +0800 Subject: [PATCH 02/24] Find correct span when suggesting using clone Signed-off-by: xizheyin --- compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs | 4 +++- .../suggest-clone-in-macro-issue-139253.stderr | 8 ++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index 7e5f1d97a8bf4..1154cc26c59a1 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -1302,8 +1302,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { None => ".clone()".to_string(), }; + let span = expr.span.find_oldest_ancestor_in_same_ctxt().shrink_to_hi(); + diag.span_suggestion_verbose( - expr.span.shrink_to_hi(), + span, "consider using clone here", suggestion, Applicability::MachineApplicable, diff --git a/tests/ui/typeck/suggestions/suggest-clone-in-macro-issue-139253.stderr b/tests/ui/typeck/suggestions/suggest-clone-in-macro-issue-139253.stderr index e615cecb36544..59e56f672374e 100644 --- a/tests/ui/typeck/suggestions/suggest-clone-in-macro-issue-139253.stderr +++ b/tests/ui/typeck/suggestions/suggest-clone-in-macro-issue-139253.stderr @@ -27,6 +27,10 @@ LL | let c: S = dbg!(field); | ^^^^^^^^^^^ expected `S`, found `&S` | = note: this error originates in the macro `dbg` (in Nightly builds, run with -Z macro-backtrace for more info) +help: consider using clone here + | +LL | let c: S = dbg!(field).clone(); + | ++++++++ error[E0308]: mismatched types --> $DIR/suggest-clone-in-macro-issue-139253.rs:16:16 @@ -35,6 +39,10 @@ LL | let c: S = dbg!(dbg!(field)); | ^^^^^^^^^^^^^^^^^ expected `S`, found `&S` | = note: this error originates in the macro `dbg` (in Nightly builds, run with -Z macro-backtrace for more info) +help: consider using clone here + | +LL | let c: S = dbg!(dbg!(field)).clone(); + | ++++++++ error: aborting due to 4 previous errors From b315e9a2ceb341e07983f028347beafaa60cda0a Mon Sep 17 00:00:00 2001 From: Marijn Schouten Date: Fri, 4 Jul 2025 11:27:13 +0000 Subject: [PATCH 03/24] clippy fix: rely on autoderef --- library/core/src/borrow.rs | 6 +++--- library/core/src/clone.rs | 2 +- library/core/src/ops/deref.rs | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/library/core/src/borrow.rs b/library/core/src/borrow.rs index ccb1cc4e974d6..da05f236d2fef 100644 --- a/library/core/src/borrow.rs +++ b/library/core/src/borrow.rs @@ -223,20 +223,20 @@ impl BorrowMut for T { #[stable(feature = "rust1", since = "1.0.0")] impl Borrow for &T { fn borrow(&self) -> &T { - &**self + self } } #[stable(feature = "rust1", since = "1.0.0")] impl Borrow for &mut T { fn borrow(&self) -> &T { - &**self + self } } #[stable(feature = "rust1", since = "1.0.0")] impl BorrowMut for &mut T { fn borrow_mut(&mut self) -> &mut T { - &mut **self + self } } diff --git a/library/core/src/clone.rs b/library/core/src/clone.rs index a34d1b4a06497..51d037ddfd2cc 100644 --- a/library/core/src/clone.rs +++ b/library/core/src/clone.rs @@ -590,7 +590,7 @@ mod impls { #[inline(always)] #[rustc_diagnostic_item = "noop_method_clone"] fn clone(&self) -> Self { - *self + self } } diff --git a/library/core/src/ops/deref.rs b/library/core/src/ops/deref.rs index 9d9d18095bc64..c2dede9fa0889 100644 --- a/library/core/src/ops/deref.rs +++ b/library/core/src/ops/deref.rs @@ -158,7 +158,7 @@ impl const Deref for &T { #[rustc_diagnostic_item = "noop_method_deref"] fn deref(&self) -> &T { - *self + self } } @@ -171,7 +171,7 @@ impl const Deref for &mut T { type Target = T; fn deref(&self) -> &T { - *self + self } } @@ -280,7 +280,7 @@ pub trait DerefMut: ~const Deref + PointeeSized { #[rustc_const_unstable(feature = "const_deref", issue = "88955")] impl const DerefMut for &mut T { fn deref_mut(&mut self) -> &mut T { - *self + self } } From 54cc45d5b4a949fa29813253efc7897e27f48fe2 Mon Sep 17 00:00:00 2001 From: Martin Nordholts Date: Thu, 3 Jul 2025 23:24:14 +0200 Subject: [PATCH 04/24] tests: Don't check for self-printed output in std-backtrace.rs test The `Display` implementation for `Backtrace` used to print stack backtrace: but that print was later removed. To make the existing test pass, the print was added to the existing test. But it doesn't make sense to check for something that the test itself does since that will not detect any regressions in the implementation of `Backtrace`. Fully remove the checks. --- tests/ui/backtrace/std-backtrace.rs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/tests/ui/backtrace/std-backtrace.rs b/tests/ui/backtrace/std-backtrace.rs index 7ccbd46152bbf..b81bdee44e480 100644 --- a/tests/ui/backtrace/std-backtrace.rs +++ b/tests/ui/backtrace/std-backtrace.rs @@ -13,9 +13,9 @@ use std::str; fn main() { let args: Vec = env::args().collect(); if args.len() >= 2 && args[1] == "force" { - println!("stack backtrace:\n{}", std::backtrace::Backtrace::force_capture()); + println!("{}", std::backtrace::Backtrace::force_capture()); } else if args.len() >= 2 { - println!("stack backtrace:\n{}", std::backtrace::Backtrace::capture()); + println!("{}", std::backtrace::Backtrace::capture()); } else { runtest(&args[0]); println!("test ok"); @@ -28,7 +28,6 @@ fn runtest(me: &str) { let p = Command::new(me).arg("a").env("RUST_BACKTRACE", "1").output().unwrap(); assert!(p.status.success()); - assert!(String::from_utf8_lossy(&p.stdout).contains("stack backtrace:\n")); assert!(String::from_utf8_lossy(&p.stdout).contains("backtrace::main")); let p = Command::new(me).arg("a").env("RUST_BACKTRACE", "0").output().unwrap(); @@ -46,7 +45,6 @@ fn runtest(me: &str) { .output() .unwrap(); assert!(p.status.success()); - assert!(String::from_utf8_lossy(&p.stdout).contains("stack backtrace:\n")); let p = Command::new(me) .arg("a") @@ -64,9 +62,7 @@ fn runtest(me: &str) { .output() .unwrap(); assert!(p.status.success()); - assert!(String::from_utf8_lossy(&p.stdout).contains("stack backtrace:\n")); let p = Command::new(me).arg("force").output().unwrap(); assert!(p.status.success()); - assert!(String::from_utf8_lossy(&p.stdout).contains("stack backtrace:\n")); } From d77c38727298ee442db4d927c13bc8b2de1db448 Mon Sep 17 00:00:00 2001 From: Daniel Paoliello Date: Sun, 13 Jul 2025 15:01:48 -0700 Subject: [PATCH 05/24] Fixes for Arm64EC --- tests/ui/cfg/conditional-compile-arch.rs | 3 +++ tests/ui/linkage-attr/incompatible-flavor.rs | 2 +- tests/ui/runtime/backtrace-debuginfo.rs | 3 ++- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/tests/ui/cfg/conditional-compile-arch.rs b/tests/ui/cfg/conditional-compile-arch.rs index 594d9344561c2..f16805474074d 100644 --- a/tests/ui/cfg/conditional-compile-arch.rs +++ b/tests/ui/cfg/conditional-compile-arch.rs @@ -38,3 +38,6 @@ pub fn main() { } #[cfg(target_arch = "loongarch64")] pub fn main() { } + +#[cfg(target_arch = "arm64ec")] +pub fn main() { } diff --git a/tests/ui/linkage-attr/incompatible-flavor.rs b/tests/ui/linkage-attr/incompatible-flavor.rs index 7f583f47e2f5d..4711343f9c9d6 100644 --- a/tests/ui/linkage-attr/incompatible-flavor.rs +++ b/tests/ui/linkage-attr/incompatible-flavor.rs @@ -1,5 +1,5 @@ //@ compile-flags: --target=x86_64-unknown-linux-gnu -C linker-flavor=msvc --crate-type=rlib -//@ needs-llvm-components: +//@ needs-llvm-components: x86 #![feature(no_core)] #![no_core] diff --git a/tests/ui/runtime/backtrace-debuginfo.rs b/tests/ui/runtime/backtrace-debuginfo.rs index 37fce2788b7f0..5fb9943d6c3c4 100644 --- a/tests/ui/runtime/backtrace-debuginfo.rs +++ b/tests/ui/runtime/backtrace-debuginfo.rs @@ -43,12 +43,13 @@ macro_rules! dump_and_die { // rust-lang/rust to test it as well, but sometimes we just gotta keep // landing PRs. // - // aarch64-msvc is broken as its backtraces are truncated. + // aarch64-msvc/arm64ec-msvc is broken as its backtraces are truncated. // See https://github.com/rust-lang/rust/issues/140489 if cfg!(any(target_os = "android", all(target_os = "linux", target_arch = "arm"), all(target_env = "msvc", target_arch = "x86"), all(target_env = "msvc", target_arch = "aarch64"), + all(target_env = "msvc", target_arch = "arm64ec"), target_os = "freebsd", target_os = "dragonfly", target_os = "openbsd")) { From 05154af3e857f23b41072793e401a78a5aa952d9 Mon Sep 17 00:00:00 2001 From: SunkenPotato <3.14radius02@gmail.com> Date: Tue, 15 Jul 2025 16:26:24 +0200 Subject: [PATCH 06/24] docs: update documentation of core::mem::copy to include const on the fn signature --- library/core/src/mem/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/core/src/mem/mod.rs b/library/core/src/mem/mod.rs index 1bd12d818cfe6..164fcdad5c40f 100644 --- a/library/core/src/mem/mod.rs +++ b/library/core/src/mem/mod.rs @@ -960,7 +960,7 @@ pub fn drop(_x: T) {} /// /// This function is not magic; it is literally defined as /// ``` -/// pub fn copy(x: &T) -> T { *x } +/// pub const fn copy(x: &T) -> T { *x } /// ``` /// /// It is useful when you want to pass a function pointer to a combinator, rather than defining a new closure. From 1d0eddbedd3b364418d2ae12b1240d487488ac5b Mon Sep 17 00:00:00 2001 From: Jens Reidel Date: Fri, 18 Jul 2025 23:26:43 +0200 Subject: [PATCH 07/24] tests: debuginfo: Work around or disable broken tests on powerpc f16 support for PowerPC has issues in LLVM, therefore we need a small workaround to prevent LLVM from emitting symbols that don't exist for PowerPC yet. It also appears that unused by-value non-immedate issue with gdb applies to PowerPC targets as well, though I've only tested 64-bit Linux targets. Signed-off-by: Jens Reidel --- src/tools/compiletest/src/directives.rs | 1 + tests/debuginfo/basic-types-globals-metadata.rs | 5 ++++- tests/debuginfo/basic-types-globals.rs | 5 ++++- tests/debuginfo/by-value-non-immediate-argument.rs | 1 + 4 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/tools/compiletest/src/directives.rs b/src/tools/compiletest/src/directives.rs index 93133ea0bfd2e..75c11c31e493a 100644 --- a/src/tools/compiletest/src/directives.rs +++ b/src/tools/compiletest/src/directives.rs @@ -854,6 +854,7 @@ const KNOWN_DIRECTIVE_NAMES: &[&str] = &[ "ignore-openbsd", "ignore-pass", "ignore-powerpc", + "ignore-powerpc64", "ignore-remote", "ignore-riscv64", "ignore-rustc-debug-assertions", diff --git a/tests/debuginfo/basic-types-globals-metadata.rs b/tests/debuginfo/basic-types-globals-metadata.rs index 53fc550a2dc8c..d14d5472f53bb 100644 --- a/tests/debuginfo/basic-types-globals-metadata.rs +++ b/tests/debuginfo/basic-types-globals-metadata.rs @@ -59,7 +59,10 @@ static mut F64: f64 = 3.5; fn main() { _zzz(); // #break - let a = unsafe { (B, I, C, I8, I16, I32, I64, U, U8, U16, U32, U64, F16, F32, F64) }; + let a = unsafe { (B, I, C, I8, I16, I32, I64, U, U8, U16, U32, U64, F32, F64) }; + // N.B. Including f16 and f32 in the same tuple emits `__gnu_h2f_ieee`, which does + // not exist on some targets like PowerPC + let b = unsafe { F16 }; } fn _zzz() {()} diff --git a/tests/debuginfo/basic-types-globals.rs b/tests/debuginfo/basic-types-globals.rs index 41b69939650da..5933c6d2440dc 100644 --- a/tests/debuginfo/basic-types-globals.rs +++ b/tests/debuginfo/basic-types-globals.rs @@ -63,7 +63,10 @@ static mut F64: f64 = 3.5; fn main() { _zzz(); // #break - let a = unsafe { (B, I, C, I8, I16, I32, I64, U, U8, U16, U32, U64, F16, F32, F64) }; + let a = unsafe { (B, I, C, I8, I16, I32, I64, U, U8, U16, U32, U64, F32, F64) }; + // N.B. Including f16 and f32 in the same tuple emits `__gnu_h2f_ieee`, which does + // not exist on some targets like PowerPC + let b = unsafe { F16 }; } fn _zzz() {()} diff --git a/tests/debuginfo/by-value-non-immediate-argument.rs b/tests/debuginfo/by-value-non-immediate-argument.rs index 5233b95f1f4ad..deacea5f6ccd8 100644 --- a/tests/debuginfo/by-value-non-immediate-argument.rs +++ b/tests/debuginfo/by-value-non-immediate-argument.rs @@ -3,6 +3,7 @@ //@ compile-flags:-g //@ ignore-windows-gnu: #128973 //@ ignore-aarch64-unknown-linux-gnu (gdb tries to read from 0x0; FIXME: #128973) +//@ ignore-powerpc64: #128973 on both -gnu and -musl // === GDB TESTS =================================================================================== From 690ae523e5c0ac9602db7bbe2b8094ee422e4d0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Sun, 20 Jul 2025 01:19:55 +0000 Subject: [PATCH 08/24] Tweak borrowck label pointing at `!Copy` value moved into closure When encountering a non-`Copy` value that is moved into a closure which is coming directly from a fn parameter, point at the parameter's type when mentioning it is not `Copy`. Before: ``` error[E0507]: cannot move out of `foo`, a captured variable in an `Fn` closure --> f111.rs:14:25 | 13 | fn do_stuff(foo: Option) { | --- captured outer variable 14 | require_fn_trait(|| async { | -- ^^^^^ `foo` is moved here | | | captured by this `Fn` closure 15 | if foo.map_or(false, |f| f.foo()) { | --- | | | variable moved due to use in coroutine | move occurs because `foo` has type `Option`, which does not implement the `Copy` trait ``` After: ``` error[E0507]: cannot move out of `foo`, a captured variable in an `Fn` closure --> f111.rs:14:25 | 13 | fn do_stuff(foo: Option) { | --- ----------- move occurs because `foo` has type `Option`, which does not implement the `Copy` trait | | | captured outer variable 14 | require_fn_trait(|| async { | -- ^^^^^ `foo` is moved here | | | captured by this `Fn` closure 15 | if foo.map_or(false, |f| f.foo()) { | --- variable moved due to use in coroutine ``` --- .../src/diagnostics/move_errors.rs | 129 +++++++++++------- tests/ui/issues/issue-4335.stderr | 6 +- 2 files changed, 82 insertions(+), 53 deletions(-) diff --git a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs index 92ca868eb9925..28024e6fecef4 100644 --- a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs @@ -115,10 +115,8 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { fn append_to_grouped_errors( &self, grouped_errors: &mut Vec>, - error: MoveError<'tcx>, + MoveError { place: original_path, location, kind }: MoveError<'tcx>, ) { - let MoveError { place: original_path, location, kind } = error; - // Note: that the only time we assign a place isn't a temporary // to a user variable is when initializing it. // If that ever stops being the case, then the ever initialized @@ -251,54 +249,47 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { } fn report(&mut self, error: GroupedMoveError<'tcx>) { - let (mut err, err_span) = { - let (span, use_spans, original_path, kind) = match error { - GroupedMoveError::MovesFromPlace { span, original_path, ref kind, .. } - | GroupedMoveError::MovesFromValue { span, original_path, ref kind, .. } => { - (span, None, original_path, kind) - } - GroupedMoveError::OtherIllegalMove { use_spans, original_path, ref kind } => { - (use_spans.args_or_use(), Some(use_spans), original_path, kind) - } - }; - debug!( - "report: original_path={:?} span={:?}, kind={:?} \ - original_path.is_upvar_field_projection={:?}", - original_path, - span, - kind, - self.is_upvar_field_projection(original_path.as_ref()) - ); - if self.has_ambiguous_copy(original_path.ty(self.body, self.infcx.tcx).ty) { - // If the type may implement Copy, skip the error. - // It's an error with the Copy implementation (e.g. duplicate Copy) rather than borrow check - self.dcx().span_delayed_bug( + let (span, use_spans, original_path, kind) = match error { + GroupedMoveError::MovesFromPlace { span, original_path, ref kind, .. } + | GroupedMoveError::MovesFromValue { span, original_path, ref kind, .. } => { + (span, None, original_path, kind) + } + GroupedMoveError::OtherIllegalMove { use_spans, original_path, ref kind } => { + (use_spans.args_or_use(), Some(use_spans), original_path, kind) + } + }; + debug!( + "report: original_path={:?} span={:?}, kind={:?} \ + original_path.is_upvar_field_projection={:?}", + original_path, + span, + kind, + self.is_upvar_field_projection(original_path.as_ref()) + ); + if self.has_ambiguous_copy(original_path.ty(self.body, self.infcx.tcx).ty) { + // If the type may implement Copy, skip the error. + // It's an error with the Copy implementation (e.g. duplicate Copy) rather than borrow check + self.dcx() + .span_delayed_bug(span, "Type may implement copy, but there is no other error."); + return; + } + let mut err = match kind { + &IllegalMoveOriginKind::BorrowedContent { target_place } => self + .report_cannot_move_from_borrowed_content( + original_path, + target_place, span, - "Type may implement copy, but there is no other error.", - ); - return; + use_spans, + ), + &IllegalMoveOriginKind::InteriorOfTypeWithDestructor { container_ty: ty } => { + self.cannot_move_out_of_interior_of_drop(span, ty) + } + &IllegalMoveOriginKind::InteriorOfSliceOrArray { ty, is_index } => { + self.cannot_move_out_of_interior_noncopy(span, ty, Some(is_index)) } - ( - match kind { - &IllegalMoveOriginKind::BorrowedContent { target_place } => self - .report_cannot_move_from_borrowed_content( - original_path, - target_place, - span, - use_spans, - ), - &IllegalMoveOriginKind::InteriorOfTypeWithDestructor { container_ty: ty } => { - self.cannot_move_out_of_interior_of_drop(span, ty) - } - &IllegalMoveOriginKind::InteriorOfSliceOrArray { ty, is_index } => { - self.cannot_move_out_of_interior_noncopy(span, ty, Some(is_index)) - } - }, - span, - ) }; - self.add_move_hints(error, &mut err, err_span); + self.add_move_hints(error, &mut err, span); self.buffer_error(err); } @@ -482,7 +473,8 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { self.cannot_move_out_of_interior_noncopy(span, ty, None) } ty::Closure(def_id, closure_args) - if def_id.as_local() == Some(self.mir_def_id()) && upvar_field.is_some() => + if def_id.as_local() == Some(self.mir_def_id()) + && let Some(upvar_field) = upvar_field => { let closure_kind_ty = closure_args.as_closure().kind_ty(); let closure_kind = match closure_kind_ty.to_opt_closure_kind() { @@ -495,7 +487,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { let capture_description = format!("captured variable in an `{closure_kind}` closure"); - let upvar = &self.upvars[upvar_field.unwrap().index()]; + let upvar = &self.upvars[upvar_field.index()]; let upvar_hir_id = upvar.get_root_variable(); let upvar_name = upvar.to_string(tcx); let upvar_span = tcx.hir_span(upvar_hir_id); @@ -604,8 +596,10 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { self.add_move_error_details(err, &binds_to); } // No binding. Nothing to suggest. - GroupedMoveError::OtherIllegalMove { ref original_path, use_spans, .. } => { - let use_span = use_spans.var_or_use(); + GroupedMoveError::OtherIllegalMove { + ref original_path, use_spans, ref kind, .. + } => { + let mut use_span = use_spans.var_or_use(); let place_ty = original_path.ty(self.body, self.infcx.tcx).ty; let place_desc = match self.describe_place(original_path.as_ref()) { Some(desc) => format!("`{desc}`"), @@ -622,6 +616,39 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { ); } + if let IllegalMoveOriginKind::BorrowedContent { target_place } = &kind + && let ty = target_place.ty(self.body, self.infcx.tcx).ty + && let ty::Closure(def_id, _) = ty.kind() + && def_id.as_local() == Some(self.mir_def_id()) + && let Some(upvar_field) = self + .prefixes(original_path.as_ref(), PrefixSet::All) + .find_map(|p| self.is_upvar_field_projection(p)) + && let upvar = &self.upvars[upvar_field.index()] + && let upvar_hir_id = upvar.get_root_variable() + && let hir::Node::Param(param) = self.infcx.tcx.parent_hir_node(upvar_hir_id) + { + // Instead of pointing at the path where we access the value within a closure, + // we point at the type on the parameter from the definition of the outer + // function: + // + // error[E0507]: cannot move out of `foo`, a captured + // variable in an `Fn` closure + // --> file.rs:14:25 + // | + // 13 | fn do_stuff(foo: Option) { + // | --- ----------- move occurs because `foo` has type + // | | `Option`, which does not implement + // | | the `Copy` trait + // | captured outer variable + // 14 | require_fn_trait(|| async { + // | -- ^^^^^ `foo` is moved here + // | | + // | captured by this `Fn` closure + // 15 | if foo.map_or(false, |f| f.foo()) { + // | --- variable moved due to use in coroutine + use_span = param.ty_span; + } + err.subdiagnostic(crate::session_diagnostics::TypeNoCopy::Label { is_partial_move: false, ty: place_ty, diff --git a/tests/ui/issues/issue-4335.stderr b/tests/ui/issues/issue-4335.stderr index 14b5cfa9f9ac4..1f1a2595a3189 100644 --- a/tests/ui/issues/issue-4335.stderr +++ b/tests/ui/issues/issue-4335.stderr @@ -2,9 +2,11 @@ error[E0507]: cannot move out of `*v`, as `v` is a captured variable in an `FnMu --> $DIR/issue-4335.rs:6:20 | LL | fn f<'r, T>(v: &'r T) -> Box T + 'r> { - | - captured outer variable + | - ----- move occurs because `*v` has type `T`, which does not implement the `Copy` trait + | | + | captured outer variable LL | id(Box::new(|| *v)) - | -- ^^ move occurs because `*v` has type `T`, which does not implement the `Copy` trait + | -- ^^ | | | captured by this `FnMut` closure | From 5082e6a300974459aec6dc73e76cc039c3a517c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Sun, 20 Jul 2025 02:15:45 +0000 Subject: [PATCH 09/24] Generalize logic pointing at binding moved into closure Account not only for `fn` parameters when moving non-`Copy` values into closure, but also for let bindings. ``` error[E0507]: cannot move out of `bar`, a captured variable in an `FnMut` closure --> $DIR/borrowck-move-by-capture.rs:9:29 | LL | let bar: Box<_> = Box::new(3); | --- ------ move occurs because `bar` has type `Box`, which does not implement the `Copy` trait | | | captured outer variable LL | let _g = to_fn_mut(|| { | -- captured by this `FnMut` closure LL | let _h = to_fn_once(move || -> isize { *bar }); | ^^^^^^^^^^^^^^^^ ---- variable moved due to use in closure | | | `bar` is moved here | help: consider cloning the value before moving it into the closure | LL ~ let value = bar.clone(); LL ~ let _h = to_fn_once(move || -> isize { value }); | ``` ``` error[E0507]: cannot move out of `y`, a captured variable in an `Fn` closure --> $DIR/unboxed-closures-move-upvar-from-non-once-ref-closure.rs:12:9 | LL | let y = vec![format!("World")]; | - ---------------------- move occurs because `y` has type `Vec`, which does not implement the `Copy` trait | | | captured outer variable LL | call(|| { | -- captured by this `Fn` closure LL | y.into_iter(); | ^ ----------- `y` moved due to this method call | | | `y` is moved here | note: `into_iter` takes ownership of the receiver `self`, which moves `y` --> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL help: you can `clone` the value and consume it, but this might not be your desired behavior | LL | as Clone>::clone(&y).into_iter(); | +++++++++++++++++++++++++++++++ + help: consider cloning the value if the performance cost is acceptable | LL | y.clone().into_iter(); | ++++++++ ``` --- .../src/diagnostics/move_errors.rs | 79 +++++++++++++------ tests/ui/borrowck/borrowck-in-static.stderr | 6 +- .../borrowck/borrowck-move-by-capture.stderr | 10 +-- tests/ui/borrowck/issue-103624.stderr | 7 +- ...ove-upvar-from-non-once-ref-closure.stderr | 6 +- tests/ui/issues/issue-4335.stderr | 2 +- ...-move-out-of-closure-env-issue-1965.stderr | 6 +- ...e-52663-span-decl-captured-variable.stderr | 6 +- ...borrowck-call-is-borrow-issue-12224.stderr | 18 +++-- .../suggestions/option-content-move2.stderr | 18 ++--- .../suggestions/option-content-move3.stderr | 18 ++--- .../unboxed-closure-illegal-move.stderr | 24 ++++-- 12 files changed, 125 insertions(+), 75 deletions(-) diff --git a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs index 28024e6fecef4..56f7fe6885441 100644 --- a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs @@ -623,30 +623,51 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { && let Some(upvar_field) = self .prefixes(original_path.as_ref(), PrefixSet::All) .find_map(|p| self.is_upvar_field_projection(p)) - && let upvar = &self.upvars[upvar_field.index()] - && let upvar_hir_id = upvar.get_root_variable() - && let hir::Node::Param(param) = self.infcx.tcx.parent_hir_node(upvar_hir_id) { - // Instead of pointing at the path where we access the value within a closure, - // we point at the type on the parameter from the definition of the outer - // function: - // - // error[E0507]: cannot move out of `foo`, a captured - // variable in an `Fn` closure - // --> file.rs:14:25 - // | - // 13 | fn do_stuff(foo: Option) { - // | --- ----------- move occurs because `foo` has type - // | | `Option`, which does not implement - // | | the `Copy` trait - // | captured outer variable - // 14 | require_fn_trait(|| async { - // | -- ^^^^^ `foo` is moved here - // | | - // | captured by this `Fn` closure - // 15 | if foo.map_or(false, |f| f.foo()) { - // | --- variable moved due to use in coroutine - use_span = param.ty_span; + let upvar = &self.upvars[upvar_field.index()]; + let upvar_hir_id = upvar.get_root_variable(); + use_span = match self.infcx.tcx.parent_hir_node(upvar_hir_id) { + hir::Node::Param(param) => { + // Instead of pointing at the path where we access the value within a + // closure, we point at the type on the parameter from the definition + // of the outer function: + // + // error[E0507]: cannot move out of `foo`, a captured + // variable in an `Fn` closure + // --> file.rs:14:25 + // | + // 13 | fn do_stuff(foo: Option) { + // | --- ----------- move occurs because `foo` has type + // | | `Option`, which does not + // | | implement the `Copy` trait + // | captured outer variable + // 14 | require_fn_trait(|| async { + // | -- ^^^^^ `foo` is moved here + // | | + // | captured by this `Fn` closure + // 15 | if foo.map_or(false, |f| f.foo()) { + // | --- variable moved due to use in coroutine + param.ty_span + } + hir::Node::LetStmt(stmt) => match (stmt.ty, stmt.init) { + // 13 | fn do_stuff(foo: Option) { + // 14 | let foo: Option = foo; + // | --- ----------- move occurs because `foo` has type + // | | `Option`, which does not implement + // | | the `Copy` trait + // | captured outer variable + (Some(ty), _) => ty.span, + // 13 | fn do_stuff(bar: Option) { + // 14 | let foo = bar; + // | --- --- move occurs because `foo` has type + // | | `Option`, which does not implement the + // | | `Copy` trait + // | captured outer variable + (None, Some(init)) => init.span, + (None, None) => use_span, + }, + _ => use_span, + }; } err.subdiagnostic(crate::session_diagnostics::TypeNoCopy::Label { @@ -656,12 +677,22 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { span: use_span, }); + let mut pointed_at_span = false; use_spans.args_subdiag(err, |args_span| { + if args_span == span || args_span == use_span { + pointed_at_span = true; + } crate::session_diagnostics::CaptureArgLabel::MoveOutPlace { - place: place_desc, + place: place_desc.clone(), args_span, } }); + if !pointed_at_span && use_span != span { + err.subdiagnostic(crate::session_diagnostics::CaptureArgLabel::MoveOutPlace { + place: place_desc, + args_span: span, + }); + } self.add_note_for_packed_struct_derive(err, original_path.local); } diff --git a/tests/ui/borrowck/borrowck-in-static.stderr b/tests/ui/borrowck/borrowck-in-static.stderr index 745b02ae21b81..9bcf64dd62e2a 100644 --- a/tests/ui/borrowck/borrowck-in-static.stderr +++ b/tests/ui/borrowck/borrowck-in-static.stderr @@ -2,9 +2,11 @@ error[E0507]: cannot move out of `x`, a captured variable in an `Fn` closure --> $DIR/borrowck-in-static.rs:5:17 | LL | let x = Box::new(0); - | - captured outer variable + | - ----------- move occurs because `x` has type `Box`, which does not implement the `Copy` trait + | | + | captured outer variable LL | Box::new(|| x) - | -- ^ move occurs because `x` has type `Box`, which does not implement the `Copy` trait + | -- ^ `x` is moved here | | | captured by this `Fn` closure | diff --git a/tests/ui/borrowck/borrowck-move-by-capture.stderr b/tests/ui/borrowck/borrowck-move-by-capture.stderr index 58d5e90e990a2..732af1593d606 100644 --- a/tests/ui/borrowck/borrowck-move-by-capture.stderr +++ b/tests/ui/borrowck/borrowck-move-by-capture.stderr @@ -2,14 +2,14 @@ error[E0507]: cannot move out of `bar`, a captured variable in an `FnMut` closur --> $DIR/borrowck-move-by-capture.rs:9:29 | LL | let bar: Box<_> = Box::new(3); - | --- captured outer variable + | --- ------ move occurs because `bar` has type `Box`, which does not implement the `Copy` trait + | | + | captured outer variable LL | let _g = to_fn_mut(|| { | -- captured by this `FnMut` closure LL | let _h = to_fn_once(move || -> isize { *bar }); - | ^^^^^^^^^^^^^^^^ ---- - | | | - | | variable moved due to use in closure - | | move occurs because `bar` has type `Box`, which does not implement the `Copy` trait + | ^^^^^^^^^^^^^^^^ ---- variable moved due to use in closure + | | | `bar` is moved here | help: consider cloning the value before moving it into the closure diff --git a/tests/ui/borrowck/issue-103624.stderr b/tests/ui/borrowck/issue-103624.stderr index 603055beadcef..af65deb16dcf8 100644 --- a/tests/ui/borrowck/issue-103624.stderr +++ b/tests/ui/borrowck/issue-103624.stderr @@ -2,13 +2,16 @@ error[E0507]: cannot move out of `self.b`, as `self` is a captured variable in a --> $DIR/issue-103624.rs:16:13 | LL | async fn foo(&self) { - | ----- captured outer variable + | ----- + | | + | captured outer variable + | move occurs because `self.b` has type `StructB`, which does not implement the `Copy` trait LL | let bar = self.b.bar().await; LL | spawn_blocking(move || { | ------- captured by this `Fn` closure LL | LL | self.b; - | ^^^^^^ move occurs because `self.b` has type `StructB`, which does not implement the `Copy` trait + | ^^^^^^ `self.b` is moved here | note: if `StructB` implemented `Clone`, you could clone the value --> $DIR/issue-103624.rs:23:1 diff --git a/tests/ui/borrowck/unboxed-closures-move-upvar-from-non-once-ref-closure.stderr b/tests/ui/borrowck/unboxed-closures-move-upvar-from-non-once-ref-closure.stderr index 177e9c8d2487e..da0e133ba023e 100644 --- a/tests/ui/borrowck/unboxed-closures-move-upvar-from-non-once-ref-closure.stderr +++ b/tests/ui/borrowck/unboxed-closures-move-upvar-from-non-once-ref-closure.stderr @@ -2,13 +2,15 @@ error[E0507]: cannot move out of `y`, a captured variable in an `Fn` closure --> $DIR/unboxed-closures-move-upvar-from-non-once-ref-closure.rs:12:9 | LL | let y = vec![format!("World")]; - | - captured outer variable + | - ---------------------- move occurs because `y` has type `Vec`, which does not implement the `Copy` trait + | | + | captured outer variable LL | call(|| { | -- captured by this `Fn` closure LL | y.into_iter(); | ^ ----------- `y` moved due to this method call | | - | move occurs because `y` has type `Vec`, which does not implement the `Copy` trait + | `y` is moved here | note: `into_iter` takes ownership of the receiver `self`, which moves `y` --> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL diff --git a/tests/ui/issues/issue-4335.stderr b/tests/ui/issues/issue-4335.stderr index 1f1a2595a3189..42ac632256401 100644 --- a/tests/ui/issues/issue-4335.stderr +++ b/tests/ui/issues/issue-4335.stderr @@ -6,7 +6,7 @@ LL | fn f<'r, T>(v: &'r T) -> Box T + 'r> { | | | captured outer variable LL | id(Box::new(|| *v)) - | -- ^^ + | -- ^^ `*v` is moved here | | | captured by this `FnMut` closure | diff --git a/tests/ui/moves/moves-based-on-type-move-out-of-closure-env-issue-1965.stderr b/tests/ui/moves/moves-based-on-type-move-out-of-closure-env-issue-1965.stderr index 523134a9425f4..51d0f85c031f5 100644 --- a/tests/ui/moves/moves-based-on-type-move-out-of-closure-env-issue-1965.stderr +++ b/tests/ui/moves/moves-based-on-type-move-out-of-closure-env-issue-1965.stderr @@ -2,9 +2,11 @@ error[E0507]: cannot move out of `i`, a captured variable in an `Fn` closure --> $DIR/moves-based-on-type-move-out-of-closure-env-issue-1965.rs:9:28 | LL | let i = Box::new(3); - | - captured outer variable + | - ----------- move occurs because `i` has type `Box`, which does not implement the `Copy` trait + | | + | captured outer variable LL | let _f = to_fn(|| test(i)); - | -- ^ move occurs because `i` has type `Box`, which does not implement the `Copy` trait + | -- ^ `i` is moved here | | | captured by this `Fn` closure | diff --git a/tests/ui/nll/issue-52663-span-decl-captured-variable.stderr b/tests/ui/nll/issue-52663-span-decl-captured-variable.stderr index fbaec8a6008a1..5754603700653 100644 --- a/tests/ui/nll/issue-52663-span-decl-captured-variable.stderr +++ b/tests/ui/nll/issue-52663-span-decl-captured-variable.stderr @@ -2,9 +2,11 @@ error[E0507]: cannot move out of `x.0`, as `x` is a captured variable in an `Fn` --> $DIR/issue-52663-span-decl-captured-variable.rs:8:26 | LL | let x = (vec![22], vec![44]); - | - captured outer variable + | - -------------------- move occurs because `x.0` has type `Vec`, which does not implement the `Copy` trait + | | + | captured outer variable LL | expect_fn(|| drop(x.0)); - | -- ^^^ move occurs because `x.0` has type `Vec`, which does not implement the `Copy` trait + | -- ^^^ `x.0` is moved here | | | captured by this `Fn` closure | diff --git a/tests/ui/span/borrowck-call-is-borrow-issue-12224.stderr b/tests/ui/span/borrowck-call-is-borrow-issue-12224.stderr index f37dc320fa315..2876169241396 100644 --- a/tests/ui/span/borrowck-call-is-borrow-issue-12224.stderr +++ b/tests/ui/span/borrowck-call-is-borrow-issue-12224.stderr @@ -35,14 +35,18 @@ LL | fn test4(f: &mut Test) { error[E0507]: cannot move out of `f`, a captured variable in an `FnMut` closure --> $DIR/borrowck-call-is-borrow-issue-12224.rs:57:13 | -LL | let mut f = move |g: Box, b: isize| { - | ----- captured outer variable -... -LL | f(Box::new(|a| { - | --- captured by this `FnMut` closure +LL | let mut f = move |g: Box, b: isize| { + | _________-----___- + | | | + | | captured outer variable +LL | | let _ = s.len(); +LL | | }; + | |_____- move occurs because `f` has type `{closure@$DIR/borrowck-call-is-borrow-issue-12224.rs:52:17: 52:58}`, which does not implement the `Copy` trait +LL | f(Box::new(|a| { + | --- captured by this `FnMut` closure LL | -LL | foo(f); - | ^ move occurs because `f` has type `{closure@$DIR/borrowck-call-is-borrow-issue-12224.rs:52:17: 52:58}`, which does not implement the `Copy` trait +LL | foo(f); + | ^ `f` is moved here | help: consider cloning the value if the performance cost is acceptable | diff --git a/tests/ui/suggestions/option-content-move2.stderr b/tests/ui/suggestions/option-content-move2.stderr index be97cba17b900..436441d6f1b9e 100644 --- a/tests/ui/suggestions/option-content-move2.stderr +++ b/tests/ui/suggestions/option-content-move2.stderr @@ -2,7 +2,9 @@ error[E0507]: cannot move out of `var`, a captured variable in an `FnMut` closur --> $DIR/option-content-move2.rs:11:9 | LL | let mut var = None; - | ------- captured outer variable + | ------- ---- move occurs because `var` has type `Option`, which does not implement the `Copy` trait + | | + | captured outer variable LL | func(|| { | -- captured by this `FnMut` closure LL | // Shouldn't suggest `move ||.as_ref()` here @@ -10,16 +12,15 @@ LL | move || { | ^^^^^^^ `var` is moved here LL | LL | var = Some(NotCopyable); - | --- - | | - | variable moved due to use in closure - | move occurs because `var` has type `Option`, which does not implement the `Copy` trait + | --- variable moved due to use in closure error[E0507]: cannot move out of `var`, a captured variable in an `FnMut` closure --> $DIR/option-content-move2.rs:21:9 | LL | let mut var = None; - | ------- captured outer variable + | ------- ---- move occurs because `var` has type `Option`, which does not implement the `Copy` trait + | | + | captured outer variable LL | func(|| { | -- captured by this `FnMut` closure LL | // Shouldn't suggest `move ||.as_ref()` nor to `clone()` here @@ -27,10 +28,7 @@ LL | move || { | ^^^^^^^ `var` is moved here LL | LL | var = Some(NotCopyableButCloneable); - | --- - | | - | variable moved due to use in closure - | move occurs because `var` has type `Option`, which does not implement the `Copy` trait + | --- variable moved due to use in closure error: aborting due to 2 previous errors diff --git a/tests/ui/suggestions/option-content-move3.stderr b/tests/ui/suggestions/option-content-move3.stderr index faaf8a9df9d72..68c52352a6512 100644 --- a/tests/ui/suggestions/option-content-move3.stderr +++ b/tests/ui/suggestions/option-content-move3.stderr @@ -26,17 +26,16 @@ error[E0507]: cannot move out of `var`, a captured variable in an `FnMut` closur --> $DIR/option-content-move3.rs:12:9 | LL | let var = NotCopyable; - | --- captured outer variable + | --- ----------- move occurs because `var` has type `NotCopyable`, which does not implement the `Copy` trait + | | + | captured outer variable LL | func(|| { | -- captured by this `FnMut` closure LL | // Shouldn't suggest `move ||.as_ref()` here LL | move || { | ^^^^^^^ `var` is moved here LL | let x = var; - | --- - | | - | variable moved due to use in closure - | move occurs because `var` has type `NotCopyable`, which does not implement the `Copy` trait + | --- variable moved due to use in closure | note: if `NotCopyable` implemented `Clone`, you could clone the value --> $DIR/option-content-move3.rs:2:1 @@ -67,17 +66,16 @@ error[E0507]: cannot move out of `var`, a captured variable in an `FnMut` closur --> $DIR/option-content-move3.rs:23:9 | LL | let var = NotCopyableButCloneable; - | --- captured outer variable + | --- ----------------------- move occurs because `var` has type `NotCopyableButCloneable`, which does not implement the `Copy` trait + | | + | captured outer variable LL | func(|| { | -- captured by this `FnMut` closure LL | // Shouldn't suggest `move ||.as_ref()` here LL | move || { | ^^^^^^^ `var` is moved here LL | let x = var; - | --- - | | - | variable moved due to use in closure - | move occurs because `var` has type `NotCopyableButCloneable`, which does not implement the `Copy` trait + | --- variable moved due to use in closure | help: consider cloning the value before moving it into the closure | diff --git a/tests/ui/unboxed-closures/unboxed-closure-illegal-move.stderr b/tests/ui/unboxed-closures/unboxed-closure-illegal-move.stderr index cf4391311d03d..8d9a61cb68126 100644 --- a/tests/ui/unboxed-closures/unboxed-closure-illegal-move.stderr +++ b/tests/ui/unboxed-closures/unboxed-closure-illegal-move.stderr @@ -2,9 +2,11 @@ error[E0507]: cannot move out of `x`, a captured variable in an `Fn` closure --> $DIR/unboxed-closure-illegal-move.rs:15:31 | LL | let x = Box::new(0); - | - captured outer variable + | - ----------- move occurs because `x` has type `Box`, which does not implement the `Copy` trait + | | + | captured outer variable LL | let f = to_fn(|| drop(x)); - | -- ^ move occurs because `x` has type `Box`, which does not implement the `Copy` trait + | -- ^ `x` is moved here | | | captured by this `Fn` closure | @@ -17,9 +19,11 @@ error[E0507]: cannot move out of `x`, a captured variable in an `FnMut` closure --> $DIR/unboxed-closure-illegal-move.rs:19:35 | LL | let x = Box::new(0); - | - captured outer variable + | - ----------- move occurs because `x` has type `Box`, which does not implement the `Copy` trait + | | + | captured outer variable LL | let f = to_fn_mut(|| drop(x)); - | -- ^ move occurs because `x` has type `Box`, which does not implement the `Copy` trait + | -- ^ `x` is moved here | | | captured by this `FnMut` closure | @@ -32,9 +36,11 @@ error[E0507]: cannot move out of `x`, a captured variable in an `Fn` closure --> $DIR/unboxed-closure-illegal-move.rs:28:36 | LL | let x = Box::new(0); - | - captured outer variable + | - ----------- move occurs because `x` has type `Box`, which does not implement the `Copy` trait + | | + | captured outer variable LL | let f = to_fn(move || drop(x)); - | ------- ^ move occurs because `x` has type `Box`, which does not implement the `Copy` trait + | ------- ^ `x` is moved here | | | captured by this `Fn` closure @@ -42,9 +48,11 @@ error[E0507]: cannot move out of `x`, a captured variable in an `FnMut` closure --> $DIR/unboxed-closure-illegal-move.rs:32:40 | LL | let x = Box::new(0); - | - captured outer variable + | - ----------- move occurs because `x` has type `Box`, which does not implement the `Copy` trait + | | + | captured outer variable LL | let f = to_fn_mut(move || drop(x)); - | ------- ^ move occurs because `x` has type `Box`, which does not implement the `Copy` trait + | ------- ^ `x` is moved here | | | captured by this `FnMut` closure From 8df93e6966e71da8a249a0022680b83eff105f42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Sun, 20 Jul 2025 02:22:55 +0000 Subject: [PATCH 10/24] Tweak spans when encountering multiline initializer in move error ``` error[E0507]: cannot move out of `f`, a captured variable in an `FnMut` closure --> $DIR/borrowck-call-is-borrow-issue-12224.rs:57:13 | LL | let mut f = move |g: Box, b: isize| { | ----- captured outer variable ... LL | f(Box::new(|a| { | --- captured by this `FnMut` closure LL | LL | foo(f); | ^ move occurs because `f` has type `{closure@$DIR/borrowck-call-is-borrow-issue-12224.rs:52:17: 52:58}`, which does not implement the `Copy` trait ``` instead of ``` error[E0507]: cannot move out of `f`, a captured variable in an `FnMut` closure --> $DIR/borrowck-call-is-borrow-issue-12224.rs:57:13 | LL | let mut f = move |g: Box, b: isize| { | _________-----___- | | | | | captured outer variable LL | | let _ = s.len(); LL | | }; | |_____- move occurs because `f` has type `{closure@$DIR/borrowck-call-is-borrow-issue-12224.rs:52:17: 52:58}`, which does not implement the `Copy` trait LL | f(Box::new(|a| { | --- captured by this `FnMut` closure LL | LL | foo(f); | ^ `f` is moved here ``` --- .../src/diagnostics/move_errors.rs | 11 +++++++++-- ...move-upvar-from-non-once-ref-closure.stderr | 6 ++---- .../borrowck-call-is-borrow-issue-12224.stderr | 18 +++++++----------- 3 files changed, 18 insertions(+), 17 deletions(-) diff --git a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs index 56f7fe6885441..40361906e0423 100644 --- a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs @@ -663,8 +663,15 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { // | | `Option`, which does not implement the // | | `Copy` trait // | captured outer variable - (None, Some(init)) => init.span, - (None, None) => use_span, + // + // We don't want the case where the initializer is something that spans + // multiple lines, like a closure, as the ASCII art gets messy. + (None, Some(init)) + if !self.infcx.tcx.sess.source_map().is_multiline(init.span) => + { + init.span + } + _ => use_span, }, _ => use_span, }; diff --git a/tests/ui/borrowck/unboxed-closures-move-upvar-from-non-once-ref-closure.stderr b/tests/ui/borrowck/unboxed-closures-move-upvar-from-non-once-ref-closure.stderr index da0e133ba023e..177e9c8d2487e 100644 --- a/tests/ui/borrowck/unboxed-closures-move-upvar-from-non-once-ref-closure.stderr +++ b/tests/ui/borrowck/unboxed-closures-move-upvar-from-non-once-ref-closure.stderr @@ -2,15 +2,13 @@ error[E0507]: cannot move out of `y`, a captured variable in an `Fn` closure --> $DIR/unboxed-closures-move-upvar-from-non-once-ref-closure.rs:12:9 | LL | let y = vec![format!("World")]; - | - ---------------------- move occurs because `y` has type `Vec`, which does not implement the `Copy` trait - | | - | captured outer variable + | - captured outer variable LL | call(|| { | -- captured by this `Fn` closure LL | y.into_iter(); | ^ ----------- `y` moved due to this method call | | - | `y` is moved here + | move occurs because `y` has type `Vec`, which does not implement the `Copy` trait | note: `into_iter` takes ownership of the receiver `self`, which moves `y` --> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL diff --git a/tests/ui/span/borrowck-call-is-borrow-issue-12224.stderr b/tests/ui/span/borrowck-call-is-borrow-issue-12224.stderr index 2876169241396..f37dc320fa315 100644 --- a/tests/ui/span/borrowck-call-is-borrow-issue-12224.stderr +++ b/tests/ui/span/borrowck-call-is-borrow-issue-12224.stderr @@ -35,18 +35,14 @@ LL | fn test4(f: &mut Test) { error[E0507]: cannot move out of `f`, a captured variable in an `FnMut` closure --> $DIR/borrowck-call-is-borrow-issue-12224.rs:57:13 | -LL | let mut f = move |g: Box, b: isize| { - | _________-----___- - | | | - | | captured outer variable -LL | | let _ = s.len(); -LL | | }; - | |_____- move occurs because `f` has type `{closure@$DIR/borrowck-call-is-borrow-issue-12224.rs:52:17: 52:58}`, which does not implement the `Copy` trait -LL | f(Box::new(|a| { - | --- captured by this `FnMut` closure +LL | let mut f = move |g: Box, b: isize| { + | ----- captured outer variable +... +LL | f(Box::new(|a| { + | --- captured by this `FnMut` closure LL | -LL | foo(f); - | ^ `f` is moved here +LL | foo(f); + | ^ move occurs because `f` has type `{closure@$DIR/borrowck-call-is-borrow-issue-12224.rs:52:17: 52:58}`, which does not implement the `Copy` trait | help: consider cloning the value if the performance cost is acceptable | From dafc9f9b534c8d1a50ba87f776eb1d6dfb1a7c94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Mon, 21 Jul 2025 16:23:13 +0000 Subject: [PATCH 11/24] Reduce comment verbosity --- .../src/diagnostics/move_errors.rs | 38 +++---------------- 1 file changed, 5 insertions(+), 33 deletions(-) diff --git a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs index 40361906e0423..67dce7615c0df 100644 --- a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs @@ -629,43 +629,15 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { use_span = match self.infcx.tcx.parent_hir_node(upvar_hir_id) { hir::Node::Param(param) => { // Instead of pointing at the path where we access the value within a - // closure, we point at the type on the parameter from the definition - // of the outer function: - // - // error[E0507]: cannot move out of `foo`, a captured - // variable in an `Fn` closure - // --> file.rs:14:25 - // | - // 13 | fn do_stuff(foo: Option) { - // | --- ----------- move occurs because `foo` has type - // | | `Option`, which does not - // | | implement the `Copy` trait - // | captured outer variable - // 14 | require_fn_trait(|| async { - // | -- ^^^^^ `foo` is moved here - // | | - // | captured by this `Fn` closure - // 15 | if foo.map_or(false, |f| f.foo()) { - // | --- variable moved due to use in coroutine + // closure, we point at the type of the outer `fn` argument. param.ty_span } hir::Node::LetStmt(stmt) => match (stmt.ty, stmt.init) { - // 13 | fn do_stuff(foo: Option) { - // 14 | let foo: Option = foo; - // | --- ----------- move occurs because `foo` has type - // | | `Option`, which does not implement - // | | the `Copy` trait - // | captured outer variable + // We point at the type of the outer let-binding. (Some(ty), _) => ty.span, - // 13 | fn do_stuff(bar: Option) { - // 14 | let foo = bar; - // | --- --- move occurs because `foo` has type - // | | `Option`, which does not implement the - // | | `Copy` trait - // | captured outer variable - // - // We don't want the case where the initializer is something that spans - // multiple lines, like a closure, as the ASCII art gets messy. + // We point at the initializer of the outer let-binding, but only if it + // isn't something that spans multiple lines, like a closure, as the + // ASCII art gets messy. (None, Some(init)) if !self.infcx.tcx.sess.source_map().is_multiline(init.span) => { From e1b6cfe62e0571d2c5bfd89fd7dae9e4cf7bcf86 Mon Sep 17 00:00:00 2001 From: Jens Reidel Date: Mon, 21 Jul 2025 21:57:08 +0200 Subject: [PATCH 12/24] Rephrase comment to include some tracking issues Signed-off-by: Jens Reidel --- tests/debuginfo/basic-types-globals-metadata.rs | 6 ++++-- tests/debuginfo/basic-types-globals.rs | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/tests/debuginfo/basic-types-globals-metadata.rs b/tests/debuginfo/basic-types-globals-metadata.rs index d14d5472f53bb..aec8ff183ad75 100644 --- a/tests/debuginfo/basic-types-globals-metadata.rs +++ b/tests/debuginfo/basic-types-globals-metadata.rs @@ -60,8 +60,10 @@ fn main() { _zzz(); // #break let a = unsafe { (B, I, C, I8, I16, I32, I64, U, U8, U16, U32, U64, F32, F64) }; - // N.B. Including f16 and f32 in the same tuple emits `__gnu_h2f_ieee`, which does - // not exist on some targets like PowerPC + // FIXME: Including f16 and f32 in the same tuple emits `__gnu_h2f_ieee`, which + // does not exist on some targets like PowerPC. + // See https://github.com/llvm/llvm-project/issues/97981 and + // https://github.com/rust-lang/compiler-builtins/issues/655 let b = unsafe { F16 }; } diff --git a/tests/debuginfo/basic-types-globals.rs b/tests/debuginfo/basic-types-globals.rs index 5933c6d2440dc..15a0deb64c125 100644 --- a/tests/debuginfo/basic-types-globals.rs +++ b/tests/debuginfo/basic-types-globals.rs @@ -64,8 +64,10 @@ fn main() { _zzz(); // #break let a = unsafe { (B, I, C, I8, I16, I32, I64, U, U8, U16, U32, U64, F32, F64) }; - // N.B. Including f16 and f32 in the same tuple emits `__gnu_h2f_ieee`, which does - // not exist on some targets like PowerPC + // FIXME: Including f16 and f32 in the same tuple emits `__gnu_h2f_ieee`, which + // does not exist on some targets like PowerPC. + // See https://github.com/llvm/llvm-project/issues/97981 and + // https://github.com/rust-lang/compiler-builtins/issues/655 let b = unsafe { F16 }; } From a9fd8d041bf40b0b2425ad79062d09a441352efa Mon Sep 17 00:00:00 2001 From: Jens Reidel Date: Tue, 22 Jul 2025 19:00:32 +0200 Subject: [PATCH 13/24] bootstrap: Move musl-root fallback out of sanity check Previously, the musl root would only be set to the fallback /usr by the sanity check, which isn't ran for the bootstrap tests. Signed-off-by: Jens Reidel --- src/bootstrap/src/core/sanity.rs | 6 ------ src/bootstrap/src/lib.rs | 26 ++++++++++++++++++-------- 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/src/bootstrap/src/core/sanity.rs b/src/bootstrap/src/core/sanity.rs index b39d464493e56..15e04f591296c 100644 --- a/src/bootstrap/src/core/sanity.rs +++ b/src/bootstrap/src/core/sanity.rs @@ -338,12 +338,6 @@ than building it. // Make sure musl-root is valid. if target.contains("musl") && !target.contains("unikraft") { - // If this is a native target (host is also musl) and no musl-root is given, - // fall back to the system toolchain in /usr before giving up - if build.musl_root(*target).is_none() && build.config.is_host_target(*target) { - let target = build.config.target_config.entry(*target).or_default(); - target.musl_root = Some("/usr".into()); - } match build.musl_libdir(*target) { Some(libdir) => { if fs::metadata(libdir.join("libc.a")).is_err() { diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs index 63aab4d116a93..2aee9f0728449 100644 --- a/src/bootstrap/src/lib.rs +++ b/src/bootstrap/src/lib.rs @@ -1329,23 +1329,33 @@ impl Build { } } - /// Returns the "musl root" for this `target`, if defined + /// Returns the "musl root" for this `target`, if defined. + /// + /// If this is a native target (host is also musl) and no musl-root is given, + /// it falls back to the system toolchain in /usr. fn musl_root(&self, target: TargetSelection) -> Option<&Path> { - self.config + let configured_root = self + .config .target_config .get(&target) .and_then(|t| t.musl_root.as_ref()) .or(self.config.musl_root.as_ref()) - .map(|p| &**p) + .map(|p| &**p); + + if self.config.is_host_target(target) && configured_root.is_none() { + return Some(Path::new("/usr")); + } else { + configured_root + } } /// Returns the "musl libdir" for this `target`. fn musl_libdir(&self, target: TargetSelection) -> Option { - let t = self.config.target_config.get(&target)?; - if let libdir @ Some(_) = &t.musl_libdir { - return libdir.clone(); - } - self.musl_root(target).map(|root| root.join("lib")) + self.config + .target_config + .get(&target) + .and_then(|t| t.musl_libdir.clone()) + .or_else(|| self.musl_root(target).map(|root| root.join("lib"))) } /// Returns the `lib` directory for the WASI target specified, if From ed11a396435fc1de59bf40b39789cf8b5c7f5307 Mon Sep 17 00:00:00 2001 From: Alisa Sireneva Date: Tue, 15 Jul 2025 12:55:46 +0300 Subject: [PATCH 14/24] Don't special-case llvm.* as nounwind Certain LLVM intrinsics, such as `llvm.wasm.throw`, can unwind. Marking them as nounwind causes us to skip cleanup of locals and optimize out `catch_unwind` under inlining or when `llvm.wasm.throw` is used directly by user code. The motivation for forcibly marking llvm.* as nounwind is no longer present: most intrinsics are linked as `extern "C"` or other non-unwinding ABIs, so we won't codegen `invoke` for them anyway. --- compiler/rustc_codegen_ssa/src/codegen_attrs.rs | 9 --------- tests/codegen-llvm/wasm_exceptions.rs | 16 +++++++++++++++- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs index dd49db26689e0..3f456fa115a44 100644 --- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs +++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs @@ -511,15 +511,6 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { err.emit(); } - // Any linkage to LLVM intrinsics for now forcibly marks them all as never - // unwinds since LLVM sometimes can't handle codegen which `invoke`s - // intrinsic functions. - if let Some(name) = &codegen_fn_attrs.link_name - && name.as_str().starts_with("llvm.") - { - codegen_fn_attrs.flags |= CodegenFnAttrFlags::NEVER_UNWIND; - } - if let Some(features) = check_tied_features( tcx.sess, &codegen_fn_attrs diff --git a/tests/codegen-llvm/wasm_exceptions.rs b/tests/codegen-llvm/wasm_exceptions.rs index 07b8ae6e9d7e5..796b69b722b51 100644 --- a/tests/codegen-llvm/wasm_exceptions.rs +++ b/tests/codegen-llvm/wasm_exceptions.rs @@ -2,7 +2,7 @@ //@ compile-flags: -C panic=unwind -Z emscripten-wasm-eh #![crate_type = "lib"] -#![feature(core_intrinsics)] +#![feature(core_intrinsics, wasm_exception_handling_intrinsics)] extern "C-unwind" { fn may_panic(); @@ -57,3 +57,17 @@ pub fn test_rtry() { // CHECK: {{.*}} = catchpad within {{.*}} [ptr null] // CHECK: catchret } + +// Make sure the intrinsic is not inferred as nounwind. This is a regression test for #132416. +// CHECK-LABEL: @test_intrinsic() {{.*}} @__gxx_wasm_personality_v0 +#[no_mangle] +pub fn test_intrinsic() { + let _log_on_drop = LogOnDrop; + unsafe { + core::arch::wasm32::throw::<0>(core::ptr::null_mut()); + } + + // CHECK-NOT: call + // CHECK: invoke void @llvm.wasm.throw(i32 noundef 0, ptr noundef null) + // CHECK: %cleanuppad = cleanuppad within none [] +} From 01fdafc9aac788e96af33f8daada26809ea2bff7 Mon Sep 17 00:00:00 2001 From: Kornel Date: Tue, 22 Jul 2025 17:07:16 +0100 Subject: [PATCH 15/24] Hint that choose_pivot returns index in bounds --- library/core/src/slice/sort/select.rs | 3 +-- library/core/src/slice/sort/shared/pivot.rs | 10 ++++++++-- library/core/src/slice/sort/stable/quicksort.rs | 4 ---- library/core/src/slice/sort/unstable/quicksort.rs | 3 +-- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/library/core/src/slice/sort/select.rs b/library/core/src/slice/sort/select.rs index 82194db7fd86b..fc31013caf88c 100644 --- a/library/core/src/slice/sort/select.rs +++ b/library/core/src/slice/sort/select.rs @@ -101,8 +101,7 @@ fn partition_at_index_loop<'a, T, F>( // slice. Partition the slice into elements equal to and elements greater than the pivot. // This case is usually hit when the slice contains many duplicate elements. if let Some(p) = ancestor_pivot { - // SAFETY: choose_pivot promises to return a valid pivot position. - let pivot = unsafe { v.get_unchecked(pivot_pos) }; + let pivot = &v[pivot_pos]; if !is_less(p, pivot) { let num_lt = partition(v, pivot_pos, &mut |a, b| !is_less(b, a)); diff --git a/library/core/src/slice/sort/shared/pivot.rs b/library/core/src/slice/sort/shared/pivot.rs index 3aace484b6a89..9eb60f854ce21 100644 --- a/library/core/src/slice/sort/shared/pivot.rs +++ b/library/core/src/slice/sort/shared/pivot.rs @@ -1,6 +1,6 @@ //! This module contains the logic for pivot selection. -use crate::intrinsics; +use crate::{hint, intrinsics}; // Recursively select a pseudomedian if above this threshold. const PSEUDO_MEDIAN_REC_THRESHOLD: usize = 64; @@ -9,6 +9,7 @@ const PSEUDO_MEDIAN_REC_THRESHOLD: usize = 64; /// /// This chooses a pivot by sampling an adaptive amount of points, approximating /// the quality of a median of sqrt(n) elements. +#[inline] pub fn choose_pivot bool>(v: &[T], is_less: &mut F) -> usize { // We use unsafe code and raw pointers here because we're dealing with // heavy recursion. Passing safe slices around would involve a lot of @@ -22,7 +23,7 @@ pub fn choose_pivot bool>(v: &[T], is_less: &mut F) -> us // SAFETY: a, b, c point to initialized regions of len_div_8 elements, // satisfying median3 and median3_rec's preconditions as v_base points // to an initialized region of n = len elements. - unsafe { + let index = unsafe { let v_base = v.as_ptr(); let len_div_8 = len / 8; @@ -35,6 +36,11 @@ pub fn choose_pivot bool>(v: &[T], is_less: &mut F) -> us } else { median3_rec(a, b, c, len_div_8, is_less).offset_from_unsigned(v_base) } + }; + // SAFETY: preconditions must have been met for offset_from_unsigned() + unsafe { + hint::assert_unchecked(index < v.len()); + index } } diff --git a/library/core/src/slice/sort/stable/quicksort.rs b/library/core/src/slice/sort/stable/quicksort.rs index 3c9688790c40b..0439ba870bd2b 100644 --- a/library/core/src/slice/sort/stable/quicksort.rs +++ b/library/core/src/slice/sort/stable/quicksort.rs @@ -37,10 +37,6 @@ pub fn quicksort bool>( limit -= 1; let pivot_pos = choose_pivot(v, is_less); - // SAFETY: choose_pivot promises to return a valid pivot index. - unsafe { - intrinsics::assume(pivot_pos < v.len()); - } // SAFETY: We only access the temporary copy for Freeze types, otherwise // self-modifications via `is_less` would not be observed and this would diff --git a/library/core/src/slice/sort/unstable/quicksort.rs b/library/core/src/slice/sort/unstable/quicksort.rs index 98efee242ebae..bdf56a8080305 100644 --- a/library/core/src/slice/sort/unstable/quicksort.rs +++ b/library/core/src/slice/sort/unstable/quicksort.rs @@ -48,8 +48,7 @@ pub(crate) fn quicksort<'a, T, F>( // slice. Partition the slice into elements equal to and elements greater than the pivot. // This case is usually hit when the slice contains many duplicate elements. if let Some(p) = ancestor_pivot { - // SAFETY: We assume choose_pivot yields an in-bounds position. - if !is_less(p, unsafe { v.get_unchecked(pivot_pos) }) { + if !is_less(p, &v[pivot_pos]) { let num_lt = partition(v, pivot_pos, &mut |a, b| !is_less(b, a)); // Continue sorting elements greater than the pivot. We know that `num_lt` contains From 93d2003c9a4a35522b3528a4df638dd4e315bd14 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Wed, 23 Jul 2025 07:27:01 -0700 Subject: [PATCH 16/24] Update `dlmalloc` dependency of libstd This primarily pulls in alexcrichton/dlmalloc-rs/55 and alexcrichton/dlmalloc-rs/54 to address 144199. Notably the highest byte in the wasm address space is no longer allocatable and additionally the allocator internally uses `wrapping_add` instead of `add` on pointers since on 32-bit platforms offsets might be larger than half the address space. --- library/Cargo.lock | 4 ++-- library/std/Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/library/Cargo.lock b/library/Cargo.lock index 94155e0839817..cb356480ead8a 100644 --- a/library/Cargo.lock +++ b/library/Cargo.lock @@ -78,9 +78,9 @@ dependencies = [ [[package]] name = "dlmalloc" -version = "0.2.9" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d01597dde41c0b9da50d5f8c219023d63d8f27f39a27095070fd191fddc83891" +checksum = "fa3a2dbee57b69fbb5dbe852fa9c0925697fb0c7fbcb1593e90e5ffaedf13d51" dependencies = [ "cfg-if", "libc", diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml index ba1e1f5218af7..57859ea914700 100644 --- a/library/std/Cargo.toml +++ b/library/std/Cargo.toml @@ -63,7 +63,7 @@ rand = { version = "0.9.0", default-features = false, features = ["alloc"] } rand_xorshift = "0.4.0" [target.'cfg(any(all(target_family = "wasm", target_os = "unknown"), target_os = "xous", all(target_vendor = "fortanix", target_env = "sgx")))'.dependencies] -dlmalloc = { version = "0.2.4", features = ['rustc-dep-of-std'] } +dlmalloc = { version = "0.2.10", features = ['rustc-dep-of-std'] } [target.x86_64-fortanix-unknown-sgx.dependencies] fortanix-sgx-abi = { version = "0.5.0", features = [ From 6237e735c4dc165b3efec236e11a44bdccf1dfd7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Mon, 21 Jul 2025 16:53:31 +0000 Subject: [PATCH 17/24] Point at the type that doesn't impl `Clone` in more cases beyond closures --- .../rustc_borrowck/src/diagnostics/move_errors.rs | 15 +++++---------- .../closure-shim-borrowck-error.stderr | 7 +++---- .../async-closures/move-out-of-ref.stderr | 5 ++++- 3 files changed, 12 insertions(+), 15 deletions(-) diff --git a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs index 67dce7615c0df..a0c1f66d35c2f 100644 --- a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs @@ -596,9 +596,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { self.add_move_error_details(err, &binds_to); } // No binding. Nothing to suggest. - GroupedMoveError::OtherIllegalMove { - ref original_path, use_spans, ref kind, .. - } => { + GroupedMoveError::OtherIllegalMove { ref original_path, use_spans, .. } => { let mut use_span = use_spans.var_or_use(); let place_ty = original_path.ty(self.body, self.infcx.tcx).ty; let place_desc = match self.describe_place(original_path.as_ref()) { @@ -616,14 +614,11 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { ); } - if let IllegalMoveOriginKind::BorrowedContent { target_place } = &kind - && let ty = target_place.ty(self.body, self.infcx.tcx).ty - && let ty::Closure(def_id, _) = ty.kind() - && def_id.as_local() == Some(self.mir_def_id()) - && let Some(upvar_field) = self - .prefixes(original_path.as_ref(), PrefixSet::All) - .find_map(|p| self.is_upvar_field_projection(p)) + if let Some(upvar_field) = self + .prefixes(original_path.as_ref(), PrefixSet::All) + .find_map(|p| self.is_upvar_field_projection(p)) { + // Look for the introduction of the original binding being moved. let upvar = &self.upvars[upvar_field.index()]; let upvar_hir_id = upvar.get_root_variable(); use_span = match self.infcx.tcx.parent_hir_node(upvar_hir_id) { diff --git a/tests/ui/async-await/async-closures/closure-shim-borrowck-error.stderr b/tests/ui/async-await/async-closures/closure-shim-borrowck-error.stderr index 03fa220b0bfae..3fe1431fda714 100644 --- a/tests/ui/async-await/async-closures/closure-shim-borrowck-error.stderr +++ b/tests/ui/async-await/async-closures/closure-shim-borrowck-error.stderr @@ -1,14 +1,13 @@ error[E0507]: cannot move out of `x` which is behind a mutable reference --> $DIR/closure-shim-borrowck-error.rs:11:18 | +LL | fn hello(x: Ty) { + | -- move occurs because `x` has type `Ty`, which does not implement the `Copy` trait LL | needs_fn_mut(async || { | ^^^^^^^^ `x` is moved here LL | LL | x.hello(); - | - - | | - | variable moved due to use in coroutine - | move occurs because `x` has type `Ty`, which does not implement the `Copy` trait + | - variable moved due to use in coroutine | note: if `Ty` implemented `Clone`, you could clone the value --> $DIR/closure-shim-borrowck-error.rs:17:1 diff --git a/tests/ui/async-await/async-closures/move-out-of-ref.stderr b/tests/ui/async-await/async-closures/move-out-of-ref.stderr index 8a63515a8a908..d443dc9d4831b 100644 --- a/tests/ui/async-await/async-closures/move-out-of-ref.stderr +++ b/tests/ui/async-await/async-closures/move-out-of-ref.stderr @@ -1,8 +1,11 @@ error[E0507]: cannot move out of `*x` which is behind a shared reference --> $DIR/move-out-of-ref.rs:9:9 | +LL | fn hello(x: &Ty) { + | --- move occurs because `*x` has type `Ty`, which does not implement the `Copy` trait +LL | let c = async || { LL | *x; - | ^^ move occurs because `*x` has type `Ty`, which does not implement the `Copy` trait + | ^^ `*x` is moved here | note: if `Ty` implemented `Clone`, you could clone the value --> $DIR/move-out-of-ref.rs:5:1 From a93a9aa2d57564660b2c28e5c1cdda7943989f17 Mon Sep 17 00:00:00 2001 From: Scott McMurray Date: Sun, 20 Jul 2025 00:17:22 -0700 Subject: [PATCH 18/24] Don't emit two `assume`s in transmutes when one is a subset of the other For example, transmuting between `bool` and `Ordering` doesn't need two `assume`s because one range is a superset of the other. Multiple are still used for things like `char` <-> `NonZero`, which overlap but where neither fully contains the other. --- compiler/rustc_abi/src/lib.rs | 22 +++++++ compiler/rustc_abi/src/tests.rs | 63 +++++++++++++++++++ compiler/rustc_codegen_ssa/src/mir/rvalue.rs | 28 +++++++-- .../intrinsics/transmute-niched.rs | 49 ++++++++++++--- 4 files changed, 150 insertions(+), 12 deletions(-) diff --git a/compiler/rustc_abi/src/lib.rs b/compiler/rustc_abi/src/lib.rs index 5bd73502d980a..8e346706877de 100644 --- a/compiler/rustc_abi/src/lib.rs +++ b/compiler/rustc_abi/src/lib.rs @@ -1376,6 +1376,28 @@ impl WrappingRange { } } + /// Returns `true` if all the values in `other` are contained in this range, + /// when the values are considered as having width `size`. + #[inline(always)] + pub fn contains_range(&self, other: Self, size: Size) -> bool { + if self.is_full_for(size) { + true + } else { + let trunc = |x| size.truncate(x); + + let delta = self.start; + let max = trunc(self.end.wrapping_sub(delta)); + + let other_start = trunc(other.start.wrapping_sub(delta)); + let other_end = trunc(other.end.wrapping_sub(delta)); + + // Having shifted both input ranges by `delta`, now we only need to check + // whether `0..=max` contains `other_start..=other_end`, which can only + // happen if the other doesn't wrap since `self` isn't everything. + (other_start <= other_end) && (other_end <= max) + } + } + /// Returns `self` with replaced `start` #[inline(always)] fn with_start(mut self, start: u128) -> Self { diff --git a/compiler/rustc_abi/src/tests.rs b/compiler/rustc_abi/src/tests.rs index d993012378c81..d49c2d44af84d 100644 --- a/compiler/rustc_abi/src/tests.rs +++ b/compiler/rustc_abi/src/tests.rs @@ -5,3 +5,66 @@ fn align_constants() { assert_eq!(Align::ONE, Align::from_bytes(1).unwrap()); assert_eq!(Align::EIGHT, Align::from_bytes(8).unwrap()); } + +#[test] +fn wrapping_range_contains_range() { + let size16 = Size::from_bytes(16); + + let a = WrappingRange { start: 10, end: 20 }; + assert!(a.contains_range(a, size16)); + assert!(a.contains_range(WrappingRange { start: 11, end: 19 }, size16)); + assert!(a.contains_range(WrappingRange { start: 10, end: 10 }, size16)); + assert!(a.contains_range(WrappingRange { start: 20, end: 20 }, size16)); + assert!(!a.contains_range(WrappingRange { start: 10, end: 21 }, size16)); + assert!(!a.contains_range(WrappingRange { start: 9, end: 20 }, size16)); + assert!(!a.contains_range(WrappingRange { start: 4, end: 6 }, size16)); + assert!(!a.contains_range(WrappingRange { start: 24, end: 26 }, size16)); + + assert!(!a.contains_range(WrappingRange { start: 16, end: 14 }, size16)); + + let b = WrappingRange { start: 20, end: 10 }; + assert!(b.contains_range(b, size16)); + assert!(b.contains_range(WrappingRange { start: 20, end: 20 }, size16)); + assert!(b.contains_range(WrappingRange { start: 10, end: 10 }, size16)); + assert!(b.contains_range(WrappingRange { start: 0, end: 10 }, size16)); + assert!(b.contains_range(WrappingRange { start: 20, end: 30 }, size16)); + assert!(b.contains_range(WrappingRange { start: 20, end: 9 }, size16)); + assert!(b.contains_range(WrappingRange { start: 21, end: 10 }, size16)); + assert!(b.contains_range(WrappingRange { start: 999, end: 9999 }, size16)); + assert!(b.contains_range(WrappingRange { start: 999, end: 9 }, size16)); + assert!(!b.contains_range(WrappingRange { start: 19, end: 19 }, size16)); + assert!(!b.contains_range(WrappingRange { start: 11, end: 11 }, size16)); + assert!(!b.contains_range(WrappingRange { start: 19, end: 11 }, size16)); + assert!(!b.contains_range(WrappingRange { start: 11, end: 19 }, size16)); + + let f = WrappingRange { start: 0, end: u128::MAX }; + assert!(f.contains_range(WrappingRange { start: 10, end: 20 }, size16)); + assert!(f.contains_range(WrappingRange { start: 20, end: 10 }, size16)); + + let g = WrappingRange { start: 2, end: 1 }; + assert!(g.contains_range(WrappingRange { start: 10, end: 20 }, size16)); + assert!(g.contains_range(WrappingRange { start: 20, end: 10 }, size16)); + + let size1 = Size::from_bytes(1); + let u8r = WrappingRange { start: 0, end: 255 }; + let i8r = WrappingRange { start: 128, end: 127 }; + assert!(u8r.contains_range(i8r, size1)); + assert!(i8r.contains_range(u8r, size1)); + assert!(!u8r.contains_range(i8r, size16)); + assert!(i8r.contains_range(u8r, size16)); + + let boolr = WrappingRange { start: 0, end: 1 }; + assert!(u8r.contains_range(boolr, size1)); + assert!(i8r.contains_range(boolr, size1)); + assert!(!boolr.contains_range(u8r, size1)); + assert!(!boolr.contains_range(i8r, size1)); + + let cmpr = WrappingRange { start: 255, end: 1 }; + assert!(u8r.contains_range(cmpr, size1)); + assert!(i8r.contains_range(cmpr, size1)); + assert!(!cmpr.contains_range(u8r, size1)); + assert!(!cmpr.contains_range(i8r, size1)); + + assert!(!boolr.contains_range(cmpr, size1)); + assert!(cmpr.contains_range(boolr, size1)); +} diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs index 610e2fd231117..a5759b79be45a 100644 --- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs +++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs @@ -288,7 +288,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { // valid ranges. For example, `char`s are passed as just `i32`, with no // way for LLVM to know that they're 0x10FFFF at most. Thus we assume // the range of the input value too, not just the output range. - assume_scalar_range(bx, imm, from_scalar, from_backend_ty); + assume_scalar_range(bx, imm, from_scalar, from_backend_ty, None); imm = match (from_scalar.primitive(), to_scalar.primitive()) { (Int(_, is_signed), Int(..)) => bx.intcast(imm, to_backend_ty, is_signed), @@ -1064,7 +1064,7 @@ pub(super) fn transmute_scalar<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( // That said, last time we tried removing this, it didn't actually help // the rustc-perf results, so might as well keep doing it // - assume_scalar_range(bx, imm, from_scalar, from_backend_ty); + assume_scalar_range(bx, imm, from_scalar, from_backend_ty, Some(&to_scalar)); imm = match (from_scalar.primitive(), to_scalar.primitive()) { (Int(..) | Float(_), Int(..) | Float(_)) => bx.bitcast(imm, to_backend_ty), @@ -1092,22 +1092,42 @@ pub(super) fn transmute_scalar<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( // since it's never passed to something with parameter metadata (especially // after MIR inlining) so the only way to tell the backend about the // constraint that the `transmute` introduced is to `assume` it. - assume_scalar_range(bx, imm, to_scalar, to_backend_ty); + assume_scalar_range(bx, imm, to_scalar, to_backend_ty, Some(&from_scalar)); imm = bx.to_immediate_scalar(imm, to_scalar); imm } +/// Emits an `assume` call that `imm`'s value is within the known range of `scalar`. +/// +/// If `known` is `Some`, only emits the assume if it's more specific than +/// whatever is already known from the range of *that* scalar. fn assume_scalar_range<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( bx: &mut Bx, imm: Bx::Value, scalar: abi::Scalar, backend_ty: Bx::Type, + known: Option<&abi::Scalar>, ) { - if matches!(bx.cx().sess().opts.optimize, OptLevel::No) || scalar.is_always_valid(bx.cx()) { + if matches!(bx.cx().sess().opts.optimize, OptLevel::No) { return; } + match (scalar, known) { + (abi::Scalar::Union { .. }, _) => return, + (_, None) => { + if scalar.is_always_valid(bx.cx()) { + return; + } + } + (abi::Scalar::Initialized { valid_range, .. }, Some(known)) => { + let known_range = known.valid_range(bx.cx()); + if valid_range.contains_range(known_range, scalar.size(bx.cx())) { + return; + } + } + } + match scalar.primitive() { abi::Primitive::Int(..) => { let range = scalar.valid_range(bx.cx()); diff --git a/tests/codegen-llvm/intrinsics/transmute-niched.rs b/tests/codegen-llvm/intrinsics/transmute-niched.rs index 8ff5cc8ee4f4c..a886d9eee5909 100644 --- a/tests/codegen-llvm/intrinsics/transmute-niched.rs +++ b/tests/codegen-llvm/intrinsics/transmute-niched.rs @@ -163,11 +163,8 @@ pub unsafe fn check_swap_pair(x: (char, NonZero)) -> (NonZero, char) { pub unsafe fn check_bool_from_ordering(x: std::cmp::Ordering) -> bool { // CHECK-NOT: icmp // CHECK-NOT: assume - // OPT: %0 = sub i8 %x, -1 - // OPT: %1 = icmp ule i8 %0, 2 - // OPT: call void @llvm.assume(i1 %1) - // OPT: %2 = icmp ule i8 %x, 1 - // OPT: call void @llvm.assume(i1 %2) + // OPT: %0 = icmp ule i8 %x, 1 + // OPT: call void @llvm.assume(i1 %0) // CHECK-NOT: icmp // CHECK-NOT: assume // CHECK: %[[R:.+]] = trunc{{( nuw)?}} i8 %x to i1 @@ -184,9 +181,6 @@ pub unsafe fn check_bool_to_ordering(x: bool) -> std::cmp::Ordering { // CHECK-NOT: assume // OPT: %0 = icmp ule i8 %_0, 1 // OPT: call void @llvm.assume(i1 %0) - // OPT: %1 = sub i8 %_0, -1 - // OPT: %2 = icmp ule i8 %1, 2 - // OPT: call void @llvm.assume(i1 %2) // CHECK-NOT: icmp // CHECK-NOT: assume // CHECK: ret i8 %_0 @@ -221,3 +215,42 @@ pub unsafe fn check_ptr_to_nonnull(x: *const u8) -> NonNull { transmute(x) } + +#[repr(usize)] +pub enum FourOrEight { + Four = 4, + Eight = 8, +} + +// CHECK-LABEL: @check_nonnull_to_four_or_eight( +#[no_mangle] +pub unsafe fn check_nonnull_to_four_or_eight(x: NonNull) -> FourOrEight { + // CHECK: start + // CHECK-NEXT: %[[RET:.+]] = ptrtoint ptr %x to i64 + // CHECK-NOT: icmp + // CHECK-NOT: assume + // OPT: %0 = sub i64 %[[RET]], 4 + // OPT: %1 = icmp ule i64 %0, 4 + // OPT: call void @llvm.assume(i1 %1) + // CHECK-NOT: icmp + // CHECK-NOT: assume + // CHECK: ret i64 %[[RET]] + + transmute(x) +} + +// CHECK-LABEL: @check_four_or_eight_to_nonnull( +#[no_mangle] +pub unsafe fn check_four_or_eight_to_nonnull(x: FourOrEight) -> NonNull { + // CHECK-NOT: icmp + // CHECK-NOT: assume + // OPT: %0 = sub i64 %x, 4 + // OPT: %1 = icmp ule i64 %0, 4 + // OPT: call void @llvm.assume(i1 %1) + // CHECK-NOT: icmp + // CHECK-NOT: assume + // CHECK: %[[RET:.+]] = getelementptr i8, ptr null, i64 %x + // CHECK-NEXT: ret ptr %[[RET]] + + transmute(x) +} From e7441fbf61d3189dae16468fe6a3900ad466ed69 Mon Sep 17 00:00:00 2001 From: Jens Reidel Date: Wed, 23 Jul 2025 20:00:42 +0200 Subject: [PATCH 19/24] Clippy fixup Signed-off-by: Jens Reidel --- src/bootstrap/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs index 2aee9f0728449..51a84ad5272c9 100644 --- a/src/bootstrap/src/lib.rs +++ b/src/bootstrap/src/lib.rs @@ -1343,7 +1343,7 @@ impl Build { .map(|p| &**p); if self.config.is_host_target(target) && configured_root.is_none() { - return Some(Path::new("/usr")); + Some(Path::new("/usr")) } else { configured_root } From e44a7386c27f821515804fa90ed39ca1e931f8de Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Thu, 24 Jul 2025 08:15:03 +0000 Subject: [PATCH 20/24] Remove dead code and extend test coverage and diagnostics around it We lost the following comment during refactorings: The current code for niche-filling relies on variant indices instead of actual discriminants, so enums with explicit discriminants (RFC 2363) would misbehave. --- compiler/rustc_abi/src/layout.rs | 12 +----- .../rustc_hir_analysis/src/check/check.rs | 38 ++++++++++++++----- compiler/rustc_ty_utils/src/layout.rs | 8 ---- .../crates/hir-ty/src/layout/adt.rs | 16 ++------ .../arbitrary_enum_discriminant-no-repr.rs | 19 +++++++--- ...arbitrary_enum_discriminant-no-repr.stderr | 20 ++++++++-- 6 files changed, 65 insertions(+), 48 deletions(-) diff --git a/compiler/rustc_abi/src/layout.rs b/compiler/rustc_abi/src/layout.rs index 80b44e432eeb0..716bb716cdb57 100644 --- a/compiler/rustc_abi/src/layout.rs +++ b/compiler/rustc_abi/src/layout.rs @@ -313,7 +313,6 @@ impl LayoutCalculator { scalar_valid_range: (Bound, Bound), discr_range_of_repr: impl Fn(i128, i128) -> (Integer, bool), discriminants: impl Iterator, - dont_niche_optimize_enum: bool, always_sized: bool, ) -> LayoutCalculatorResult { let (present_first, present_second) = { @@ -352,13 +351,7 @@ impl LayoutCalculator { // structs. (We have also handled univariant enums // that allow representation optimization.) assert!(is_enum); - self.layout_of_enum( - repr, - variants, - discr_range_of_repr, - discriminants, - dont_niche_optimize_enum, - ) + self.layout_of_enum(repr, variants, discr_range_of_repr, discriminants) } } @@ -599,7 +592,6 @@ impl LayoutCalculator { variants: &IndexSlice>, discr_range_of_repr: impl Fn(i128, i128) -> (Integer, bool), discriminants: impl Iterator, - dont_niche_optimize_enum: bool, ) -> LayoutCalculatorResult { // Until we've decided whether to use the tagged or // niche filling LayoutData, we don't want to intern the @@ -618,7 +610,7 @@ impl LayoutCalculator { } let calculate_niche_filling_layout = || -> Option> { - if dont_niche_optimize_enum { + if repr.inhibit_enum_layout_opt() { return None; } diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index 03c026cd6c892..bb1de5bcfc3b7 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -1642,20 +1642,40 @@ fn check_enum(tcx: TyCtxt<'_>, def_id: LocalDefId) { if def.repr().int.is_none() { let is_unit = |var: &ty::VariantDef| matches!(var.ctor_kind(), Some(CtorKind::Const)); - let has_disr = |var: &ty::VariantDef| matches!(var.discr, ty::VariantDiscr::Explicit(_)); + let get_disr = |var: &ty::VariantDef| match var.discr { + ty::VariantDiscr::Explicit(disr) => Some(disr), + ty::VariantDiscr::Relative(_) => None, + }; - let has_non_units = def.variants().iter().any(|var| !is_unit(var)); - let disr_units = def.variants().iter().any(|var| is_unit(var) && has_disr(var)); - let disr_non_unit = def.variants().iter().any(|var| !is_unit(var) && has_disr(var)); + let non_unit = def.variants().iter().find(|var| !is_unit(var)); + let disr_unit = + def.variants().iter().filter(|var| is_unit(var)).find_map(|var| get_disr(var)); + let disr_non_unit = + def.variants().iter().filter(|var| !is_unit(var)).find_map(|var| get_disr(var)); - if disr_non_unit || (disr_units && has_non_units) { - struct_span_code_err!( + if disr_non_unit.is_some() || (disr_unit.is_some() && non_unit.is_some()) { + let mut err = struct_span_code_err!( tcx.dcx(), tcx.def_span(def_id), E0732, - "`#[repr(inttype)]` must be specified" - ) - .emit(); + "`#[repr(inttype)]` must be specified for enums with explicit discriminants and non-unit variants" + ); + if let Some(disr_non_unit) = disr_non_unit { + err.span_label( + tcx.def_span(disr_non_unit), + "explicit discriminant on non-unit variant specified here", + ); + } else { + err.span_label( + tcx.def_span(disr_unit.unwrap()), + "explicit discriminant specified here", + ); + err.span_label( + tcx.def_span(non_unit.unwrap().def_id), + "non-unit discriminant declared here", + ); + } + err.emit(); } } diff --git a/compiler/rustc_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs index 163e2b3088374..79f7e228e2adc 100644 --- a/compiler/rustc_ty_utils/src/layout.rs +++ b/compiler/rustc_ty_utils/src/layout.rs @@ -603,12 +603,6 @@ fn layout_of_uncached<'tcx>( .flatten() }; - let dont_niche_optimize_enum = def.repr().inhibit_enum_layout_opt() - || def - .variants() - .iter_enumerated() - .any(|(i, v)| v.discr != ty::VariantDiscr::Relative(i.as_u32())); - let maybe_unsized = def.is_struct() && def.non_enum_variant().tail_opt().is_some_and(|last_field| { let typing_env = ty::TypingEnv::post_analysis(tcx, def.did()); @@ -625,7 +619,6 @@ fn layout_of_uncached<'tcx>( tcx.layout_scalar_valid_range(def.did()), get_discriminant_type, discriminants_iter(), - dont_niche_optimize_enum, !maybe_unsized, ) .map_err(|err| map_error(cx, ty, err))?; @@ -651,7 +644,6 @@ fn layout_of_uncached<'tcx>( tcx.layout_scalar_valid_range(def.did()), get_discriminant_type, discriminants_iter(), - dont_niche_optimize_enum, !maybe_unsized, ) else { bug!("failed to compute unsized layout of {ty:?}"); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs index 236f316366dc9..372a9dfc43d27 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs @@ -3,9 +3,9 @@ use std::{cmp, ops::Bound}; use hir_def::{ - AdtId, VariantId, layout::{Integer, ReprOptions, TargetDataLayout}, signatures::{StructFlags, VariantFields}, + AdtId, VariantId, }; use intern::sym; use rustc_index::IndexVec; @@ -13,9 +13,9 @@ use smallvec::SmallVec; use triomphe::Arc; use crate::{ - Substitution, TraitEnvironment, db::HirDatabase, - layout::{Layout, LayoutError, field_ty}, + layout::{field_ty, Layout, LayoutError}, + Substitution, TraitEnvironment, }; use super::LayoutCx; @@ -85,16 +85,6 @@ pub fn layout_of_adt_query( let d = db.const_eval_discriminant(e.enum_variants(db).variants[id.0].0).ok()?; Some((id, d)) }), - // FIXME: The current code for niche-filling relies on variant indices - // instead of actual discriminants, so enums with - // explicit discriminants (RFC #2363) would misbehave and we should disable - // niche optimization for them. - // The code that do it in rustc: - // repr.inhibit_enum_layout_opt() || def - // .variants() - // .iter_enumerated() - // .any(|(i, v)| v.discr != ty::VariantDiscr::Relative(i.as_u32())) - repr.inhibit_enum_layout_opt(), !matches!(def, AdtId::EnumId(..)) && variants .iter() diff --git a/tests/ui/enum-discriminant/arbitrary_enum_discriminant-no-repr.rs b/tests/ui/enum-discriminant/arbitrary_enum_discriminant-no-repr.rs index a6e5f70fdefa6..6bbafbf1434ef 100644 --- a/tests/ui/enum-discriminant/arbitrary_enum_discriminant-no-repr.rs +++ b/tests/ui/enum-discriminant/arbitrary_enum_discriminant-no-repr.rs @@ -1,8 +1,17 @@ -#![crate_type="lib"] +#![crate_type = "lib"] +// Test that if any variant is non-unit, +// we need a repr. enum Enum { -//~^ ERROR `#[repr(inttype)]` must be specified - Unit = 1, - Tuple() = 2, - Struct{} = 3, + //~^ ERROR `#[repr(inttype)]` must be specified + Unit = 1, + Tuple(), + Struct {}, +} + +// Test that if any non-unit variant has an explicit +// discriminant we need a repr. +enum Enum2 { + //~^ ERROR `#[repr(inttype)]` must be specified + Tuple() = 2, } diff --git a/tests/ui/enum-discriminant/arbitrary_enum_discriminant-no-repr.stderr b/tests/ui/enum-discriminant/arbitrary_enum_discriminant-no-repr.stderr index 3b718c6465bdf..35c0208951aeb 100644 --- a/tests/ui/enum-discriminant/arbitrary_enum_discriminant-no-repr.stderr +++ b/tests/ui/enum-discriminant/arbitrary_enum_discriminant-no-repr.stderr @@ -1,9 +1,23 @@ -error[E0732]: `#[repr(inttype)]` must be specified - --> $DIR/arbitrary_enum_discriminant-no-repr.rs:3:1 +error[E0732]: `#[repr(inttype)]` must be specified for enums with explicit discriminants and non-unit variants + --> $DIR/arbitrary_enum_discriminant-no-repr.rs:5:1 | LL | enum Enum { | ^^^^^^^^^ +LL | +LL | Unit = 1, + | - explicit discriminant specified here +LL | Tuple(), + | ----- non-unit discriminant declared here -error: aborting due to 1 previous error +error[E0732]: `#[repr(inttype)]` must be specified for enums with explicit discriminants and non-unit variants + --> $DIR/arbitrary_enum_discriminant-no-repr.rs:14:1 + | +LL | enum Enum2 { + | ^^^^^^^^^^ +LL | +LL | Tuple() = 2, + | - explicit discriminant on non-unit variant specified here + +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0732`. From 642deb3c8f88073b8417a067e8de6b4dc5b84a6a Mon Sep 17 00:00:00 2001 From: Makai Date: Thu, 24 Jul 2025 18:26:08 +0800 Subject: [PATCH 21/24] remove movability from `RigidTy::Coroutine` and `AggregateKind::Coroutine` --- compiler/rustc_public/src/mir/body.rs | 7 ++----- compiler/rustc_public/src/mir/pretty.rs | 2 +- compiler/rustc_public/src/ty.rs | 7 +++---- compiler/rustc_public/src/unstable/convert/internal.rs | 2 +- compiler/rustc_public/src/unstable/convert/stable/mir.rs | 1 - compiler/rustc_public/src/unstable/convert/stable/ty.rs | 1 - compiler/rustc_public/src/visitor.rs | 2 +- 7 files changed, 8 insertions(+), 14 deletions(-) diff --git a/compiler/rustc_public/src/mir/body.rs b/compiler/rustc_public/src/mir/body.rs index 3320b98cd610e..3d595286041c7 100644 --- a/compiler/rustc_public/src/mir/body.rs +++ b/compiler/rustc_public/src/mir/body.rs @@ -654,9 +654,7 @@ impl Rvalue { )), AggregateKind::Adt(def, _, ref args, _, _) => Ok(def.ty_with_args(args)), AggregateKind::Closure(def, ref args) => Ok(Ty::new_closure(def, args.clone())), - AggregateKind::Coroutine(def, ref args, mov) => { - Ok(Ty::new_coroutine(def, args.clone(), mov)) - } + AggregateKind::Coroutine(def, ref args) => Ok(Ty::new_coroutine(def, args.clone())), AggregateKind::CoroutineClosure(def, ref args) => { Ok(Ty::new_coroutine_closure(def, args.clone())) } @@ -674,8 +672,7 @@ pub enum AggregateKind { Tuple, Adt(AdtDef, VariantIdx, GenericArgs, Option, Option), Closure(ClosureDef, GenericArgs), - // FIXME(rustc_public): Movability here is redundant - Coroutine(CoroutineDef, GenericArgs, Movability), + Coroutine(CoroutineDef, GenericArgs), CoroutineClosure(CoroutineClosureDef, GenericArgs), RawPtr(Ty, Mutability), } diff --git a/compiler/rustc_public/src/mir/pretty.rs b/compiler/rustc_public/src/mir/pretty.rs index a433df2dba1a1..3183c020772a7 100644 --- a/compiler/rustc_public/src/mir/pretty.rs +++ b/compiler/rustc_public/src/mir/pretty.rs @@ -428,7 +428,7 @@ fn pretty_aggregate( write!(writer, "{{closure@{}}}(", def.span().diagnostic())?; ")" } - AggregateKind::Coroutine(def, _, _) => { + AggregateKind::Coroutine(def, _) => { write!(writer, "{{coroutine@{}}}(", def.span().diagnostic())?; ")" } diff --git a/compiler/rustc_public/src/ty.rs b/compiler/rustc_public/src/ty.rs index bc67a2f987d9e..de4b21b176472 100644 --- a/compiler/rustc_public/src/ty.rs +++ b/compiler/rustc_public/src/ty.rs @@ -60,8 +60,8 @@ impl Ty { } /// Create a new coroutine type. - pub fn new_coroutine(def: CoroutineDef, args: GenericArgs, mov: Movability) -> Ty { - Ty::from_rigid_kind(RigidTy::Coroutine(def, args, mov)) + pub fn new_coroutine(def: CoroutineDef, args: GenericArgs) -> Ty { + Ty::from_rigid_kind(RigidTy::Coroutine(def, args)) } /// Create a new closure type. @@ -560,8 +560,7 @@ pub enum RigidTy { FnDef(FnDef, GenericArgs), FnPtr(PolyFnSig), Closure(ClosureDef, GenericArgs), - // FIXME(rustc_public): Movability here is redundant - Coroutine(CoroutineDef, GenericArgs, Movability), + Coroutine(CoroutineDef, GenericArgs), CoroutineClosure(CoroutineClosureDef, GenericArgs), Dynamic(Vec>, Region, DynKind), Never, diff --git a/compiler/rustc_public/src/unstable/convert/internal.rs b/compiler/rustc_public/src/unstable/convert/internal.rs index b2d38e497bc82..66f767a98f5bd 100644 --- a/compiler/rustc_public/src/unstable/convert/internal.rs +++ b/compiler/rustc_public/src/unstable/convert/internal.rs @@ -177,7 +177,7 @@ impl RustcInternal for RigidTy { RigidTy::Closure(def, args) => { rustc_ty::TyKind::Closure(def.0.internal(tables, tcx), args.internal(tables, tcx)) } - RigidTy::Coroutine(def, args, _mov) => { + RigidTy::Coroutine(def, args) => { rustc_ty::TyKind::Coroutine(def.0.internal(tables, tcx), args.internal(tables, tcx)) } RigidTy::CoroutineClosure(def, args) => rustc_ty::TyKind::CoroutineClosure( diff --git a/compiler/rustc_public/src/unstable/convert/stable/mir.rs b/compiler/rustc_public/src/unstable/convert/stable/mir.rs index 8dee579e598b5..be8ee80bed3c0 100644 --- a/compiler/rustc_public/src/unstable/convert/stable/mir.rs +++ b/compiler/rustc_public/src/unstable/convert/stable/mir.rs @@ -653,7 +653,6 @@ impl<'tcx> Stable<'tcx> for mir::AggregateKind<'tcx> { crate::mir::AggregateKind::Coroutine( tables.coroutine_def(*def_id), generic_arg.stable(tables, cx), - cx.coroutine_movability(*def_id).stable(tables, cx), ) } mir::AggregateKind::CoroutineClosure(def_id, generic_args) => { diff --git a/compiler/rustc_public/src/unstable/convert/stable/ty.rs b/compiler/rustc_public/src/unstable/convert/stable/ty.rs index d679615b3bdc9..5a661072bc7ee 100644 --- a/compiler/rustc_public/src/unstable/convert/stable/ty.rs +++ b/compiler/rustc_public/src/unstable/convert/stable/ty.rs @@ -457,7 +457,6 @@ impl<'tcx> Stable<'tcx> for ty::TyKind<'tcx> { ty::Coroutine(def_id, generic_args) => TyKind::RigidTy(RigidTy::Coroutine( tables.coroutine_def(*def_id), generic_args.stable(tables, cx), - cx.coroutine_movability(*def_id).stable(tables, cx), )), ty::Never => TyKind::RigidTy(RigidTy::Never), ty::Tuple(fields) => TyKind::RigidTy(RigidTy::Tuple( diff --git a/compiler/rustc_public/src/visitor.rs b/compiler/rustc_public/src/visitor.rs index 45e2a81547085..87f1cc6ae69d8 100644 --- a/compiler/rustc_public/src/visitor.rs +++ b/compiler/rustc_public/src/visitor.rs @@ -166,7 +166,7 @@ impl Visitable for RigidTy { } RigidTy::Adt(_, args) | RigidTy::Closure(_, args) - | RigidTy::Coroutine(_, args, _) + | RigidTy::Coroutine(_, args) | RigidTy::CoroutineWitness(_, args) | RigidTy::CoroutineClosure(_, args) | RigidTy::FnDef(_, args) => args.visit(visitor), From 9ffa77523284d9301a2dacdf0e1064844d640592 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Tue, 22 Jul 2025 23:13:09 +0300 Subject: [PATCH 22/24] resolve: Remove `Scope::CrateRoot` Use `Scope::Module` with the crate root module inside instead, which should be identical. --- compiler/rustc_resolve/src/diagnostics.rs | 5 -- compiler/rustc_resolve/src/ident.rs | 72 +++++++++-------------- compiler/rustc_resolve/src/imports.rs | 8 +-- compiler/rustc_resolve/src/lib.rs | 11 ++-- 4 files changed, 39 insertions(+), 57 deletions(-) diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index d72fbc189e70e..75eed1e9ad3fc 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -1076,11 +1076,6 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } } } - Scope::CrateRoot => { - let root_ident = Ident::new(kw::PathRoot, ident.span); - let root_module = this.resolve_crate_root(root_ident); - this.add_module_candidates(root_module, &mut suggestions, filter_fn, None); - } Scope::Module(module, _) => { this.add_module_candidates(module, &mut suggestions, filter_fn, None); } diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs index 34941398a2bb4..71cc68af499db 100644 --- a/compiler/rustc_resolve/src/ident.rs +++ b/compiler/rustc_resolve/src/ident.rs @@ -93,20 +93,21 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { // 6. Language prelude: builtin attributes (closed, controlled). let rust_2015 = ctxt.edition().is_rust_2015(); - let (ns, macro_kind, is_absolute_path) = match scope_set { - ScopeSet::All(ns) => (ns, None, false), - ScopeSet::AbsolutePath(ns) => (ns, None, true), - ScopeSet::Macro(macro_kind) => (MacroNS, Some(macro_kind), false), - ScopeSet::Late(ns, ..) => (ns, None, false), + let (ns, macro_kind) = match scope_set { + ScopeSet::All(ns) + | ScopeSet::ModuleAndExternPrelude(ns, _) + | ScopeSet::Late(ns, ..) => (ns, None), + ScopeSet::Macro(macro_kind) => (MacroNS, Some(macro_kind)), }; let module = match scope_set { // Start with the specified module. - ScopeSet::Late(_, module, _) => module, + ScopeSet::Late(_, module, _) | ScopeSet::ModuleAndExternPrelude(_, module) => module, // Jump out of trait or enum modules, they do not act as scopes. _ => parent_scope.module.nearest_item_scope(), }; + let module_and_extern_prelude = matches!(scope_set, ScopeSet::ModuleAndExternPrelude(..)); let mut scope = match ns { - _ if is_absolute_path => Scope::CrateRoot, + _ if module_and_extern_prelude => Scope::Module(module, None), TypeNS | ValueNS => Scope::Module(module, None), MacroNS => Scope::DeriveHelpers(parent_scope.expansion), }; @@ -134,11 +135,10 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } true } - Scope::CrateRoot => true, Scope::Module(..) => true, Scope::MacroUsePrelude => use_prelude || rust_2015, Scope::BuiltinAttrs => true, - Scope::ExternPrelude => use_prelude || is_absolute_path, + Scope::ExternPrelude => use_prelude || module_and_extern_prelude, Scope::ToolPrelude => use_prelude, Scope::StdLibPrelude => use_prelude || ns == MacroNS, Scope::BuiltinTypes => true, @@ -174,7 +174,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } MacroRulesScope::Empty => Scope::Module(module, None), }, - Scope::CrateRoot => match ns { + Scope::Module(..) if module_and_extern_prelude => match ns { TypeNS => { ctxt.adjust(ExpnId::root()); Scope::ExternPrelude @@ -203,7 +203,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } Scope::MacroUsePrelude => Scope::StdLibPrelude, Scope::BuiltinAttrs => break, // nowhere else to search - Scope::ExternPrelude if is_absolute_path => break, + Scope::ExternPrelude if module_and_extern_prelude => break, Scope::ExternPrelude => Scope::ToolPrelude, Scope::ToolPrelude => Scope::StdLibPrelude, Scope::StdLibPrelude => match ns { @@ -404,10 +404,10 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } let (ns, macro_kind) = match scope_set { - ScopeSet::All(ns) => (ns, None), - ScopeSet::AbsolutePath(ns) => (ns, None), + ScopeSet::All(ns) + | ScopeSet::ModuleAndExternPrelude(ns, _) + | ScopeSet::Late(ns, ..) => (ns, None), ScopeSet::Macro(macro_kind) => (MacroNS, Some(macro_kind)), - ScopeSet::Late(ns, ..) => (ns, None), }; // This is *the* result, resolution from the scope closest to the resolved identifier. @@ -487,31 +487,16 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { MacroRulesScope::Invocation(_) => Err(Determinacy::Undetermined), _ => Err(Determinacy::Determined), }, - Scope::CrateRoot => { - let root_ident = Ident::new(kw::PathRoot, ident.span); - let root_module = this.resolve_crate_root(root_ident); - let binding = this.resolve_ident_in_module( - ModuleOrUniformRoot::Module(root_module), - ident, - ns, - parent_scope, - finalize, - ignore_binding, - ignore_import, - ); - match binding { - Ok(binding) => Ok((binding, Flags::MODULE | Flags::MISC_SUGGEST_CRATE)), - Err((Determinacy::Undetermined, Weak::No)) => { - return Some(Err(Determinacy::determined(force))); - } - Err((Determinacy::Undetermined, Weak::Yes)) => { - Err(Determinacy::Undetermined) - } - Err((Determinacy::Determined, _)) => Err(Determinacy::Determined), - } - } Scope::Module(module, derive_fallback_lint_id) => { - let adjusted_parent_scope = &ParentScope { module, ..*parent_scope }; + let (adjusted_parent_scope, finalize) = + if matches!(scope_set, ScopeSet::ModuleAndExternPrelude(..)) { + (parent_scope, finalize) + } else { + ( + &ParentScope { module, ..*parent_scope }, + finalize.map(|f| Finalize { used: Used::Scope, ..f }), + ) + }; let binding = this.resolve_ident_in_module_unadjusted( ModuleOrUniformRoot::Module(module), ident, @@ -522,7 +507,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } else { Shadowing::Restricted }, - finalize.map(|finalize| Finalize { used: Used::Scope, ..finalize }), + finalize, ignore_binding, ignore_import, ); @@ -776,7 +761,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { ModuleOrUniformRoot::ExternPrelude => { ident.span.normalize_to_macros_2_0_and_adjust(ExpnId::root()); } - ModuleOrUniformRoot::CrateRootAndExternPrelude | ModuleOrUniformRoot::CurrentScope => { + ModuleOrUniformRoot::ModuleAndExternPrelude(..) | ModuleOrUniformRoot::CurrentScope => { // No adjustments } } @@ -810,11 +795,11 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { ) -> Result, (Determinacy, Weak)> { let module = match module { ModuleOrUniformRoot::Module(module) => module, - ModuleOrUniformRoot::CrateRootAndExternPrelude => { + ModuleOrUniformRoot::ModuleAndExternPrelude(module) => { assert_eq!(shadowing, Shadowing::Unrestricted); let binding = self.early_resolve_ident_in_lexical_scope( ident, - ScopeSet::AbsolutePath(ns), + ScopeSet::ModuleAndExternPrelude(ns, module), parent_scope, finalize, finalize.is_some(), @@ -1531,7 +1516,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { && self.tcx.sess.at_least_rust_2018() { // `::a::b` from 2015 macro on 2018 global edition - module = Some(ModuleOrUniformRoot::CrateRootAndExternPrelude); + let crate_root = self.resolve_crate_root(ident); + module = Some(ModuleOrUniformRoot::ModuleAndExternPrelude(crate_root)); continue; } if name == kw::PathRoot || name == kw::Crate || name == kw::DollarCrate { diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs index 783c5005cca92..986e703c0d23b 100644 --- a/compiler/rustc_resolve/src/imports.rs +++ b/compiler/rustc_resolve/src/imports.rs @@ -181,10 +181,10 @@ pub(crate) struct ImportData<'ra> { /// /// | `module_path` | `imported_module` | remark | /// |-|-|-| - /// |`use prefix::foo`| `ModuleOrUniformRoot::Module(prefix)` | - | - /// |`use ::foo` | `ModuleOrUniformRoot::ExternPrelude` | 2018+ editions | - /// |`use ::foo` | `ModuleOrUniformRoot::CrateRootAndExternPrelude` | a special case in 2015 edition | - /// |`use foo` | `ModuleOrUniformRoot::CurrentScope` | - | + /// |`use prefix::foo`| `ModuleOrUniformRoot::Module(prefix)` | - | + /// |`use ::foo` | `ModuleOrUniformRoot::ExternPrelude` | 2018+ editions | + /// |`use ::foo` | `ModuleOrUniformRoot::ModuleAndExternPrelude` | a special case in 2015 edition | + /// |`use foo` | `ModuleOrUniformRoot::CurrentScope` | - | pub imported_module: Cell>>, pub vis: Visibility, } diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 08f1f61ea86ee..27e14e0e62bf0 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -119,7 +119,6 @@ enum Scope<'ra> { DeriveHelpers(LocalExpnId), DeriveHelpersCompat, MacroRules(MacroRulesScopeRef<'ra>), - CrateRoot, // The node ID is for reporting the `PROC_MACRO_DERIVE_RESOLUTION_FALLBACK` // lint if it should be reported. Module(Module<'ra>, Option), @@ -139,8 +138,8 @@ enum Scope<'ra> { enum ScopeSet<'ra> { /// All scopes with the given namespace. All(Namespace), - /// Crate root, then extern prelude (used for mixed 2015-2018 mode in macros). - AbsolutePath(Namespace), + /// A module, then extern prelude (used for mixed 2015-2018 mode in macros). + ModuleAndExternPrelude(Namespace, Module<'ra>), /// All scopes with macro namespace and the given macro kind restriction. Macro(MacroKind), /// All scopes with the given namespace, used for partially performing late resolution. @@ -419,8 +418,10 @@ enum ModuleOrUniformRoot<'ra> { /// Regular module. Module(Module<'ra>), - /// Virtual module that denotes resolution in crate root with fallback to extern prelude. - CrateRootAndExternPrelude, + /// Virtual module that denotes resolution in a module with fallback to extern prelude. + /// Used for paths starting with `::` coming from 2015 edition macros + /// used in 2018+ edition crates. + ModuleAndExternPrelude(Module<'ra>), /// Virtual module that denotes resolution in extern prelude. /// Used for paths starting with `::` on 2018 edition. From 94c0cf891e4427083072b9050b89dd0332a616ae Mon Sep 17 00:00:00 2001 From: Oneirical Date: Tue, 22 Jul 2025 21:42:50 -0400 Subject: [PATCH 23/24] Rename tests/ui/SUMMARY.md and update rustc dev guide on error-pattern --- src/doc/rustc-dev-guide/src/tests/ui.md | 8 ++++---- tests/ui/{SUMMARY.md => README.md} | 0 2 files changed, 4 insertions(+), 4 deletions(-) rename tests/ui/{SUMMARY.md => README.md} (100%) diff --git a/src/doc/rustc-dev-guide/src/tests/ui.md b/src/doc/rustc-dev-guide/src/tests/ui.md index 9bfc60e08a6ea..b1feef9ed0cc8 100644 --- a/src/doc/rustc-dev-guide/src/tests/ui.md +++ b/src/doc/rustc-dev-guide/src/tests/ui.md @@ -309,7 +309,8 @@ fn main((ؼ Use `//~?` to match an error without line information. `//~?` is precise and will not match errors if their line information is available. -It should be preferred to using `error-pattern`, which is imprecise and non-exhaustive. +For tests wishing to match against compiler diagnostics, error annotations should +be preferred over //@ error-pattern, //@ error-pattern is imprecise and non-exhaustive. ```rust,ignore //@ compile-flags: --print yyyy @@ -347,8 +348,6 @@ fn main() { } ``` -Use of `error-pattern` is not recommended in general. - For strict testing of compile time output, try to use the line annotations `//~` as much as possible, including `//~?` annotations for diagnostics without spans. @@ -359,7 +358,8 @@ Some of the compiler messages can stay uncovered by annotations in this mode. For checking runtime output, `//@ check-run-results` may be preferable. -Only use `error-pattern` if none of the above works. +Only use `error-pattern` if none of the above works, such as when finding a +specific string pattern in a runtime panic output. Line annotations `//~` and `error-pattern` are compatible and can be used in the same test. diff --git a/tests/ui/SUMMARY.md b/tests/ui/README.md similarity index 100% rename from tests/ui/SUMMARY.md rename to tests/ui/README.md From 97676e609f1382605d75637149f4c8e4bb05904d Mon Sep 17 00:00:00 2001 From: Boxy Date: Fri, 25 Jul 2025 00:02:39 +0100 Subject: [PATCH 24/24] Allow setting `release-blog-post` label with rustbot --- triagebot.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/triagebot.toml b/triagebot.toml index 5b522a6617cdc..6cfb744f0ac0b 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -28,6 +28,7 @@ allow-unauthenticated = [ "llvm-*", "needs-fcp", "relnotes", + "release-blog-post", "requires-*", "regression-*", "rla-*",