From f7548dd67188ff4f71059fa266b4319ddbade1e1 Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Wed, 12 Aug 2026 21:25:35 -0400 Subject: [PATCH 01/30] Stabilize `funnel_shifts` (including `const`) `funnel_shl` and `funnel_shr` have been around for close to a year, the unchecked versions for a number of months. These are reasonably small and uncontroversial, and it can be tricky to get similar performance with a fallback; stabilize them here. Newly stable API: impl {u8, u16, u32, u64, u128, usize} { pub const fn funnel_shl(self, right: Self, shift: u32) -> Self; pub const fn funnel_shr(self, right: Self, shift: u32) -> Self; pub const unsafe fn unchecked_funnel_shl(self, right: Self, shift: u32) -> Self; pub const unsafe fn unchecked_funnel_shr(self, right: Self, shift: u32) -> Self; } The tracking issue also mentions a `wrapping_` version but it has not been implemented. Tracking issue: RUST-145686 --- library/core/src/intrinsics/mod.rs | 12 ++++----- library/core/src/lib.rs | 1 - library/core/src/num/uint_macros.rs | 26 +++++++------------ library/coretests/tests/lib.rs | 1 - library/std/src/lib.rs | 1 - library/stdarch/crates/core_arch/src/lib.rs | 1 - .../miri/tests/fail/intrinsics/funnel_shl.rs | 2 +- .../miri/tests/fail/intrinsics/funnel_shr.rs | 2 +- .../miri/tests/pass/intrinsics/integer.rs | 2 +- .../codegen-llvm/force-intrinsic-fallback.rs | 2 +- tests/ui/std/overflow-check-ops.rs | 1 - 11 files changed, 20 insertions(+), 31 deletions(-) diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index 673454abaf04f..305bab8ec27e0 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -2211,7 +2211,7 @@ pub const unsafe fn unchecked_mul(x: T, y: T) -> T; #[rustc_intrinsic_const_stable_indirect] #[rustc_nounwind] #[rustc_intrinsic] -#[rustc_allow_const_fn_unstable(const_trait_impl, funnel_shifts)] +#[rustc_allow_const_fn_unstable(const_trait_impl)] #[miri::intrinsic_fallback_is_spec] pub const fn rotate_left(x: T, shift: u32) -> T { // Make sure to call the intrinsic for `funnel_shl`, not the fallback impl. @@ -2233,7 +2233,7 @@ pub const fn rotate_left(x: T, shift: u32) -> #[rustc_intrinsic_const_stable_indirect] #[rustc_nounwind] #[rustc_intrinsic] -#[rustc_allow_const_fn_unstable(const_trait_impl, funnel_shifts)] +#[rustc_allow_const_fn_unstable(const_trait_impl)] #[miri::intrinsic_fallback_is_spec] pub const fn rotate_right(x: T, shift: u32) -> T { // Make sure to call the intrinsic for `funnel_shr`, not the fallback impl. @@ -2329,11 +2329,11 @@ pub const fn saturating_sub(a: T, b: T) -> T; /// /// Safe versions of this intrinsic are available on the integer primitives /// via the `funnel_shl` method. For example, [`u32::funnel_shl`]. +#[rustc_intrinsic_const_stable_indirect] #[rustc_intrinsic] #[rustc_nounwind] -#[rustc_const_unstable(feature = "funnel_shifts", issue = "145686")] -#[unstable(feature = "funnel_shifts", issue = "145686")] #[track_caller] +#[rustc_allow_const_fn_unstable(const_trait_impl, core_intrinsics_fallbacks)] #[miri::intrinsic_fallback_is_spec] pub const unsafe fn unchecked_funnel_shl( a: T, @@ -2357,11 +2357,11 @@ pub const unsafe fn unchecked_funnel_shl( /// /// Safer versions of this intrinsic are available on the integer primitives /// via the `funnel_shr` method. For example, [`u32::funnel_shr`] +#[rustc_intrinsic_const_stable_indirect] #[rustc_intrinsic] #[rustc_nounwind] -#[rustc_const_unstable(feature = "funnel_shifts", issue = "145686")] -#[unstable(feature = "funnel_shifts", issue = "145686")] #[track_caller] +#[rustc_allow_const_fn_unstable(const_trait_impl, core_intrinsics_fallbacks)] #[miri::intrinsic_fallback_is_spec] pub const unsafe fn unchecked_funnel_shr( a: T, diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index 4fbd3c6dc2142..b047657cf10de 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -131,7 +131,6 @@ #![feature(final_associated_functions)] #![feature(freeze_impls)] #![feature(fundamental)] -#![feature(funnel_shifts)] #![feature(impl_restriction)] #![feature(intra_doc_pointers)] #![feature(intrinsics)] diff --git a/library/core/src/num/uint_macros.rs b/library/core/src/num/uint_macros.rs index a98ef99b9d7bb..2f9de03adced8 100644 --- a/library/core/src/num/uint_macros.rs +++ b/library/core/src/num/uint_macros.rs @@ -526,8 +526,6 @@ macro_rules! uint_impl { /// # Examples /// /// ``` - /// #![feature(funnel_shifts)] - /// #[doc = concat!("let a = ", $rot_op, "_", stringify!($SelfT), ";")] #[doc = concat!("let b = ", $fsh_op, "_", stringify!($SelfT), ";")] /// @@ -547,10 +545,8 @@ macro_rules! uint_impl { /// rotating by an unbounded amount like [`rotate_left`](Self::rotate_left) does: /// /// ```should_panic - /// #![feature(funnel_shifts)] /// # #![feature(cfg_overflow_checks)] /// # #[cfg(overflow_checks)] { - /// #[doc = concat!("let a = ", stringify!($SelfT), "::MAX;")] /// // Okay #[doc = concat!("let _ = a.rotate_left(", stringify!($SelfT), "::BITS);")] @@ -559,8 +555,8 @@ macro_rules! uint_impl { /// # } /// # #[cfg(not(overflow_checks))] panic!("fulfill should_panic"); /// ``` - #[rustc_const_unstable(feature = "funnel_shifts", issue = "145686")] - #[unstable(feature = "funnel_shifts", issue = "145686")] + #[rustc_const_stable(feature = "funnel_shifts", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "funnel_shifts", since = "CURRENT_RUSTC_VERSION")] #[must_use = "this returns the result of the operation, without modifying the original"] #[inline(always)] #[rustc_inherit_overflow_checks] @@ -597,8 +593,6 @@ macro_rules! uint_impl { /// # Examples /// /// ``` - /// #![feature(funnel_shifts)] - /// #[doc = concat!("let a = ", $rot_op, "_", stringify!($SelfT), ";")] #[doc = concat!("let b = ", $fsh_op, "_", stringify!($SelfT), ";")] /// @@ -618,10 +612,8 @@ macro_rules! uint_impl { /// rotating by an unbounded amount like [`rotate_right`](Self::rotate_right) does: /// /// ```should_panic - /// #![feature(funnel_shifts)] /// # #![feature(cfg_overflow_checks)] /// # #[cfg(overflow_checks)] { - /// #[doc = concat!("let a = ", stringify!($SelfT), "::MAX;")] /// // Okay #[doc = concat!("let _ = a.rotate_right(", stringify!($SelfT), "::BITS);")] @@ -630,8 +622,8 @@ macro_rules! uint_impl { /// # } /// # #[cfg(not(overflow_checks))] panic!("fulfill should_panic"); /// ``` - #[rustc_const_unstable(feature = "funnel_shifts", issue = "145686")] - #[unstable(feature = "funnel_shifts", issue = "145686")] + #[rustc_const_stable(feature = "funnel_shifts", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "funnel_shifts", since = "CURRENT_RUSTC_VERSION")] #[must_use = "this returns the result of the operation, without modifying the original"] #[inline(always)] #[rustc_inherit_overflow_checks] @@ -654,8 +646,9 @@ macro_rules! uint_impl { #[doc = concat!("`", stringify!($SelfT) , "::BITS`,")] /// i.e. when [`funnel_shl`](Self::funnel_shl) would panic. /// - #[rustc_const_unstable(feature = "funnel_shifts", issue = "145686")] - #[unstable(feature = "funnel_shifts", issue = "145686")] + #[rustc_const_stable(feature = "funnel_shifts", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "funnel_shifts", since = "CURRENT_RUSTC_VERSION")] + #[rustc_allow_const_fn_unstable(const_trait_impl)] #[must_use = "this returns the result of the operation, without modifying the original"] #[inline(always)] #[track_caller] @@ -680,8 +673,9 @@ macro_rules! uint_impl { #[doc = concat!("`", stringify!($SelfT) , "::BITS`,")] /// i.e. when [`funnel_shr`](Self::funnel_shr) would panic. /// - #[rustc_const_unstable(feature = "funnel_shifts", issue = "145686")] - #[unstable(feature = "funnel_shifts", issue = "145686")] + #[rustc_const_stable(feature = "funnel_shifts", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "funnel_shifts", since = "CURRENT_RUSTC_VERSION")] + #[rustc_allow_const_fn_unstable(const_trait_impl)] #[must_use = "this returns the result of the operation, without modifying the original"] #[inline(always)] #[track_caller] diff --git a/library/coretests/tests/lib.rs b/library/coretests/tests/lib.rs index c993c947929cd..116e1eb48d260 100644 --- a/library/coretests/tests/lib.rs +++ b/library/coretests/tests/lib.rs @@ -63,7 +63,6 @@ #![feature(fmt_internals)] #![feature(formatting_options)] #![feature(freeze)] -#![feature(funnel_shifts)] #![feature(future_join)] #![feature(generic_assert_internals)] #![feature(hasher_prefixfree_extras)] diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 92eccc27ee05d..921a1ff49511c 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -349,7 +349,6 @@ #![feature(fmt_internals)] #![feature(fn_static)] #![feature(formatting_options)] -#![feature(funnel_shifts)] #![feature(generic_atomic)] #![feature(hash_map_internals)] #![feature(hash_map_macro)] diff --git a/library/stdarch/crates/core_arch/src/lib.rs b/library/stdarch/crates/core_arch/src/lib.rs index 57eca221e1aba..2cb4c43d760e4 100644 --- a/library/stdarch/crates/core_arch/src/lib.rs +++ b/library/stdarch/crates/core_arch/src/lib.rs @@ -33,7 +33,6 @@ x86_amx_intrinsics, f16, aarch64_unstable_target_feature, - funnel_shifts, avx10_target_feature, const_trait_impl, const_cmp, diff --git a/src/tools/miri/tests/fail/intrinsics/funnel_shl.rs b/src/tools/miri/tests/fail/intrinsics/funnel_shl.rs index 40b61f65ebe88..b64f4551fd605 100644 --- a/src/tools/miri/tests/fail/intrinsics/funnel_shl.rs +++ b/src/tools/miri/tests/fail/intrinsics/funnel_shl.rs @@ -1,4 +1,4 @@ -#![feature(core_intrinsics, funnel_shifts)] +#![feature(core_intrinsics)] fn main() { unsafe { diff --git a/src/tools/miri/tests/fail/intrinsics/funnel_shr.rs b/src/tools/miri/tests/fail/intrinsics/funnel_shr.rs index 9582265639716..922144d6d6042 100644 --- a/src/tools/miri/tests/fail/intrinsics/funnel_shr.rs +++ b/src/tools/miri/tests/fail/intrinsics/funnel_shr.rs @@ -1,4 +1,4 @@ -#![feature(core_intrinsics, funnel_shifts)] +#![feature(core_intrinsics)] fn main() { unsafe { diff --git a/src/tools/miri/tests/pass/intrinsics/integer.rs b/src/tools/miri/tests/pass/intrinsics/integer.rs index 555a85147ee5f..2260068704adf 100644 --- a/src/tools/miri/tests/pass/intrinsics/integer.rs +++ b/src/tools/miri/tests/pass/intrinsics/integer.rs @@ -1,5 +1,5 @@ //@run-native -#![feature(core_intrinsics, funnel_shifts)] +#![feature(core_intrinsics)] use std::intrinsics::*; fn main() { diff --git a/tests/codegen-llvm/force-intrinsic-fallback.rs b/tests/codegen-llvm/force-intrinsic-fallback.rs index 1fb80b4e83f12..3a934b27f828e 100644 --- a/tests/codegen-llvm/force-intrinsic-fallback.rs +++ b/tests/codegen-llvm/force-intrinsic-fallback.rs @@ -2,7 +2,7 @@ // //@ revisions: NORMAL FALLBACK //@ [FALLBACK] compile-flags: -Zforce-intrinsic-fallback -#![feature(core_intrinsics, funnel_shifts)] +#![feature(core_intrinsics)] // Check the effect of `-Zforce-intrinsic-fallback`. // diff --git a/tests/ui/std/overflow-check-ops.rs b/tests/ui/std/overflow-check-ops.rs index 458c1b121664e..57db92610b20a 100644 --- a/tests/ui/std/overflow-check-ops.rs +++ b/tests/ui/std/overflow-check-ops.rs @@ -7,7 +7,6 @@ //@[WRAP] compile-flags: -C overflow-checks=false #![feature(cfg_overflow_checks)] -#![feature(funnel_shifts)] use std::hint::black_box as bb; use std::{assert_matches, fmt, panic}; From 1bd00d4f7277abbcdaa62382aff4e314869d8b28 Mon Sep 17 00:00:00 2001 From: Jakob Koschel Date: Tue, 1 Sep 2026 14:36:21 +0000 Subject: [PATCH 02/30] Add support for -Zsanitizer-cfi-minimal-runtime For production use, we should only link in the ubsan_minimal runtime, instead of the complete ubsan runtime. This adds support for both cfi-recover and cfi-diag to use the minimal runtime when `-Zsanitizer-cfi-minimal-runtime` is specified. This also includes tests, to ensure the flag can only be used if either cfi-recover or cfi-diag is enabled, it doesn't disrupt the original behavior, and links in the correct runtime when specified. Co-Authored-By: Bastian Kersting --- compiler/rustc_codegen_llvm/src/builder.rs | 33 +++- compiler/rustc_codegen_ssa/src/back/link.rs | 6 +- compiler/rustc_session/src/diagnostics.rs | 6 + compiler/rustc_session/src/options.rs | 2 + compiler/rustc_session/src/session.rs | 11 ++ src/bootstrap/src/core/build_steps/llvm.rs | 14 +- .../cfi/emit-type-checks-diag-mode.rs | 5 +- .../cfi/emit-type-checks-recover-mode.rs | 5 +- .../run-make/sanitizer-cfi-runtime/program.rs | 17 ++ tests/run-make/sanitizer-cfi-runtime/rmake.rs | 171 ++++++++++++++++++ .../cfi/fn-ptr-type-mismatch-recover.rs | 38 ++++ .../ui/sanitizer/cfi/fn-ptr-type-mismatch.rs | 9 +- ...al-runtime-requires-cfi-recover-or-diag.rs | 11 ++ ...untime-requires-cfi-recover-or-diag.stderr | 4 + 14 files changed, 319 insertions(+), 13 deletions(-) create mode 100644 tests/run-make/sanitizer-cfi-runtime/program.rs create mode 100644 tests/run-make/sanitizer-cfi-runtime/rmake.rs create mode 100644 tests/ui/sanitizer/cfi/fn-ptr-type-mismatch-recover.rs create mode 100644 tests/ui/sanitizer/cfi/minimal-runtime-requires-cfi-recover-or-diag.rs create mode 100644 tests/ui/sanitizer/cfi/minimal-runtime-requires-cfi-recover-or-diag.stderr diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index abc71f450a515..438accb85f377 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -2065,17 +2065,31 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { let is_diag = self.tcx.sess.opts.unstable_opts.sanitizer_cfi_diag.unwrap_or(false); let is_recover = self.tcx.sess.opts.unstable_opts.sanitizer_cfi_recover.unwrap_or(false); + let is_minimal = + self.tcx.sess.opts.unstable_opts.sanitizer_cfi_minimal_runtime.unwrap_or(false); if is_diag || is_recover { - let fty = self.cx.type_func( - &[self.cx.type_ptr(), self.cx.type_isize(), self.cx.type_isize()], - self.cx.type_void(), - ); + let fty = if is_minimal { + self.cx.type_func(&[], self.cx.type_void()) + } else { + self.cx.type_func( + &[self.cx.type_ptr(), self.cx.type_isize(), self.cx.type_isize()], + self.cx.type_void(), + ) + }; let ubsan_handler = self.declare_cfn( if is_recover { - "__ubsan_handle_cfi_check_fail" + if is_minimal { + "__ubsan_handle_cfi_check_fail_minimal" + } else { + "__ubsan_handle_cfi_check_fail" + } } else { - "__ubsan_handle_cfi_check_fail_abort" + if is_minimal { + "__ubsan_handle_cfi_check_fail_minimal_abort" + } else { + "__ubsan_handle_cfi_check_fail_abort" + } }, llvm::UnnamedAddr::Global, fty, @@ -2101,13 +2115,18 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { self.generate_ubsan_cfi_diag_data(self.span, expected_ty, check_kind); let function_address = self.ptrtoint(llfn, self.cx.type_isize()); + let arguments: &[_] = if is_minimal { + &[] + } else { + &[diag_data, function_address, self.const_usize(0)] + }; self.call( fty, None, None, ubsan_handler, ReturnSlot::Direct, - &[diag_data, function_address, self.const_usize(0)], + arguments, None, None, ); diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index c283e464821e5..7c8bb4f59dfba 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -1764,7 +1764,11 @@ fn add_sanitizer_libraries( && (sess.opts.unstable_opts.sanitizer_cfi_diag.unwrap_or(false) || sess.opts.unstable_opts.sanitizer_cfi_recover.unwrap_or(false)) { - link_sanitizer_runtime(sess, flavor, linker, "ubsan"); + if sess.opts.unstable_opts.sanitizer_cfi_minimal_runtime.unwrap_or(false) { + link_sanitizer_runtime(sess, flavor, linker, "ubsan_minimal"); + } else { + link_sanitizer_runtime(sess, flavor, linker, "ubsan"); + } } } diff --git a/compiler/rustc_session/src/diagnostics.rs b/compiler/rustc_session/src/diagnostics.rs index a2bf9a0d2dfc3..d27b616d7e359 100644 --- a/compiler/rustc_session/src/diagnostics.rs +++ b/compiler/rustc_session/src/diagnostics.rs @@ -340,6 +340,12 @@ pub(crate) struct SanitizerCfiRecoverRequiresCfi; #[diag("`-Zsanitizer-cfi-diag` requires `-Zsanitizer=cfi`")] pub(crate) struct SanitizerCfiDiagRequiresCfi; +#[derive(Diagnostic)] +#[diag( + "`-Zsanitizer-cfi-minimal-runtime` requires `-Zsanitizer-cfi-recover` or `-Zsanitizer-cfi-diag`" +)] +pub(crate) struct SanitizerCfiMinimalRuntimeRequiresCfiRecoverOrDiag; + #[derive(Diagnostic)] #[diag("`-Zsanitizer-kcfi-arity` requires `-Zsanitizer=kcfi`")] pub(crate) struct SanitizerKcfiArityRequiresKcfi; diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 13fc598d86bfa..f11bdadf5b67d 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -2825,6 +2825,8 @@ written to standard error output)"), "enable CFI diagnostics (default: no)"), sanitizer_cfi_recover: Option = (None, parse_opt_bool, [TRACKED], "enable CFI recovery (default: no)"), + sanitizer_cfi_minimal_runtime: Option = (None, parse_opt_bool, [TRACKED], + "enable minimal UBSan runtime for CFI (default: no)"), sanitizer_dataflow_abilist: Vec = (Vec::new(), parse_comma_list, [TRACKED], "additional ABI list files that control how shadow parameters are passed (comma separated)"), sanitizer_kcfi_arity: Option = (None, parse_opt_bool, [TRACKED], diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 274cfe8d7eb8a..bf310270d0a19 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -746,6 +746,10 @@ impl Session { self.opts.unstable_opts.sanitizer_cfi_diag == Some(true) } + pub fn is_sanitizer_cfi_minimal_runtime_enabled(&self) -> bool { + self.opts.unstable_opts.sanitizer_cfi_minimal_runtime == Some(true) + } + pub fn is_sanitizer_kcfi_arity_enabled(&self) -> bool { self.opts.unstable_opts.sanitizer_kcfi_arity == Some(true) } @@ -1684,6 +1688,13 @@ fn validate_commandline_args_with_session_available(sess: &Session) { } } + // LLVM CFI minimal runtime requires CFI Recover or CFI Diag. + if sess.is_sanitizer_cfi_minimal_runtime_enabled() { + if !(sess.is_sanitizer_cfi_recover_enabled() || sess.is_sanitizer_cfi_diag_enabled()) { + sess.dcx().emit_err(diagnostics::SanitizerCfiMinimalRuntimeRequiresCfiRecoverOrDiag); + } + } + // LLVM CFI integer normalization requires CFI or KCFI. if sess.is_sanitizer_cfi_normalize_integers_enabled() { if !(sess.is_sanitizer_cfi_enabled() || sess.is_sanitizer_kcfi_enabled()) { diff --git a/src/bootstrap/src/core/build_steps/llvm.rs b/src/bootstrap/src/core/build_steps/llvm.rs index 096184ee9e5b4..2062461de4c2a 100644 --- a/src/bootstrap/src/core/build_steps/llvm.rs +++ b/src/bootstrap/src/core/build_steps/llvm.rs @@ -1903,7 +1903,7 @@ fn supported_sanitizers( "aarch64-unknown-linux-gnu" => common_libs( "linux", "aarch64", - &["asan", "lsan", "msan", "tsan", "hwasan", "rtsan", "ubsan"], + &["asan", "lsan", "msan", "tsan", "hwasan", "rtsan", "ubsan", "ubsan_minimal"], ), "aarch64-unknown-linux-ohos" => { common_libs("linux", "aarch64", &["asan", "lsan", "msan", "tsan", "hwasan"]) @@ -1924,7 +1924,17 @@ fn supported_sanitizers( "x86_64-unknown-linux-gnu" => common_libs( "linux", "x86_64", - &["asan", "dfsan", "lsan", "msan", "safestack", "tsan", "rtsan", "ubsan"], + &[ + "asan", + "dfsan", + "lsan", + "msan", + "safestack", + "tsan", + "rtsan", + "ubsan", + "ubsan_minimal", + ], ), "x86_64-unknown-linux-gnuasan" => common_libs("linux", "x86_64", &["asan"]), "x86_64-unknown-linux-gnumsan" => common_libs("linux", "x86_64", &["msan"]), diff --git a/tests/codegen-llvm/sanitizer/cfi/emit-type-checks-diag-mode.rs b/tests/codegen-llvm/sanitizer/cfi/emit-type-checks-diag-mode.rs index 07688a8e5cb0b..96cfb94289724 100644 --- a/tests/codegen-llvm/sanitizer/cfi/emit-type-checks-diag-mode.rs +++ b/tests/codegen-llvm/sanitizer/cfi/emit-type-checks-diag-mode.rs @@ -1,7 +1,9 @@ // Verifies that pointer type membership tests for indirect calls are emitted. // +//@ revisions: cfi-diag cfi-diag-minimal-runtime //@ needs-sanitizer-cfi //@ compile-flags: -Clto -Cno-prepopulate-passes -Ctarget-feature=-crt-static -Zsanitizer=cfi -Zsanitizer-cfi-diag=true -Copt-level=0 -C unsafe-allow-abi-mismatch=sanitizer +//@ [cfi-diag-minimal-runtime] compile-flags: -Zsanitizer-cfi-minimal-runtime=true #![crate_type = "lib"] @@ -14,7 +16,8 @@ pub fn foo(f: fn(i32) -> i32, arg: i32) -> i32 { // CHECK-NEXT: {{%.+}} = call i32 %f(i32{{.*}} %arg) // CHECK: type_test.fail: // CHECK-NEXT: {{%.+}} = ptrtoint ptr {{%f|%0}} to i64 - // CHECK-NEXT: call void @__ubsan_handle_cfi_check_fail_abort( + // cfi-diag-NEXT: call void @__ubsan_handle_cfi_check_fail_abort( + // cfi-diag-minimal-runtime-NEXT: call void @__ubsan_handle_cfi_check_fail_minimal_abort() // CHECK-NEXT: unreachable f(arg) } diff --git a/tests/codegen-llvm/sanitizer/cfi/emit-type-checks-recover-mode.rs b/tests/codegen-llvm/sanitizer/cfi/emit-type-checks-recover-mode.rs index 0dbcb7a833fb7..b3a1e2cb0b02e 100644 --- a/tests/codegen-llvm/sanitizer/cfi/emit-type-checks-recover-mode.rs +++ b/tests/codegen-llvm/sanitizer/cfi/emit-type-checks-recover-mode.rs @@ -1,7 +1,9 @@ // Verifies that pointer type membership tests for indirect calls are emitted. // +//@ revisions: cfi-recover cfi-recover-minimal-runtime //@ needs-sanitizer-cfi //@ compile-flags: -Clto -Cno-prepopulate-passes -Ctarget-feature=-crt-static -Zsanitizer=cfi -Zsanitizer-cfi-recover=true -Copt-level=0 -C unsafe-allow-abi-mismatch=sanitizer +//@ [cfi-recover-minimal-runtime] compile-flags: -Zsanitizer-cfi-minimal-runtime=true #![crate_type = "lib"] @@ -14,7 +16,8 @@ pub fn foo(f: fn(i32) -> i32, arg: i32) -> i32 { // CHECK-NEXT: {{%.+}} = call i32 %f(i32{{.*}} %arg) // CHECK: type_test.fail: // CHECK-NEXT: {{%.+}} = ptrtoint ptr {{%f|%0}} to i64 - // CHECK-NEXT: call void @__ubsan_handle_cfi_check_fail( + // cfi-recover-NEXT: call void @__ubsan_handle_cfi_check_fail( + // cfi-recover-minimal-runtime-NEXT: call void @__ubsan_handle_cfi_check_fail_minimal() // CHECK-NEXT: br label %type_test.pass f(arg) } diff --git a/tests/run-make/sanitizer-cfi-runtime/program.rs b/tests/run-make/sanitizer-cfi-runtime/program.rs new file mode 100644 index 0000000000000..0a46dcedef9de --- /dev/null +++ b/tests/run-make/sanitizer-cfi-runtime/program.rs @@ -0,0 +1,17 @@ +use std::hint::black_box; +use std::mem; + +fn add_one(x: i32) -> i32 { + x + 1 +} + +#[inline(never)] +fn call_with_mismatch(f: fn(i32) -> i32) { + let g: fn(i32, i32) -> i32 = unsafe { mem::transmute(f) }; + let res = g(1, 2); + assert_eq!(res, 2); +} + +fn main() { + call_with_mismatch(black_box(add_one)); +} diff --git a/tests/run-make/sanitizer-cfi-runtime/rmake.rs b/tests/run-make/sanitizer-cfi-runtime/rmake.rs new file mode 100644 index 0000000000000..adb739e43c341 --- /dev/null +++ b/tests/run-make/sanitizer-cfi-runtime/rmake.rs @@ -0,0 +1,171 @@ +//@ needs-sanitizer-support +//@ needs-sanitizer-cfi + +use run_make_support::{run, run_fail, rustc}; + +fn main() { + // 1. Check link args for default CFI (no diag/recover, no UBSan runtime) + let link_args_default = rustc() + .arg("-Clto") + .arg("-Ccodegen-units=1") + .arg("-Ctarget-feature=-crt-static") + .arg("-Cunsafe-allow-abi-mismatch=sanitizer") + .arg("-Zsanitizer=cfi") + .print("link-args") + .input("program.rs") + .run() + .stdout_utf8(); + assert!( + !link_args_default.contains("ubsan"), + "did not expect any ubsan runtime in link args, got: {link_args_default}" + ); + + // 2. Check link args for full UBSan runtime with CFI diag mode + let link_args_full_diag = rustc() + .arg("-Clto") + .arg("-Ccodegen-units=1") + .arg("-Ctarget-feature=-crt-static") + .arg("-Cunsafe-allow-abi-mismatch=sanitizer") + .arg("-Zsanitizer=cfi") + .arg("-Zsanitizer-cfi-diag=true") + .print("link-args") + .input("program.rs") + .run() + .stdout_utf8(); + assert!( + link_args_full_diag.contains("rt.ubsan.") || link_args_full_diag.contains("rt.ubsan\""), + "expected ubsan runtime in link args, got: {link_args_full_diag}" + ); + assert!( + !link_args_full_diag.contains("ubsan_minimal"), + "did not expect ubsan_minimal in link args, got: {link_args_full_diag}" + ); + + // 3. Check link args for full UBSan runtime with CFI recover mode + let link_args_full_recover = rustc() + .arg("-Clto") + .arg("-Ccodegen-units=1") + .arg("-Ctarget-feature=-crt-static") + .arg("-Cunsafe-allow-abi-mismatch=sanitizer") + .arg("-Zsanitizer=cfi") + .arg("-Zsanitizer-cfi-recover=true") + .print("link-args") + .input("program.rs") + .run() + .stdout_utf8(); + assert!( + link_args_full_recover.contains("rt.ubsan.") + || link_args_full_recover.contains("rt.ubsan\""), + "expected ubsan runtime in link args, got: {link_args_full_recover}" + ); + assert!( + !link_args_full_recover.contains("ubsan_minimal"), + "did not expect ubsan_minimal in link args, got: {link_args_full_recover}" + ); + + // 4. Check link args for minimal UBSan runtime with CFI diag mode + let link_args_min_diag = rustc() + .arg("-Clto") + .arg("-Ccodegen-units=1") + .arg("-Ctarget-feature=-crt-static") + .arg("-Cunsafe-allow-abi-mismatch=sanitizer") + .arg("-Zsanitizer=cfi") + .arg("-Zsanitizer-cfi-diag=true") + .arg("-Zsanitizer-cfi-minimal-runtime=true") + .print("link-args") + .input("program.rs") + .run() + .stdout_utf8(); + assert!( + link_args_min_diag.contains("rt.ubsan_minimal.") + || link_args_min_diag.contains("rt.ubsan_minimal\""), + "expected ubsan_minimal runtime in link args, got: {link_args_min_diag}" + ); + + // 5. Check link args for minimal UBSan runtime with CFI recover mode + let link_args_min_recover = rustc() + .arg("-Clto") + .arg("-Ccodegen-units=1") + .arg("-Ctarget-feature=-crt-static") + .arg("-Cunsafe-allow-abi-mismatch=sanitizer") + .arg("-Zsanitizer=cfi") + .arg("-Zsanitizer-cfi-recover=true") + .arg("-Zsanitizer-cfi-minimal-runtime=true") + .print("link-args") + .input("program.rs") + .run() + .stdout_utf8(); + assert!( + link_args_min_recover.contains("rt.ubsan_minimal.") + || link_args_min_recover.contains("rt.ubsan_minimal\""), + "expected ubsan_minimal runtime in link args, got: {link_args_min_recover}" + ); + + // 6. Build and run binary with default CFI (trap mode, no runtime) + rustc() + .arg("-Clto") + .arg("-Ccodegen-units=1") + .arg("-Ctarget-feature=-crt-static") + .arg("-Cunsafe-allow-abi-mismatch=sanitizer") + .arg("-Zsanitizer=cfi") + .output("program_default") + .input("program.rs") + .run(); + run_fail("program_default"); + + // 7. Build and run binary with full runtime in diag (abort) mode + rustc() + .arg("-Clto") + .arg("-Ccodegen-units=1") + .arg("-Ctarget-feature=-crt-static") + .arg("-Cunsafe-allow-abi-mismatch=sanitizer") + .arg("-Zsanitizer=cfi") + .arg("-Zsanitizer-cfi-diag=true") + .output("program_full_diag") + .input("program.rs") + .run(); + run_fail("program_full_diag") + .assert_stderr_contains("runtime error: control flow integrity check for type"); + + // 8. Build and run binary with full runtime in recover mode + rustc() + .arg("-Clto") + .arg("-Ccodegen-units=1") + .arg("-Ctarget-feature=-crt-static") + .arg("-Cunsafe-allow-abi-mismatch=sanitizer") + .arg("-Zsanitizer=cfi") + .arg("-Zsanitizer-cfi-recover=true") + .output("program_full_recover") + .input("program.rs") + .run(); + run("program_full_recover") + .assert_stderr_contains("runtime error: control flow integrity check for type"); + + // 9. Build and run binary with minimal runtime in diag (abort) mode + rustc() + .arg("-Clto") + .arg("-Ccodegen-units=1") + .arg("-Ctarget-feature=-crt-static") + .arg("-Cunsafe-allow-abi-mismatch=sanitizer") + .arg("-Zsanitizer=cfi") + .arg("-Zsanitizer-cfi-diag=true") + .arg("-Zsanitizer-cfi-minimal-runtime=true") + .output("program_min_diag") + .input("program.rs") + .run(); + run_fail("program_min_diag").assert_stderr_contains("ubsan: cfi-check-fail"); + + // 10. Build and run binary with minimal runtime in recover mode + rustc() + .arg("-Clto") + .arg("-Ccodegen-units=1") + .arg("-Ctarget-feature=-crt-static") + .arg("-Cunsafe-allow-abi-mismatch=sanitizer") + .arg("-Zsanitizer=cfi") + .arg("-Zsanitizer-cfi-recover=true") + .arg("-Zsanitizer-cfi-minimal-runtime=true") + .output("program_min_recover") + .input("program.rs") + .run(); + run("program_min_recover").assert_stderr_contains("ubsan: cfi-check-fail"); +} diff --git a/tests/ui/sanitizer/cfi/fn-ptr-type-mismatch-recover.rs b/tests/ui/sanitizer/cfi/fn-ptr-type-mismatch-recover.rs new file mode 100644 index 0000000000000..21953675684ee --- /dev/null +++ b/tests/ui/sanitizer/cfi/fn-ptr-type-mismatch-recover.rs @@ -0,0 +1,38 @@ +// Verifies that calling a function pointer with a mismatched type with CFI +// recovery enabled reports the failure and continues execution. + +//@ revisions: cfi cfi-minimal-runtime +// FIXME(#122848) Remove only-linux once OSX CFI binaries work +//@ only-linux +//@ ignore-backends: gcc +//@ [cfi] needs-sanitizer-cfi +//@ [cfi] needs-sanitizer-support +//@ [cfi-minimal-runtime] needs-sanitizer-cfi +//@ [cfi-minimal-runtime] needs-sanitizer-support +//@ compile-flags: -C target-feature=-crt-static +//@ compile-flags: -C unsafe-allow-abi-mismatch=sanitizer +//@ compile-flags: -C opt-level=0 -C codegen-units=1 -C lto +//@ compile-flags: -C prefer-dynamic=off +//@ compile-flags: -Z sanitizer=cfi +//@ [cfi] compile-flags: -Z sanitizer-cfi-recover=true +//@ [cfi-minimal-runtime] compile-flags: -Z sanitizer-cfi-recover=true +//@ [cfi-minimal-runtime] compile-flags: -Z sanitizer-cfi-minimal-runtime=true +//@ run-pass + +use std::hint::black_box; +use std::mem; + +fn add_one(x: i32) -> i32 { + x + 1 +} + +#[inline(never)] +fn call_with_mismatch(f: fn(i32) -> i32) { + let g: fn(i32, i32) -> i32 = unsafe { mem::transmute(f) }; + let res = g(1, 2); + assert_eq!(res, 2); +} + +fn main() { + call_with_mismatch(black_box(add_one)); +} diff --git a/tests/ui/sanitizer/cfi/fn-ptr-type-mismatch.rs b/tests/ui/sanitizer/cfi/fn-ptr-type-mismatch.rs index 6fe7eec7c6920..d64f6c1f43965 100644 --- a/tests/ui/sanitizer/cfi/fn-ptr-type-mismatch.rs +++ b/tests/ui/sanitizer/cfi/fn-ptr-type-mismatch.rs @@ -1,12 +1,14 @@ // Verifies that calling a function pointer with a mismatched type triggers a // CFI violation and causes the process to trap. -//@ revisions: cfi kcfi +//@ revisions: cfi cfi-minimal-runtime kcfi // FIXME(#122848) Remove only-linux once OSX CFI binaries work //@ only-linux //@ ignore-backends: gcc //@ [cfi] needs-sanitizer-cfi //@ [cfi] needs-sanitizer-support +//@ [cfi-minimal-runtime] needs-sanitizer-cfi +//@ [cfi-minimal-runtime] needs-sanitizer-support //@ [kcfi] needs-sanitizer-kcfi //@ compile-flags: -C target-feature=-crt-static //@ compile-flags: -C unsafe-allow-abi-mismatch=sanitizer @@ -14,6 +16,11 @@ //@ [cfi] compile-flags: -C prefer-dynamic=off //@ [cfi] compile-flags: -Z sanitizer=cfi //@ [cfi] compile-flags: -Z sanitizer-cfi-diag=true +//@ [cfi-minimal-runtime] compile-flags: -C opt-level=0 -C codegen-units=1 -C lto +//@ [cfi-minimal-runtime] compile-flags: -C prefer-dynamic=off +//@ [cfi-minimal-runtime] compile-flags: -Z sanitizer=cfi +//@ [cfi-minimal-runtime] compile-flags: -Z sanitizer-cfi-diag=true +//@ [cfi-minimal-runtime] compile-flags: -Z sanitizer-cfi-minimal-runtime=true //@ [kcfi] compile-flags: -Z sanitizer=kcfi //@ [kcfi] compile-flags: -C panic=abort -C prefer-dynamic=off //@ run-fail-or-crash diff --git a/tests/ui/sanitizer/cfi/minimal-runtime-requires-cfi-recover-or-diag.rs b/tests/ui/sanitizer/cfi/minimal-runtime-requires-cfi-recover-or-diag.rs new file mode 100644 index 0000000000000..f241598131d65 --- /dev/null +++ b/tests/ui/sanitizer/cfi/minimal-runtime-requires-cfi-recover-or-diag.rs @@ -0,0 +1,11 @@ +// Verifies that `-Zsanitizer-cfi-minimal-runtime` requires +// `-Zsanitizer-cfi-recover` or `-Zsanitizer-cfi-diag`. +// +//@ needs-sanitizer-cfi +//@ compile-flags: -Cno-prepopulate-passes -Ctarget-feature=-crt-static -Zsanitizer-cfi-minimal-runtime + +#![feature(no_core)] +#![no_core] +#![no_main] + +//~? ERROR `-Zsanitizer-cfi-minimal-runtime` requires `-Zsanitizer-cfi-recover` or `-Zsanitizer-cfi-diag` diff --git a/tests/ui/sanitizer/cfi/minimal-runtime-requires-cfi-recover-or-diag.stderr b/tests/ui/sanitizer/cfi/minimal-runtime-requires-cfi-recover-or-diag.stderr new file mode 100644 index 0000000000000..f3d483bef7eb7 --- /dev/null +++ b/tests/ui/sanitizer/cfi/minimal-runtime-requires-cfi-recover-or-diag.stderr @@ -0,0 +1,4 @@ +error: `-Zsanitizer-cfi-minimal-runtime` requires `-Zsanitizer-cfi-recover` or `-Zsanitizer-cfi-diag` + +error: aborting due to 1 previous error + From 4d68be3d806fd0894977dea0a5758800cab25ac6 Mon Sep 17 00:00:00 2001 From: Jakob Koschel Date: Thu, 10 Sep 2026 14:11:10 +0000 Subject: [PATCH 03/30] minor cleanup for minimal runtime test --- .../cfi/minimal-runtime-requires-cfi-recover-or-diag.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ui/sanitizer/cfi/minimal-runtime-requires-cfi-recover-or-diag.rs b/tests/ui/sanitizer/cfi/minimal-runtime-requires-cfi-recover-or-diag.rs index f241598131d65..cbc0bac40c2b3 100644 --- a/tests/ui/sanitizer/cfi/minimal-runtime-requires-cfi-recover-or-diag.rs +++ b/tests/ui/sanitizer/cfi/minimal-runtime-requires-cfi-recover-or-diag.rs @@ -2,7 +2,7 @@ // `-Zsanitizer-cfi-recover` or `-Zsanitizer-cfi-diag`. // //@ needs-sanitizer-cfi -//@ compile-flags: -Cno-prepopulate-passes -Ctarget-feature=-crt-static -Zsanitizer-cfi-minimal-runtime +//@ compile-flags: -Clto -Cno-prepopulate-passes -Ctarget-feature=-crt-static -Zsanitizer-cfi-minimal-runtime #![feature(no_core)] #![no_core] From 855d8a275b860bf5bb6d0a2499278a90a8d1b547 Mon Sep 17 00:00:00 2001 From: Jakob Koschel Date: Thu, 10 Sep 2026 14:42:35 +0000 Subject: [PATCH 04/30] Add target modifier for -Zsanitizer-cfi-minimal-runtime Co-Authored-By: Bastian Kersting --- compiler/rustc_session/src/options.rs | 22 ++++++++++++++++++- .../auxiliary/cfi-minimal-runtime.rs | 7 ++++++ .../sanitizer-cfi-minimal-runtime.rs | 18 +++++++++++++++ ...izer-cfi-minimal-runtime.wrong_flag.stderr | 9 ++++++++ ...cfi-minimal-runtime.wrong_sanitizer.stderr | 9 ++++++++ 5 files changed, 64 insertions(+), 1 deletion(-) create mode 100644 tests/ui/target_modifiers/auxiliary/cfi-minimal-runtime.rs create mode 100644 tests/ui/target_modifiers/sanitizer-cfi-minimal-runtime.rs create mode 100644 tests/ui/target_modifiers/sanitizer-cfi-minimal-runtime.wrong_flag.stderr create mode 100644 tests/ui/target_modifiers/sanitizer-cfi-minimal-runtime.wrong_sanitizer.stderr diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index f11bdadf5b67d..63bc2da30f063 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -136,6 +136,21 @@ mod target_modifier_consistency_check { } true } + pub(super) fn sanitizer_cfi_minimal_runtime( + sess: &Session, + l: &TargetModifier, + r: Option<&TargetModifier>, + ) -> bool { + // For CFI, the helper flag -Zsanitizer-cfi-minimal-runtime should also be a target modifier + if sess.sanitizers().contains(SanitizerSet::CFI) { + if let Some(r) = r { + return l.extend().tech_value == r.extend().tech_value; + } else { + return false; + } + } + true + } pub(super) fn target_cpu( sess: &Session, l: &TargetModifier, @@ -178,6 +193,11 @@ impl TargetModifier { sess, self, other, ); } + UnstableOptionsTargetModifiers::SanitizerCfiMinimalRuntime => { + return target_modifier_consistency_check::sanitizer_cfi_minimal_runtime( + sess, self, other, + ); + } _ => {} }, OptionsTargetModifiers::CodegenOptions(codegen) => match codegen { @@ -2825,7 +2845,7 @@ written to standard error output)"), "enable CFI diagnostics (default: no)"), sanitizer_cfi_recover: Option = (None, parse_opt_bool, [TRACKED], "enable CFI recovery (default: no)"), - sanitizer_cfi_minimal_runtime: Option = (None, parse_opt_bool, [TRACKED], + sanitizer_cfi_minimal_runtime: Option = (None, parse_opt_bool, [TRACKED] { TARGET_MODIFIER: SanitizerCfiMinimalRuntime }, "enable minimal UBSan runtime for CFI (default: no)"), sanitizer_dataflow_abilist: Vec = (Vec::new(), parse_comma_list, [TRACKED], "additional ABI list files that control how shadow parameters are passed (comma separated)"), diff --git a/tests/ui/target_modifiers/auxiliary/cfi-minimal-runtime.rs b/tests/ui/target_modifiers/auxiliary/cfi-minimal-runtime.rs new file mode 100644 index 0000000000000..aacea1ee65dd2 --- /dev/null +++ b/tests/ui/target_modifiers/auxiliary/cfi-minimal-runtime.rs @@ -0,0 +1,7 @@ +//@ no-prefer-dynamic +//@ needs-sanitizer-cfi +//@ compile-flags: -Clto -Zsanitizer=cfi -Zsanitizer-cfi-recover -Zsanitizer-cfi-minimal-runtime + +#![feature(no_core)] +#![crate_type = "rlib"] +#![no_core] diff --git a/tests/ui/target_modifiers/sanitizer-cfi-minimal-runtime.rs b/tests/ui/target_modifiers/sanitizer-cfi-minimal-runtime.rs new file mode 100644 index 0000000000000..45c6ad7b521a3 --- /dev/null +++ b/tests/ui/target_modifiers/sanitizer-cfi-minimal-runtime.rs @@ -0,0 +1,18 @@ +// For CFI, the helper flag -Zsanitizer-cfi-minimal-runtime should also be a target modifier. + +//@ needs-sanitizer-cfi +//@ aux-build:cfi-minimal-runtime.rs + +//@ revisions: ok wrong_flag wrong_sanitizer +//@[ok] compile-flags: -Clto -Zsanitizer=cfi -Zsanitizer-cfi-recover -Zsanitizer-cfi-minimal-runtime +//@[wrong_flag] compile-flags: -Clto -Zsanitizer=cfi -Zsanitizer-cfi-recover +//@[ok] check-pass + +#![feature(no_core)] +#![crate_type = "rlib"] +#![no_core] + +extern crate cfi_minimal_runtime; + +//[wrong_flag]~? ERROR mixing `-Zsanitizer-cfi-minimal-runtime` will cause an ABI mismatch in crate `sanitizer_cfi_minimal_runtime` +//[wrong_sanitizer]~? ERROR mixing `-Zsanitizer` will cause an ABI mismatch in crate `sanitizer_cfi_minimal_runtime` diff --git a/tests/ui/target_modifiers/sanitizer-cfi-minimal-runtime.wrong_flag.stderr b/tests/ui/target_modifiers/sanitizer-cfi-minimal-runtime.wrong_flag.stderr new file mode 100644 index 0000000000000..6dfc1a3bb7d16 --- /dev/null +++ b/tests/ui/target_modifiers/sanitizer-cfi-minimal-runtime.wrong_flag.stderr @@ -0,0 +1,9 @@ +error: mixing `-Zsanitizer-cfi-minimal-runtime` will cause an ABI mismatch in crate `sanitizer_cfi_minimal_runtime` + | + = help: the `-Zsanitizer-cfi-minimal-runtime` flag modifies the ABI so Rust crates compiled with different values of this flag cannot be used together safely + = note: `-Zsanitizer-cfi-minimal-runtime` is unset in this crate which is incompatible with `-Zsanitizer-cfi-minimal-runtime` being set in dependency `cfi_minimal_runtime` + = help: set `-Zsanitizer-cfi-minimal-runtime` in this crate or unset `-Zsanitizer-cfi-minimal-runtime` in `cfi_minimal_runtime` + = help: if you are sure this will not cause problems, you may use `-Cunsafe-allow-abi-mismatch=sanitizer-cfi-minimal-runtime` to silence this error + +error: aborting due to 1 previous error + diff --git a/tests/ui/target_modifiers/sanitizer-cfi-minimal-runtime.wrong_sanitizer.stderr b/tests/ui/target_modifiers/sanitizer-cfi-minimal-runtime.wrong_sanitizer.stderr new file mode 100644 index 0000000000000..32ba848b3b2ad --- /dev/null +++ b/tests/ui/target_modifiers/sanitizer-cfi-minimal-runtime.wrong_sanitizer.stderr @@ -0,0 +1,9 @@ +error: mixing `-Zsanitizer` will cause an ABI mismatch in crate `sanitizer_cfi_minimal_runtime` + | + = help: the `-Zsanitizer` flag modifies the ABI so Rust crates compiled with different values of this flag cannot be used together safely + = note: `-Zsanitizer` is unset in this crate which is incompatible with `-Zsanitizer=cfi` in dependency `cfi_minimal_runtime` + = help: set `-Zsanitizer=cfi` in this crate or unset `-Zsanitizer` in `cfi_minimal_runtime` + = help: if you are sure this will not cause problems, you may use `-Cunsafe-allow-abi-mismatch=sanitizer` to silence this error + +error: aborting due to 1 previous error + From a42ab2f36cf031d3f95181edc8a613d38c7e00b5 Mon Sep 17 00:00:00 2001 From: Jakob Koschel Date: Fri, 11 Sep 2026 08:47:12 +0000 Subject: [PATCH 05/30] Make -Zsanitizer-cfi-minimal-runtime also dependent on -Zsanitizer=cfi --- compiler/rustc_session/src/diagnostics.rs | 6 ++++++ compiler/rustc_session/src/session.rs | 3 +++ .../minimal-runtime-requires-cfi-recover-or-diag.rs | 2 +- .../ui/sanitizer/cfi/minimal-runtime-requires-cfi.rs | 11 +++++++++++ .../sanitizer/cfi/minimal-runtime-requires-cfi.stderr | 6 ++++++ 5 files changed, 27 insertions(+), 1 deletion(-) create mode 100644 tests/ui/sanitizer/cfi/minimal-runtime-requires-cfi.rs create mode 100644 tests/ui/sanitizer/cfi/minimal-runtime-requires-cfi.stderr diff --git a/compiler/rustc_session/src/diagnostics.rs b/compiler/rustc_session/src/diagnostics.rs index d27b616d7e359..b0bbccd7fe91f 100644 --- a/compiler/rustc_session/src/diagnostics.rs +++ b/compiler/rustc_session/src/diagnostics.rs @@ -340,6 +340,12 @@ pub(crate) struct SanitizerCfiRecoverRequiresCfi; #[diag("`-Zsanitizer-cfi-diag` requires `-Zsanitizer=cfi`")] pub(crate) struct SanitizerCfiDiagRequiresCfi; +#[derive(Diagnostic)] +#[diag( + "`-Zsanitizer-cfi-minimal-runtime` requires `-Zsanitizer=cfi`" +)] +pub(crate) struct SanitizerCfiMinimalRuntimeRequiresCfi; + #[derive(Diagnostic)] #[diag( "`-Zsanitizer-cfi-minimal-runtime` requires `-Zsanitizer-cfi-recover` or `-Zsanitizer-cfi-diag`" diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index bf310270d0a19..a209219758026 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -1690,6 +1690,9 @@ fn validate_commandline_args_with_session_available(sess: &Session) { // LLVM CFI minimal runtime requires CFI Recover or CFI Diag. if sess.is_sanitizer_cfi_minimal_runtime_enabled() { + if !sess.is_sanitizer_cfi_enabled() { + sess.dcx().emit_err(diagnostics::SanitizerCfiMinimalRuntimeRequiresCfi); + } if !(sess.is_sanitizer_cfi_recover_enabled() || sess.is_sanitizer_cfi_diag_enabled()) { sess.dcx().emit_err(diagnostics::SanitizerCfiMinimalRuntimeRequiresCfiRecoverOrDiag); } diff --git a/tests/ui/sanitizer/cfi/minimal-runtime-requires-cfi-recover-or-diag.rs b/tests/ui/sanitizer/cfi/minimal-runtime-requires-cfi-recover-or-diag.rs index cbc0bac40c2b3..16f39f09ec7a0 100644 --- a/tests/ui/sanitizer/cfi/minimal-runtime-requires-cfi-recover-or-diag.rs +++ b/tests/ui/sanitizer/cfi/minimal-runtime-requires-cfi-recover-or-diag.rs @@ -2,7 +2,7 @@ // `-Zsanitizer-cfi-recover` or `-Zsanitizer-cfi-diag`. // //@ needs-sanitizer-cfi -//@ compile-flags: -Clto -Cno-prepopulate-passes -Ctarget-feature=-crt-static -Zsanitizer-cfi-minimal-runtime +//@ compile-flags: -Clto -Cno-prepopulate-passes -Ctarget-feature=-crt-static -Zsanitizer=cfi -Zsanitizer-cfi-minimal-runtime #![feature(no_core)] #![no_core] diff --git a/tests/ui/sanitizer/cfi/minimal-runtime-requires-cfi.rs b/tests/ui/sanitizer/cfi/minimal-runtime-requires-cfi.rs new file mode 100644 index 0000000000000..13b296b4329ee --- /dev/null +++ b/tests/ui/sanitizer/cfi/minimal-runtime-requires-cfi.rs @@ -0,0 +1,11 @@ +// Verifies that `-Zsanitizer-cfi-minimal-runtime` requires `-Zsanitizer=cfi`. +// +//@ needs-sanitizer-cfi +//@ compile-flags: -Clto -Cno-prepopulate-passes -Ctarget-feature=-crt-static -Zsanitizer-cfi-minimal-runtime + +#![feature(no_core)] +#![no_core] +#![no_main] + +//~? ERROR `-Zsanitizer-cfi-minimal-runtime` requires `-Zsanitizer=cfi` +//~? ERROR `-Zsanitizer-cfi-minimal-runtime` requires `-Zsanitizer-cfi-recover` or `-Zsanitizer-cfi-diag` diff --git a/tests/ui/sanitizer/cfi/minimal-runtime-requires-cfi.stderr b/tests/ui/sanitizer/cfi/minimal-runtime-requires-cfi.stderr new file mode 100644 index 0000000000000..251d0d0817631 --- /dev/null +++ b/tests/ui/sanitizer/cfi/minimal-runtime-requires-cfi.stderr @@ -0,0 +1,6 @@ +error: `-Zsanitizer-cfi-minimal-runtime` requires `-Zsanitizer=cfi` + +error: `-Zsanitizer-cfi-minimal-runtime` requires `-Zsanitizer-cfi-recover` or `-Zsanitizer-cfi-diag` + +error: aborting due to 2 previous errors + From bba4a3f986f4e2d4b0bea8711c436bf3ffe4a92a Mon Sep 17 00:00:00 2001 From: Jakob Koschel Date: Fri, 11 Sep 2026 09:00:38 +0000 Subject: [PATCH 06/30] Fix tests with new cfi-minimal-runtime target modifier --- tests/ui/sanitizer/cfi/fn-ptr-type-mismatch-recover.rs | 2 +- tests/ui/sanitizer/cfi/fn-ptr-type-mismatch.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/ui/sanitizer/cfi/fn-ptr-type-mismatch-recover.rs b/tests/ui/sanitizer/cfi/fn-ptr-type-mismatch-recover.rs index 21953675684ee..069ac811f481d 100644 --- a/tests/ui/sanitizer/cfi/fn-ptr-type-mismatch-recover.rs +++ b/tests/ui/sanitizer/cfi/fn-ptr-type-mismatch-recover.rs @@ -10,7 +10,7 @@ //@ [cfi-minimal-runtime] needs-sanitizer-cfi //@ [cfi-minimal-runtime] needs-sanitizer-support //@ compile-flags: -C target-feature=-crt-static -//@ compile-flags: -C unsafe-allow-abi-mismatch=sanitizer +//@ compile-flags: -C unsafe-allow-abi-mismatch=sanitizer,sanitizer-cfi-minimal-runtime //@ compile-flags: -C opt-level=0 -C codegen-units=1 -C lto //@ compile-flags: -C prefer-dynamic=off //@ compile-flags: -Z sanitizer=cfi diff --git a/tests/ui/sanitizer/cfi/fn-ptr-type-mismatch.rs b/tests/ui/sanitizer/cfi/fn-ptr-type-mismatch.rs index d64f6c1f43965..1e32a1042c848 100644 --- a/tests/ui/sanitizer/cfi/fn-ptr-type-mismatch.rs +++ b/tests/ui/sanitizer/cfi/fn-ptr-type-mismatch.rs @@ -11,7 +11,7 @@ //@ [cfi-minimal-runtime] needs-sanitizer-support //@ [kcfi] needs-sanitizer-kcfi //@ compile-flags: -C target-feature=-crt-static -//@ compile-flags: -C unsafe-allow-abi-mismatch=sanitizer +//@ compile-flags: -C unsafe-allow-abi-mismatch=sanitizer,sanitizer-cfi-minimal-runtime //@ [cfi] compile-flags: -C opt-level=0 -C codegen-units=1 -C lto //@ [cfi] compile-flags: -C prefer-dynamic=off //@ [cfi] compile-flags: -Z sanitizer=cfi From 775c187f3add8c381e818575687df32c01abf92a Mon Sep 17 00:00:00 2001 From: Jakob Koschel Date: Fri, 11 Sep 2026 09:37:04 +0000 Subject: [PATCH 07/30] Fix linter issue in diagnostics.rs --- compiler/rustc_session/src/diagnostics.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/compiler/rustc_session/src/diagnostics.rs b/compiler/rustc_session/src/diagnostics.rs index b0bbccd7fe91f..0ec847767336e 100644 --- a/compiler/rustc_session/src/diagnostics.rs +++ b/compiler/rustc_session/src/diagnostics.rs @@ -341,9 +341,7 @@ pub(crate) struct SanitizerCfiRecoverRequiresCfi; pub(crate) struct SanitizerCfiDiagRequiresCfi; #[derive(Diagnostic)] -#[diag( - "`-Zsanitizer-cfi-minimal-runtime` requires `-Zsanitizer=cfi`" -)] +#[diag("`-Zsanitizer-cfi-minimal-runtime` requires `-Zsanitizer=cfi`")] pub(crate) struct SanitizerCfiMinimalRuntimeRequiresCfi; #[derive(Diagnostic)] From 3058339f0e35413263d71e595a8bb1421646edd3 Mon Sep 17 00:00:00 2001 From: Jakob Koschel Date: Fri, 11 Sep 2026 10:53:13 +0000 Subject: [PATCH 08/30] Fix broken test introduced by cfi-minimal-runtime target modifier --- .../sanitizer/cfi/emit-type-checks-diag-mode.rs | 2 +- .../sanitizer/cfi/emit-type-checks-recover-mode.rs | 2 +- tests/run-make/sanitizer-cfi-runtime/rmake.rs | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/codegen-llvm/sanitizer/cfi/emit-type-checks-diag-mode.rs b/tests/codegen-llvm/sanitizer/cfi/emit-type-checks-diag-mode.rs index 96cfb94289724..4d58872db7c45 100644 --- a/tests/codegen-llvm/sanitizer/cfi/emit-type-checks-diag-mode.rs +++ b/tests/codegen-llvm/sanitizer/cfi/emit-type-checks-diag-mode.rs @@ -2,7 +2,7 @@ // //@ revisions: cfi-diag cfi-diag-minimal-runtime //@ needs-sanitizer-cfi -//@ compile-flags: -Clto -Cno-prepopulate-passes -Ctarget-feature=-crt-static -Zsanitizer=cfi -Zsanitizer-cfi-diag=true -Copt-level=0 -C unsafe-allow-abi-mismatch=sanitizer +//@ compile-flags: -Clto -Cno-prepopulate-passes -Ctarget-feature=-crt-static -Zsanitizer=cfi -Zsanitizer-cfi-diag=true -Copt-level=0 -C unsafe-allow-abi-mismatch=sanitizer,sanitizer-cfi-minimal-runtime //@ [cfi-diag-minimal-runtime] compile-flags: -Zsanitizer-cfi-minimal-runtime=true #![crate_type = "lib"] diff --git a/tests/codegen-llvm/sanitizer/cfi/emit-type-checks-recover-mode.rs b/tests/codegen-llvm/sanitizer/cfi/emit-type-checks-recover-mode.rs index b3a1e2cb0b02e..3ea97e44c21e7 100644 --- a/tests/codegen-llvm/sanitizer/cfi/emit-type-checks-recover-mode.rs +++ b/tests/codegen-llvm/sanitizer/cfi/emit-type-checks-recover-mode.rs @@ -2,7 +2,7 @@ // //@ revisions: cfi-recover cfi-recover-minimal-runtime //@ needs-sanitizer-cfi -//@ compile-flags: -Clto -Cno-prepopulate-passes -Ctarget-feature=-crt-static -Zsanitizer=cfi -Zsanitizer-cfi-recover=true -Copt-level=0 -C unsafe-allow-abi-mismatch=sanitizer +//@ compile-flags: -Clto -Cno-prepopulate-passes -Ctarget-feature=-crt-static -Zsanitizer=cfi -Zsanitizer-cfi-recover=true -Copt-level=0 -C unsafe-allow-abi-mismatch=sanitizer,sanitizer-cfi-minimal-runtime //@ [cfi-recover-minimal-runtime] compile-flags: -Zsanitizer-cfi-minimal-runtime=true #![crate_type = "lib"] diff --git a/tests/run-make/sanitizer-cfi-runtime/rmake.rs b/tests/run-make/sanitizer-cfi-runtime/rmake.rs index adb739e43c341..8e92f5927d952 100644 --- a/tests/run-make/sanitizer-cfi-runtime/rmake.rs +++ b/tests/run-make/sanitizer-cfi-runtime/rmake.rs @@ -68,7 +68,7 @@ fn main() { .arg("-Clto") .arg("-Ccodegen-units=1") .arg("-Ctarget-feature=-crt-static") - .arg("-Cunsafe-allow-abi-mismatch=sanitizer") + .arg("-Cunsafe-allow-abi-mismatch=sanitizer,sanitizer-cfi-minimal-runtime") .arg("-Zsanitizer=cfi") .arg("-Zsanitizer-cfi-diag=true") .arg("-Zsanitizer-cfi-minimal-runtime=true") @@ -87,7 +87,7 @@ fn main() { .arg("-Clto") .arg("-Ccodegen-units=1") .arg("-Ctarget-feature=-crt-static") - .arg("-Cunsafe-allow-abi-mismatch=sanitizer") + .arg("-Cunsafe-allow-abi-mismatch=sanitizer,sanitizer-cfi-minimal-runtime") .arg("-Zsanitizer=cfi") .arg("-Zsanitizer-cfi-recover=true") .arg("-Zsanitizer-cfi-minimal-runtime=true") @@ -146,7 +146,7 @@ fn main() { .arg("-Clto") .arg("-Ccodegen-units=1") .arg("-Ctarget-feature=-crt-static") - .arg("-Cunsafe-allow-abi-mismatch=sanitizer") + .arg("-Cunsafe-allow-abi-mismatch=sanitizer,sanitizer-cfi-minimal-runtime") .arg("-Zsanitizer=cfi") .arg("-Zsanitizer-cfi-diag=true") .arg("-Zsanitizer-cfi-minimal-runtime=true") @@ -160,7 +160,7 @@ fn main() { .arg("-Clto") .arg("-Ccodegen-units=1") .arg("-Ctarget-feature=-crt-static") - .arg("-Cunsafe-allow-abi-mismatch=sanitizer") + .arg("-Cunsafe-allow-abi-mismatch=sanitizer,sanitizer-cfi-minimal-runtime") .arg("-Zsanitizer=cfi") .arg("-Zsanitizer-cfi-recover=true") .arg("-Zsanitizer-cfi-minimal-runtime=true") From 7aacc8a2dab9300860afc1d912193e490ceeb5a1 Mon Sep 17 00:00:00 2001 From: Jakob Koschel Date: Fri, 11 Sep 2026 12:05:31 +0000 Subject: [PATCH 09/30] Skip gcc for CFI runtime tests --- tests/run-make/sanitizer-cfi-runtime/rmake.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/run-make/sanitizer-cfi-runtime/rmake.rs b/tests/run-make/sanitizer-cfi-runtime/rmake.rs index 8e92f5927d952..3cad996a14054 100644 --- a/tests/run-make/sanitizer-cfi-runtime/rmake.rs +++ b/tests/run-make/sanitizer-cfi-runtime/rmake.rs @@ -1,5 +1,6 @@ //@ needs-sanitizer-support //@ needs-sanitizer-cfi +//@ ignore-backends: gcc use run_make_support::{run, run_fail, rustc}; From 8e5ad6c1e4d2590797636dd5c8e7049e002b3b39 Mon Sep 17 00:00:00 2001 From: Jakob Koschel Date: Mon, 21 Sep 2026 11:14:06 +0000 Subject: [PATCH 10/30] use match to determine correct ubsan cfi handler --- compiler/rustc_codegen_llvm/src/builder.rs | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index 438accb85f377..7fa079a01a6b1 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -2078,18 +2078,11 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { ) }; let ubsan_handler = self.declare_cfn( - if is_recover { - if is_minimal { - "__ubsan_handle_cfi_check_fail_minimal" - } else { - "__ubsan_handle_cfi_check_fail" - } - } else { - if is_minimal { - "__ubsan_handle_cfi_check_fail_minimal_abort" - } else { - "__ubsan_handle_cfi_check_fail_abort" - } + match (is_minimal, is_recover) { + (true, true) => "__ubsan_handle_cfi_check_fail_minimal", + (true, false) => "__ubsan_handle_cfi_check_fail_minimal_abort", + (false, true) => "__ubsan_handle_cfi_check_fail", + (false, false) => "__ubsan_handle_cfi_check_fail_abort", }, llvm::UnnamedAddr::Global, fty, From 452a7cac6e79b4bff25906e2e45bb820d46f793b Mon Sep 17 00:00:00 2001 From: dianne Date: Tue, 22 Sep 2026 23:53:16 -0700 Subject: [PATCH 11/30] make `RustaceansAreAwesome` satisfy trait bounds --- .../src/traits/dyn_compatibility.rs | 9 ++++ ...tchability-placeholder-satisfies-bounds.rs | 37 ++++++++++++++ .../ice-generics-of-crate-root-152335.rs | 2 + .../ice-generics-of-crate-root-152335.stderr | 50 +++++++++++++++---- .../unhandled-crate-mod-issue-144888.rs | 2 + .../unhandled-crate-mod-issue-144888.stderr | 46 ++++++++++++++--- 6 files changed, 129 insertions(+), 17 deletions(-) create mode 100644 tests/ui/dyn-compatibility/dispatchability-placeholder-satisfies-bounds.rs diff --git a/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs b/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs index e964c09420b0e..7f05f7e04aadf 100644 --- a/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs +++ b/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs @@ -749,6 +749,15 @@ fn receiver_is_dispatchable<'tcx>( let trait_predicate = ty::TraitRef::new_from_args(tcx, trait_def_id, args); clauses.push(trait_predicate.upcast(tcx)); + // U satisfies `Trait`'s where-bounds. + clauses.extend( + tcx.clauses_of(trait_def_id) + .instantiate(tcx, args) + .clauses + .into_iter() + .map(Unnormalized::skip_norm_wip), + ); + let meta_sized_predicate = { let meta_sized_did = tcx.require_lang_item(LangItem::MetaSized, DUMMY_SP); ty::TraitRef::new(tcx, meta_sized_did, [unsized_self_ty]) diff --git a/tests/ui/dyn-compatibility/dispatchability-placeholder-satisfies-bounds.rs b/tests/ui/dyn-compatibility/dispatchability-placeholder-satisfies-bounds.rs new file mode 100644 index 0000000000000..421b9a512c1e4 --- /dev/null +++ b/tests/ui/dyn-compatibility/dispatchability-placeholder-satisfies-bounds.rs @@ -0,0 +1,37 @@ +//! Regression tests for . When checking whether +//! method receivers for a trait `Trait` are dynamically dispatchable, we use a placeholder type in +//! place of `dyn Trait` to avoid a cycle (as that would require determining whether `Trait` is dyn- +//! compatible). This placeholder must satisfy `Trait`'s where-bounds to avoid errors in param- +//! environment normalization. +//@ check-pass + +// Test 1 + +use std::ops::Deref; + +trait SignatureToFnPtr { + type Ptr; +} + +trait DerefsToFn +where + (Args, Self::Output): SignatureToFnPtr, + Self: Deref::Ptr>, +{ + type Output; + fn method(&self); +} + +// Test 2 + +trait Associator { + type Point; +} + +trait Marker {} + +trait Trait + ?Sized>: Marker { + fn method(&self); +} + +fn main() {} diff --git a/tests/ui/dyn-compatibility/ice-generics-of-crate-root-152335.rs b/tests/ui/dyn-compatibility/ice-generics-of-crate-root-152335.rs index cfafca68bfd2f..188f63fa94bab 100644 --- a/tests/ui/dyn-compatibility/ice-generics-of-crate-root-152335.rs +++ b/tests/ui/dyn-compatibility/ice-generics-of-crate-root-152335.rs @@ -26,6 +26,8 @@ where //~| ERROR the size for values of type `Self` cannot be known //~| ERROR type mismatch resolving //~| ERROR the size for values of type `Self` cannot be known + //~| ERROR type mismatch resolving + //~| ERROR the size for values of type `RustaceansAreAwesome` cannot be known } trait Mirror { diff --git a/tests/ui/dyn-compatibility/ice-generics-of-crate-root-152335.stderr b/tests/ui/dyn-compatibility/ice-generics-of-crate-root-152335.stderr index b8796f673f4a5..06fd8bced4af1 100644 --- a/tests/ui/dyn-compatibility/ice-generics-of-crate-root-152335.stderr +++ b/tests/ui/dyn-compatibility/ice-generics-of-crate-root-152335.stderr @@ -5,11 +5,29 @@ LL | fn transmute(&self, t: T) -> ::Assoc; | ^ not found in this scope error[E0601]: `main` function not found in crate `ice_generics_of_crate_root_152335` - --> $DIR/ice-generics-of-crate-root-152335.rs:34:57 + --> $DIR/ice-generics-of-crate-root-152335.rs:36:57 | LL | impl> Mirror for T {} | ^ consider adding a `main` function to `$DIR/ice-generics-of-crate-root-152335.rs` +error[E0271]: type mismatch resolving `>::Assoc == RustaceansAreAwesome` + --> $DIR/ice-generics-of-crate-root-152335.rs:23:5 + | +LL | fn transmute(&self, t: T) -> ::Assoc; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected type parameter `RustaceansAreAwesome`, found type parameter `T` + | + = note: expected type parameter `RustaceansAreAwesome` + found type parameter `T` + = note: a type parameter was expected, but a different one was found; you might be missing a type parameter or trait bound + = note: for more information, visit https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters +note: required for `RustaceansAreAwesome` to implement `Mirror` + --> $DIR/ice-generics-of-crate-root-152335.rs:36:42 + | +LL | impl> Mirror for T {} + | --------- ^^^^^^ ^ + | | + | unsatisfied trait bound introduced here + error[E0271]: type mismatch resolving `>::Assoc == Self` --> $DIR/ice-generics-of-crate-root-152335.rs:23:5 | @@ -21,13 +39,27 @@ LL | fn transmute(&self, t: T) -> ::Assoc; = note: a type parameter was expected, but a different one was found; you might be missing a type parameter or trait bound = note: for more information, visit https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters note: required for `Self` to implement `Mirror` - --> $DIR/ice-generics-of-crate-root-152335.rs:34:42 + --> $DIR/ice-generics-of-crate-root-152335.rs:36:42 | LL | impl> Mirror for T {} | --------- ^^^^^^ ^ | | | unsatisfied trait bound introduced here +error[E0277]: the size for values of type `RustaceansAreAwesome` cannot be known at compilation time + --> $DIR/ice-generics-of-crate-root-152335.rs:23:5 + | +LL | fn transmute(&self, t: T) -> ::Assoc; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | +note: required for `RustaceansAreAwesome` to implement `Mirror` + --> $DIR/ice-generics-of-crate-root-152335.rs:36:42 + | +LL | impl> Mirror for T {} + | - ^^^^^^ ^ + | | + | unsatisfied trait bound implicitly introduced here + error[E0277]: the size for values of type `Self` cannot be known at compilation time --> $DIR/ice-generics-of-crate-root-152335.rs:23:5 | @@ -35,7 +67,7 @@ LL | fn transmute(&self, t: T) -> ::Assoc; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time | note: required for `Self` to implement `Mirror` - --> $DIR/ice-generics-of-crate-root-152335.rs:34:42 + --> $DIR/ice-generics-of-crate-root-152335.rs:36:42 | LL | impl> Mirror for T {} | - ^^^^^^ ^ @@ -69,7 +101,7 @@ LL | trait Foo: Super = note: a type parameter was expected, but a different one was found; you might be missing a type parameter or trait bound = note: for more information, visit https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters note: required for `Self` to implement `Mirror` - --> $DIR/ice-generics-of-crate-root-152335.rs:34:42 + --> $DIR/ice-generics-of-crate-root-152335.rs:36:42 | LL | impl> Mirror for T {} | --------- ^^^^^^ ^ @@ -83,7 +115,7 @@ LL | trait Foo: Super | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time | note: required for `Self` to implement `Mirror` - --> $DIR/ice-generics-of-crate-root-152335.rs:34:42 + --> $DIR/ice-generics-of-crate-root-152335.rs:36:42 | LL | impl> Mirror for T {} | - ^^^^^^ ^ @@ -95,7 +127,7 @@ LL | trait Foo: Super + Sized | +++++++ error[E0046]: not all trait items implemented, missing: `Assoc` - --> $DIR/ice-generics-of-crate-root-152335.rs:34:1 + --> $DIR/ice-generics-of-crate-root-152335.rs:36:1 | LL | type Assoc: ?Sized; | ------------------ `Assoc` from trait @@ -120,7 +152,7 @@ LL | fn transmute(&self, t: T) -> ::Assoc; = note: a type parameter was expected, but a different one was found; you might be missing a type parameter or trait bound = note: for more information, visit https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters note: required for `Self` to implement `Mirror` - --> $DIR/ice-generics-of-crate-root-152335.rs:34:42 + --> $DIR/ice-generics-of-crate-root-152335.rs:36:42 | LL | impl> Mirror for T {} | --------- ^^^^^^ ^ @@ -134,7 +166,7 @@ LL | fn transmute(&self, t: T) -> ::Assoc; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time | note: required for `Self` to implement `Mirror` - --> $DIR/ice-generics-of-crate-root-152335.rs:34:42 + --> $DIR/ice-generics-of-crate-root-152335.rs:36:42 | LL | impl> Mirror for T {} | - ^^^^^^ ^ @@ -145,7 +177,7 @@ help: consider further restricting `Self` LL | fn transmute(&self, t: T) -> ::Assoc where Self: Sized; | +++++++++++++++++ -error: aborting due to 10 previous errors +error: aborting due to 12 previous errors Some errors have detailed explanations: E0038, E0046, E0271, E0277, E0405, E0601. For more information about an error, try `rustc --explain E0038`. diff --git a/tests/ui/traits/unhandled-crate-mod-issue-144888.rs b/tests/ui/traits/unhandled-crate-mod-issue-144888.rs index 8c9085ee80b35..41f233398d83b 100644 --- a/tests/ui/traits/unhandled-crate-mod-issue-144888.rs +++ b/tests/ui/traits/unhandled-crate-mod-issue-144888.rs @@ -18,6 +18,8 @@ where //~| ERROR the size for values of type `Self` cannot be known at compilation time //~| ERROR type mismatch resolving //~| ERROR the size for values of type `Self` cannot be known at compilation time + //~| ERROR type mismatch resolving + //~| ERROR the size for values of type `RustaceansAreAwesome` cannot be known at compilation time } trait Mirror { diff --git a/tests/ui/traits/unhandled-crate-mod-issue-144888.stderr b/tests/ui/traits/unhandled-crate-mod-issue-144888.stderr index 3208945a5b732..4cc5b41588efa 100644 --- a/tests/ui/traits/unhandled-crate-mod-issue-144888.stderr +++ b/tests/ui/traits/unhandled-crate-mod-issue-144888.stderr @@ -1,3 +1,19 @@ +error[E0271]: type mismatch resolving `::Assoc == ()` + --> $DIR/unhandled-crate-mod-issue-144888.rs:16:5 + | +LL | fn transmute(&self) {} + | ^^^^^^^^^^^^^^^^^^^ expected `()`, found type parameter `T` + | + = note: expected unit type `()` + found type parameter `T` +note: required for `RustaceansAreAwesome` to implement `Mirror` + --> $DIR/unhandled-crate-mod-issue-144888.rs:29:28 + | +LL | impl> Mirror for T {} + | ---------- ^^^^^^ ^ + | | + | unsatisfied trait bound introduced here + error[E0271]: type mismatch resolving `::Assoc == ()` --> $DIR/unhandled-crate-mod-issue-144888.rs:16:5 | @@ -7,13 +23,27 @@ LL | fn transmute(&self) {} = note: expected unit type `()` found type parameter `T` note: required for `Self` to implement `Mirror` - --> $DIR/unhandled-crate-mod-issue-144888.rs:27:28 + --> $DIR/unhandled-crate-mod-issue-144888.rs:29:28 | LL | impl> Mirror for T {} | ---------- ^^^^^^ ^ | | | unsatisfied trait bound introduced here +error[E0277]: the size for values of type `RustaceansAreAwesome` cannot be known at compilation time + --> $DIR/unhandled-crate-mod-issue-144888.rs:16:5 + | +LL | fn transmute(&self) {} + | ^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | +note: required for `RustaceansAreAwesome` to implement `Mirror` + --> $DIR/unhandled-crate-mod-issue-144888.rs:29:28 + | +LL | impl> Mirror for T {} + | - ^^^^^^ ^ + | | + | unsatisfied trait bound implicitly introduced here + error[E0277]: the size for values of type `Self` cannot be known at compilation time --> $DIR/unhandled-crate-mod-issue-144888.rs:16:5 | @@ -21,7 +51,7 @@ LL | fn transmute(&self) {} | ^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time | note: required for `Self` to implement `Mirror` - --> $DIR/unhandled-crate-mod-issue-144888.rs:27:28 + --> $DIR/unhandled-crate-mod-issue-144888.rs:29:28 | LL | impl> Mirror for T {} | - ^^^^^^ ^ @@ -37,7 +67,7 @@ LL | trait Foo: Super = note: expected unit type `()` found type parameter `T` note: required for `Self` to implement `Mirror` - --> $DIR/unhandled-crate-mod-issue-144888.rs:27:28 + --> $DIR/unhandled-crate-mod-issue-144888.rs:29:28 | LL | impl> Mirror for T {} | ---------- ^^^^^^ ^ @@ -51,7 +81,7 @@ LL | trait Foo: Super | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time | note: required for `Self` to implement `Mirror` - --> $DIR/unhandled-crate-mod-issue-144888.rs:27:28 + --> $DIR/unhandled-crate-mod-issue-144888.rs:29:28 | LL | impl> Mirror for T {} | - ^^^^^^ ^ @@ -63,7 +93,7 @@ LL | trait Foo: Super + Sized | +++++++ error[E0046]: not all trait items implemented, missing: `Assoc` - --> $DIR/unhandled-crate-mod-issue-144888.rs:27:1 + --> $DIR/unhandled-crate-mod-issue-144888.rs:29:1 | LL | type Assoc; | ---------- `Assoc` from trait @@ -83,7 +113,7 @@ LL | fn transmute(&self) {} = note: expected unit type `()` found type parameter `T` note: required for `Self` to implement `Mirror` - --> $DIR/unhandled-crate-mod-issue-144888.rs:27:28 + --> $DIR/unhandled-crate-mod-issue-144888.rs:29:28 | LL | impl> Mirror for T {} | ---------- ^^^^^^ ^ @@ -97,7 +127,7 @@ LL | fn transmute(&self) {} | ^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time | note: required for `Self` to implement `Mirror` - --> $DIR/unhandled-crate-mod-issue-144888.rs:27:28 + --> $DIR/unhandled-crate-mod-issue-144888.rs:29:28 | LL | impl> Mirror for T {} | - ^^^^^^ ^ @@ -108,7 +138,7 @@ help: consider further restricting `Self` LL | fn transmute(&self) where Self: Sized {} | +++++++++++++++++ -error: aborting due to 7 previous errors +error: aborting due to 9 previous errors Some errors have detailed explanations: E0046, E0271, E0277. For more information about an error, try `rustc --explain E0046`. From b6f9bfc9ff8f5f8880e2787a59e84d68c4805a41 Mon Sep 17 00:00:00 2001 From: ltdk Date: Mon, 12 Aug 2024 22:24:51 -0400 Subject: [PATCH 12/30] Additional NonZero conversions --- library/core/src/num/nonzero.rs | 163 +++++++++++++++++- .../issue-71394-no-from-impl.stderr | 2 +- 2 files changed, 162 insertions(+), 3 deletions(-) diff --git a/library/core/src/num/nonzero.rs b/library/core/src/num/nonzero.rs index d0ae6c31267f3..20ff01eb30c31 100644 --- a/library/core/src/num/nonzero.rs +++ b/library/core/src/num/nonzero.rs @@ -1,6 +1,6 @@ //! Definitions of integer that is known not to equal zero. -use super::{IntErrorKind, ParseIntError}; +use super::{IntErrorKind, ParseIntError, TryFromIntError}; use crate::clone::{TrivialClone, UseCloned}; use crate::cmp::Ordering; use crate::hash::{Hash, Hasher}; @@ -9,7 +9,7 @@ use crate::num::imp; use crate::ops::{BitOr, BitOrAssign, Div, DivAssign, Neg, Rem, RemAssign}; use crate::panic::{RefUnwindSafe, UnwindSafe}; use crate::str::FromStr; -use crate::{fmt, intrinsics, ptr, ub_checks}; +use crate::{fmt, intrinsics, ptr, slice, ub_checks}; /// A marker trait for primitive types which can be zero. /// @@ -305,6 +305,114 @@ where } } +#[stable(feature = "more_from_nonzero", since = "CURRENT_RUSTC_VERSION")] +impl<'a, T> From<&'a NonZero> for &'a T +where + T: ZeroablePrimitive, +{ + #[inline] + fn from(nonzero: &'a NonZero) -> &'a T { + nonzero.as_ref() + } +} + +// FIXME: see library/std/tests/slice-from-array-issue-113238.rs +/* +#[stable(feature = "more_from_nonzero", since = "CURRENT_RUSTC_VERSION")] +impl<'a, T> From<&'a [NonZero]> for &'a [T] +where + T: ZeroablePrimitive, +{ + #[inline] + fn from(nonzero: &'a [NonZero]) -> &'a [T] { + nonzero.as_zeroable() + } +} +impl [NonZero] +where + T: ZeroablePrimitive, +{ + /// Implementation of `From<&[NonZero]> for &[T]`. + #[must_use] + #[inline] + fn as_zeroable(&self) -> &[T] { + // SAFETY: `repr(transparent)` ensures that `NonZero` has same layout as `T`, and thus + // `[NonZero]` has same layout as `[T]` + unsafe { &*(slice::from_raw_parts(self.as_ptr().cast::(), self.len())) } + } +} +*/ + +#[stable(feature = "more_from_nonzero", since = "CURRENT_RUSTC_VERSION")] +impl From<[NonZero; N]> for [T; N] +where + T: ZeroablePrimitive, +{ + #[inline] + fn from(nonzero: [NonZero; N]) -> [T; N] { + nonzero.into_zeroable() + } +} + +// FIXME: can't detect that ZeroablePrimitive is a sealed trait +macro_rules! impl_ref_try_from { + ($($t:ty),* $(,)?) => { + $( + #[stable(feature = "more_from_nonzero", since = "CURRENT_RUSTC_VERSION")] + impl<'a> TryFrom<&'a $t> for &'a NonZero<$t> { + type Error = TryFromIntError; + + #[inline] + fn try_from(zeroable: &'a $t) -> Result<&'a NonZero<$t>, TryFromIntError> { + NonZero::from_ref(zeroable).ok_or(TryFromIntError(IntErrorKind::Zero)) + } + } + )* + } +} + +impl_ref_try_from! { + u8, + u16, + u32, + u64, + u128, + usize, + i8, + i16, + i32, + i64, + i128, + isize, + char, +} + +#[stable(feature = "more_from_nonzero", since = "CURRENT_RUSTC_VERSION")] +impl<'a, T> TryFrom<&'a [T]> for &'a [NonZero] +where + T: ZeroablePrimitive, +{ + type Error = TryFromIntError; + + #[inline] + fn try_from(zeroable: &'a [T]) -> Result<&'a [NonZero], TryFromIntError> { + NonZero::from_slice(zeroable).ok_or(TryFromIntError(IntErrorKind::Zero)) + } +} + +#[stable(feature = "more_from_nonzero", since = "CURRENT_RUSTC_VERSION")] +impl TryFrom<[T; N]> for [NonZero; N] +where + T: ZeroablePrimitive, +{ + type Error = TryFromIntError; + + #[inline] + fn try_from(zeroable: [T; N]) -> Result<[NonZero; N], TryFromIntError> { + NonZero::from_array(zeroable).ok_or(TryFromIntError(IntErrorKind::Zero)) + } +} + #[stable(feature = "nonzero_bitor", since = "1.45.0")] #[rustc_const_unstable(feature = "const_ops", issue = "143802")] const impl BitOr for NonZero @@ -460,6 +568,45 @@ where } } + /// Implementation of `From<&NonZero> for &T`. + #[must_use] + #[inline] + fn as_ref(&self) -> &T { + // SAFETY: `repr(transparent)` ensures that `NonZero` has same layout as `T` + unsafe { &*(ptr::from_ref(self).cast::()) } + } + + /// Implementation of `TryFrom<&T> for &NonZero`. + #[must_use] + #[inline] + fn from_ref(n: &T) -> Option<&Self> { + // SAFETY: Memory layout optimization guarantees that `Option>` has + // the same layout and size as `T`, with `0` representing `None`. + let opt_n = unsafe { &*(ptr::from_ref(n).cast::>()) }; + + opt_n.as_ref() + } + + /// Implementation of `TryFrom<&[T]> for &[NonZero]`. + #[must_use] + #[inline] + fn from_slice(n: &[T]) -> Option<&[Self]> { + if n.iter().all(|x| NonZero::new(*x).is_some()) { + // SAFETY: We explicitly checked that all elements are nonzero, and because of `repr(transparent)` + // the layout remains unchanged + Some(unsafe { slice::from_raw_parts(n.as_ptr().cast::>(), n.len()) }) + } else { + None + } + } + + /// Implementation of `TryFrom<[T; N]> for [NonZero; N]`. + #[must_use] + #[inline] + fn from_array(n: [T; N]) -> Option<[Self; N]> { + n.try_map(NonZero::new) + } + /// Returns the contained value as a primitive type. #[stable(feature = "nonzero", since = "1.28.0")] #[rustc_const_stable(feature = "const_nonzero_get", since = "1.34.0")] @@ -487,6 +634,18 @@ where } } +impl [NonZero; N] +where + T: ZeroablePrimitive, +{ + /// Implementation of `From<[NonZero; N]> for [T; N]`. + #[must_use] + #[inline] + fn into_zeroable(self) -> [T; N] { + self.map(NonZero::get) + } +} + macro_rules! nonzero_integer { ( #[$stability:meta] diff --git a/tests/ui/suggestions/issue-71394-no-from-impl.stderr b/tests/ui/suggestions/issue-71394-no-from-impl.stderr index b8a240f7f07c5..e3d01bd588a4f 100644 --- a/tests/ui/suggestions/issue-71394-no-from-impl.stderr +++ b/tests/ui/suggestions/issue-71394-no-from-impl.stderr @@ -13,7 +13,7 @@ LL | let _: &[i8] = data.into(); `[T; 6]` implements `From<(T, T, T, T, T, T)>` `[T; 7]` implements `From<(T, T, T, T, T, T, T)>` `[T; 8]` implements `From<(T, T, T, T, T, T, T, T)>` - and 7 others + and 8 others = note: required for `&[u8]` to implement `Into<&[i8]>` error: aborting due to 1 previous error From 20b6fd622136db582c32a964e3cec9a56030af0d Mon Sep 17 00:00:00 2001 From: Jakob Koschel Date: Wed, 23 Sep 2026 20:06:33 +0000 Subject: [PATCH 13/30] Address review feedback Only check `SanitizerCfiMinimalRuntimeRequiresCfiRecoverOrDiag` if CFI is actually enabled. Use `is_sanitizer_cfi_minimal_runtime_enabled()` helper within `link.rs`. --- compiler/rustc_codegen_ssa/src/back/link.rs | 2 +- compiler/rustc_session/src/session.rs | 6 +++--- tests/ui/sanitizer/cfi/minimal-runtime-requires-cfi.rs | 1 - tests/ui/sanitizer/cfi/minimal-runtime-requires-cfi.stderr | 4 +--- 4 files changed, 5 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index 7c8bb4f59dfba..3a405f275cf4b 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -1764,7 +1764,7 @@ fn add_sanitizer_libraries( && (sess.opts.unstable_opts.sanitizer_cfi_diag.unwrap_or(false) || sess.opts.unstable_opts.sanitizer_cfi_recover.unwrap_or(false)) { - if sess.opts.unstable_opts.sanitizer_cfi_minimal_runtime.unwrap_or(false) { + if sess.is_sanitizer_cfi_minimal_runtime_enabled() { link_sanitizer_runtime(sess, flavor, linker, "ubsan_minimal"); } else { link_sanitizer_runtime(sess, flavor, linker, "ubsan"); diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index a209219758026..cea3a32729b98 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -1688,12 +1688,12 @@ fn validate_commandline_args_with_session_available(sess: &Session) { } } - // LLVM CFI minimal runtime requires CFI Recover or CFI Diag. + // LLVM CFI minimal runtime requires CFI recovery or CFI diagnostics. if sess.is_sanitizer_cfi_minimal_runtime_enabled() { if !sess.is_sanitizer_cfi_enabled() { sess.dcx().emit_err(diagnostics::SanitizerCfiMinimalRuntimeRequiresCfi); - } - if !(sess.is_sanitizer_cfi_recover_enabled() || sess.is_sanitizer_cfi_diag_enabled()) { + } else if !(sess.is_sanitizer_cfi_recover_enabled() || sess.is_sanitizer_cfi_diag_enabled()) + { sess.dcx().emit_err(diagnostics::SanitizerCfiMinimalRuntimeRequiresCfiRecoverOrDiag); } } diff --git a/tests/ui/sanitizer/cfi/minimal-runtime-requires-cfi.rs b/tests/ui/sanitizer/cfi/minimal-runtime-requires-cfi.rs index 13b296b4329ee..cb68734608ac7 100644 --- a/tests/ui/sanitizer/cfi/minimal-runtime-requires-cfi.rs +++ b/tests/ui/sanitizer/cfi/minimal-runtime-requires-cfi.rs @@ -8,4 +8,3 @@ #![no_main] //~? ERROR `-Zsanitizer-cfi-minimal-runtime` requires `-Zsanitizer=cfi` -//~? ERROR `-Zsanitizer-cfi-minimal-runtime` requires `-Zsanitizer-cfi-recover` or `-Zsanitizer-cfi-diag` diff --git a/tests/ui/sanitizer/cfi/minimal-runtime-requires-cfi.stderr b/tests/ui/sanitizer/cfi/minimal-runtime-requires-cfi.stderr index 251d0d0817631..5cb83e40dc0c4 100644 --- a/tests/ui/sanitizer/cfi/minimal-runtime-requires-cfi.stderr +++ b/tests/ui/sanitizer/cfi/minimal-runtime-requires-cfi.stderr @@ -1,6 +1,4 @@ error: `-Zsanitizer-cfi-minimal-runtime` requires `-Zsanitizer=cfi` -error: `-Zsanitizer-cfi-minimal-runtime` requires `-Zsanitizer-cfi-recover` or `-Zsanitizer-cfi-diag` - -error: aborting due to 2 previous errors +error: aborting due to 1 previous error From f3b4f0f80c74e8ed19e38b14608effb51e7d339a Mon Sep 17 00:00:00 2001 From: "Tim (Theemathas Chirananthavat)" Date: Tue, 25 Aug 2026 07:25:17 +0700 Subject: [PATCH 14/30] Stabilize `Result::into_{ok,err}` Tracking issue: https://github.com/rust-lang/rust/issues/61695 --- compiler/rustc_arena/src/lib.rs | 2 +- compiler/rustc_data_structures/src/lib.rs | 2 +- compiler/rustc_hir_analysis/src/lib.rs | 2 +- compiler/rustc_trait_selection/src/lib.rs | 2 +- library/alloc/src/lib.rs | 1 - library/alloctests/lib.rs | 1 - library/core/src/result.rs | 8 ++------ library/coretests/tests/lib.rs | 1 - library/std/src/lib.rs | 1 - src/tools/clippy/clippy_lints/src/lib.rs | 2 +- src/tools/clippy/clippy_utils/src/lib.rs | 2 +- 11 files changed, 8 insertions(+), 16 deletions(-) diff --git a/compiler/rustc_arena/src/lib.rs b/compiler/rustc_arena/src/lib.rs index 0fc1f96dfe469..f2c6ffe44cdfd 100644 --- a/compiler/rustc_arena/src/lib.rs +++ b/compiler/rustc_arena/src/lib.rs @@ -11,13 +11,13 @@ #![allow(clippy::mut_from_ref)] // Arena allocators are one place where this pattern is fine. #![allow(internal_features)] #![cfg_attr(bootstrap, feature(never_type))] +#![cfg_attr(bootstrap, feature(unwrap_infallible))] #![cfg_attr(test, feature(test))] #![deny(unsafe_op_in_unsafe_fn)] #![doc(test(no_crate_inject, attr(deny(warnings))))] #![feature(decl_macro)] #![feature(dropck_eyepatch)] #![feature(rustc_attrs)] -#![feature(unwrap_infallible)] // tidy-alphabetical-end use std::alloc::Layout; diff --git a/compiler/rustc_data_structures/src/lib.rs b/compiler/rustc_data_structures/src/lib.rs index 041763f57fc9e..4ebd6ef9f92b3 100644 --- a/compiler/rustc_data_structures/src/lib.rs +++ b/compiler/rustc_data_structures/src/lib.rs @@ -12,6 +12,7 @@ #![allow(rustc::potential_query_instability)] #![cfg_attr(bootstrap, feature(allocator_api))] #![cfg_attr(bootstrap, feature(never_type))] +#![cfg_attr(bootstrap, feature(unwrap_infallible))] #![cfg_attr(not(bootstrap), feature(allocator_ext))] #![cfg_attr(test, feature(test))] #![deny(unsafe_op_in_unsafe_fn)] @@ -35,7 +36,6 @@ #![feature(thread_id_value)] #![feature(trusted_len)] #![feature(type_alias_impl_trait)] -#![feature(unwrap_infallible)] // tidy-alphabetical-end // This allows derive macros to reference this crate diff --git a/compiler/rustc_hir_analysis/src/lib.rs b/compiler/rustc_hir_analysis/src/lib.rs index dbbf068185c23..c0abcc3a6119a 100644 --- a/compiler/rustc_hir_analysis/src/lib.rs +++ b/compiler/rustc_hir_analysis/src/lib.rs @@ -57,12 +57,12 @@ This API is completely unstable and subject to change. // tidy-alphabetical-start #![cfg_attr(bootstrap, feature(never_type))] +#![cfg_attr(bootstrap, feature(unwrap_infallible))] #![feature(default_field_values)] #![feature(gen_blocks)] #![feature(iter_intersperse)] #![feature(slice_partition_dedup)] #![feature(try_blocks)] -#![feature(unwrap_infallible)] // tidy-alphabetical-end // These are used by Clippy. diff --git a/compiler/rustc_trait_selection/src/lib.rs b/compiler/rustc_trait_selection/src/lib.rs index bbaf683f4288a..a5cb7d0c7bb1a 100644 --- a/compiler/rustc_trait_selection/src/lib.rs +++ b/compiler/rustc_trait_selection/src/lib.rs @@ -12,6 +12,7 @@ // tidy-alphabetical-start #![cfg_attr(bootstrap, feature(never_type))] +#![cfg_attr(bootstrap, feature(unwrap_infallible))] #![feature(associated_type_defaults)] #![feature(default_field_values)] #![feature(deref_patterns)] @@ -20,7 +21,6 @@ #![feature(iterator_try_reduce)] #![feature(option_into_flat_iter)] #![feature(try_blocks)] -#![feature(unwrap_infallible)] #![feature(yeet_expr)] #![recursion_limit = "512"] // For rustdoc // tidy-alphabetical-end diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index de6830a514656..5e4b3b3d536f6 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -186,7 +186,6 @@ #![feature(ub_checks)] #![feature(unicode_internals)] #![feature(unsize)] -#![feature(unwrap_infallible)] #![feature(write_all_vectored)] #![feature(wtf8_internals)] // tidy-alphabetical-end diff --git a/library/alloctests/lib.rs b/library/alloctests/lib.rs index 76550081a703c..a5f46099173c0 100644 --- a/library/alloctests/lib.rs +++ b/library/alloctests/lib.rs @@ -50,7 +50,6 @@ #![feature(trusted_random_access)] #![feature(try_reserve_kind)] #![feature(try_trait_v2)] -#![feature(unwrap_infallible)] #![feature(wtf8_internals)] // tidy-alphabetical-end // diff --git a/library/core/src/result.rs b/library/core/src/result.rs index 472d568c5cf66..da6be4d6d48a0 100644 --- a/library/core/src/result.rs +++ b/library/core/src/result.rs @@ -1343,8 +1343,6 @@ impl Result { /// # Examples /// /// ``` - /// # #![feature(unwrap_infallible)] - /// /// fn only_good_news() -> Result { /// Ok("this is fine".into()) /// } @@ -1352,7 +1350,7 @@ impl Result { /// let s: String = only_good_news().into_ok(); /// println!("{s}"); /// ``` - #[unstable(feature = "unwrap_infallible", issue = "61695")] + #[stable(feature = "unwrap_infallible", since = "CURRENT_RUSTC_VERSION")] #[inline] #[rustc_allow_const_fn_unstable(const_precise_live_drops)] #[rustc_const_unstable(feature = "const_convert", issue = "143773")] @@ -1379,8 +1377,6 @@ impl Result { /// # Examples /// /// ``` - /// # #![feature(unwrap_infallible)] - /// /// fn only_bad_news() -> Result { /// Err("Oops, it failed".into()) /// } @@ -1388,7 +1384,7 @@ impl Result { /// let error: String = only_bad_news().into_err(); /// println!("{error}"); /// ``` - #[unstable(feature = "unwrap_infallible", issue = "61695")] + #[stable(feature = "unwrap_infallible", since = "CURRENT_RUSTC_VERSION")] #[inline] #[rustc_allow_const_fn_unstable(const_precise_live_drops)] #[rustc_const_unstable(feature = "const_convert", issue = "143773")] diff --git a/library/coretests/tests/lib.rs b/library/coretests/tests/lib.rs index 69bc6edd1b833..c52357da0279d 100644 --- a/library/coretests/tests/lib.rs +++ b/library/coretests/tests/lib.rs @@ -130,7 +130,6 @@ #![feature(uint_gather_scatter_bits)] #![feature(unicode_internals)] #![feature(unsize)] -#![feature(unwrap_infallible)] // tidy-alphabetical-end #![allow(internal_features)] #![deny(implicit_provenance_casts)] diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index d59d204d6e144..bfdfb6ba9a4fd 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -313,7 +313,6 @@ #![feature(try_blocks)] #![feature(try_trait_v2)] #![feature(type_alias_impl_trait)] -#![feature(unwrap_infallible)] // tidy-alphabetical-end // // Library features (core): diff --git a/src/tools/clippy/clippy_lints/src/lib.rs b/src/tools/clippy/clippy_lints/src/lib.rs index 435f787f7012f..87bb44875aee9 100644 --- a/src/tools/clippy/clippy_lints/src/lib.rs +++ b/src/tools/clippy/clippy_lints/src/lib.rs @@ -8,9 +8,9 @@ #![feature(macro_metavar_expr)] #![feature(macro_metavar_expr_concat)] #![cfg_attr(bootstrap, feature(never_type))] +#![cfg_attr(bootstrap, feature(unwrap_infallible))] #![feature(rustc_private)] #![feature(stmt_expr_attributes)] -#![feature(unwrap_infallible)] #![recursion_limit = "512"] #![expect(clippy::literal_string_with_formatting_args, clippy::must_use_candidate)] #![warn( diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs index 0030f1cdc8208..5579dd1fc62d8 100644 --- a/src/tools/clippy/clippy_utils/src/lib.rs +++ b/src/tools/clippy/clippy_utils/src/lib.rs @@ -1,7 +1,7 @@ +#![cfg_attr(bootstrap, feature(unwrap_infallible))] #![feature(deref_patterns)] #![feature(macro_metavar_expr)] #![feature(rustc_private)] -#![feature(unwrap_infallible)] #![recursion_limit = "512"] #![expect(clippy::missing_errors_doc, clippy::missing_panics_doc, clippy::must_use_candidate)] #![warn( From a579f7e4b5bd444a569b18ac26e9b4ab52fcc338 Mon Sep 17 00:00:00 2001 From: Jakob Koschel Date: Fri, 25 Sep 2026 08:14:05 +0000 Subject: [PATCH 15/30] Add -Ctarget-feature=-crt-static to CFI minimal runtime tests --- tests/ui/target_modifiers/auxiliary/cfi-minimal-runtime.rs | 2 +- tests/ui/target_modifiers/sanitizer-cfi-minimal-runtime.rs | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/ui/target_modifiers/auxiliary/cfi-minimal-runtime.rs b/tests/ui/target_modifiers/auxiliary/cfi-minimal-runtime.rs index aacea1ee65dd2..689c141811f60 100644 --- a/tests/ui/target_modifiers/auxiliary/cfi-minimal-runtime.rs +++ b/tests/ui/target_modifiers/auxiliary/cfi-minimal-runtime.rs @@ -1,6 +1,6 @@ //@ no-prefer-dynamic //@ needs-sanitizer-cfi -//@ compile-flags: -Clto -Zsanitizer=cfi -Zsanitizer-cfi-recover -Zsanitizer-cfi-minimal-runtime +//@ compile-flags: -Clto -Ctarget-feature=-crt-static -Zsanitizer=cfi -Zsanitizer-cfi-recover -Zsanitizer-cfi-minimal-runtime #![feature(no_core)] #![crate_type = "rlib"] diff --git a/tests/ui/target_modifiers/sanitizer-cfi-minimal-runtime.rs b/tests/ui/target_modifiers/sanitizer-cfi-minimal-runtime.rs index 45c6ad7b521a3..fe2be11e6a3d5 100644 --- a/tests/ui/target_modifiers/sanitizer-cfi-minimal-runtime.rs +++ b/tests/ui/target_modifiers/sanitizer-cfi-minimal-runtime.rs @@ -2,6 +2,7 @@ //@ needs-sanitizer-cfi //@ aux-build:cfi-minimal-runtime.rs +//@ compile-flags: -Ctarget-feature=-crt-static //@ revisions: ok wrong_flag wrong_sanitizer //@[ok] compile-flags: -Clto -Zsanitizer=cfi -Zsanitizer-cfi-recover -Zsanitizer-cfi-minimal-runtime From 08793166b03bc268c09eff6ce50d669a2ad03ff4 Mon Sep 17 00:00:00 2001 From: Jakob Koschel Date: Sat, 26 Sep 2026 09:16:32 +0000 Subject: [PATCH 16/30] Limit CFI run-make test to Linux --- tests/run-make/sanitizer-cfi-runtime/rmake.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/run-make/sanitizer-cfi-runtime/rmake.rs b/tests/run-make/sanitizer-cfi-runtime/rmake.rs index 3cad996a14054..a86e9084dff12 100644 --- a/tests/run-make/sanitizer-cfi-runtime/rmake.rs +++ b/tests/run-make/sanitizer-cfi-runtime/rmake.rs @@ -1,3 +1,5 @@ +// FIXME(#122848) Remove only-linux once OSX CFI binaries work +//@ only-linux //@ needs-sanitizer-support //@ needs-sanitizer-cfi //@ ignore-backends: gcc From 188011ac2901b1762e8bb9a3baaf8338cc839e55 Mon Sep 17 00:00:00 2001 From: Raushan kumar Date: Thu, 9 Jul 2026 05:49:29 +0000 Subject: [PATCH 17/30] test(imports): add baseline regression for hidden extern crate suggestion --- .../auxiliary/macro-generated-extern-crate.rs | 3 +++ .../ui/imports/macro-generated-extern-crate.rs | 14 ++++++++++++++ .../macro-generated-extern-crate.stderr | 18 ++++++++++++++++++ 3 files changed, 35 insertions(+) create mode 100644 tests/ui/imports/auxiliary/macro-generated-extern-crate.rs create mode 100644 tests/ui/imports/macro-generated-extern-crate.rs create mode 100644 tests/ui/imports/macro-generated-extern-crate.stderr diff --git a/tests/ui/imports/auxiliary/macro-generated-extern-crate.rs b/tests/ui/imports/auxiliary/macro-generated-extern-crate.rs new file mode 100644 index 0000000000000..8a2c17266b7a9 --- /dev/null +++ b/tests/ui/imports/auxiliary/macro-generated-extern-crate.rs @@ -0,0 +1,3 @@ +pub trait MyTrait { + fn custom() {} +} diff --git a/tests/ui/imports/macro-generated-extern-crate.rs b/tests/ui/imports/macro-generated-extern-crate.rs new file mode 100644 index 0000000000000..a7b87efdd8fbe --- /dev/null +++ b/tests/ui/imports/macro-generated-extern-crate.rs @@ -0,0 +1,14 @@ +//@ edition: 2021 +//@ aux-build: macro-generated-extern-crate.rs + +const _: () = { + extern crate macro_generated_extern_crate as _my_crate; + impl _my_crate::MyTrait for Local {} +}; + +struct Local; + +fn main() { + Local::custom(); + //~^ ERROR no associated function or constant named `custom` found for struct `Local` +} diff --git a/tests/ui/imports/macro-generated-extern-crate.stderr b/tests/ui/imports/macro-generated-extern-crate.stderr new file mode 100644 index 0000000000000..7c022f715cac3 --- /dev/null +++ b/tests/ui/imports/macro-generated-extern-crate.stderr @@ -0,0 +1,18 @@ +error[E0599]: no associated function or constant named `custom` found for struct `Local` in the current scope + --> $DIR/macro-generated-extern-crate.rs:12:12 + | +LL | struct Local; + | ------------ associated function or constant `custom` not found for this struct +... +LL | Local::custom(); + | ^^^^^^ associated function or constant not found in `Local` + | + = help: items from traits can only be used if the trait is in scope +help: trait `MyTrait` which provides `custom` is implemented but not in scope; perhaps you want to import it + | +LL + use crate::_::_my_crate::MyTrait; + | + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0599`. From 2e4b8e5f17e7ce1bc95e35091eabe530f275a394 Mon Sep 17 00:00:00 2001 From: Raushan kumar Date: Thu, 9 Jul 2026 05:41:41 +0000 Subject: [PATCH 18/30] fix(metadata): demote unnameable `extern crate` items to `ExternCrateSource::Path` Use the `DefPath` to determine whether an `extern crate` item is globally nameable. `extern crate` items nested inside value namespaces cannot be referred to by a stable path outside their enclosing scope. Record them as `ExternCrateSource::Path` instead of `ExternCrateSource::Extern` so later path resolution does not reconstruct unnameable paths. --- compiler/rustc_metadata/src/creader.rs | 18 ++++- .../macro-generated-extern-crate.stderr | 2 +- .../uniform-paths/issue-87932.stderr | 2 +- tests/ui/traits/bound/same-crate-name.rs | 9 --- tests/ui/traits/bound/same-crate-name.stderr | 76 +++++-------------- .../ui/type/type-mismatch-same-crate-name.rs | 13 ++-- .../type/type-mismatch-same-crate-name.stderr | 33 +++----- 7 files changed, 52 insertions(+), 101 deletions(-) diff --git a/compiler/rustc_metadata/src/creader.rs b/compiler/rustc_metadata/src/creader.rs index aea843ac0384c..10693448ea685 100644 --- a/compiler/rustc_metadata/src/creader.rs +++ b/compiler/rustc_metadata/src/creader.rs @@ -15,7 +15,7 @@ use rustc_data_structures::sync::{self, FreezeReadGuard, FreezeWriteGuard}; use rustc_data_structures::unord::UnordMap; use rustc_expand::base::SyntaxExtension; use rustc_hir::def_id::{CrateNum, LOCAL_CRATE, LocalDefId, StableCrateId}; -use rustc_hir::definitions::Definitions; +use rustc_hir::definitions::{DefPathData, Definitions}; use rustc_index::IndexVec; use rustc_lint_defs as lint; use rustc_lint_defs::builtin::UNUSED_CRATE_DEPENDENCIES; @@ -1358,14 +1358,24 @@ impl CStore { let cnum = self.resolve_crate(tcx, name, item.span, dep_kind, CrateOrigin::Extern)?; - let path_len = definitions.def_path(def_id).data.len(); + let def_path = definitions.def_path(def_id); + // An extern crate is only globally nameable if every segment in its path + // is in the type namespace (i.e., it is purely nested inside modules). + // If any segment is in the value namespace (e.g. inside a fn or const), + // it is unnameable from outside that block. + let is_unnameable = + def_path.data.iter().any(|d| !matches!(d.data, DefPathData::TypeNs(_))); self.update_extern_crate( cnum, name, ExternCrate { - src: ExternCrateSource::Extern(def_id.to_def_id()), + src: if is_unnameable { + ExternCrateSource::Path + } else { + ExternCrateSource::Extern(def_id.to_def_id()) + }, span: item.span, - path_len, + path_len: if is_unnameable { usize::MAX } else { def_path.data.len() }, dependency_of: LOCAL_CRATE, }, ); diff --git a/tests/ui/imports/macro-generated-extern-crate.stderr b/tests/ui/imports/macro-generated-extern-crate.stderr index 7c022f715cac3..e103b185789ac 100644 --- a/tests/ui/imports/macro-generated-extern-crate.stderr +++ b/tests/ui/imports/macro-generated-extern-crate.stderr @@ -10,7 +10,7 @@ LL | Local::custom(); = help: items from traits can only be used if the trait is in scope help: trait `MyTrait` which provides `custom` is implemented but not in scope; perhaps you want to import it | -LL + use crate::_::_my_crate::MyTrait; +LL + use macro_generated_extern_crate::MyTrait; | error: aborting due to 1 previous error diff --git a/tests/ui/rust-2018/uniform-paths/issue-87932.stderr b/tests/ui/rust-2018/uniform-paths/issue-87932.stderr index c408b747b506f..ff069320f413d 100644 --- a/tests/ui/rust-2018/uniform-paths/issue-87932.stderr +++ b/tests/ui/rust-2018/uniform-paths/issue-87932.stderr @@ -10,7 +10,7 @@ LL | A::deserialize(); = help: items from traits can only be used if the trait is in scope help: trait `Deserialize` which provides `deserialize` is implemented but not in scope; perhaps you want to import it | -LL + use ::deserialize::_a::Deserialize; +LL + use issue_87932_a::Deserialize; | error: aborting due to 1 previous error diff --git a/tests/ui/traits/bound/same-crate-name.rs b/tests/ui/traits/bound/same-crate-name.rs index acd4894612872..5de6620bf4856 100644 --- a/tests/ui/traits/bound/same-crate-name.rs +++ b/tests/ui/traits/bound/same-crate-name.rs @@ -32,15 +32,11 @@ fn main() { extern crate crate_a1 as a; a::try_foo(foo); //~^ ERROR E0277 - //~| NOTE there are multiple different versions of crate `crate_a1` in the dependency graph - //~| HELP you can use `cargo tree` to explore your dependency tree // We don't want to see the "version mismatch" help message here // because `implements_no_traits` has no impl for `Foo` a::try_foo(implements_no_traits); //~^ ERROR E0277 - //~| NOTE there are multiple different versions of crate `crate_a1` in the dependency graph - //~| HELP you can use `cargo tree` to explore your dependency tree // We don't want to see the "version mismatch" help message here // because `other_variant_implements_mismatched_trait` @@ -48,16 +44,11 @@ fn main() { // only for its `` variant. a::try_foo(other_variant_implements_mismatched_trait); //~^ ERROR E0277 - //~| NOTE there are multiple different versions of crate `crate_a1` in the dependency graph - //~| HELP you can use `cargo tree` to explore your dependency tree // We don't want to see the "version mismatch" help message here // because `ImplementsTraitForUsize` only has // impls for the correct trait where the path is not misleading. a::try_foo(other_variant_implements_correct_trait); //~^ ERROR E0277 - //~| HELP the trait `main::a::Bar` is implemented for `ImplementsTraitForUsize` - //~| NOTE there are multiple different versions of crate `crate_a1` in the dependency graph - //~| HELP you can use `cargo tree` to explore your dependency tree } } diff --git a/tests/ui/traits/bound/same-crate-name.stderr b/tests/ui/traits/bound/same-crate-name.stderr index 9e5e110f8624e..27cad715b0738 100644 --- a/tests/ui/traits/bound/same-crate-name.stderr +++ b/tests/ui/traits/bound/same-crate-name.stderr @@ -1,23 +1,13 @@ -error[E0277]: the trait bound `Foo: main::a::Bar` is not satisfied +error[E0277]: the trait bound `Foo: crate_a1::Bar` is not satisfied --> $DIR/same-crate-name.rs:33:20 | LL | a::try_foo(foo); - | ---------- ^^^ the trait `main::a::Bar` is not implemented for `Foo` + | ---------- ^^^ the trait `crate_a1::Bar` is not implemented for `Foo` | | | required by a bound introduced by this call | -note: there are multiple different versions of crate `crate_a1` in the dependency graph - --> $DIR/auxiliary/crate_a1.rs:1:1 - | -LL | pub trait Bar {} - | ^^^^^^^^^^^^^ this is the expected trait - | - ::: $DIR/auxiliary/crate_a2.rs:3:1 - | -LL | pub trait Bar {} - | ------------- this is the found trait - = help: you can use `cargo tree` to explore your dependency tree -help: the trait `main::a::Bar` is implemented for `ImplementsTraitForUsize` + = note: `Foo` implements similarly named trait `crate_a2::Bar`, but not `crate_a1::Bar` +help: the trait `crate_a1::Bar` is implemented for `ImplementsTraitForUsize` --> $DIR/auxiliary/crate_a1.rs:9:1 | LL | impl Bar for ImplementsTraitForUsize {} @@ -28,26 +18,15 @@ note: required by a bound in `try_foo` LL | pub fn try_foo(x: impl Bar) {} | ^^^ required by this bound in `try_foo` -error[E0277]: the trait bound `DoesNotImplementTrait: main::a::Bar` is not satisfied - --> $DIR/same-crate-name.rs:40:20 +error[E0277]: the trait bound `DoesNotImplementTrait: crate_a1::Bar` is not satisfied + --> $DIR/same-crate-name.rs:38:20 | LL | a::try_foo(implements_no_traits); - | ---------- ^^^^^^^^^^^^^^^^^^^^ the trait `main::a::Bar` is not implemented for `DoesNotImplementTrait` + | ---------- ^^^^^^^^^^^^^^^^^^^^ the trait `crate_a1::Bar` is not implemented for `DoesNotImplementTrait` | | | required by a bound introduced by this call | -note: there are multiple different versions of crate `crate_a1` in the dependency graph - --> $DIR/auxiliary/crate_a1.rs:1:1 - | -LL | pub trait Bar {} - | ^^^^^^^^^^^^^ this is the expected trait - | - ::: $DIR/auxiliary/crate_a2.rs:3:1 - | -LL | pub trait Bar {} - | ------------- this is the trait that was imported - = help: you can use `cargo tree` to explore your dependency tree -help: the trait `main::a::Bar` is implemented for `ImplementsTraitForUsize` +help: the trait `crate_a1::Bar` is implemented for `ImplementsTraitForUsize` --> $DIR/auxiliary/crate_a1.rs:9:1 | LL | impl Bar for ImplementsTraitForUsize {} @@ -58,26 +37,16 @@ note: required by a bound in `try_foo` LL | pub fn try_foo(x: impl Bar) {} | ^^^ required by this bound in `try_foo` -error[E0277]: the trait bound `ImplementsWrongTraitConditionally: main::a::Bar` is not satisfied - --> $DIR/same-crate-name.rs:49:20 +error[E0277]: the trait bound `ImplementsWrongTraitConditionally: crate_a1::Bar` is not satisfied + --> $DIR/same-crate-name.rs:45:20 | LL | a::try_foo(other_variant_implements_mismatched_trait); - | ---------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `main::a::Bar` is not implemented for `ImplementsWrongTraitConditionally` + | ---------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `crate_a1::Bar` is not implemented for `ImplementsWrongTraitConditionally` | | | required by a bound introduced by this call | -note: there are multiple different versions of crate `crate_a1` in the dependency graph - --> $DIR/auxiliary/crate_a1.rs:1:1 - | -LL | pub trait Bar {} - | ^^^^^^^^^^^^^ this is the expected trait - | - ::: $DIR/auxiliary/crate_a2.rs:3:1 - | -LL | pub trait Bar {} - | ------------- this is the found trait - = help: you can use `cargo tree` to explore your dependency tree -help: the trait `main::a::Bar` is implemented for `ImplementsTraitForUsize` + = note: `ImplementsWrongTraitConditionally` implements similarly named trait `crate_a2::Bar`, but not `crate_a1::Bar` +help: the trait `crate_a1::Bar` is implemented for `ImplementsTraitForUsize` --> $DIR/auxiliary/crate_a1.rs:9:1 | LL | impl Bar for ImplementsTraitForUsize {} @@ -88,26 +57,15 @@ note: required by a bound in `try_foo` LL | pub fn try_foo(x: impl Bar) {} | ^^^ required by this bound in `try_foo` -error[E0277]: the trait bound `ImplementsTraitForUsize: main::a::Bar` is not satisfied - --> $DIR/same-crate-name.rs:57:20 +error[E0277]: the trait bound `ImplementsTraitForUsize: crate_a1::Bar` is not satisfied + --> $DIR/same-crate-name.rs:51:20 | LL | a::try_foo(other_variant_implements_correct_trait); - | ---------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `main::a::Bar` is not implemented for `ImplementsTraitForUsize` + | ---------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `crate_a1::Bar` is not implemented for `ImplementsTraitForUsize` | | | required by a bound introduced by this call | -note: there are multiple different versions of crate `crate_a1` in the dependency graph - --> $DIR/auxiliary/crate_a1.rs:1:1 - | -LL | pub trait Bar {} - | ^^^^^^^^^^^^^ this is the expected trait - | - ::: $DIR/auxiliary/crate_a2.rs:3:1 - | -LL | pub trait Bar {} - | ------------- this is the trait that was imported - = help: you can use `cargo tree` to explore your dependency tree -help: the trait `main::a::Bar` is implemented for `ImplementsTraitForUsize` +help: the trait `crate_a1::Bar` is implemented for `ImplementsTraitForUsize` --> $DIR/auxiliary/crate_a1.rs:9:1 | LL | impl Bar for ImplementsTraitForUsize {} diff --git a/tests/ui/type/type-mismatch-same-crate-name.rs b/tests/ui/type/type-mismatch-same-crate-name.rs index df6d7aba8fd69..31fd47251660e 100644 --- a/tests/ui/type/type-mismatch-same-crate-name.rs +++ b/tests/ui/type/type-mismatch-same-crate-name.rs @@ -16,17 +16,18 @@ fn main() { extern crate crate_a1 as a; a::try_foo(foo2); //~^ ERROR mismatched types - //~| NOTE expected `main::a::Foo`, found a different `main::a::Foo` + //~| NOTE expected `crate_a1::Foo`, found `crate_a2::Foo` //~| NOTE arguments to this function are incorrect - //~| NOTE there are multiple different versions of crate `crate_a1` in the dependency graph + //~| NOTE `crate_a2::Foo` and `crate_a1::Foo` have similar names, but are actually distinct types + //~| NOTE `crate_a2::Foo` is defined in crate `crate_a2` + //~| NOTE `crate_a1::Foo` is defined in crate `crate_a1` //~| NOTE function defined here - //~| HELP you can use `cargo tree` to explore your dependency tree a::try_bar(bar2); //~^ ERROR mismatched types - //~| NOTE expected trait `main::a::Bar`, found a different trait `main::a::Bar` + //~| NOTE expected trait `crate_a1::Bar`, found trait `crate_a2::Bar` //~| NOTE arguments to this function are incorrect - //~| NOTE there are multiple different versions of crate `crate_a1` in the dependency graph + //~| NOTE expected struct `Box<(dyn crate_a1::Bar + 'static)>` + //~| NOTE found struct `Box` //~| NOTE function defined here - //~| HELP you can use `cargo tree` to explore your dependency tree } } diff --git a/tests/ui/type/type-mismatch-same-crate-name.stderr b/tests/ui/type/type-mismatch-same-crate-name.stderr index 2913b994e9529..71648741c2dd5 100644 --- a/tests/ui/type/type-mismatch-same-crate-name.stderr +++ b/tests/ui/type/type-mismatch-same-crate-name.stderr @@ -2,21 +2,21 @@ error[E0308]: mismatched types --> $DIR/type-mismatch-same-crate-name.rs:17:20 | LL | a::try_foo(foo2); - | ---------- ^^^^ expected `main::a::Foo`, found a different `main::a::Foo` + | ---------- ^^^^ expected `crate_a1::Foo`, found `crate_a2::Foo` | | | arguments to this function are incorrect | -note: there are multiple different versions of crate `crate_a1` in the dependency graph - --> $DIR/auxiliary/crate_a1.rs:1:1 + = note: `crate_a2::Foo` and `crate_a1::Foo` have similar names, but are actually distinct types +note: `crate_a2::Foo` is defined in crate `crate_a2` + --> $DIR/auxiliary/crate_a2.rs:1:1 | LL | pub struct Foo; - | ^^^^^^^^^^^^^^ this is the expected type - | - ::: $DIR/auxiliary/crate_a2.rs:1:1 + | ^^^^^^^^^^^^^^ +note: `crate_a1::Foo` is defined in crate `crate_a1` + --> $DIR/auxiliary/crate_a1.rs:1:1 | LL | pub struct Foo; - | -------------- this is the found type - = help: you can use `cargo tree` to explore your dependency tree + | ^^^^^^^^^^^^^^ note: function defined here --> $DIR/auxiliary/crate_a1.rs:10:8 | @@ -24,24 +24,15 @@ LL | pub fn try_foo(x: Foo){} | ^^^^^^^ error[E0308]: mismatched types - --> $DIR/type-mismatch-same-crate-name.rs:24:20 + --> $DIR/type-mismatch-same-crate-name.rs:25:20 | LL | a::try_bar(bar2); - | ---------- ^^^^ expected trait `main::a::Bar`, found a different trait `main::a::Bar` + | ---------- ^^^^ expected trait `crate_a1::Bar`, found trait `crate_a2::Bar` | | | arguments to this function are incorrect | -note: there are multiple different versions of crate `crate_a1` in the dependency graph - --> $DIR/auxiliary/crate_a1.rs:3:1 - | -LL | pub trait Bar {} - | ^^^^^^^^^^^^^ this is the expected trait - | - ::: $DIR/auxiliary/crate_a2.rs:3:1 - | -LL | pub trait Bar {} - | ------------- this is the found trait - = help: you can use `cargo tree` to explore your dependency tree + = note: expected struct `Box<(dyn crate_a1::Bar + 'static)>` + found struct `Box` note: function defined here --> $DIR/auxiliary/crate_a1.rs:11:8 | From 26b5f90dd7f716ffbba11ffc7b04415b5a72b342 Mon Sep 17 00:00:00 2001 From: EFanZh Date: Mon, 28 Sep 2026 16:41:56 +0800 Subject: [PATCH 19/30] Move `alloc::rc` into `alloc::rcs` --- library/alloc/src/lib.rs | 12 ++++++++++-- library/alloc/src/{ => rcs}/rc.rs | 0 src/etc/natvis/liballoc.natvis | 8 ++++---- src/tools/miri/tests/fail/memleak_rc.stderr | 4 ++-- tests/debuginfo/rc_arc.rs | 12 ++++++------ tests/debuginfo/strings-and-strs.rs | 6 +++--- .../methods/shadowed-intrinsic-method-deref.stderr | 2 +- 7 files changed, 26 insertions(+), 18 deletions(-) rename library/alloc/src/{ => rcs}/rc.rs (100%) diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index de6830a514656..60ada2ba866bb 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -230,12 +230,22 @@ // from other crates, but since this can only appear for lang items, it doesn't seem worth fixing. #![feature(intra_doc_pointers)] +#[cfg(not(no_rc))] +#[stable(feature = "rust1", since = "1.0.0")] +pub use rcs::rc; + // Module with internal macros used by other modules (needs to be included before other modules). #[macro_use] mod macros; mod raw_vec; +/// Implementations of reference-counted pointers. +#[cfg(not(no_rc))] +mod rcs { + pub mod rc; +} + // Heaps provided for low-level allocation strategies pub mod alloc; @@ -256,8 +266,6 @@ pub mod intrinsics; #[unstable(feature = "alloc_io", issue = "154046")] pub mod io; pub mod panicking; -#[cfg(not(no_rc))] -pub mod rc; pub mod slice; pub mod str; pub mod string; diff --git a/library/alloc/src/rc.rs b/library/alloc/src/rcs/rc.rs similarity index 100% rename from library/alloc/src/rc.rs rename to library/alloc/src/rcs/rc.rs diff --git a/src/etc/natvis/liballoc.natvis b/src/etc/natvis/liballoc.natvis index b56f6f5800eee..cfcca9014e70c 100644 --- a/src/etc/natvis/liballoc.natvis +++ b/src/etc/natvis/liballoc.natvis @@ -72,7 +72,7 @@ it is necessary for them. --> - + {ptr.pointer->value} @@ -87,7 +87,7 @@ - + {{ len={ptr.pointer.length} }} ptr.pointer.length @@ -102,7 +102,7 @@ - + {ptr.pointer->value} @@ -117,7 +117,7 @@ - + {{ len={ptr.pointer.length} }} ptr.pointer.length diff --git a/src/tools/miri/tests/fail/memleak_rc.stderr b/src/tools/miri/tests/fail/memleak_rc.stderr index a77cf55b0c317..26156050ae5d3 100644 --- a/src/tools/miri/tests/fail/memleak_rc.stderr +++ b/src/tools/miri/tests/fail/memleak_rc.stderr @@ -1,5 +1,5 @@ error: memory leaked: ALLOC (Rust heap, SIZE, ALIGN), allocated here: - --> RUSTLIB/alloc/src/rc.rs:LL:CC + --> RUSTLIB/alloc/src/rcs/rc.rs:LL:CC | LL | Self::from_inner(Box::into_non_null(Box::new(RcInner { | _________________________________________________^ @@ -11,7 +11,7 @@ LL | | }))) | = note: stack backtrace: 0: std::rc::Rc::>>::new - at RUSTLIB/alloc/src/rc.rs:LL:CC + at RUSTLIB/alloc/src/rcs/rc.rs:LL:CC 1: main at tests/fail/memleak_rc.rs:LL:CC diff --git a/tests/debuginfo/rc_arc.rs b/tests/debuginfo/rc_arc.rs index 4399dfe54e7a5..39b5dc0d84408 100644 --- a/tests/debuginfo/rc_arc.rs +++ b/tests/debuginfo/rc_arc.rs @@ -27,12 +27,12 @@ //@ cdb-command:g //@ cdb-command:dx rc,d -//@ cdb-check:rc,d : 111 [Type: alloc::rc::Rc] +//@ cdb-check:rc,d : 111 [Type: alloc::rcs::rc::Rc] //@ cdb-check: [Reference count] : 11 [Type: core::cell::Cell] //@ cdb-check: [Weak reference count] : 2 [Type: core::cell::Cell] //@ cdb-command:dx weak_rc,d -//@ cdb-check:weak_rc,d : 111 [Type: alloc::rc::Weak] +//@ cdb-check:weak_rc,d : 111 [Type: alloc::rcs::rc::Weak] //@ cdb-check: [Reference count] : 11 [Type: core::cell::Cell] //@ cdb-check: [Weak reference count] : 2 [Type: core::cell::Cell] @@ -47,17 +47,17 @@ //@ cdb-check: [Weak reference count] : 2 [Type: core::sync::atomic::Atomic] //@ cdb-command:dx dyn_rc,d -//@ cdb-check:dyn_rc,d [Type: alloc::rc::Rc,alloc::alloc::Global>] +//@ cdb-check:dyn_rc,d [Type: alloc::rcs::rc::Rc,alloc::alloc::Global>] //@ cdb-check: [Reference count] : 31 [Type: core::cell::Cell] //@ cdb-check: [Weak reference count] : 2 [Type: core::cell::Cell] //@ cdb-command:dx dyn_rc_weak,d -//@ cdb-check:dyn_rc_weak,d [Type: alloc::rc::Weak,alloc::alloc::Global>] +//@ cdb-check:dyn_rc_weak,d [Type: alloc::rcs::rc::Weak,alloc::alloc::Global>] //@ cdb-check: [Reference count] : 31 [Type: core::cell::Cell] //@ cdb-check: [Weak reference count] : 2 [Type: core::cell::Cell] //@ cdb-command:dx slice_rc,d -//@ cdb-check:slice_rc,d : { len=3 } [Type: alloc::rc::Rc,alloc::alloc::Global>] +//@ cdb-check:slice_rc,d : { len=3 } [Type: alloc::rcs::rc::Rc,alloc::alloc::Global>] //@ cdb-check: [Length] : 3 [Type: [...]] //@ cdb-check: [Reference count] : 41 [Type: core::cell::Cell] //@ cdb-check: [Weak reference count] : 2 [Type: core::cell::Cell] @@ -66,7 +66,7 @@ //@ cdb-check: [2] : 3 [Type: u32] //@ cdb-command:dx slice_rc_weak,d -//@ cdb-check:slice_rc_weak,d : { len=3 } [Type: alloc::rc::Weak,alloc::alloc::Global>] +//@ cdb-check:slice_rc_weak,d : { len=3 } [Type: alloc::rcs::rc::Weak,alloc::alloc::Global>] //@ cdb-check: [Length] : 3 [Type: [...]] //@ cdb-check: [Reference count] : 41 [Type: core::cell::Cell] //@ cdb-check: [Weak reference count] : 2 [Type: core::cell::Cell] diff --git a/tests/debuginfo/strings-and-strs.rs b/tests/debuginfo/strings-and-strs.rs index 81e3037f1e566..56e9f6a5b9773 100644 --- a/tests/debuginfo/strings-and-strs.rs +++ b/tests/debuginfo/strings-and-strs.rs @@ -20,13 +20,13 @@ //@ gdb-check:$4 = ("Hello", "World") //@ gdb-command:print str_in_rc -//@ gdb-check:$5 = alloc::rc::Rc<&str, alloc::alloc::Global> {ptr: core::ptr::non_null::NonNull> {pointer: 0x[...]}, phantom: core::marker::PhantomData>, alloc: alloc::alloc::Global} +//@ gdb-check:$5 = alloc::rcs::rc::Rc<&str, alloc::alloc::Global> {ptr: core::ptr::non_null::NonNull> {pointer: 0x[...]}, phantom: core::marker::PhantomData>, alloc: alloc::alloc::Global} //@ gdb-command:print box_str //@ gdb-check:$6 = alloc::boxed::Box [87, 111, 114, 108, 100] //@ gdb-command:print rc_str -//@ gdb-check:$7 = alloc::rc::Rc {ptr: core::ptr::non_null::NonNull> {pointer: alloc::rc::RcInner {strong: core::cell::Cell {value: core::cell::UnsafeCell {value: 1}}, weak: core::cell::Cell {value: core::cell::UnsafeCell {value: 1}}, value: 0x[...]}}, phantom: core::marker::PhantomData>, alloc: alloc::alloc::Global} +//@ gdb-check:$7 = alloc::rcs::rc::Rc {ptr: core::ptr::non_null::NonNull> {pointer: alloc::rcs::rc::RcInner {strong: core::cell::Cell {value: core::cell::UnsafeCell {value: 1}}, weak: core::cell::Cell {value: core::cell::UnsafeCell {value: 1}}, value: 0x[...]}}, phantom: core::marker::PhantomData>, alloc: alloc::alloc::Global} // === LLDB TESTS ================================================================================== //@ lldb-command:run @@ -56,7 +56,7 @@ // lldb-command:v rc_str // ignore-tidy-linelength -// lldb-check:(alloc::rc::Rc) rc_str = strong=1, weak=1 { value = "World" } +// lldb-check:(alloc::rcs::rc::Rc) rc_str = strong=1, weak=1 { value = "World" } #![allow(unused_variables)] diff --git a/tests/ui/methods/shadowed-intrinsic-method-deref.stderr b/tests/ui/methods/shadowed-intrinsic-method-deref.stderr index 4e0c5dfc55f09..d4dccd6dc5fdb 100644 --- a/tests/ui/methods/shadowed-intrinsic-method-deref.stderr +++ b/tests/ui/methods/shadowed-intrinsic-method-deref.stderr @@ -6,7 +6,7 @@ LL | let sb : &S = &s.borrow(); | help: the trait `Borrow` is not implemented for `Rc>` but trait `Borrow>` is implemented for it - --> $SRC_DIR/alloc/src/rc.rs:LL:COL + --> $SRC_DIR/alloc/src/rcs/rc.rs:LL:COL = help: for that trait implementation, expected `RefCell`, found `S` = note: there's an inherent method on `RefCell` of the same name, which can be auto-dereferenced from `&RefCell` help: to access the inherent method on `RefCell`, use the fully-qualified path From f74f6ea68cb07dc75af48fffc1efc41b80c411d1 Mon Sep 17 00:00:00 2001 From: EFanZh Date: Mon, 28 Sep 2026 16:41:59 +0800 Subject: [PATCH 20/30] Rename `library/alloc/src/sync.rs` to `library/alloc/src/rcs/arc.rs` temporarily to keep file history --- library/alloc/src/{sync.rs => rcs/arc.rs} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename library/alloc/src/{sync.rs => rcs/arc.rs} (100%) diff --git a/library/alloc/src/sync.rs b/library/alloc/src/rcs/arc.rs similarity index 100% rename from library/alloc/src/sync.rs rename to library/alloc/src/rcs/arc.rs From cba32372c7b057d58d8d9f4d3fd17848d83d2b93 Mon Sep 17 00:00:00 2001 From: EFanZh Date: Mon, 28 Sep 2026 16:41:59 +0800 Subject: [PATCH 21/30] Re-export `alloc::rcs::arc::*` in `alloc::sync` --- library/alloc/src/lib.rs | 3 +++ library/alloc/src/rcs/arc.rs | 10 ---------- library/alloc/src/sync.rs | 14 ++++++++++++++ src/etc/natvis/liballoc.natvis | 8 ++++---- tests/codegen-llvm/debuginfo-cyclic-structure.rs | 2 +- tests/codegen-llvm/issues/issue-111603.rs | 8 ++++---- tests/debuginfo/rc_arc.rs | 12 ++++++------ tests/debuginfo/thread.rs | 2 +- 8 files changed, 33 insertions(+), 26 deletions(-) create mode 100644 library/alloc/src/sync.rs diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index 60ada2ba866bb..b1aa1a9a4f525 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -244,6 +244,9 @@ mod raw_vec; #[cfg(not(no_rc))] mod rcs { pub mod rc; + + #[cfg(all(not(no_sync), target_has_atomic = "ptr"))] + pub(crate) mod arc; } // Heaps provided for low-level allocation strategies diff --git a/library/alloc/src/rcs/arc.rs b/library/alloc/src/rcs/arc.rs index e412cee62d8f6..e61ea5193eccb 100644 --- a/library/alloc/src/rcs/arc.rs +++ b/library/alloc/src/rcs/arc.rs @@ -1,13 +1,3 @@ -#![stable(feature = "rust1", since = "1.0.0")] - -//! Thread-safe reference-counting pointers. -//! -//! See the [`Arc`][Arc] documentation for more details. -//! -//! **Note**: This module is only available on platforms that support atomic -//! loads and stores of pointers. This may be detected at compile time using -//! `#[cfg(target_has_atomic = "ptr")]`. - use core::any::Any; use core::cell::CloneFromCell; #[cfg(not(no_global_oom_handling))] diff --git a/library/alloc/src/sync.rs b/library/alloc/src/sync.rs new file mode 100644 index 0000000000000..afb1848d21a24 --- /dev/null +++ b/library/alloc/src/sync.rs @@ -0,0 +1,14 @@ +#![stable(feature = "rust1", since = "1.0.0")] + +//! Thread-safe reference-counting pointers. +//! +//! See the [`Arc`][Arc] documentation for more details. +//! +//! **Note**: This module is only available on platforms that support atomic +//! loads and stores of pointers. This may be detected at compile time using +//! `#[cfg(target_has_atomic = "ptr")]`. + +#[unstable(feature = "unique_rc_arc", issue = "112566")] +pub use crate::rcs::arc::UniqueArc; +#[stable(feature = "rust1", since = "1.0.0")] +pub use crate::rcs::arc::{Arc, Weak}; diff --git a/src/etc/natvis/liballoc.natvis b/src/etc/natvis/liballoc.natvis index cfcca9014e70c..c8e143b49911b 100644 --- a/src/etc/natvis/liballoc.natvis +++ b/src/etc/natvis/liballoc.natvis @@ -131,7 +131,7 @@ - + {ptr.pointer->data} @@ -146,7 +146,7 @@ - + {{ len={ptr.pointer.length} }} ptr.pointer.length @@ -160,7 +160,7 @@ - + {ptr.pointer->data} @@ -175,7 +175,7 @@ - + {{ len={ptr.pointer.length} }} ptr.pointer.length diff --git a/tests/codegen-llvm/debuginfo-cyclic-structure.rs b/tests/codegen-llvm/debuginfo-cyclic-structure.rs index b8cc544774158..dc7193dd08ed2 100644 --- a/tests/codegen-llvm/debuginfo-cyclic-structure.rs +++ b/tests/codegen-llvm/debuginfo-cyclic-structure.rs @@ -3,7 +3,7 @@ // Check that debug information exists for structures containing loops (cyclic references). // Previously it may incorrectly prune member information during recursive type inference check. -// CHECK: !DICompositeType(tag: DW_TAG_structure_type, name: "Arc Arc<[u64]> { // CHECK-LABEL: @new_uninit #[no_mangle] pub fn new_uninit(x: u64) -> Arc<[u64; 1000]> { - // CHECK: call alloc::sync::arcinner_layout_for_value_layout - // CHECK-NOT: call alloc::sync::arcinner_layout_for_value_layout + // CHECK: call alloc::rcs::arc::arcinner_layout_for_value_layout + // CHECK-NOT: call alloc::rcs::arc::arcinner_layout_for_value_layout let mut arc = Arc::new_uninit(); unsafe { Arc::get_mut_unchecked(&mut arc) }.write([x; 1000]); unsafe { arc.assume_init() } @@ -30,8 +30,8 @@ pub fn new_uninit(x: u64) -> Arc<[u64; 1000]> { // CHECK-LABEL: @new_uninit_slice #[no_mangle] pub fn new_uninit_slice(x: u64) -> Arc<[u64]> { - // CHECK: call alloc::sync::arcinner_layout_for_value_layout - // CHECK-NOT: call alloc::sync::arcinner_layout_for_value_layout + // CHECK: call alloc::rcs::arc::arcinner_layout_for_value_layout + // CHECK-NOT: call alloc::rcs::arc::arcinner_layout_for_value_layout let mut arc = Arc::new_uninit_slice(1000); for elem in unsafe { Arc::get_mut_unchecked(&mut arc) } { elem.write(x); diff --git a/tests/debuginfo/rc_arc.rs b/tests/debuginfo/rc_arc.rs index 39b5dc0d84408..adeb96dba1b80 100644 --- a/tests/debuginfo/rc_arc.rs +++ b/tests/debuginfo/rc_arc.rs @@ -37,12 +37,12 @@ //@ cdb-check: [Weak reference count] : 2 [Type: core::cell::Cell] //@ cdb-command:dx arc,d -//@ cdb-check:arc,d : 222 [Type: alloc::sync::Arc] +//@ cdb-check:arc,d : 222 [Type: alloc::rcs::arc::Arc] //@ cdb-check: [Reference count] : 21 [Type: core::sync::atomic::Atomic] //@ cdb-check: [Weak reference count] : 2 [Type: core::sync::atomic::Atomic] //@ cdb-command:dx weak_arc,d -//@ cdb-check:weak_arc,d : 222 [Type: alloc::sync::Weak] +//@ cdb-check:weak_arc,d : 222 [Type: alloc::rcs::arc::Weak] //@ cdb-check: [Reference count] : 21 [Type: core::sync::atomic::Atomic] //@ cdb-check: [Weak reference count] : 2 [Type: core::sync::atomic::Atomic] @@ -75,17 +75,17 @@ //@ cdb-check: [2] : 3 [Type: u32] //@ cdb-command:dx dyn_arc,d -//@ cdb-check:dyn_arc,d [Type: alloc::sync::Arc,alloc::alloc::Global>] +//@ cdb-check:dyn_arc,d [Type: alloc::rcs::arc::Arc,alloc::alloc::Global>] //@ cdb-check: [Reference count] : 51 [Type: core::sync::atomic::Atomic] //@ cdb-check: [Weak reference count] : 2 [Type: core::sync::atomic::Atomic] //@ cdb-command:dx dyn_arc_weak,d -//@ cdb-check:dyn_arc_weak,d [Type: alloc::sync::Weak,alloc::alloc::Global>] +//@ cdb-check:dyn_arc_weak,d [Type: alloc::rcs::arc::Weak,alloc::alloc::Global>] //@ cdb-check: [Reference count] : 51 [Type: core::sync::atomic::Atomic] //@ cdb-check: [Weak reference count] : 2 [Type: core::sync::atomic::Atomic] //@ cdb-command:dx slice_arc,d -//@ cdb-check:slice_arc,d : { len=3 } [Type: alloc::sync::Arc,alloc::alloc::Global>] +//@ cdb-check:slice_arc,d : { len=3 } [Type: alloc::rcs::arc::Arc,alloc::alloc::Global>] //@ cdb-check: [Length] : 3 [Type: [...]] //@ cdb-check: [Reference count] : 61 [Type: core::sync::atomic::Atomic] //@ cdb-check: [Weak reference count] : 2 [Type: core::sync::atomic::Atomic] @@ -94,7 +94,7 @@ //@ cdb-check: [2] : 6 [Type: u32] //@ cdb-command:dx slice_arc_weak,d -//@ cdb-check:slice_arc_weak,d : { len=3 } [Type: alloc::sync::Weak,alloc::alloc::Global>] +//@ cdb-check:slice_arc_weak,d : { len=3 } [Type: alloc::rcs::arc::Weak,alloc::alloc::Global>] //@ cdb-check: [Length] : 3 [Type: [...]] //@ cdb-check: [Reference count] : 61 [Type: core::sync::atomic::Atomic] //@ cdb-check: [Weak reference count] : 2 [Type: core::sync::atomic::Atomic] diff --git a/tests/debuginfo/thread.rs b/tests/debuginfo/thread.rs index d78dad406ac6e..ee33f921cc033 100644 --- a/tests/debuginfo/thread.rs +++ b/tests/debuginfo/thread.rs @@ -14,7 +14,7 @@ // //@ cdb-command:dx t,d //@ cdb-check:t,d : [...] [Type: std::thread::thread::Thread *] -//@ cdb-check:[...] inner [...][Type: core::pin::Pin >] +//@ cdb-check:[...] inner [...][Type: core::pin::Pin >] use std::thread; From 2e58a77da46625e9233cfac7b5cc758c4db29a18 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Wed, 16 Sep 2026 20:34:55 +0200 Subject: [PATCH 22/30] Fix intra doc link resolution when a doc comment is composed of both inner and outer doc comment --- compiler/rustc_resolve/src/late.rs | 34 +++++++++--- compiler/rustc_resolve/src/rustdoc.rs | 53 +++++++++++-------- compiler/rustc_resolve/src/rustdoc/tests.rs | 3 ++ src/librustdoc/clean/types.rs | 12 +++-- src/librustdoc/clean/types/tests.rs | 1 + .../passes/collect_intra_doc_links.rs | 48 ++++++++++++----- .../passes/lint/redundant_explicit_links.rs | 9 ++-- .../intra-doc/mixed-inner-outer.rs | 22 ++++++++ 8 files changed, 132 insertions(+), 50 deletions(-) create mode 100644 tests/rustdoc-html/intra-doc/mixed-inner-outer.rs diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index e1787eadf5fb9..4050ec8eee5d5 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -12,6 +12,7 @@ use std::debug_assert_matches; use std::mem::{replace, swap, take}; use std::ops::{ControlFlow, Range}; +use rustc_ast::attr::AttributeExt; use rustc_ast::visit::{ AssocCtxt, BoundKind, FnCtxt, FnKind, Visitor, try_visit, visit_opt, walk_list, }; @@ -2860,10 +2861,22 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { } fn resolve_item(&mut self, item: &'ast Item) { - let mod_inner_docs = - matches!(item.kind, ItemKind::Mod(..)) && rustdoc::inner_docs(&item.attrs); - if !mod_inner_docs && !matches!(item.kind, ItemKind::Impl(..) | ItemKind::Use(..)) { - self.resolve_doc_links(&item.attrs, MaybeExported::Ok(item.id)); + match item.kind { + ItemKind::Mod(..) => { + // We only handle outer doc comments for modules here. + let attrs = if let Some(pos) = item.attrs.iter().position(|a| { + a.doc_resolution_scope().is_some_and(|style| style == AttrStyle::Inner) + }) { + &item.attrs[..pos] + } else { + &item.attrs + }; + self.resolve_doc_links(attrs, MaybeExported::Ok(item.id)); + } + ItemKind::Impl(..) | ItemKind::Use(..) => {} + _ => { + self.resolve_doc_links(&item.attrs, MaybeExported::Ok(item.id)); + } } debug!("(resolving item) resolving {:?} ({:?})", item.kind.ident(), item.kind); @@ -2957,9 +2970,16 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { let orig_module = replace(&mut self.parent_scope.module, module); self.with_rib(ValueNS, RibKind::Module(module.expect_local()), |this| { this.with_rib(TypeNS, RibKind::Module(module.expect_local()), |this| { - if mod_inner_docs { - this.resolve_doc_links(&item.attrs, MaybeExported::Ok(item.id)); - } + // Outer doc comments were already handled above, now we handle + // inner doc comments. + let attrs = if let Some(pos) = item.attrs.iter().position(|a| { + a.doc_resolution_scope().is_some_and(|style| style == AttrStyle::Inner) + }) { + &item.attrs[pos..] + } else { + &[] + }; + this.resolve_doc_links(attrs, MaybeExported::Ok(item.id)); let old_macro_rules = this.parent_scope.macro_rules; visit::walk_item(this, item); // Maintain macro_rules scopes in the same way as during early resolution diff --git a/compiler/rustc_resolve/src/rustdoc.rs b/compiler/rustc_resolve/src/rustdoc.rs index ddf5600f76659..ac463297ad84d 100644 --- a/compiler/rustc_resolve/src/rustdoc.rs +++ b/compiler/rustc_resolve/src/rustdoc.rs @@ -9,9 +9,9 @@ use pulldown_cmark::{ }; use rustc_ast as ast; use rustc_ast::attr::AttributeExt; -use rustc_ast::join_path_syms; use rustc_ast::token::DocFragmentKind; use rustc_ast::util::comments::beautify_doc_string; +use rustc_ast::{AttrStyle, join_path_syms}; use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::unord::UnordSet; use rustc_middle::ty::TyCtxt; @@ -48,6 +48,7 @@ pub struct DocFragment { /// Because we tamper with the spans context, this information cannot be correctly retrieved /// later on. So instead, we compute it and store it here. pub from_expansion: bool, + pub style: AttrStyle, } #[derive(Clone, Copy, Debug)] @@ -215,8 +216,15 @@ pub fn attrs_to_doc_fragments<'a, A: AttributeExt + Clone + 'a>( (value_span.with_ctxt(attr_span.ctxt()), value_span.from_expansion()) } }; - let fragment = - DocFragment { span, doc, kind: fragment_kind, item_id, indent: 0, from_expansion }; + let fragment = DocFragment { + span, + doc, + kind: fragment_kind, + item_id, + indent: 0, + from_expansion, + style: attr.doc_resolution_scope().unwrap(), + }; doc_fragments.push(fragment); } else if !doc_only { other_attrs.push(attr.clone()); @@ -231,6 +239,14 @@ pub fn attrs_to_doc_fragments<'a, A: AttributeExt + Clone + 'a>( (doc_fragments, other_attrs) } +/// Represents a doc comment, split in two parts, outer and inner attributes. It's needed for +/// the intra-doc link resolution context. +#[derive(Default)] +pub struct DocStrings { + pub outer: String, + pub inner: String, +} + /// Return the doc-comments on this item, grouped by the module they came from. /// The module can be different if this is a re-export with added documentation. /// @@ -238,11 +254,16 @@ pub fn attrs_to_doc_fragments<'a, A: AttributeExt + Clone + 'a>( /// early and late doc link resolution regardless of their position. pub fn prepare_to_doc_link_resolution( doc_fragments: &[DocFragment], -) -> FxIndexMap, String> { - let mut res = FxIndexMap::default(); +) -> FxIndexMap, DocStrings> { + let mut res: FxIndexMap, DocStrings> = FxIndexMap::default(); for fragment in doc_fragments { - let out_str = res.entry(fragment.item_id).or_default(); - add_doc_fragment(out_str, fragment); + let out_strs = res.entry(fragment.item_id).or_default(); + let out = if fragment.style == AttrStyle::Inner { + &mut out_strs.inner + } else { + &mut out_strs.outer + }; + add_doc_fragment(out, fragment); } res } @@ -351,19 +372,6 @@ pub fn strip_generics_from_path(path_str: &str) -> Result, MalformedGen } } -/// Returns whether the first doc-comment is an inner attribute. -/// -/// If there are no doc-comments, return true. -/// FIXME(#78591): Support both inner and outer attributes on the same item. -pub fn inner_docs(attrs: &[impl AttributeExt]) -> bool { - for attr in attrs { - if let Some(attr_style) = attr.doc_resolution_scope() { - return attr_style == ast::AttrStyle::Inner; - } - } - true -} - /// Has `#[rustc_doc_primitive]` or `#[doc(keyword)]` or `#[doc(attribute)]`. pub fn has_primitive_or_keyword_or_attribute_docs(attrs: &[impl AttributeExt]) -> bool { for attr in attrs { @@ -413,7 +421,10 @@ pub(crate) fn attrs_to_preprocessed_links(attrs: &[ast::Attribute]) -> Vec) -> bool { + use rustc_ast::attr::AttributeExt; + self.item_id .as_def_id() .map(|did| { - inner_docs( - #[allow(deprecated)] - tcx.get_all_attrs(did), - ) + #[allow(deprecated)] + tcx.get_all_attrs(did).iter().any(|attr| { + attr.doc_resolution_scope().is_some_and(|style| style == ast::AttrStyle::Inner) + }) }) .unwrap_or(false) } diff --git a/src/librustdoc/clean/types/tests.rs b/src/librustdoc/clean/types/tests.rs index a0fc623c20c48..29a8fd5e2678e 100644 --- a/src/librustdoc/clean/types/tests.rs +++ b/src/librustdoc/clean/types/tests.rs @@ -12,6 +12,7 @@ fn create_doc_fragment(s: &str) -> Vec { kind: DocFragmentKind::Sugared(CommentKind::Line), indent: 0, from_expansion: false, + style: rustc_ast::AttrStyle::Outer, }] } diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs index d7815e64178a7..ac7cf2a42bd3b 100644 --- a/src/librustdoc/passes/collect_intra_doc_links.rs +++ b/src/librustdoc/passes/collect_intra_doc_links.rs @@ -21,8 +21,8 @@ use rustc_middle::ty; use rustc_middle::ty::{Ty, TyCtxt}; use rustc_resolve::rustdoc::pulldown_cmark::LinkType; use rustc_resolve::rustdoc::{ - MalformedGenerics, has_primitive_or_keyword_or_attribute_docs, prepare_to_doc_link_resolution, - source_span_for_markdown_range, strip_generics_from_path, + DocStrings, MalformedGenerics, has_primitive_or_keyword_or_attribute_docs, + prepare_to_doc_link_resolution, source_span_for_markdown_range, strip_generics_from_path, }; use rustc_span::def_id::ModId; use rustc_span::edit_distance::find_best_match_for_name; @@ -1091,14 +1091,7 @@ impl LinkCollector<'_, '_> { return; } - let mut try_insert_links = |item_id, doc: &str| { - if should_skip_link_resolution(item_id) { - return; - } - let module_id = match tcx.def_kind(item_id) { - DefKind::Mod if item.inner_docs(tcx) => ModId::new_unchecked(item_id), - _ => find_nearest_parent_module(tcx, item_id).unwrap(), - }; + let mut try_insert_links_inner = |item_id, module_id, doc: &str| { for md_link in preprocessed_markdown_links(&doc) { let link = self.resolve_link(&doc, item, item_id, module_id, &md_link); if let Some(link) = link { @@ -1112,16 +1105,45 @@ impl LinkCollector<'_, '_> { } }; + let mut try_insert_links = |item_id, docs: &DocStrings| { + if should_skip_link_resolution(item_id) { + return; + } + match tcx.def_kind(item_id) { + DefKind::Mod => { + if !docs.outer.is_empty() { + try_insert_links_inner( + item_id, + find_nearest_parent_module(tcx, item_id).unwrap(), + &docs.outer, + ); + } + if !docs.inner.is_empty() { + try_insert_links_inner(item_id, ModId::new_unchecked(item_id), &docs.inner); + } + } + _ => { + // It's the same handling for non-module items. + let module_id = find_nearest_parent_module(tcx, item_id).unwrap(); + try_insert_links_inner( + item_id, + module_id, + &format!("{}{}", docs.outer, docs.inner), + ); + } + } + }; + // We want to resolve in the lexical scope of the documentation. // In the presence of re-exports, this is not the same as the module of the item. // Rather than merging all documentation into one, resolve it one attribute at a time // so we know which module it came from. for (item_id, doc) in prepare_to_doc_link_resolution(&item.attrs.doc_strings) { - if !may_have_doc_links(&doc) { + if !may_have_doc_links(&doc.inner) && !may_have_doc_links(&doc.outer) { continue; } - debug!("combined_docs={doc}"); + debug!("combined_docs=outer: {} inner: {}", doc.outer, doc.inner); // NOTE: if there are links that start in one crate and end in another, this will not resolve them. // This is a degenerate case and it's not supported by rustdoc. let item_id = item_id.unwrap_or_else(|| item.item_id.expect_def_id()); @@ -1163,7 +1185,7 @@ impl LinkCollector<'_, '_> { } else { item.item_id.expect_def_id() }; - try_insert_links(item_id, note) + try_insert_links(item_id, &DocStrings { outer: note.to_owned(), inner: String::new() }); } } diff --git a/src/librustdoc/passes/lint/redundant_explicit_links.rs b/src/librustdoc/passes/lint/redundant_explicit_links.rs index 902054c917699..e02fbae3e0568 100644 --- a/src/librustdoc/passes/lint/redundant_explicit_links.rs +++ b/src/librustdoc/passes/lint/redundant_explicit_links.rs @@ -30,10 +30,11 @@ struct LinkData { pub(crate) fn visit_item(cx: &DocContext<'_>, item: &Item, hir_id: HirId) { let hunks = prepare_to_doc_link_resolution(&item.attrs.doc_strings); for (item_id, doc) in hunks { - if let Some(item_id) = item_id.or(item.def_id()) - && !doc.is_empty() - { - check_redundant_explicit_link_for_did(cx, item, item_id, hir_id, &doc); + if let Some(item_id) = item_id.or(item.def_id()) { + if !doc.outer.is_empty() || !doc.inner.is_empty() { + let doc = format!("{}{}", doc.outer, doc.inner); + check_redundant_explicit_link_for_did(cx, item, item_id, hir_id, &doc); + } } } } diff --git a/tests/rustdoc-html/intra-doc/mixed-inner-outer.rs b/tests/rustdoc-html/intra-doc/mixed-inner-outer.rs new file mode 100644 index 0000000000000..5a0f92522662d --- /dev/null +++ b/tests/rustdoc-html/intra-doc/mixed-inner-outer.rs @@ -0,0 +1,22 @@ +// This test ensures that when a documentation is composed of both inner and outer +// doc comments, the intra-doc link resolution still works as expected. +// Regression test for . +// Regression test for . +// Regression test for . + +#![crate_name = "foo"] +#![deny(rustdoc::broken_intra_doc_links)] + +//@ has foo/demo/index.html +//@ has - '//a[@href="../struct.Foo.html"]' 'Foo' +//@ has - '//a[@href="struct.DemoStruct.html"]' 'DemoStruct' + +/// Outer doc-comment [`Foo`]. +pub mod demo { + //! + //! Inner doc-comment with link: [`DemoStruct`] + + pub struct DemoStruct; +} + +pub struct Foo; From 9e8ca583f8115555cc39997e4468328c655879f3 Mon Sep 17 00:00:00 2001 From: khyperia <953151+khyperia@users.noreply.github.com> Date: Mon, 28 Sep 2026 15:50:47 +0200 Subject: [PATCH 23/30] implement #![feature(gca_adts)] --- Cargo.lock | 1 + compiler/rustc_ast_lowering/Cargo.toml | 1 + compiler/rustc_ast_lowering/src/lib.rs | 149 ++++++++++++------ compiler/rustc_ast_passes/src/feature_gate.rs | 59 ++++--- compiler/rustc_feature/src/lib.rs | 4 +- compiler/rustc_feature/src/unstable.rs | 34 +++- compiler/rustc_hir_analysis/src/collect.rs | 2 +- .../src/collect/generics_of.rs | 2 +- .../src/hir_ty_lowering/errors.rs | 2 +- .../src/hir_ty_lowering/mod.rs | 2 +- .../rustc_resolve/src/diagnostics/impls.rs | 25 +-- compiler/rustc_resolve/src/diagnostics/mod.rs | 4 +- compiler/rustc_resolve/src/ident.rs | 4 +- compiler/rustc_resolve/src/late.rs | 2 +- .../rustc_resolve/src/late/diagnostics.rs | 5 +- compiler/rustc_resolve/src/lib.rs | 2 +- compiler/rustc_span/src/symbol.rs | 1 + .../traits/fulfillment_errors.rs | 3 +- .../src/traits/const_evaluatable.rs | 2 +- .../rustc_trait_selection/src/traits/wf.rs | 2 +- compiler/rustc_type_ir/src/const_kind.rs | 3 - library/core/src/marker.rs | 9 +- library/std/src/lib.rs | 2 +- .../src/language-features/gca-adts.md | 21 +++ .../language-features/gca-min-const-items.md | 2 +- ...onst-pattern-field-type-mismatch-162394.rs | 13 +- ...-pattern-field-type-mismatch-162394.stderr | 2 +- .../const-generics/gca/gca-macro-fn-call.rs | 12 +- .../gca/gca-macro-fn-call.stderr | 10 +- .../gca_adts/missing-dependent-features.rs | 3 + .../missing-dependent-features.stderr | 10 ++ .../gca_adts/none-incomplete.rs | 21 +++ .../gca_adts/none-incomplete.stderr | 8 + tests/ui/const-generics/gca_adts/none.rs | 21 +++ .../inherent-type-const.rs | 5 +- .../mgca/adt_expr_arg_simple.rs | 3 +- .../mgca/adt_expr_arg_simple.stderr | 4 +- .../mgca/adt_expr_arg_tuple_expr_fail.rs | 6 +- .../mgca/adt_expr_erroneuous_inits.rs | 15 +- .../mgca/adt_expr_erroneuous_inits.stderr | 38 ++--- .../mgca/adt_expr_fields_type_check.rs | 3 +- .../mgca/adt_expr_fields_type_check.stderr | 14 +- .../mgca/adt_expr_infers_from_value.rs | 12 +- .../mgca/adt_expr_unit_enum_extra_field.rs | 8 +- .../adt_expr_unit_enum_extra_field.stderr | 6 +- .../mgca/adt_expr_unit_struct_extra_field.rs | 2 +- .../mgca/array-const-arg-len-mismatch.rs | 11 +- .../mgca/array-const-arg-len-mismatch.stderr | 20 +-- .../mgca/array-const-arg-type-mismatch.rs | 3 +- .../mgca/array-const-arg-type-mismatch.stderr | 2 +- .../mgca/array-expr-complex.r1.stderr | 2 +- .../mgca/array-expr-complex.r2.stderr | 2 +- .../mgca/array-expr-complex.r3.stderr | 2 +- .../const-generics/mgca/array-expr-complex.rs | 3 +- .../const-generics/mgca/array-expr-simple.rs | 3 +- ...array-expr-type-mismatch-in-where-bound.rs | 4 +- ...y-expr-type-mismatch-in-where-bound.stderr | 4 +- .../mgca/array-expr-with-assoc-const.rs | 3 +- .../mgca/array-expr-with-macro.rs | 3 +- .../mgca/array-expr-with-struct.rs | 3 +- .../mgca/array-expr-with-tuple.rs | 6 +- .../mgca/array-with-wrong-tuple-type.rs | 4 +- .../mgca/array_elem_type_mismatch.rs | 6 +- .../mgca/array_expr_arg_complex.rs | 3 +- .../mgca/array_expr_arg_complex.stderr | 4 +- ...eric-args-on-enum-variant-segments-fail.rs | 7 +- ...-args-on-enum-variant-segments-fail.stderr | 6 +- .../generic-args-on-enum-variant-segments.rs | 3 +- ...eneric_const_items-mismatched-array-len.rs | 10 +- ...ic_const_items-mismatched-array-len.stderr | 4 +- ...onst_parameter_types-inferred-array-len.rs | 2 +- .../mgca/generic_const_type_mismatch.rs | 8 +- .../mgca/invalid-array-in-tuple.rs | 4 +- ...ixed-direct-anon-expression-diagnostics.rs | 3 +- ...-direct-anon-expression-diagnostics.stderr | 4 +- .../mgca/nonsensical-negated-literal.rs | 4 +- .../printing_valtrees_supports_non_values.rs | 3 +- ...inting_valtrees_supports_non_values.stderr | 18 +-- .../mgca/size-of-generic-ptr-in-array-len.rs | 3 +- .../size-of-generic-ptr-in-array-len.stderr | 6 +- .../mgca/syntactic-type-mismatch.rs | 3 +- .../mgca/syntactic-type-mismatch.stderr | 4 +- .../mgca/tuple_ctor_arg_simple.rs | 3 +- .../mgca/tuple_ctor_complex_args.rs | 3 +- .../mgca/tuple_ctor_complex_args.stderr | 4 +- .../mgca/tuple_ctor_erroneous.rs | 3 +- .../mgca/tuple_ctor_erroneous.stderr | 22 +-- .../const-generics/mgca/tuple_ctor_nested.rs | 3 +- .../mgca/tuple_ctor_type_relative.rs | 3 +- .../mgca/tuple_expr_arg_bad-issue-151048.rs | 3 +- .../tuple_expr_arg_bad-issue-151048.stderr | 2 +- .../mgca/tuple_expr_arg_complex.rs | 3 +- .../mgca/tuple_expr_arg_complex.stderr | 8 +- .../mgca/tuple_expr_arg_macroless.rs | 6 +- .../mgca/tuple_expr_arg_mismatch_type.rs | 3 +- .../mgca/tuple_expr_arg_mismatch_type.stderr | 2 +- .../mgca/tuple_expr_arg_simple.rs | 3 +- .../tuple_expr_arg_unbounded_assoc_const.rs | 2 +- .../mgca/tuple_expr_type_mismatch.rs | 4 +- .../mgca/type_const-adt-expr-missing-field.rs | 12 +- .../type_const-adt-expr-missing-field.stderr | 6 +- ..._const-generic-param-in-type.nogate.stderr | 18 +-- .../mgca/type_const-generic-param-in-type.rs | 9 +- .../mgca/wrapped_array_elem_type_mismatch.rs | 2 +- .../mgca/wrong_type_const_arr_diag.rs | 3 +- .../mgca/wrong_type_const_arr_diag.stderr | 2 +- .../mgca/wrong_type_const_arr_diag_trait.rs | 3 +- .../wrong_type_const_arr_diag_trait.stderr | 2 +- .../tuple_with_infer_arg.rs | 2 +- .../ui/feature-gates/feature-gate-gca-adts.rs | 24 +++ .../feature-gate-gca-adts.without.stderr | 58 +++++++ ...ate-gca-adts.without_with_min_items.stderr | 14 ++ .../non_scalar_alignment_value.rs | 8 +- .../non_scalar_alignment_value.stderr | 8 +- ...struct-exprs-tuple-call-pretty-printing.rs | 3 +- ...ct-exprs-tuple-call-pretty-printing.stdout | 5 +- 116 files changed, 640 insertions(+), 355 deletions(-) create mode 100644 src/doc/unstable-book/src/language-features/gca-adts.md create mode 100644 tests/ui/const-generics/gca_adts/missing-dependent-features.rs create mode 100644 tests/ui/const-generics/gca_adts/missing-dependent-features.stderr create mode 100644 tests/ui/const-generics/gca_adts/none-incomplete.rs create mode 100644 tests/ui/const-generics/gca_adts/none-incomplete.stderr create mode 100644 tests/ui/const-generics/gca_adts/none.rs create mode 100644 tests/ui/feature-gates/feature-gate-gca-adts.rs create mode 100644 tests/ui/feature-gates/feature-gate-gca-adts.without.stderr create mode 100644 tests/ui/feature-gates/feature-gate-gca-adts.without_with_min_items.stderr diff --git a/Cargo.lock b/Cargo.lock index b5fb5057716ac..002ee75873ee5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3626,6 +3626,7 @@ dependencies = [ name = "rustc_ast_lowering" version = "0.0.0" dependencies = [ + "bitflags", "rustc_abi", "rustc_ast", "rustc_attr_ir", diff --git a/compiler/rustc_ast_lowering/Cargo.toml b/compiler/rustc_ast_lowering/Cargo.toml index 9dc5f81581e87..350687ba299c7 100644 --- a/compiler/rustc_ast_lowering/Cargo.toml +++ b/compiler/rustc_ast_lowering/Cargo.toml @@ -8,6 +8,7 @@ doctest = false [dependencies] # tidy-alphabetical-start +bitflags = "2.4.1" rustc_abi = { path = "../rustc_abi" } rustc_ast = { path = "../rustc_ast" } rustc_attr_ir = { path = "../rustc_attr_ir" } diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 4bc61a22b3b3d..eee6d7af5593d 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -56,7 +56,7 @@ use rustc_data_structures::steal::Steal; use rustc_data_structures::tagged_ptr::TaggedRef; use rustc_errors::codes::*; use rustc_errors::{DiagArgFromDisplay, DiagCtxtHandle, ErrorGuaranteed}; -use rustc_hir::def::{DefKind, Namespace, PerNS, Res}; +use rustc_hir::def::{CtorKind, DefKind, Namespace, PerNS, Res}; use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId, LocalDefIdMap}; use rustc_hir::definitions::PerParentDisambiguatorState; use rustc_hir::lints::DelayedLint; @@ -1420,10 +1420,10 @@ impl<'hir> LoweringContext<'_, 'hir> { let ct = self.arena.alloc(ct); return GenericArg::Const(ct.try_as_ambig_ct().unwrap()); } - TyKind::GcaMacro(expr) if self.tcx.features().gca_min_const_items() => { + TyKind::GcaMacro(expr) if self.tcx.features().gca() => { let ct = match self.can_lower_expr_to_const_arg_direct( expr, - DirectConstArgContext::MacrolessMinGenericConstArgs, + self.direct_const_arg_context_enabled_by_gca_macro(), ) { Ok(()) => self.lower_expr_to_const_arg_direct(expr, None), Err(e) => e.emit(self), @@ -2632,18 +2632,12 @@ impl<'hir> LoweringContext<'_, 'hir> { span: Span, ) -> hir::ConstItemRhs<'hir> { let is_direct = |body| { - if self.tcx.features().gca_macroless_items() { - self.can_lower_expr_to_const_arg_direct( - body, - DirectConstArgContext::MacrolessMinGenericConstArgs, - ) - .is_ok() + let context = if self.tcx.features().gca_macroless_items() { + self.direct_const_arg_context_enabled_by_gca_macro() } else { - // do not check can_lower_expr_to_const_arg_direct, but rather just - // ExprKind::GcaMacro, because we don't want e.g. - // `impl { const C: u8 = N; }` to be a direct-rhs const - matches!(body, Expr { kind: ExprKind::GcaMacro(_), .. }) - } + DirectConstArgContext::GCA_MACRO + }; + self.can_lower_expr_to_const_arg_direct(body, context).is_ok() }; if self.tcx.features().gca_min_const_items() && let Some(body) = body @@ -2657,13 +2651,25 @@ impl<'hir> LoweringContext<'_, 'hir> { } } + fn direct_const_arg_context_enabled_by_gca_macro(&self) -> DirectConstArgContext { + let mut result = DirectConstArgContext::GCA_BASE_FEATURES; + if self.tcx.features().gca_min_const_items() { + result |= DirectConstArgContext::GCA_MIN_CONST_ITEMS; + } + if self.tcx.features().gca_adts() { + result |= DirectConstArgContext::GCA_ADTS; + } + result + } + + /// Only valid for const *arg* contexts, not const *item* contexts. fn ambient_direct_const_arg_context(&self) -> DirectConstArgContext { if self.tcx.features().gca_macroless_args() { - DirectConstArgContext::MacrolessMinGenericConstArgs - } else if self.tcx.features().gca_min_const_items() { - DirectConstArgContext::MinGenericConstArgs + self.direct_const_arg_context_enabled_by_gca_macro() + } else if self.tcx.features().gca() { + DirectConstArgContext::GCA } else { - DirectConstArgContext::Stable + DirectConstArgContext::STABLE } } @@ -2675,9 +2681,16 @@ impl<'hir> LoweringContext<'_, 'hir> { res: Option>, context: DirectConstArgContext, ) -> Result<(), UnrepresentableConstArgError> { - if let DirectConstArgContext::MacrolessMinGenericConstArgs = context { + if context.contains(DirectConstArgContext::PATH_ANY) { + Ok(()) + } else if context.contains(DirectConstArgContext::PATH_CONST_CTOR) + && matches!(res, Some(Res::Def(DefKind::Ctor(_, CtorKind::Const), _))) + { + // FIXME(gca_adts): This check is incomplete. Type-relative paths and other complicating + // factors make things very difficult - e.g. `>::None` Ok(()) - } else if qself.is_none() + } else if context.contains(DirectConstArgContext::PATH_PLAIN_PARAM) + && qself.is_none() && path.is_single_argless_ident() && matches!(res, Some(Res::Def(DefKind::ConstParam, _))) { @@ -2693,59 +2706,61 @@ impl<'hir> LoweringContext<'_, 'hir> { expr: &Expr, context: DirectConstArgContext, ) -> Result<(), UnrepresentableConstArgError> { - use DirectConstArgContext::*; // Note the only stable case is currently ExprKind::Path - match (&expr.kind, context) { - ( - ExprKind::Call(Expr { kind: ExprKind::Path(_, _), .. }, args), - MacrolessMinGenericConstArgs, - ) => { + match &expr.kind { + ExprKind::Call(Expr { kind: ExprKind::Path(_, _), .. }, args) + if context.contains(DirectConstArgContext::TUPLE_CALL) => + { for arg in args { self.can_lower_expr_to_const_arg_direct(arg, context)?; } Ok(()) } - (ExprKind::Tup(exprs), MacrolessMinGenericConstArgs) => { + ExprKind::Tup(exprs) if context.contains(DirectConstArgContext::TUPLE) => { for expr in exprs { self.can_lower_expr_to_const_arg_direct(expr, context)?; } Ok(()) } - (ExprKind::Path(qself, path), _) => { + ExprKind::Path(qself, path) => { let res = self.get_partial_res(expr.id).and_then(|partial_res| partial_res.full_res()); self.can_lower_path_to_const_arg_direct(qself, path, expr.span, res, context) } - (ExprKind::Struct(se), MacrolessMinGenericConstArgs) => { + ExprKind::Struct(se) if context.contains(DirectConstArgContext::STRUCT) => { for f in &se.fields { self.can_lower_expr_to_const_arg_direct(&f.expr, context)?; } Ok(()) } - (ExprKind::Array(elements), MacrolessMinGenericConstArgs) => { + ExprKind::Array(elements) if context.contains(DirectConstArgContext::ARRAY) => { for element in elements { self.can_lower_expr_to_const_arg_direct(element, context)?; } Ok(()) } - (ExprKind::Underscore, MacrolessMinGenericConstArgs) => Ok(()), - (ExprKind::Paren(expr), MacrolessMinGenericConstArgs) => { + ExprKind::Underscore if context.contains(DirectConstArgContext::UNDERSCORE) => Ok(()), + ExprKind::Paren(expr) if context.contains(DirectConstArgContext::PAREN) => { self.can_lower_expr_to_const_arg_direct(expr, context) } - (ExprKind::Block(block, _), MacrolessMinGenericConstArgs) - if let [stmt] = block.stmts.as_slice() + ExprKind::Block(block, _) + if context.contains(DirectConstArgContext::BLOCK) + && let [stmt] = block.stmts.as_slice() && let StmtKind::Expr(expr) = &stmt.kind => { self.can_lower_expr_to_const_arg_direct(expr, context) } - (ExprKind::Lit(_), MacrolessMinGenericConstArgs) => Ok(()), - (ExprKind::Unary(UnOp::Neg, inner_expr), MacrolessMinGenericConstArgs) - if let ExprKind::Lit(_) = &inner_expr.kind => + ExprKind::Lit(_) if context.contains(DirectConstArgContext::LIT) => Ok(()), + ExprKind::Unary(UnOp::Neg, inner_expr) + if context.contains(DirectConstArgContext::LIT) + && let ExprKind::Lit(_) = &inner_expr.kind => { Ok(()) } - (ExprKind::ConstBlock(_), MacrolessMinGenericConstArgs) => Ok(()), - (ExprKind::GcaMacro(_), MacrolessMinGenericConstArgs | MinGenericConstArgs) => { + ExprKind::ConstBlock(_) if context.contains(DirectConstArgContext::CONST_BLOCK) => { + Ok(()) + } + ExprKind::GcaMacro(_) if context.contains(DirectConstArgContext::GCA_MACRO) => { // Always report this as able to be represented directly. If it turns out not to be, // `lower_expr_to_const_arg_direct` will report an error. Ok(()) @@ -2931,11 +2946,9 @@ impl<'hir> LoweringContext<'_, 'hir> { // `can_lower_expr_to_const_arg_direct` always returns success upon encountering a // ExprKind::GcaMacro, which effectively forces the expression to be lowered as a // direct arg. If it actually turns out to not be possible, emit an error instead. - // Always use MacrolessMinGenericConstArgs, even if we're under regular GCA, because - // that's what the macro means: to enter a context that is like macroless GCA. match self.can_lower_expr_to_const_arg_direct( expr, - DirectConstArgContext::MacrolessMinGenericConstArgs, + self.direct_const_arg_context_enabled_by_gca_macro(), ) { Ok(()) => self.lower_expr_to_const_arg_direct(expr, id_override), Err(err) => err.emit(self), @@ -3277,19 +3290,53 @@ impl<'hir> GenericArgsCtor<'hir> { } } -#[derive(Copy, Clone, Debug)] -enum DirectConstArgContext { +bitflags::bitflags! { + #[derive(Copy, Clone, Debug)] + struct DirectConstArgContext: u16 { + const TUPLE_CALL = 1 << 0; + const TUPLE = 1 << 1; + const PATH_PLAIN_PARAM = 1 << 2; + const PATH_CONST_CTOR = 1 << 3; + const PATH_ANY = 1 << 4; + const STRUCT = 1 << 5; + const ARRAY = 1 << 6; + const UNDERSCORE = 1 << 7; + const PAREN = 1 << 8; + const BLOCK = 1 << 9; + const LIT = 1 << 10; + const CONST_BLOCK = 1 << 11; + const GCA_MACRO = 1 << 12; + } +} + +impl DirectConstArgContext { /// The only allowed direct const arg representation is simple paths that nameres to generic /// const parameters. - Stable, + const STABLE: DirectConstArgContext = Self::PATH_PLAIN_PARAM; + /// The allowed representations are what is allowed on stable, plus the `gca!` macro. - MinGenericConstArgs, - /// Expressions attempt to be lowered directly, and if that fails, the expression falls back to - /// being represented as an anon const. + const GCA: DirectConstArgContext = Self::STABLE.union(Self::GCA_MACRO); + + /// These expressions are allowed inside the `gca!` macro, regardless of what feature is + /// enabling the base `gca!` functionality. /// - /// This context is also used under MinGenericConstArgs inside a `gca!` macro, for - /// simplicity, as they allow the same code. - MacrolessMinGenericConstArgs, + /// These are also allowed under macroless features without a `gca!` macro. + const GCA_BASE_FEATURES: DirectConstArgContext = Self::GCA + .union(DirectConstArgContext::UNDERSCORE) + .union(DirectConstArgContext::PAREN) + .union(DirectConstArgContext::BLOCK) + .union(DirectConstArgContext::LIT) + .union(DirectConstArgContext::CONST_BLOCK); + + /// These are allowed under `#![feature(gca_min_const_items)]` + const GCA_MIN_CONST_ITEMS: DirectConstArgContext = DirectConstArgContext::PATH_ANY; + + /// These are allowed under `#![feature(gca_adts)]` + const GCA_ADTS: DirectConstArgContext = DirectConstArgContext::PATH_CONST_CTOR + .union(DirectConstArgContext::TUPLE_CALL) + .union(DirectConstArgContext::TUPLE) + .union(DirectConstArgContext::STRUCT) + .union(DirectConstArgContext::ARRAY); } #[derive(Debug)] diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index 9d0ca67ce93d5..bbc80f6a5728d 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -3,7 +3,7 @@ use rustc_ast::{self as ast, AttrVec, GenericBound, NodeId, PatKind, attr, token use rustc_attr_ir::{Attribute, AttributeKind}; use rustc_attr_parsing::AttributeParser; use rustc_errors::msg; -use rustc_feature::Features; +use rustc_feature::{DependentFeature, Features}; use rustc_session::Session; use rustc_session::diagnostics::{feature_err, feature_warn}; use rustc_span::{Span, Spanned, sym}; @@ -651,26 +651,47 @@ fn check_incompatible_features(sess: &Session, features: &Features) { } fn check_dependent_features(sess: &Session, features: &Features) { - for &(parent, children) in - rustc_feature::DEPENDENT_FEATURES.iter().filter(|(parent, _)| features.enabled(*parent)) + for &(parent, ref children) in + rustc_feature::DEPENDENT_FEATURES.iter().filter(|(parent, children)| { + features.enabled(*parent) && !check_enabled(features, children) + }) { - if children.iter().any(|f| !features.enabled(*f)) { - let parent_span = features - .enabled_features_iter_stable_order() - .find_map(|(name, span)| (name == parent).then_some(span)) - .unwrap(); - // FIXME: should probably format this in fluent instead of here - let missing = children + let parent_span = features + .enabled_features_iter_stable_order() + .find_map(|(name, span)| (name == parent).then_some(span)) + .unwrap(); + // FIXME: should probably format this in fluent instead of here + let missing = format(features, &children); + sess.dcx().emit_err(diagnostics::MissingDependentFeatures { parent_span, parent, missing }); + } + + fn check_enabled(features: &Features, feature: &DependentFeature) -> bool { + match feature { + DependentFeature::And(children) => { + children.iter().all(|child| check_enabled(features, child)) + } + DependentFeature::Or(children) => { + children.iter().any(|child| check_enabled(features, child)) + } + DependentFeature::Leaf(symbol) => features.enabled(*symbol), + } + } + + fn format(features: &Features, feature: &DependentFeature) -> String { + // FIXME: parentheses/precedence + match feature { + DependentFeature::And(children) => children .iter() - .filter(|f| !features.enabled(**f)) - .map(|s| format!("`{}`", s.as_str())) - .intersperse(String::from(", ")) - .collect(); - sess.dcx().emit_err(diagnostics::MissingDependentFeatures { - parent_span, - parent, - missing, - }); + .filter(|child| !check_enabled(features, child)) + .map(|child| format(features, child)) + .intersperse(String::from(" and ")) + .collect(), + DependentFeature::Or(children) => children + .iter() + .map(|child| format(features, child)) + .intersperse(String::from(" or ")) + .collect(), + DependentFeature::Leaf(symbol) => format!("`{}`", symbol.as_str()), } } } diff --git a/compiler/rustc_feature/src/lib.rs b/compiler/rustc_feature/src/lib.rs index 5b9b899fc463f..83bf4f47a8ac9 100644 --- a/compiler/rustc_feature/src/lib.rs +++ b/compiler/rustc_feature/src/lib.rs @@ -133,6 +133,6 @@ pub use builtin_attrs::{ }; pub use removed::REMOVED_LANG_FEATURES; pub use unstable::{ - DEPENDENT_FEATURES, EnabledLangFeature, EnabledLibFeature, Features, INCOMPATIBLE_FEATURES, - TRACK_FEATURE, UNSTABLE_LANG_FEATURES, + DEPENDENT_FEATURES, DependentFeature, EnabledLangFeature, EnabledLibFeature, Features, + INCOMPATIBLE_FEATURES, TRACK_FEATURE, UNSTABLE_LANG_FEATURES, }; diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index 49489c0ed903d..71ab8250254f4 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -119,6 +119,13 @@ impl Features { false } } + + /// The generic_const_args family of features has a set of common behavior that can be enabled + /// by either `gca_min_const_items` or `gca_adts`. There is no actual `gca` base feature, but + /// this method acts as one. + pub fn gca(&self) -> bool { + self.gca_min_const_items() || self.gca_adts() + } } macro_rules! declare_features { @@ -546,6 +553,8 @@ declare_features! ( (internal, freeze_impls, "1.78.0", Some(121675)), /// Frontmatter `---` blocks for use by external tools. (unstable, frontmatter, "1.88.0", Some(136889)), + /// Allows using ADTs in directly represented generic const args. + (incomplete, gca_adts, "CURRENT_RUSTC_VERSION", Some(163420)), /// Allows using generics in more complex const expressions, based on definitional equality. (incomplete, gca_const_items, "1.95.0", Some(151972)), /// Allows directly represented gca_const_items without the `gca!` macro. @@ -857,10 +866,25 @@ pub const INCOMPATIBLE_FEATURES: &[(Symbol, Symbol)] = &[ (sym::ref_pat_eat_one_layer_2024, sym::ref_pat_eat_one_layer_2024_structural), ]; +pub enum DependentFeature { + And(&'static [DependentFeature]), + Or(&'static [DependentFeature]), + Leaf(Symbol), +} + /// Some features require one or more other features to be enabled. -pub const DEPENDENT_FEATURES: &[(Symbol, &[Symbol])] = &[ - (sym::gca_const_items, &[sym::gca_min_const_items]), - (sym::gca_macroless_args, &[sym::gca_min_const_items]), - (sym::gca_macroless_items, &[sym::gca_min_const_items]), - (sym::unsized_const_params, &[sym::adt_const_params]), +pub const DEPENDENT_FEATURES: &[(Symbol, DependentFeature)] = &[ + // tidy-alphabetical-start + ( + sym::gca_adts, + DependentFeature::Or(&[ + DependentFeature::Leaf(sym::min_adt_const_params), + DependentFeature::Leaf(sym::adt_const_params), + ]), + ), + (sym::gca_const_items, DependentFeature::Leaf(sym::gca_min_const_items)), + (sym::gca_macroless_args, DependentFeature::Leaf(sym::gca_min_const_items)), + (sym::gca_macroless_items, DependentFeature::Leaf(sym::gca_min_const_items)), + (sym::unsized_const_params, DependentFeature::Leaf(sym::adt_const_params)), + // tidy-alphabetical-end ]; diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs index c3cc3bcd6cd0c..9504fce939733 100644 --- a/compiler/rustc_hir_analysis/src/collect.rs +++ b/compiler/rustc_hir_analysis/src/collect.rs @@ -1803,7 +1803,7 @@ fn anon_const_kind<'tcx>(tcx: TyCtxt<'tcx>, def: LocalDefId) -> ty::AnonConstKin debug_assert_matches!(const_arg.kind, hir::ConstArgKind::Anon(hir::AnonConst { def_id, .. }) if *def_id == def); if tcx.features().generic_const_exprs() { ty::AnonConstKind::GCE - } else if tcx.features().gca_min_const_items() { + } else if tcx.features().gca() { ty::AnonConstKind::MCG } else if let hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Repeat(_, repeat_count), diff --git a/compiler/rustc_hir_analysis/src/collect/generics_of.rs b/compiler/rustc_hir_analysis/src/collect/generics_of.rs index 9605c777e55a9..4d43a9ca5700e 100644 --- a/compiler/rustc_hir_analysis/src/collect/generics_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/generics_of.rs @@ -224,7 +224,7 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics { // Node::AnonConst, or whether it will be represented directly, so it must generate a // DefId. If it ends up being direct, this DefId is then attached to the top-level // ConstArg, which is what we are seeing here. - debug_assert!(tcx.features().gca_min_const_items()); + debug_assert!(tcx.features().gca()); // Forward to the real parent. Some(tcx.local_parent(def_id)) } diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs index bee062b27e0f5..1538b3afaf236 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs @@ -370,7 +370,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { && let Some(hir_ty) = constraint.ty() && let ty = self.lower_ty(hir_ty) && (ty.is_enum() || ty.references_error()) - && tcx.features().gca_min_const_items() + && tcx.features().gca() { Some(diagnostics::AssocKindMismatchWrapInBracesSugg { lo: hir_ty.span.shrink_to_lo(), diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index e9dc2a61d698d..037996fc926b2 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -3120,7 +3120,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { }) } - /// `def_id` is a const item used in the type system. Checks if that's OK. + /// `alias_const` is a const item used in the type system. Checks if that's OK. fn check_const_item_in_type_system( &self, alias_const: ty::AliasConstKind<'tcx>, diff --git a/compiler/rustc_resolve/src/diagnostics/impls.rs b/compiler/rustc_resolve/src/diagnostics/impls.rs index 2ea2f52c696ba..bf1feb2ae5172 100644 --- a/compiler/rustc_resolve/src/diagnostics/impls.rs +++ b/compiler/rustc_resolve/src/diagnostics/impls.rs @@ -1327,18 +1327,19 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { enable_feature: self.tcx().sess.is_nightly_build(), }) } - ResolutionError::ParamInNonTrivialAnonConst { is_gca, name, param_kind: is_type } => { - self.dcx().create_err(diagnostics::ParamInNonTrivialAnonConst { - span, - name, - param_kind: is_type, - help: self.tcx.sess.is_nightly_build() - && !self.tcx.features().gca_min_const_items(), - is_gca, - help_gca: is_gca, - help_suggest_gca: self.tcx.sess.is_nightly_build() && !is_gca, - }) - } + ResolutionError::ParamInNonTrivialAnonConst { + is_gca_const_items, + name, + param_kind, + } => self.dcx().create_err(diagnostics::ParamInNonTrivialAnonConst { + span, + name, + param_kind, + help: self.tcx.sess.is_nightly_build() && !self.tcx.features().gca(), + is_gca_const_items, + help_gca: is_gca_const_items, + help_suggest_gca: self.tcx.sess.is_nightly_build() && !is_gca_const_items, + }), ResolutionError::ParamInEnumDiscriminant { name, param_kind: is_type } => { self.dcx().create_err(diagnostics::ParamInEnumDiscriminant { span, diff --git a/compiler/rustc_resolve/src/diagnostics/mod.rs b/compiler/rustc_resolve/src/diagnostics/mod.rs index ae4f7cabbe87f..c0e687fc94a38 100644 --- a/compiler/rustc_resolve/src/diagnostics/mod.rs +++ b/compiler/rustc_resolve/src/diagnostics/mod.rs @@ -409,7 +409,7 @@ pub(crate) struct SelfInConstGenericTy { #[derive(Diagnostic)] #[diag( - "{$is_gca -> + "{$is_gca_const_items -> [true] generic parameters in const blocks are not allowed; use a named `const` item instead *[false] generic parameters may not be used in const operations }" @@ -423,7 +423,7 @@ pub(crate) struct ParamInNonTrivialAnonConst { pub(crate) param_kind: ParamKindInNonTrivialAnonConst, #[help("add `#![feature(generic_const_exprs)]` to allow generic const expressions")] pub(crate) help: bool, - pub(crate) is_gca: bool, + pub(crate) is_gca_const_items: bool, #[help( "consider factoring the expression into a `type const` item and use it as the const argument instead" )] diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs index d743093aae92f..138bb4bd72ab0 100644 --- a/compiler/rustc_resolve/src/ident.rs +++ b/compiler/rustc_resolve/src/ident.rs @@ -1658,7 +1658,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } NoConstantGenericsReason::NonTrivialConstArg => { ResolutionError::ParamInNonTrivialAnonConst { - is_gca: self.features.gca_const_items(), + is_gca_const_items: self.features.gca_const_items(), name: rib_ident.name, param_kind: ParamKindInNonTrivialAnonConst::Type, } @@ -1750,7 +1750,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } NoConstantGenericsReason::NonTrivialConstArg => { ResolutionError::ParamInNonTrivialAnonConst { - is_gca: self.features.gca_const_items(), + is_gca_const_items: self.features.gca_const_items(), name: rib_ident.name, param_kind: ParamKindInNonTrivialAnonConst::Const { name: rib_ident.name, diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index e1787eadf5fb9..ebb041bf3878d 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -5232,7 +5232,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { AnonConstKind::InlineConst => ConstantHasGenerics::Yes, AnonConstKind::ConstArg(_) | AnonConstKind::ArrayLength => { if self.r.features.generic_const_exprs() - || self.r.features.gca_min_const_items() + || self.r.features.gca() || is_trivial_const_arg { ConstantHasGenerics::Yes diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index 84c07d0fb0f44..6a4bc7470038d 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -4163,9 +4163,8 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { span: lifetime_ref.ident.span, name: lifetime_ref.ident.name, param_kind: diagnostics::ParamKindInNonTrivialAnonConst::Lifetime, - help: self.r.tcx.sess.is_nightly_build() - && !self.r.features.gca_min_const_items(), - is_gca: self.r.features.gca_const_items(), + help: self.r.tcx.sess.is_nightly_build() && !self.r.features.gca(), + is_gca_const_items: self.r.features.gca_const_items(), help_gca: self.r.features.gca_const_items(), help_suggest_gca: self.r.tcx.sess.is_nightly_build() && !self.r.features.gca_const_items(), diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 0a96becf993e9..b62ad9a708c29 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -304,7 +304,7 @@ enum ResolutionError<'ra> { /// /// This error is only emitted when using `min_const_generics`. ParamInNonTrivialAnonConst { - is_gca: bool, + is_gca_const_items: bool, name: Symbol, param_kind: ParamKindInNonTrivialAnonConst, }, diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index e62ad42875248..68e7ee914f483 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1067,6 +1067,7 @@ symbols! { future_trait, fxsr, gca, + gca_adts, gca_const_items, gca_macroless_args, gca_macroless_items, diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index 63b0733b0a095..e751c87d37007 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -3984,8 +3984,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { obligation: &PredicateObligation<'tcx>, span: Span, ) -> Result, ErrorGuaranteed> { - if !self.tcx.features().generic_const_exprs() && !self.tcx.features().gca_min_const_items() - { + if !self.tcx.features().generic_const_exprs() && !self.tcx.features().gca() { let guar = self .dcx() .struct_span_err(span, "constant expression depends on a generic parameter") diff --git a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs index 9acc0682e6641..242aa260b8372 100644 --- a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs +++ b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs @@ -84,7 +84,7 @@ pub fn is_const_evaluatable<'tcx>( } _ => bug!("unexpected constkind in `is_const_evalautable: {unexpanded_ct:?}`"), } - } else if tcx.features().gca_min_const_items() { + } else if tcx.features().gca() { // This is a sanity check to make sure that non-generics consts are checked to // be evaluatable in case they aren't cchecked elsewhere. This will NOT error // if the const uses generics, as desired. diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs index 955ab1799a794..ba463184a41f8 100644 --- a/compiler/rustc_trait_selection/src/traits/wf.rs +++ b/compiler/rustc_trait_selection/src/traits/wf.rs @@ -1169,7 +1169,7 @@ impl<'a, 'tcx> TypeVisitor> for WfPredicates<'a, 'tcx> { } ty::ConstKind::Value(val) => { // FIXME(mgca): no need to feature-gate once valtree lifetimes are not erased - if tcx.features().gca_min_const_items() { + if tcx.features().gca() { match val.ty.kind() { ty::Adt(adt_def, args) => { let adt_val = val.destructure_adt_const(); diff --git a/compiler/rustc_type_ir/src/const_kind.rs b/compiler/rustc_type_ir/src/const_kind.rs index d969cca3dcda2..1ef498f072f22 100644 --- a/compiler/rustc_type_ir/src/const_kind.rs +++ b/compiler/rustc_type_ir/src/const_kind.rs @@ -280,9 +280,6 @@ pub enum AnonConstKind { /// `feature(generic_const_exprs)` anon consts are allowed to use arbitrary generic parameters in scope GCE, /// stable `min_const_generics` anon consts are not allowed to use any generic parameters - /// - /// under `feature(gca_min_const_items)`, these may be inline consts as well, and should be - /// treated the same as anon consts. MCG, /// anon consts used as the length of a repeat expr are syntactically allowed to use generic parameters /// but must not depend on the actual instantiation. See #76200 for more information diff --git a/library/core/src/marker.rs b/library/core/src/marker.rs index 514c2aa1c9bd1..d1a68412247ef 100644 --- a/library/core/src/marker.rs +++ b/library/core/src/marker.rs @@ -1074,13 +1074,20 @@ pub const trait Destruct: PointeeSized {} #[rustc_dyn_incompatible_trait] pub trait Tuple {} +// HACK(khyperia): if this is missing, the gca macro's `implied_by = "gca_adts"` emits an error +#[unstable(feature = "gca_adts", issue = "163420")] +#[expect(dead_code)] +struct HackGcaAdtsFeature; + /// Creates a new style directly represented const argument. /// ```ignore (cannot test this from within core yet) /// const BAR: usize = gca!(N); /// const FOO: usize = gca!(BAR::); /// ``` #[rustc_builtin_macro(gca)] -#[unstable(feature = "gca_min_const_items", issue = "132980")] +// HACK: abuse implied_by, this should be enabled if gca_min_const_items or gca_adts is enabled, +// neither one is more particularly special than the other +#[unstable(feature = "gca_min_const_items", issue = "132980", implied_by = "gca_adts")] #[macro_export] macro_rules! gca { ($($arg:tt)*) => { diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index f716f49ffd60a..6d579f0cb9601 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -758,7 +758,7 @@ pub use core::concat_bytes; )] #[unstable(feature = "derive_macro_global_path", issue = "154645")] pub use core::derive; -#[unstable(feature = "gca_min_const_items", issue = "132980")] +#[unstable(feature = "gca_min_const_items", issue = "132980", implied_by = "gca_adts")] pub use core::gca; #[stable(feature = "matches_macro", since = "1.42.0")] pub use core::matches; diff --git a/src/doc/unstable-book/src/language-features/gca-adts.md b/src/doc/unstable-book/src/language-features/gca-adts.md new file mode 100644 index 0000000000000..49905d57a9a4c --- /dev/null +++ b/src/doc/unstable-book/src/language-features/gca-adts.md @@ -0,0 +1,21 @@ +# gca_adts + +Enables ADTs under the generic const args family of features (e.g. `gca!` macro). + +The tracking issue for this feature is: [#163420] + +[#163420]: https://github.com/rust-lang/rust/issues/163420 + +## Examples + +```rust +#![feature(gca_min_const_items, min_adt_const_params, gca_adts)] + +use std::gca; + +struct S; + +fn main() { + let _: S = S::; +} +``` diff --git a/src/doc/unstable-book/src/language-features/gca-min-const-items.md b/src/doc/unstable-book/src/language-features/gca-min-const-items.md index 4027e782714ed..486db1f50885b 100644 --- a/src/doc/unstable-book/src/language-features/gca-min-const-items.md +++ b/src/doc/unstable-book/src/language-features/gca-min-const-items.md @@ -1,6 +1,6 @@ # gca_min_const_items -Enables the generic const args MVP (paths to direct const items and constructors for ADTs and primitives). +Enables the generic const args MVP (paths to direct const items and primitives). The tracking issue for this feature is: [#132980] diff --git a/tests/ui/const-generics/gca/const-pattern-field-type-mismatch-162394.rs b/tests/ui/const-generics/gca/const-pattern-field-type-mismatch-162394.rs index 7600c4882a91c..b4bfdb494a863 100644 --- a/tests/ui/const-generics/gca/const-pattern-field-type-mismatch-162394.rs +++ b/tests/ui/const-generics/gca/const-pattern-field-type-mismatch-162394.rs @@ -1,10 +1,13 @@ //@ compile-flags: -Znext-solver=globally -#![allow(incomplete_features)] -#![feature(gca_macroless_args)] -#![feature(gca_macroless_items)] -#![feature(gca_const_items, gca_min_const_items)] -#![feature(min_adt_const_params)] +#![feature( + gca_adts, + gca_const_items, + gca_macroless_args, + gca_macroless_items, + gca_min_const_items, + min_adt_const_params +)] use std::marker::ConstParamTy; diff --git a/tests/ui/const-generics/gca/const-pattern-field-type-mismatch-162394.stderr b/tests/ui/const-generics/gca/const-pattern-field-type-mismatch-162394.stderr index 9dd229cc46a34..42d1d8b5fd1d7 100644 --- a/tests/ui/const-generics/gca/const-pattern-field-type-mismatch-162394.stderr +++ b/tests/ui/const-generics/gca/const-pattern-field-type-mismatch-162394.stderr @@ -1,5 +1,5 @@ error: the constant `"foo"` is not of type `()` - --> $DIR/const-pattern-field-type-mismatch-162394.rs:17:5 + --> $DIR/const-pattern-field-type-mismatch-162394.rs:20:5 | LL | const A2: Foo = Self::FooA("foo"); | ^^^^^^^^^^^^^ expected `()`, found `&'static str` diff --git a/tests/ui/const-generics/gca/gca-macro-fn-call.rs b/tests/ui/const-generics/gca/gca-macro-fn-call.rs index 6035c14e5eb75..73945b14af4b9 100644 --- a/tests/ui/const-generics/gca/gca-macro-fn-call.rs +++ b/tests/ui/const-generics/gca/gca-macro-fn-call.rs @@ -13,10 +13,14 @@ //! tuple-variant constructor (unions, primitives, foreign types), not just structs. //@ compile-flags: -Znext-solver -#![feature(gca_min_const_items, gca_macroless_args)] -#![feature(gca_const_items)] -#![feature(extern_types)] -#![expect(incomplete_features)] +#![feature( + extern_types, + gca_adts, + gca_const_items, + gca_macroless_args, + gca_min_const_items, + min_adt_const_params +)] struct FieldName([u8; N]); diff --git a/tests/ui/const-generics/gca/gca-macro-fn-call.stderr b/tests/ui/const-generics/gca/gca-macro-fn-call.stderr index 1399bcb1dee92..4b7bf15ace96d 100644 --- a/tests/ui/const-generics/gca/gca-macro-fn-call.stderr +++ b/tests/ui/const-generics/gca/gca-macro-fn-call.stderr @@ -1,29 +1,29 @@ error: complex const arguments must be placed inside of a `const` block - --> $DIR/gca-macro-fn-call.rs:35:23 + --> $DIR/gca-macro-fn-call.rs:39:23 | LL | fn bad(_: FieldName<{ FieldName::len() }>) {} | ^^^^^^^^^^^^^^^^ error: complex const arguments must be placed inside of a `const` block - --> $DIR/gca-macro-fn-call.rs:44:31 + --> $DIR/gca-macro-fn-call.rs:48:31 | LL | fn bad_tracing(_: FieldName<{ FieldName::len_of("id") }>) {} | ^^^^^^^^^^^^^^^^^^^^^^^ error: complex const arguments must be placed inside of a `const` block - --> $DIR/gca-macro-fn-call.rs:61:23 + --> $DIR/gca-macro-fn-call.rs:65:23 | LL | fn bad_union(_: Tag<{ Tag::width() }>) {} | ^^^^^^^^^^^^ error: complex const arguments must be placed inside of a `const` block - --> $DIR/gca-macro-fn-call.rs:70:28 + --> $DIR/gca-macro-fn-call.rs:74:28 | LL | fn bad_prim(_: FieldName<{ u32::from_str_radix("10", 10) }>) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: complex const arguments must be placed inside of a `const` block - --> $DIR/gca-macro-fn-call.rs:81:31 + --> $DIR/gca-macro-fn-call.rs:85:31 | LL | fn bad_foreign(_: FieldName<{ Opaque::foo() }>) {} | ^^^^^^^^^^^^^ diff --git a/tests/ui/const-generics/gca_adts/missing-dependent-features.rs b/tests/ui/const-generics/gca_adts/missing-dependent-features.rs new file mode 100644 index 0000000000000..55a6338dadb64 --- /dev/null +++ b/tests/ui/const-generics/gca_adts/missing-dependent-features.rs @@ -0,0 +1,3 @@ +#![feature(gca_adts)] +//~^ ERROR `gca_adts` requires `min_adt_const_params` or `adt_const_params` to be enabled +fn main() {} diff --git a/tests/ui/const-generics/gca_adts/missing-dependent-features.stderr b/tests/ui/const-generics/gca_adts/missing-dependent-features.stderr new file mode 100644 index 0000000000000..19c96d5d7bd9e --- /dev/null +++ b/tests/ui/const-generics/gca_adts/missing-dependent-features.stderr @@ -0,0 +1,10 @@ +error: `gca_adts` requires `min_adt_const_params` or `adt_const_params` to be enabled + --> $DIR/missing-dependent-features.rs:1:12 + | +LL | #![feature(gca_adts)] + | ^^^^^^^^ + | + = help: enable all of these features + +error: aborting due to 1 previous error + diff --git a/tests/ui/const-generics/gca_adts/none-incomplete.rs b/tests/ui/const-generics/gca_adts/none-incomplete.rs new file mode 100644 index 0000000000000..76078a42fa08a --- /dev/null +++ b/tests/ui/const-generics/gca_adts/none-incomplete.rs @@ -0,0 +1,21 @@ +//! This test file ought to be part of `none.rs`, but, type-relative paths are difficult + +#![feature(gca_adts, adt_const_params)] + +use std::gca; +use std::marker::ConstParamTy; + +#[derive(ConstParamTy, PartialEq, Eq)] +enum MyOption { + MySome, + MyNone, +} + +use MyOption::*; + +struct Struct; + +fn main() { + let _: Struct::MyNone)>; + //~^ ERROR complex const arguments must be placed inside of a `const` block +} diff --git a/tests/ui/const-generics/gca_adts/none-incomplete.stderr b/tests/ui/const-generics/gca_adts/none-incomplete.stderr new file mode 100644 index 0000000000000..bf8c538207579 --- /dev/null +++ b/tests/ui/const-generics/gca_adts/none-incomplete.stderr @@ -0,0 +1,8 @@ +error: complex const arguments must be placed inside of a `const` block + --> $DIR/none-incomplete.rs:19:24 + | +LL | let _: Struct::MyNone)>; + | ^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/const-generics/gca_adts/none.rs b/tests/ui/const-generics/gca_adts/none.rs new file mode 100644 index 0000000000000..223f01d81a5a8 --- /dev/null +++ b/tests/ui/const-generics/gca_adts/none.rs @@ -0,0 +1,21 @@ +//@check-pass + +#![feature(gca_adts, adt_const_params)] + +use std::gca; +use std::marker::ConstParamTy; + +#[derive(ConstParamTy, PartialEq, Eq)] +enum MyOption { + MySome, + MyNone, +} + +use MyOption::*; + +struct Struct; + +fn main() { + let _: Struct; + let _: Struct; +} diff --git a/tests/ui/const-generics/generic_const_parameter_types/inherent-type-const.rs b/tests/ui/const-generics/generic_const_parameter_types/inherent-type-const.rs index b4fe7a9a5da2a..0053db1608ea4 100644 --- a/tests/ui/const-generics/generic_const_parameter_types/inherent-type-const.rs +++ b/tests/ui/const-generics/generic_const_parameter_types/inherent-type-const.rs @@ -3,11 +3,12 @@ //@[next] compile-flags: -Znext-solver //@ ignore-compare-mode-next-solver (explicit revisions) #![feature( + const_param_ty_trait, + gca_adts, gca_min_const_items, generic_const_parameter_types, inherent_associated_types, - min_adt_const_params, - const_param_ty_trait + min_adt_const_params )] use std::gca; diff --git a/tests/ui/const-generics/mgca/adt_expr_arg_simple.rs b/tests/ui/const-generics/mgca/adt_expr_arg_simple.rs index 112cf5563a59e..cd90cc6f66b99 100644 --- a/tests/ui/const-generics/mgca/adt_expr_arg_simple.rs +++ b/tests/ui/const-generics/mgca/adt_expr_arg_simple.rs @@ -1,5 +1,4 @@ -#![feature(gca_min_const_items, gca_macroless_args, adt_const_params)] -#![expect(incomplete_features)] +#![feature(gca_adts, gca_min_const_items, gca_macroless_args, adt_const_params)] use std::gca; diff --git a/tests/ui/const-generics/mgca/adt_expr_arg_simple.stderr b/tests/ui/const-generics/mgca/adt_expr_arg_simple.stderr index 96f8461b9cb82..70ed0cbc41f64 100644 --- a/tests/ui/const-generics/mgca/adt_expr_arg_simple.stderr +++ b/tests/ui/const-generics/mgca/adt_expr_arg_simple.stderr @@ -1,11 +1,11 @@ error: complex const arguments must be placed inside of a `const` block - --> $DIR/adt_expr_arg_simple.rs:31:35 + --> $DIR/adt_expr_arg_simple.rs:30:35 | LL | foo::<{ gca!(Some:: { 0: N + 1 }) }>(); | ^^^^^ error: generic parameters may not be used in const operations - --> $DIR/adt_expr_arg_simple.rs:36:38 + --> $DIR/adt_expr_arg_simple.rs:35:38 | LL | foo::<{ Some:: { 0: const { N + 1 } } }>(); | ^ diff --git a/tests/ui/const-generics/mgca/adt_expr_arg_tuple_expr_fail.rs b/tests/ui/const-generics/mgca/adt_expr_arg_tuple_expr_fail.rs index c488b90aec14b..c9652b1c83385 100644 --- a/tests/ui/const-generics/mgca/adt_expr_arg_tuple_expr_fail.rs +++ b/tests/ui/const-generics/mgca/adt_expr_arg_tuple_expr_fail.rs @@ -1,10 +1,10 @@ #![feature( - gca_min_const_items, - gca_macroless_args, adt_const_params, + gca_adts, + gca_macroless_args, + gca_min_const_items, unsized_const_params )] -#![expect(incomplete_features)] trait Trait { #[rustc_always_gca] diff --git a/tests/ui/const-generics/mgca/adt_expr_erroneuous_inits.rs b/tests/ui/const-generics/mgca/adt_expr_erroneuous_inits.rs index 20bac03fab1c0..5bcfea9246066 100644 --- a/tests/ui/const-generics/mgca/adt_expr_erroneuous_inits.rs +++ b/tests/ui/const-generics/mgca/adt_expr_erroneuous_inits.rs @@ -1,26 +1,27 @@ -#![feature(gca_min_const_items, gca_macroless_args, adt_const_params)] -#![expect(incomplete_features)] +#![feature(gca_adts, gca_min_const_items, gca_macroless_args, adt_const_params)] #![crate_type = "lib"] // Miscellaneous assortment of invalid cases of directly represented // `ConstArgKind::Struct`'s under mgca. #[derive(Eq, PartialEq, std::marker::ConstParamTy)] -struct Foo { field: T } +struct Foo { + field: T, +} fn NonStruct() {} fn accepts>() {} fn bar() { - accepts::<{ Foo:: { }}>(); + accepts::<{ Foo:: {} }>(); //~^ ERROR: struct expression with missing field initialiser for `field` - accepts::<{ Foo:: { field: const { 1 }, field: const { 2} }}>(); + accepts::<{ Foo:: { field: const { 1 }, field: const { 2 } } }>(); //~^ ERROR: struct expression with multiple initialisers for `field` - accepts::<{ Fooo:: { field: const { 1 } }}>(); + accepts::<{ Fooo:: { field: const { 1 } } }>(); //~^ ERROR: cannot find struct, variant or union type `Fooo` in this scope //~| ERROR: struct expression with invalid base path - accepts::<{ NonStruct { }}>(); + accepts::<{ NonStruct {} }>(); //~^ ERROR: cannot find struct, variant or union type `NonStruct` in this scope //~| ERROR: struct expression with invalid base path } diff --git a/tests/ui/const-generics/mgca/adt_expr_erroneuous_inits.stderr b/tests/ui/const-generics/mgca/adt_expr_erroneuous_inits.stderr index 370a73b5a4bc6..707cfa1b73b31 100644 --- a/tests/ui/const-generics/mgca/adt_expr_erroneuous_inits.stderr +++ b/tests/ui/const-generics/mgca/adt_expr_erroneuous_inits.stderr @@ -1,51 +1,51 @@ error[E0422]: cannot find struct, variant or union type `Fooo` in this scope - --> $DIR/adt_expr_erroneuous_inits.rs:20:17 + --> $DIR/adt_expr_erroneuous_inits.rs:21:17 | -LL | accepts::<{ Fooo:: { field: const { 1 } }}>(); +LL | accepts::<{ Fooo:: { field: const { 1 } } }>(); | ^^^^ not found in this scope | note: similarly named struct `Foo` defined here - --> $DIR/adt_expr_erroneuous_inits.rs:9:1 + --> $DIR/adt_expr_erroneuous_inits.rs:8:1 | -LL | struct Foo { field: T } +LL | struct Foo { | ^^^^^^^^^^^^^ help: a struct with a similar name exists | -LL - accepts::<{ Fooo:: { field: const { 1 } }}>(); -LL + accepts::<{ Foo:: { field: const { 1 } }}>(); +LL - accepts::<{ Fooo:: { field: const { 1 } } }>(); +LL + accepts::<{ Foo:: { field: const { 1 } } }>(); | error[E0574]: cannot find struct, variant or union type `NonStruct` in this scope - --> $DIR/adt_expr_erroneuous_inits.rs:23:17 + --> $DIR/adt_expr_erroneuous_inits.rs:24:17 | -LL | accepts::<{ NonStruct { }}>(); +LL | accepts::<{ NonStruct {} }>(); | ^^^^^^^^^ not found in this scope | = note: a function named `NonStruct` exists in another namespace error: struct expression with missing field initialiser for `field` - --> $DIR/adt_expr_erroneuous_inits.rs:16:17 + --> $DIR/adt_expr_erroneuous_inits.rs:17:17 | -LL | accepts::<{ Foo:: { }}>(); - | ^^^^^^^^^^^^^ +LL | accepts::<{ Foo:: {} }>(); + | ^^^^^^^^^^^^ error: struct expression with multiple initialisers for `field` - --> $DIR/adt_expr_erroneuous_inits.rs:18:49 + --> $DIR/adt_expr_erroneuous_inits.rs:19:49 | -LL | accepts::<{ Foo:: { field: const { 1 }, field: const { 2} }}>(); - | ^^^^^^^^^^^^^^^^^ +LL | accepts::<{ Foo:: { field: const { 1 }, field: const { 2 } } }>(); + | ^^^^^^^^^^^^^^^^^^ error: struct expression with invalid base path - --> $DIR/adt_expr_erroneuous_inits.rs:20:17 + --> $DIR/adt_expr_erroneuous_inits.rs:21:17 | -LL | accepts::<{ Fooo:: { field: const { 1 } }}>(); +LL | accepts::<{ Fooo:: { field: const { 1 } } }>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: struct expression with invalid base path - --> $DIR/adt_expr_erroneuous_inits.rs:23:17 + --> $DIR/adt_expr_erroneuous_inits.rs:24:17 | -LL | accepts::<{ NonStruct { }}>(); - | ^^^^^^^^^^^^^ +LL | accepts::<{ NonStruct {} }>(); + | ^^^^^^^^^^^^ error: aborting due to 6 previous errors diff --git a/tests/ui/const-generics/mgca/adt_expr_fields_type_check.rs b/tests/ui/const-generics/mgca/adt_expr_fields_type_check.rs index 38eb04760c598..f4b59aaa1404e 100644 --- a/tests/ui/const-generics/mgca/adt_expr_fields_type_check.rs +++ b/tests/ui/const-generics/mgca/adt_expr_fields_type_check.rs @@ -1,5 +1,4 @@ -#![feature(gca_min_const_items, gca_macroless_args, adt_const_params)] -#![expect(incomplete_features)] +#![feature(gca_adts, gca_min_const_items, gca_macroless_args, adt_const_params)] #[derive(Eq, PartialEq, std::marker::ConstParamTy)] struct S1 { diff --git a/tests/ui/const-generics/mgca/adt_expr_fields_type_check.stderr b/tests/ui/const-generics/mgca/adt_expr_fields_type_check.stderr index 81f7c5366b461..dea156db19c14 100644 --- a/tests/ui/const-generics/mgca/adt_expr_fields_type_check.stderr +++ b/tests/ui/const-generics/mgca/adt_expr_fields_type_check.stderr @@ -1,41 +1,41 @@ error: the constant `N` is not of type `u8` - --> $DIR/adt_expr_fields_type_check.rs:24:19 + --> $DIR/adt_expr_fields_type_check.rs:23:19 | LL | accepts_1::<{ S1:: { f1: N, f2: N } }>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ expected `u8`, found `bool` error: the constant `N` is not of type `isize` - --> $DIR/adt_expr_fields_type_check.rs:24:19 + --> $DIR/adt_expr_fields_type_check.rs:23:19 | LL | accepts_1::<{ S1:: { f1: N, f2: N } }>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ expected `isize`, found `bool` error: the constant `N` is not of type `u8` - --> $DIR/adt_expr_fields_type_check.rs:27:19 + --> $DIR/adt_expr_fields_type_check.rs:26:19 | LL | accepts_2::<{ S2::(N, N) }>(); | ^^^^^^^^^^^^^^ expected `u8`, found `bool` error: the constant `N` is not of type `isize` - --> $DIR/adt_expr_fields_type_check.rs:27:19 + --> $DIR/adt_expr_fields_type_check.rs:26:19 | LL | accepts_2::<{ S2::(N, N) }>(); | ^^^^^^^^^^^^^^ expected `isize`, found `bool` error: the constant `N` is not of type `u8` - --> $DIR/adt_expr_fields_type_check.rs:30:19 + --> $DIR/adt_expr_fields_type_check.rs:29:19 | LL | accepts_3::<{ En::Var1::(N, N) }>(); | ^^^^^^^^^^^^^^^^^^^^ expected `u8`, found `bool` error: the constant `N` is not of type `i64` - --> $DIR/adt_expr_fields_type_check.rs:32:19 + --> $DIR/adt_expr_fields_type_check.rs:31:19 | LL | accepts_3::<{ En::Var2:: { field: N } }>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `i64`, found `bool` error[E0308]: mismatched types - --> $DIR/adt_expr_fields_type_check.rs:34:51 + --> $DIR/adt_expr_fields_type_check.rs:33:51 | LL | accepts_3::<{ En::Var2:: { field: const { false } } }>(); | ^^^^^ expected `i64`, found `bool` diff --git a/tests/ui/const-generics/mgca/adt_expr_infers_from_value.rs b/tests/ui/const-generics/mgca/adt_expr_infers_from_value.rs index 30b8ca3dd2752..13feedadbcfd7 100644 --- a/tests/ui/const-generics/mgca/adt_expr_infers_from_value.rs +++ b/tests/ui/const-generics/mgca/adt_expr_infers_from_value.rs @@ -1,14 +1,14 @@ //@ check-pass #![feature( - generic_const_items, - gca_min_const_items, - gca_macroless_args, adt_const_params, - generic_const_parameter_types, - const_param_ty_trait + const_param_ty_trait, + gca_adts, + gca_macroless_args, + gca_min_const_items, + generic_const_items, + generic_const_parameter_types )] -#![expect(incomplete_features)] use std::gca; use std::marker::{ConstParamTy, ConstParamTy_, PhantomData}; diff --git a/tests/ui/const-generics/mgca/adt_expr_unit_enum_extra_field.rs b/tests/ui/const-generics/mgca/adt_expr_unit_enum_extra_field.rs index d48b5cb9e7100..7cc24d660d04c 100644 --- a/tests/ui/const-generics/mgca/adt_expr_unit_enum_extra_field.rs +++ b/tests/ui/const-generics/mgca/adt_expr_unit_enum_extra_field.rs @@ -1,13 +1,13 @@ -#![feature(gca_min_const_items, gca_macroless_args, adt_const_params)] +#![feature(gca_adts, gca_min_const_items, gca_macroless_args, adt_const_params)] #[derive(Eq, PartialEq, std::marker::ConstParamTy)] enum E { - S {} + S {}, } fn foo() {} fn main() { - foo::<{E::S { x: const { 1 } }}>(); - //~^ ERROR variant `E::S` has no field named `x` [E0559] + foo::<{ E::S { x: const { 1 } } }>(); + //~^ ERROR variant `E::S` has no field named `x` [E0559] } diff --git a/tests/ui/const-generics/mgca/adt_expr_unit_enum_extra_field.stderr b/tests/ui/const-generics/mgca/adt_expr_unit_enum_extra_field.stderr index 7ac3b28cdca5b..8d201a2b8943c 100644 --- a/tests/ui/const-generics/mgca/adt_expr_unit_enum_extra_field.stderr +++ b/tests/ui/const-generics/mgca/adt_expr_unit_enum_extra_field.stderr @@ -1,8 +1,8 @@ error[E0559]: variant `E::S` has no field named `x` - --> $DIR/adt_expr_unit_enum_extra_field.rs:11:19 + --> $DIR/adt_expr_unit_enum_extra_field.rs:11:20 | -LL | foo::<{E::S { x: const { 1 } }}>(); - | ^ `E::S` does not have this field +LL | foo::<{ E::S { x: const { 1 } } }>(); + | ^ `E::S` does not have this field error: aborting due to 1 previous error diff --git a/tests/ui/const-generics/mgca/adt_expr_unit_struct_extra_field.rs b/tests/ui/const-generics/mgca/adt_expr_unit_struct_extra_field.rs index 0eaadd1a9b31d..68a1600e218eb 100644 --- a/tests/ui/const-generics/mgca/adt_expr_unit_struct_extra_field.rs +++ b/tests/ui/const-generics/mgca/adt_expr_unit_struct_extra_field.rs @@ -1,4 +1,4 @@ -#![feature(gca_min_const_items, gca_macroless_args, adt_const_params)] +#![feature(gca_adts, gca_min_const_items, gca_macroless_args, adt_const_params)] #[derive(Eq, PartialEq, std::marker::ConstParamTy)] struct Foo; diff --git a/tests/ui/const-generics/mgca/array-const-arg-len-mismatch.rs b/tests/ui/const-generics/mgca/array-const-arg-len-mismatch.rs index 134a82e9d3490..59587c0093295 100644 --- a/tests/ui/const-generics/mgca/array-const-arg-len-mismatch.rs +++ b/tests/ui/const-generics/mgca/array-const-arg-len-mismatch.rs @@ -2,9 +2,14 @@ //! //! Ensure that providing an array const arg with the wrong number of elements //! doesn't ICE or silently cause UB. -#![expect(incomplete_features)] -#![feature(adt_const_params, gca_min_const_items, gca_macroless_args)] -#![feature(unsized_const_params, generic_const_parameter_types)] +#![feature( + adt_const_params, + gca_adts, + gca_macroless_args, + gca_min_const_items, + generic_const_parameter_types, + unsized_const_params +)] use std::gca; use std::marker::ConstParamTy_; diff --git a/tests/ui/const-generics/mgca/array-const-arg-len-mismatch.stderr b/tests/ui/const-generics/mgca/array-const-arg-len-mismatch.stderr index 72c2a367c7246..c0625e737d307 100644 --- a/tests/ui/const-generics/mgca/array-const-arg-len-mismatch.stderr +++ b/tests/ui/const-generics/mgca/array-const-arg-len-mismatch.stderr @@ -1,59 +1,59 @@ error: the constant `*b""` is not of type `[u8; 2]` - --> $DIR/array-const-arg-len-mismatch.rs:31:11 + --> $DIR/array-const-arg-len-mismatch.rs:36:11 | LL | foo::(); | ^^ expected `[u8; 2]`, found `[u8; 0]` | note: required by a const generic parameter in `foo` - --> $DIR/array-const-arg-len-mismatch.rs:12:42 + --> $DIR/array-const-arg-len-mismatch.rs:17:42 | LL | fn foo() -> [T; N] { | ^^^^^^^^^^^^^^^ required by this const generic parameter in `foo` error: the constant `*b"\x00\x00\x00"` is not of type `[u8; 2]` - --> $DIR/array-const-arg-len-mismatch.rs:33:11 + --> $DIR/array-const-arg-len-mismatch.rs:38:11 | LL | foo::(); | ^^ expected `[u8; 2]`, found `[u8; 3]` | note: required by a const generic parameter in `foo` - --> $DIR/array-const-arg-len-mismatch.rs:12:42 + --> $DIR/array-const-arg-len-mismatch.rs:17:42 | LL | fn foo() -> [T; N] { | ^^^^^^^^^^^^^^^ required by this const generic parameter in `foo` error: the constant `*b""` is not of type `[u8; 2]` - --> $DIR/array-const-arg-len-mismatch.rs:35:13 + --> $DIR/array-const-arg-len-mismatch.rs:40:13 | LL | bar::<{ [] }>(); | ^^ expected `[u8; 2]`, found `[u8; 0]` | note: required by a const generic parameter in `bar` - --> $DIR/array-const-arg-len-mismatch.rs:16:8 + --> $DIR/array-const-arg-len-mismatch.rs:21:8 | LL | fn bar() {} | ^^^^^^^^^^^^^^^^ required by this const generic parameter in `bar` error: the constant `*b"\x01\x02\x03"` is not of type `[u8; 2]` - --> $DIR/array-const-arg-len-mismatch.rs:37:13 + --> $DIR/array-const-arg-len-mismatch.rs:42:13 | LL | bar::<{ [1, 2, 3] }>(); | ^^^^^^^^^ expected `[u8; 2]`, found `[u8; 3]` | note: required by a const generic parameter in `bar` - --> $DIR/array-const-arg-len-mismatch.rs:16:8 + --> $DIR/array-const-arg-len-mismatch.rs:21:8 | LL | fn bar() {} | ^^^^^^^^^^^^^^^^ required by this const generic parameter in `bar` error: the constant `*b"*"` is not of type `[u8; 3]` - --> $DIR/array-const-arg-len-mismatch.rs:39:13 + --> $DIR/array-const-arg-len-mismatch.rs:44:13 | LL | baz::<{ [42] }>(); | ^^^^ expected `[u8; 3]`, found `[u8; 1]` | note: required by a const generic parameter in `baz` - --> $DIR/array-const-arg-len-mismatch.rs:28:8 + --> $DIR/array-const-arg-len-mismatch.rs:33:8 | LL | fn baz::LEN]>() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this const generic parameter in `baz` diff --git a/tests/ui/const-generics/mgca/array-const-arg-type-mismatch.rs b/tests/ui/const-generics/mgca/array-const-arg-type-mismatch.rs index def077e4d292d..a6d1acb773a23 100644 --- a/tests/ui/const-generics/mgca/array-const-arg-type-mismatch.rs +++ b/tests/ui/const-generics/mgca/array-const-arg-type-mismatch.rs @@ -1,5 +1,4 @@ -#![expect(incomplete_features)] -#![feature(adt_const_params, gca_min_const_items, gca_macroless_args)] +#![feature(adt_const_params, gca_adts, gca_min_const_items, gca_macroless_args)] use std::marker::ConstParamTy; #[derive(Eq, PartialEq, ConstParamTy)] diff --git a/tests/ui/const-generics/mgca/array-const-arg-type-mismatch.stderr b/tests/ui/const-generics/mgca/array-const-arg-type-mismatch.stderr index 573620366cbce..f8b9a3c9cee37 100644 --- a/tests/ui/const-generics/mgca/array-const-arg-type-mismatch.stderr +++ b/tests/ui/const-generics/mgca/array-const-arg-type-mismatch.stderr @@ -1,5 +1,5 @@ error: the constant `Bar` is not of type `Foo` - --> $DIR/array-const-arg-type-mismatch.rs:13:14 + --> $DIR/array-const-arg-type-mismatch.rs:12:14 | LL | test::<{ [Bar] }>(); | ^^^^^ expected `Foo`, found `Bar` diff --git a/tests/ui/const-generics/mgca/array-expr-complex.r1.stderr b/tests/ui/const-generics/mgca/array-expr-complex.r1.stderr index 2e3b2172b7325..43a8312a21dbd 100644 --- a/tests/ui/const-generics/mgca/array-expr-complex.r1.stderr +++ b/tests/ui/const-generics/mgca/array-expr-complex.r1.stderr @@ -1,5 +1,5 @@ error: complex const arguments must be placed inside of a `const` block - --> $DIR/array-expr-complex.rs:13:33 + --> $DIR/array-expr-complex.rs:12:33 | LL | takes_array::<{ gca!([1, 2, 1 + 2]) }>(); | ^^^^^ diff --git a/tests/ui/const-generics/mgca/array-expr-complex.r2.stderr b/tests/ui/const-generics/mgca/array-expr-complex.r2.stderr index 2b25efb6ab090..ed9e7d184c023 100644 --- a/tests/ui/const-generics/mgca/array-expr-complex.r2.stderr +++ b/tests/ui/const-generics/mgca/array-expr-complex.r2.stderr @@ -1,5 +1,5 @@ error: complex const arguments must be placed inside of a `const` block - --> $DIR/array-expr-complex.rs:16:26 + --> $DIR/array-expr-complex.rs:15:26 | LL | takes_array::<{ gca!([X; 3]) }>(); | ^^^^^^ diff --git a/tests/ui/const-generics/mgca/array-expr-complex.r3.stderr b/tests/ui/const-generics/mgca/array-expr-complex.r3.stderr index 09515bafa7025..e67703aab474f 100644 --- a/tests/ui/const-generics/mgca/array-expr-complex.r3.stderr +++ b/tests/ui/const-generics/mgca/array-expr-complex.r3.stderr @@ -1,5 +1,5 @@ error: complex const arguments must be placed inside of a `const` block - --> $DIR/array-expr-complex.rs:19:26 + --> $DIR/array-expr-complex.rs:18:26 | LL | takes_array::<{ gca!([0; Y]) }>(); | ^^^^^^ diff --git a/tests/ui/const-generics/mgca/array-expr-complex.rs b/tests/ui/const-generics/mgca/array-expr-complex.rs index 1c96c492b3cfd..12104e5c5bd08 100644 --- a/tests/ui/const-generics/mgca/array-expr-complex.rs +++ b/tests/ui/const-generics/mgca/array-expr-complex.rs @@ -1,7 +1,6 @@ //@ revisions: r1 r2 r3 -#![expect(incomplete_features)] -#![feature(gca_min_const_items, adt_const_params)] +#![feature(gca_adts, gca_min_const_items, adt_const_params)] use std::gca; diff --git a/tests/ui/const-generics/mgca/array-expr-simple.rs b/tests/ui/const-generics/mgca/array-expr-simple.rs index eba86898c41a1..d9436afb6015d 100644 --- a/tests/ui/const-generics/mgca/array-expr-simple.rs +++ b/tests/ui/const-generics/mgca/array-expr-simple.rs @@ -1,6 +1,5 @@ //@ run-pass -#![expect(incomplete_features)] -#![feature(gca_min_const_items, gca_macroless_args, adt_const_params)] +#![feature(gca_adts, gca_min_const_items, gca_macroless_args, adt_const_params)] #![allow(dead_code)] fn takes_array_u32() {} diff --git a/tests/ui/const-generics/mgca/array-expr-type-mismatch-in-where-bound.rs b/tests/ui/const-generics/mgca/array-expr-type-mismatch-in-where-bound.rs index a7582258de2f5..fe1641ff38b2f 100644 --- a/tests/ui/const-generics/mgca/array-expr-type-mismatch-in-where-bound.rs +++ b/tests/ui/const-generics/mgca/array-expr-type-mismatch-in-where-bound.rs @@ -1,7 +1,5 @@ //! regression test for -#![feature(gca_min_const_items, gca_macroless_args)] -#![feature(adt_const_params)] -#![expect(incomplete_features)] +#![feature(adt_const_params, gca_adts, gca_min_const_items, gca_macroless_args)] trait Trait1 {} trait Trait2 {} diff --git a/tests/ui/const-generics/mgca/array-expr-type-mismatch-in-where-bound.stderr b/tests/ui/const-generics/mgca/array-expr-type-mismatch-in-where-bound.stderr index 169282547fc1d..f930849466eae 100644 --- a/tests/ui/const-generics/mgca/array-expr-type-mismatch-in-where-bound.stderr +++ b/tests/ui/const-generics/mgca/array-expr-type-mismatch-in-where-bound.stderr @@ -1,11 +1,11 @@ error: expected `usize`, found const array - --> $DIR/array-expr-type-mismatch-in-where-bound.rs:11:17 + --> $DIR/array-expr-type-mismatch-in-where-bound.rs:9:17 | LL | T: Trait1<{ [] }>, | ^^ error: type annotations needed for the literal - --> $DIR/array-expr-type-mismatch-in-where-bound.rs:17:15 + --> $DIR/array-expr-type-mismatch-in-where-bound.rs:15:15 | LL | T: Trait2<3>, | ^ diff --git a/tests/ui/const-generics/mgca/array-expr-with-assoc-const.rs b/tests/ui/const-generics/mgca/array-expr-with-assoc-const.rs index 85ef64c498a4c..5b22ff101030b 100644 --- a/tests/ui/const-generics/mgca/array-expr-with-assoc-const.rs +++ b/tests/ui/const-generics/mgca/array-expr-with-assoc-const.rs @@ -1,6 +1,5 @@ //@ run-pass -#![expect(incomplete_features)] -#![feature(gca_min_const_items, gca_macroless_args, adt_const_params)] +#![feature(gca_adts, gca_min_const_items, gca_macroless_args, adt_const_params)] #![allow(dead_code)] fn takes_array() {} diff --git a/tests/ui/const-generics/mgca/array-expr-with-macro.rs b/tests/ui/const-generics/mgca/array-expr-with-macro.rs index db768339118f3..bf586b90a8e5a 100644 --- a/tests/ui/const-generics/mgca/array-expr-with-macro.rs +++ b/tests/ui/const-generics/mgca/array-expr-with-macro.rs @@ -1,6 +1,5 @@ //@ run-pass -#![expect(incomplete_features)] -#![feature(gca_min_const_items, gca_macroless_args, adt_const_params)] +#![feature(gca_adts, gca_min_const_items, gca_macroless_args, adt_const_params)] #![allow(dead_code)] macro_rules! make_array { diff --git a/tests/ui/const-generics/mgca/array-expr-with-struct.rs b/tests/ui/const-generics/mgca/array-expr-with-struct.rs index 1d4c051e53f0b..bdc7b6009ea04 100644 --- a/tests/ui/const-generics/mgca/array-expr-with-struct.rs +++ b/tests/ui/const-generics/mgca/array-expr-with-struct.rs @@ -1,6 +1,5 @@ //@ run-pass -#![feature(gca_min_const_items, gca_macroless_args, adt_const_params)] -#![expect(incomplete_features)] +#![feature(gca_adts, gca_min_const_items, gca_macroless_args, adt_const_params)] #![allow(dead_code)] use std::marker::ConstParamTy; diff --git a/tests/ui/const-generics/mgca/array-expr-with-tuple.rs b/tests/ui/const-generics/mgca/array-expr-with-tuple.rs index f429ca20ce832..a4749c63f3a8a 100644 --- a/tests/ui/const-generics/mgca/array-expr-with-tuple.rs +++ b/tests/ui/const-generics/mgca/array-expr-with-tuple.rs @@ -1,11 +1,11 @@ //@ run-pass #![feature( - gca_min_const_items, - gca_macroless_args, adt_const_params, + gca_adts, + gca_macroless_args, + gca_min_const_items, unsized_const_params )] -#![expect(incomplete_features)] #![allow(dead_code)] fn takes_tuple() {} diff --git a/tests/ui/const-generics/mgca/array-with-wrong-tuple-type.rs b/tests/ui/const-generics/mgca/array-with-wrong-tuple-type.rs index a4131a22337aa..624484c4d1467 100644 --- a/tests/ui/const-generics/mgca/array-with-wrong-tuple-type.rs +++ b/tests/ui/const-generics/mgca/array-with-wrong-tuple-type.rs @@ -1,10 +1,10 @@ #![feature( adt_const_params, - gca_min_const_items, + gca_adts, gca_macroless_args, + gca_min_const_items, unsized_const_params )] -#![allow(incomplete_features)] use std::marker::ConstParamTy; diff --git a/tests/ui/const-generics/mgca/array_elem_type_mismatch.rs b/tests/ui/const-generics/mgca/array_elem_type_mismatch.rs index aa6385c28ded0..7b5f3747f5a58 100644 --- a/tests/ui/const-generics/mgca/array_elem_type_mismatch.rs +++ b/tests/ui/const-generics/mgca/array_elem_type_mismatch.rs @@ -1,10 +1,10 @@ //! Regression test for -#![expect(incomplete_features)] #![feature( adt_const_params, - generic_const_parameter_types, + gca_adts, + gca_macroless_args, gca_min_const_items, - gca_macroless_args + generic_const_parameter_types )] fn foo() {} diff --git a/tests/ui/const-generics/mgca/array_expr_arg_complex.rs b/tests/ui/const-generics/mgca/array_expr_arg_complex.rs index 7edeb25f30e48..308ba90d72646 100644 --- a/tests/ui/const-generics/mgca/array_expr_arg_complex.rs +++ b/tests/ui/const-generics/mgca/array_expr_arg_complex.rs @@ -1,5 +1,4 @@ -#![feature(gca_min_const_items, adt_const_params, unsized_const_params)] -#![expect(incomplete_features)] +#![feature(gca_adts, gca_min_const_items, adt_const_params, unsized_const_params)] use std::gca; diff --git a/tests/ui/const-generics/mgca/array_expr_arg_complex.stderr b/tests/ui/const-generics/mgca/array_expr_arg_complex.stderr index b044775b80c94..fd963a5055d0e 100644 --- a/tests/ui/const-generics/mgca/array_expr_arg_complex.stderr +++ b/tests/ui/const-generics/mgca/array_expr_arg_complex.stderr @@ -1,11 +1,11 @@ error: complex const arguments must be placed inside of a `const` block - --> $DIR/array_expr_arg_complex.rs:15:30 + --> $DIR/array_expr_arg_complex.rs:14:30 | LL | takes_array::<{ gca!([N, N + 1]) }>(); | ^^^^^ error: complex const arguments must be placed inside of a `const` block - --> $DIR/array_expr_arg_complex.rs:16:42 + --> $DIR/array_expr_arg_complex.rs:15:42 | LL | takes_tuple_with_array::<{ gca!(([N, N + 1], N)) }>(); | ^^^^^ diff --git a/tests/ui/const-generics/mgca/generic-args-on-enum-variant-segments-fail.rs b/tests/ui/const-generics/mgca/generic-args-on-enum-variant-segments-fail.rs index 9642a066ad32b..258160017f179 100644 --- a/tests/ui/const-generics/mgca/generic-args-on-enum-variant-segments-fail.rs +++ b/tests/ui/const-generics/mgca/generic-args-on-enum-variant-segments-fail.rs @@ -1,15 +1,18 @@ -#![feature(gca_min_const_items)] -#![feature(adt_const_params, unsized_const_params)] +#![feature(adt_const_params, gca_adts, gca_min_const_items, unsized_const_params)] + use std::gca; + #[derive(PartialEq, Eq, std::marker::ConstParamTy)] pub enum Enum { Unit, Tuple(), Store(T), } + pub mod module { pub use super::Enum::Store; } + fn main() { const _: Enum<()> = gca!(Enum::<()>::Unit::<()>); //~^ ERROR: type arguments are not allowed on unit variant `Unit` [E0109] diff --git a/tests/ui/const-generics/mgca/generic-args-on-enum-variant-segments-fail.stderr b/tests/ui/const-generics/mgca/generic-args-on-enum-variant-segments-fail.stderr index 02903379932ae..c9d8033d15b68 100644 --- a/tests/ui/const-generics/mgca/generic-args-on-enum-variant-segments-fail.stderr +++ b/tests/ui/const-generics/mgca/generic-args-on-enum-variant-segments-fail.stderr @@ -1,5 +1,5 @@ error[E0109]: type arguments are not allowed on unit variant `Unit` - --> $DIR/generic-args-on-enum-variant-segments-fail.rs:14:49 + --> $DIR/generic-args-on-enum-variant-segments-fail.rs:17:49 | LL | const _: Enum<()> = gca!(Enum::<()>::Unit::<()>); | ---- ^^ type argument not allowed @@ -14,7 +14,7 @@ LL + const _: Enum<()> = gca!(Enum::<()>::Unit); | error[E0109]: type arguments are not allowed on tuple variant `Tuple` - --> $DIR/generic-args-on-enum-variant-segments-fail.rs:16:50 + --> $DIR/generic-args-on-enum-variant-segments-fail.rs:19:50 | LL | const _: Enum<()> = gca!(Enum::<()>::Tuple::<()>()); | ----- ^^ type argument not allowed @@ -29,7 +29,7 @@ LL + const _: Enum<()> = gca!(Enum::<()>::Tuple()); | error[E0109]: type arguments are not allowed on module `generic_args_on_enum_variant_segments_fail` - --> $DIR/generic-args-on-enum-variant-segments-fail.rs:18:37 + --> $DIR/generic-args-on-enum-variant-segments-fail.rs:21:37 | LL | const _: Enum<()> = gca!(self::::Enum::<()>::Store); | ---- ^^^ type argument not allowed diff --git a/tests/ui/const-generics/mgca/generic-args-on-enum-variant-segments.rs b/tests/ui/const-generics/mgca/generic-args-on-enum-variant-segments.rs index f89dd4b87f32a..b705e695754da 100644 --- a/tests/ui/const-generics/mgca/generic-args-on-enum-variant-segments.rs +++ b/tests/ui/const-generics/mgca/generic-args-on-enum-variant-segments.rs @@ -1,7 +1,6 @@ //@ check-pass -#![feature(gca_min_const_items)] -#![feature(adt_const_params, unsized_const_params)] +#![feature(adt_const_params, gca_adts, gca_min_const_items, unsized_const_params)] use std::gca; diff --git a/tests/ui/const-generics/mgca/generic_const_items-mismatched-array-len.rs b/tests/ui/const-generics/mgca/generic_const_items-mismatched-array-len.rs index 009722fbe93a5..2377a3051307c 100644 --- a/tests/ui/const-generics/mgca/generic_const_items-mismatched-array-len.rs +++ b/tests/ui/const-generics/mgca/generic_const_items-mismatched-array-len.rs @@ -1,7 +1,11 @@ //! Regression test for #160553 (used to ICE) -#![allow(incomplete_features)] -#![feature(adt_const_params, gca_min_const_items, gca_macroless_args)] -#![feature(generic_const_parameter_types)] +#![feature( + adt_const_params, + gca_adts, + gca_macroless_args, + gca_min_const_items, + generic_const_parameter_types +)] use std::gca; diff --git a/tests/ui/const-generics/mgca/generic_const_items-mismatched-array-len.stderr b/tests/ui/const-generics/mgca/generic_const_items-mismatched-array-len.stderr index 4906b05939900..08c115d5dedb5 100644 --- a/tests/ui/const-generics/mgca/generic_const_items-mismatched-array-len.stderr +++ b/tests/ui/const-generics/mgca/generic_const_items-mismatched-array-len.stderr @@ -1,11 +1,11 @@ error: the constant `*b"\x01\x02\x03"` is not of type `[u8; ::LEN]` - --> $DIR/generic_const_items-mismatched-array-len.rs:23:11 + --> $DIR/generic_const_items-mismatched-array-len.rs:27:11 | LL | foo::() | ^ expected `[u8; ::LEN]`, found `[u8; 3]` | note: required by a const generic parameter in `foo` - --> $DIR/generic_const_items-mismatched-array-len.rs:18:18 + --> $DIR/generic_const_items-mismatched-array-len.rs:22:18 | LL | fn foo::LEN]>() -> [u8; ::LEN] { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this const generic parameter in `foo` diff --git a/tests/ui/const-generics/mgca/generic_const_parameter_types-inferred-array-len.rs b/tests/ui/const-generics/mgca/generic_const_parameter_types-inferred-array-len.rs index 075237a2094b5..bdf7a50bd3041 100644 --- a/tests/ui/const-generics/mgca/generic_const_parameter_types-inferred-array-len.rs +++ b/tests/ui/const-generics/mgca/generic_const_parameter_types-inferred-array-len.rs @@ -1,6 +1,6 @@ //@ check-pass -#![feature(min_adt_const_params, gca_min_const_items, generic_const_parameter_types)] +#![feature(min_adt_const_params, gca_adts, gca_min_const_items, generic_const_parameter_types)] use std::gca; diff --git a/tests/ui/const-generics/mgca/generic_const_type_mismatch.rs b/tests/ui/const-generics/mgca/generic_const_type_mismatch.rs index aecd2b81b1c02..5563d35bb92af 100644 --- a/tests/ui/const-generics/mgca/generic_const_type_mismatch.rs +++ b/tests/ui/const-generics/mgca/generic_const_type_mismatch.rs @@ -1,11 +1,11 @@ //! Regression test for -#![expect(incomplete_features)] #![feature( adt_const_params, - generic_const_items, - generic_const_parameter_types, + const_param_ty_trait, + gca_adts, gca_min_const_items, - const_param_ty_trait + generic_const_items, + generic_const_parameter_types )] use std::gca; diff --git a/tests/ui/const-generics/mgca/invalid-array-in-tuple.rs b/tests/ui/const-generics/mgca/invalid-array-in-tuple.rs index 69c45462fb011..80bfe0d13b35e 100644 --- a/tests/ui/const-generics/mgca/invalid-array-in-tuple.rs +++ b/tests/ui/const-generics/mgca/invalid-array-in-tuple.rs @@ -1,10 +1,10 @@ #![feature( adt_const_params, - gca_min_const_items, + gca_adts, gca_macroless_args, + gca_min_const_items, unsized_const_params )] -#![allow(incomplete_features)] use std::marker::ConstParamTy; diff --git a/tests/ui/const-generics/mgca/mixed-direct-anon-expression-diagnostics.rs b/tests/ui/const-generics/mgca/mixed-direct-anon-expression-diagnostics.rs index 995f715984d54..3654a2c6ff249 100644 --- a/tests/ui/const-generics/mgca/mixed-direct-anon-expression-diagnostics.rs +++ b/tests/ui/const-generics/mgca/mixed-direct-anon-expression-diagnostics.rs @@ -2,8 +2,7 @@ //! things that must be an anon const, are currently less than ideal. This test merely asserts the //! current (bad) state of diagnostics, so we can track improvements over time. -#![feature(gca_min_const_items, min_adt_const_params)] -#![allow(incomplete_features)] +#![feature(gca_adts, gca_min_const_items, min_adt_const_params)] use std::gca; diff --git a/tests/ui/const-generics/mgca/mixed-direct-anon-expression-diagnostics.stderr b/tests/ui/const-generics/mgca/mixed-direct-anon-expression-diagnostics.stderr index 4d5f071037f97..744014bb18ca5 100644 --- a/tests/ui/const-generics/mgca/mixed-direct-anon-expression-diagnostics.stderr +++ b/tests/ui/const-generics/mgca/mixed-direct-anon-expression-diagnostics.stderr @@ -1,11 +1,11 @@ error: complex const arguments must be placed inside of a `const` block - --> $DIR/mixed-direct-anon-expression-diagnostics.rs:15:20 + --> $DIR/mixed-direct-anon-expression-diagnostics.rs:14:20 | LL | f::<{ gca!((N, 1 + 1)) }>(); | ^^^^^ error: generic parameters may not be used in const operations - --> $DIR/mixed-direct-anon-expression-diagnostics.rs:13:12 + --> $DIR/mixed-direct-anon-expression-diagnostics.rs:12:12 | LL | f::<{ (N, 1 + 1) }>(); | ^ diff --git a/tests/ui/const-generics/mgca/nonsensical-negated-literal.rs b/tests/ui/const-generics/mgca/nonsensical-negated-literal.rs index c5f3a6caeb112..5680e0fffe5f9 100644 --- a/tests/ui/const-generics/mgca/nonsensical-negated-literal.rs +++ b/tests/ui/const-generics/mgca/nonsensical-negated-literal.rs @@ -1,10 +1,10 @@ #![feature( adt_const_params, - gca_min_const_items, + gca_adts, gca_macroless_args, + gca_min_const_items, unsized_const_params )] -#![expect(incomplete_features)] use std::marker::ConstParamTy; diff --git a/tests/ui/const-generics/mgca/printing_valtrees_supports_non_values.rs b/tests/ui/const-generics/mgca/printing_valtrees_supports_non_values.rs index 6888a45c3bd3f..d35971802e7f2 100644 --- a/tests/ui/const-generics/mgca/printing_valtrees_supports_non_values.rs +++ b/tests/ui/const-generics/mgca/printing_valtrees_supports_non_values.rs @@ -1,8 +1,7 @@ //! Regression test for //@ edition: 2024 -#![allow(incomplete_features)] -#![feature(gca_min_const_items, gca_macroless_args, adt_const_params)] +#![feature(gca_adts, gca_min_const_items, gca_macroless_args, adt_const_params)] #[derive(Eq, PartialEq, core::marker::ConstParamTy)] struct Foo; diff --git a/tests/ui/const-generics/mgca/printing_valtrees_supports_non_values.stderr b/tests/ui/const-generics/mgca/printing_valtrees_supports_non_values.stderr index 0184aebf157ae..9087f98cbb3f4 100644 --- a/tests/ui/const-generics/mgca/printing_valtrees_supports_non_values.stderr +++ b/tests/ui/const-generics/mgca/printing_valtrees_supports_non_values.stderr @@ -1,29 +1,29 @@ error: the constant `Option::::Some(N)` is not of type `Foo` - --> $DIR/printing_valtrees_supports_non_values.rs:18:13 + --> $DIR/printing_valtrees_supports_non_values.rs:17:13 | LL | foo::<{ Option::Some:: { 0: N } }>; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `Foo`, found `Option` | note: required by a const generic parameter in `foo` - --> $DIR/printing_valtrees_supports_non_values.rs:15:8 + --> $DIR/printing_valtrees_supports_non_values.rs:14:8 | LL | fn foo() {} | ^^^^^^^^^^^^ required by this const generic parameter in `foo` error: the constant `Option::::Some(::ASSOC)` is not of type `Foo` - --> $DIR/printing_valtrees_supports_non_values.rs:23:13 + --> $DIR/printing_valtrees_supports_non_values.rs:22:13 | LL | foo::<{ Option::Some:: { 0: ::ASSOC } }>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `Foo`, found `Option` | note: required by a const generic parameter in `foo` - --> $DIR/printing_valtrees_supports_non_values.rs:15:8 + --> $DIR/printing_valtrees_supports_non_values.rs:14:8 | LL | fn foo() {} | ^^^^^^^^^^^^ required by this const generic parameter in `foo` error[E0277]: the trait bound `T: Trait` is not satisfied - --> $DIR/printing_valtrees_supports_non_values.rs:30:5 + --> $DIR/printing_valtrees_supports_non_values.rs:29:5 | LL | foo::<{ Option::Some:: { 0: ::ASSOC } }>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Trait` is not implemented for `T` @@ -34,25 +34,25 @@ LL | fn test_ice_missing_bound() { | +++++++ error: the constant `Option::::Some(_)` is not of type `Foo` - --> $DIR/printing_valtrees_supports_non_values.rs:30:13 + --> $DIR/printing_valtrees_supports_non_values.rs:29:13 | LL | foo::<{ Option::Some:: { 0: ::ASSOC } }>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `Foo`, found `Option` | note: required by a const generic parameter in `foo` - --> $DIR/printing_valtrees_supports_non_values.rs:15:8 + --> $DIR/printing_valtrees_supports_non_values.rs:14:8 | LL | fn foo() {} | ^^^^^^^^^^^^ required by this const generic parameter in `foo` error: the constant `Option::::Some(_)` is not of type `Foo` - --> $DIR/printing_valtrees_supports_non_values.rs:36:13 + --> $DIR/printing_valtrees_supports_non_values.rs:35:13 | LL | foo::<{ Option::Some:: { 0: _ } }>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `Foo`, found `Option` | note: required by a const generic parameter in `foo` - --> $DIR/printing_valtrees_supports_non_values.rs:15:8 + --> $DIR/printing_valtrees_supports_non_values.rs:14:8 | LL | fn foo() {} | ^^^^^^^^^^^^ required by this const generic parameter in `foo` diff --git a/tests/ui/const-generics/mgca/size-of-generic-ptr-in-array-len.rs b/tests/ui/const-generics/mgca/size-of-generic-ptr-in-array-len.rs index 9a4d4ca05be33..115b32d91ea46 100644 --- a/tests/ui/const-generics/mgca/size-of-generic-ptr-in-array-len.rs +++ b/tests/ui/const-generics/mgca/size-of-generic-ptr-in-array-len.rs @@ -1,6 +1,5 @@ //! regression test for -#![expect(incomplete_features)] -#![feature(gca_min_const_items, gca_macroless_args)] +#![feature(gca_adts, gca_min_const_items, gca_macroless_args, min_adt_const_params)] fn foo() { [0; size_of::<*mut T>()]; diff --git a/tests/ui/const-generics/mgca/size-of-generic-ptr-in-array-len.stderr b/tests/ui/const-generics/mgca/size-of-generic-ptr-in-array-len.stderr index 1abfff9518f44..2d725d66fcdc4 100644 --- a/tests/ui/const-generics/mgca/size-of-generic-ptr-in-array-len.stderr +++ b/tests/ui/const-generics/mgca/size-of-generic-ptr-in-array-len.stderr @@ -1,17 +1,17 @@ error: function items cannot be used as const args - --> $DIR/size-of-generic-ptr-in-array-len.rs:6:9 + --> $DIR/size-of-generic-ptr-in-array-len.rs:5:9 | LL | [0; size_of::<*mut T>()]; | ^^^^^^^^^^^^^^^^^ error: tuple constructor with invalid base path - --> $DIR/size-of-generic-ptr-in-array-len.rs:6:9 + --> $DIR/size-of-generic-ptr-in-array-len.rs:5:9 | LL | [0; size_of::<*mut T>()]; | ^^^^^^^^^^^^^^^^^^^ error: generic parameters may not be used in const operations - --> $DIR/size-of-generic-ptr-in-array-len.rs:9:32 + --> $DIR/size-of-generic-ptr-in-array-len.rs:8:32 | LL | [0; const { size_of::<*mut T>() }]; | ^ diff --git a/tests/ui/const-generics/mgca/syntactic-type-mismatch.rs b/tests/ui/const-generics/mgca/syntactic-type-mismatch.rs index f14309f7492f4..9cca963a13f00 100644 --- a/tests/ui/const-generics/mgca/syntactic-type-mismatch.rs +++ b/tests/ui/const-generics/mgca/syntactic-type-mismatch.rs @@ -1,8 +1,7 @@ // This test ensures proper diagnostics emission during HIR ty lowering // See https://github.com/rust-lang/rust/issues/153254 -#![feature(gca_min_const_items)] -#![expect(incomplete_features)] +#![feature(gca_adts, gca_min_const_items, min_adt_const_params)] use std::gca; diff --git a/tests/ui/const-generics/mgca/syntactic-type-mismatch.stderr b/tests/ui/const-generics/mgca/syntactic-type-mismatch.stderr index 3d09bbdab6072..73f0e76498770 100644 --- a/tests/ui/const-generics/mgca/syntactic-type-mismatch.stderr +++ b/tests/ui/const-generics/mgca/syntactic-type-mismatch.stderr @@ -1,11 +1,11 @@ error[E0121]: the placeholder `_` is not allowed within types on item signatures for constants - --> $DIR/syntactic-type-mismatch.rs:9:11 + --> $DIR/syntactic-type-mismatch.rs:8:11 | LL | const T0: _ = gca!(()); | ^ not allowed in type signatures error: missing type for `const` item - --> $DIR/syntactic-type-mismatch.rs:12:9 + --> $DIR/syntactic-type-mismatch.rs:11:9 | LL | const T1 = gca!([0]); | ^ diff --git a/tests/ui/const-generics/mgca/tuple_ctor_arg_simple.rs b/tests/ui/const-generics/mgca/tuple_ctor_arg_simple.rs index da66ce8195d0f..318bf82691316 100644 --- a/tests/ui/const-generics/mgca/tuple_ctor_arg_simple.rs +++ b/tests/ui/const-generics/mgca/tuple_ctor_arg_simple.rs @@ -1,6 +1,5 @@ //@ run-pass -#![feature(gca_min_const_items, gca_macroless_args, adt_const_params)] -#![expect(incomplete_features)] +#![feature(gca_adts, gca_min_const_items, gca_macroless_args, adt_const_params)] #![allow(dead_code)] use std::marker::ConstParamTy; diff --git a/tests/ui/const-generics/mgca/tuple_ctor_complex_args.rs b/tests/ui/const-generics/mgca/tuple_ctor_complex_args.rs index 42ebe44172b05..f16106cd798d0 100644 --- a/tests/ui/const-generics/mgca/tuple_ctor_complex_args.rs +++ b/tests/ui/const-generics/mgca/tuple_ctor_complex_args.rs @@ -1,5 +1,4 @@ -#![feature(gca_min_const_items, gca_macroless_args, adt_const_params)] -#![expect(incomplete_features)] +#![feature(gca_adts, gca_min_const_items, gca_macroless_args, adt_const_params)] use std::gca; use std::marker::ConstParamTy; diff --git a/tests/ui/const-generics/mgca/tuple_ctor_complex_args.stderr b/tests/ui/const-generics/mgca/tuple_ctor_complex_args.stderr index 925572c27cb0b..e3f6b3b8834b2 100644 --- a/tests/ui/const-generics/mgca/tuple_ctor_complex_args.stderr +++ b/tests/ui/const-generics/mgca/tuple_ctor_complex_args.stderr @@ -1,11 +1,11 @@ error: complex const arguments must be placed inside of a `const` block - --> $DIR/tuple_ctor_complex_args.rs:13:31 + --> $DIR/tuple_ctor_complex_args.rs:12:31 | LL | with_point::<{ gca!(Point(N + 1, N)) }>(); | ^^^^^ error: generic parameters may not be used in const operations - --> $DIR/tuple_ctor_complex_args.rs:16:34 + --> $DIR/tuple_ctor_complex_args.rs:15:34 | LL | with_point::<{ Point(const { N + 1 }, N) }>(); | ^ diff --git a/tests/ui/const-generics/mgca/tuple_ctor_erroneous.rs b/tests/ui/const-generics/mgca/tuple_ctor_erroneous.rs index db158f3f38b00..6e720aba1b4ac 100644 --- a/tests/ui/const-generics/mgca/tuple_ctor_erroneous.rs +++ b/tests/ui/const-generics/mgca/tuple_ctor_erroneous.rs @@ -1,5 +1,4 @@ -#![feature(gca_min_const_items, gca_macroless_args, adt_const_params)] -#![expect(incomplete_features)] +#![feature(gca_adts, gca_min_const_items, gca_macroless_args, adt_const_params)] use std::gca; use std::marker::ConstParamTy; diff --git a/tests/ui/const-generics/mgca/tuple_ctor_erroneous.stderr b/tests/ui/const-generics/mgca/tuple_ctor_erroneous.stderr index 56b11b2894aa2..b9bbfcc37469d 100644 --- a/tests/ui/const-generics/mgca/tuple_ctor_erroneous.stderr +++ b/tests/ui/const-generics/mgca/tuple_ctor_erroneous.stderr @@ -1,5 +1,5 @@ error[E0425]: cannot find function, tuple struct or tuple variant `UnresolvedIdent` in this scope - --> $DIR/tuple_ctor_erroneous.rs:30:23 + --> $DIR/tuple_ctor_erroneous.rs:29:23 | LL | accepts_point::<{ UnresolvedIdent(N, N) }>(); | ^^^^^^^^^^^^^^^ not found in this scope @@ -10,61 +10,61 @@ LL | fn test_errors() { | +++++++++++++++++++++++++++++++++++ error: tuple constructor has 2 arguments but 1 were provided - --> $DIR/tuple_ctor_erroneous.rs:24:23 + --> $DIR/tuple_ctor_erroneous.rs:23:23 | LL | accepts_point::<{ Point(N) }>(); | ^^^^^^^^ error: tuple constructor has 2 arguments but 3 were provided - --> $DIR/tuple_ctor_erroneous.rs:27:23 + --> $DIR/tuple_ctor_erroneous.rs:26:23 | LL | accepts_point::<{ Point(N, N, N) }>(); | ^^^^^^^^^^^^^^ error: tuple constructor with invalid base path - --> $DIR/tuple_ctor_erroneous.rs:30:23 + --> $DIR/tuple_ctor_erroneous.rs:29:23 | LL | accepts_point::<{ UnresolvedIdent(N, N) }>(); | ^^^^^^^^^^^^^^^^^^^^^ error: function items cannot be used as const args - --> $DIR/tuple_ctor_erroneous.rs:34:23 + --> $DIR/tuple_ctor_erroneous.rs:33:23 | LL | accepts_point::<{ non_ctor(N, N) }>(); | ^^^^^^^^ error: tuple constructor with invalid base path - --> $DIR/tuple_ctor_erroneous.rs:34:23 + --> $DIR/tuple_ctor_erroneous.rs:33:23 | LL | accepts_point::<{ non_ctor(N, N) }>(); | ^^^^^^^^^^^^^^ error: tuple constructor with invalid base path - --> $DIR/tuple_ctor_erroneous.rs:38:23 + --> $DIR/tuple_ctor_erroneous.rs:37:23 | LL | accepts_point::<{ CONST_ITEM(N, N) }>(); | ^^^^^^^^^^^^^^^^ error: the constant `Point` is not of type `Point` - --> $DIR/tuple_ctor_erroneous.rs:41:23 + --> $DIR/tuple_ctor_erroneous.rs:40:23 | LL | accepts_point::<{ Point }>(); | ^^^^^ expected `Point`, found struct constructor | note: required by a const generic parameter in `accepts_point` - --> $DIR/tuple_ctor_erroneous.rs:18:18 + --> $DIR/tuple_ctor_erroneous.rs:17:18 | LL | fn accepts_point() {} | ^^^^^^^^^^^^^^ required by this const generic parameter in `accepts_point` error: the constant `MyEnum::::Variant` is not of type `MyEnum` - --> $DIR/tuple_ctor_erroneous.rs:44:22 + --> $DIR/tuple_ctor_erroneous.rs:43:22 | LL | accepts_enum::<{ MyEnum::Variant:: }>(); | ^^^^^^^^^^^^^^^^^^^^^^ expected `MyEnum`, found enum constructor | note: required by a const generic parameter in `accepts_enum` - --> $DIR/tuple_ctor_erroneous.rs:19:17 + --> $DIR/tuple_ctor_erroneous.rs:18:17 | LL | fn accepts_enum>() {} | ^^^^^^^^^^^^^^^^^^^^ required by this const generic parameter in `accepts_enum` diff --git a/tests/ui/const-generics/mgca/tuple_ctor_nested.rs b/tests/ui/const-generics/mgca/tuple_ctor_nested.rs index 096f56d9512a8..e03db5d7b0fdc 100644 --- a/tests/ui/const-generics/mgca/tuple_ctor_nested.rs +++ b/tests/ui/const-generics/mgca/tuple_ctor_nested.rs @@ -1,6 +1,5 @@ //@ run-pass -#![feature(gca_min_const_items, gca_macroless_args, adt_const_params)] -#![expect(incomplete_features)] +#![feature(gca_adts, gca_min_const_items, gca_macroless_args, adt_const_params)] use std::marker::ConstParamTy; diff --git a/tests/ui/const-generics/mgca/tuple_ctor_type_relative.rs b/tests/ui/const-generics/mgca/tuple_ctor_type_relative.rs index 6a5c28bca6898..eaa1744bc5bbd 100644 --- a/tests/ui/const-generics/mgca/tuple_ctor_type_relative.rs +++ b/tests/ui/const-generics/mgca/tuple_ctor_type_relative.rs @@ -1,6 +1,5 @@ //@ run-pass -#![feature(gca_min_const_items, gca_macroless_args, adt_const_params)] -#![expect(incomplete_features)] +#![feature(gca_adts, gca_min_const_items, gca_macroless_args, adt_const_params)] use std::marker::ConstParamTy; diff --git a/tests/ui/const-generics/mgca/tuple_expr_arg_bad-issue-151048.rs b/tests/ui/const-generics/mgca/tuple_expr_arg_bad-issue-151048.rs index 50abdd97e908f..53216e3c5c14b 100644 --- a/tests/ui/const-generics/mgca/tuple_expr_arg_bad-issue-151048.rs +++ b/tests/ui/const-generics/mgca/tuple_expr_arg_bad-issue-151048.rs @@ -1,5 +1,4 @@ -#![feature(gca_min_const_items, gca_macroless_args)] -#![expect(incomplete_features)] +#![feature(gca_adts, gca_min_const_items, gca_macroless_args, min_adt_const_params)] struct Y { stuff: [u8; { ([1, 2], 3, [4, 5]) }], //~ ERROR expected `usize`, found `([1, 2], 3, [4, 5])` diff --git a/tests/ui/const-generics/mgca/tuple_expr_arg_bad-issue-151048.stderr b/tests/ui/const-generics/mgca/tuple_expr_arg_bad-issue-151048.stderr index aa54a5ae77048..098a676d6d918 100644 --- a/tests/ui/const-generics/mgca/tuple_expr_arg_bad-issue-151048.stderr +++ b/tests/ui/const-generics/mgca/tuple_expr_arg_bad-issue-151048.stderr @@ -1,5 +1,5 @@ error: expected `usize`, found `([1, 2], 3, [4, 5])` - --> $DIR/tuple_expr_arg_bad-issue-151048.rs:5:19 + --> $DIR/tuple_expr_arg_bad-issue-151048.rs:4:19 | LL | stuff: [u8; { ([1, 2], 3, [4, 5]) }], | ^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/const-generics/mgca/tuple_expr_arg_complex.rs b/tests/ui/const-generics/mgca/tuple_expr_arg_complex.rs index 2b39a8b585faf..ff73b128fd522 100644 --- a/tests/ui/const-generics/mgca/tuple_expr_arg_complex.rs +++ b/tests/ui/const-generics/mgca/tuple_expr_arg_complex.rs @@ -1,5 +1,4 @@ -#![feature(gca_min_const_items, adt_const_params, unsized_const_params)] -#![expect(incomplete_features)] +#![feature(gca_adts, gca_min_const_items, adt_const_params, unsized_const_params)] use std::gca; diff --git a/tests/ui/const-generics/mgca/tuple_expr_arg_complex.stderr b/tests/ui/const-generics/mgca/tuple_expr_arg_complex.stderr index e6f004b2f9d68..cdee5ff343ba3 100644 --- a/tests/ui/const-generics/mgca/tuple_expr_arg_complex.stderr +++ b/tests/ui/const-generics/mgca/tuple_expr_arg_complex.stderr @@ -1,23 +1,23 @@ error: complex const arguments must be placed inside of a `const` block - --> $DIR/tuple_expr_arg_complex.rs:15:30 + --> $DIR/tuple_expr_arg_complex.rs:14:30 | LL | takes_tuple::<{ gca!((N, N + 1)) }>(); | ^^^^^ error: complex const arguments must be placed inside of a `const` block - --> $DIR/tuple_expr_arg_complex.rs:16:30 + --> $DIR/tuple_expr_arg_complex.rs:15:30 | LL | takes_tuple::<{ gca!((N, T::ASSOC + 1)) }>(); | ^^^^^^^^^^^^ error: complex const arguments must be placed inside of a `const` block - --> $DIR/tuple_expr_arg_complex.rs:18:41 + --> $DIR/tuple_expr_arg_complex.rs:17:41 | LL | takes_nested_tuple::<{ gca!((N, (N, N + 1))) }>(); | ^^^^^ error: generic parameters may not be used in const operations - --> $DIR/tuple_expr_arg_complex.rs:19:49 + --> $DIR/tuple_expr_arg_complex.rs:18:49 | LL | takes_nested_tuple::<{ gca!((N, (N, const { N + 1 }))) }>(); | ^ diff --git a/tests/ui/const-generics/mgca/tuple_expr_arg_macroless.rs b/tests/ui/const-generics/mgca/tuple_expr_arg_macroless.rs index 2b67665d7281b..685d29ed4cec1 100644 --- a/tests/ui/const-generics/mgca/tuple_expr_arg_macroless.rs +++ b/tests/ui/const-generics/mgca/tuple_expr_arg_macroless.rs @@ -1,12 +1,12 @@ //@ check-pass #![feature( - gca_min_const_items, - gca_macroless_args, adt_const_params, + gca_adts, + gca_macroless_args, + gca_min_const_items, unsized_const_params )] -#![expect(incomplete_features)] trait Trait { #[rustc_always_gca] diff --git a/tests/ui/const-generics/mgca/tuple_expr_arg_mismatch_type.rs b/tests/ui/const-generics/mgca/tuple_expr_arg_mismatch_type.rs index 780b757c0df1c..5247fcdec0e2f 100644 --- a/tests/ui/const-generics/mgca/tuple_expr_arg_mismatch_type.rs +++ b/tests/ui/const-generics/mgca/tuple_expr_arg_mismatch_type.rs @@ -1,5 +1,4 @@ -#![feature(gca_min_const_items, gca_macroless_args)] -#![expect(incomplete_features)] +#![feature(gca_adts, gca_min_const_items, gca_macroless_args, min_adt_const_params)] pub fn takes_nested_tuple() { takes_nested_tuple::<{ () }> //~ ERROR expected `u32`, found `()` diff --git a/tests/ui/const-generics/mgca/tuple_expr_arg_mismatch_type.stderr b/tests/ui/const-generics/mgca/tuple_expr_arg_mismatch_type.stderr index d8314a6b6a57e..a45ca91d69e3c 100644 --- a/tests/ui/const-generics/mgca/tuple_expr_arg_mismatch_type.stderr +++ b/tests/ui/const-generics/mgca/tuple_expr_arg_mismatch_type.stderr @@ -1,5 +1,5 @@ error: expected `u32`, found `()` - --> $DIR/tuple_expr_arg_mismatch_type.rs:5:28 + --> $DIR/tuple_expr_arg_mismatch_type.rs:4:28 | LL | takes_nested_tuple::<{ () }> | ^^ diff --git a/tests/ui/const-generics/mgca/tuple_expr_arg_simple.rs b/tests/ui/const-generics/mgca/tuple_expr_arg_simple.rs index 8c4f64adf34dc..315636bc2e75d 100644 --- a/tests/ui/const-generics/mgca/tuple_expr_arg_simple.rs +++ b/tests/ui/const-generics/mgca/tuple_expr_arg_simple.rs @@ -1,7 +1,6 @@ //@ check-pass -#![feature(gca_min_const_items, adt_const_params, unsized_const_params)] -#![expect(incomplete_features)] +#![feature(gca_adts, gca_min_const_items, adt_const_params, unsized_const_params)] use std::gca; diff --git a/tests/ui/const-generics/mgca/tuple_expr_arg_unbounded_assoc_const.rs b/tests/ui/const-generics/mgca/tuple_expr_arg_unbounded_assoc_const.rs index 7d8a1a1264bd9..c1c118d575a69 100644 --- a/tests/ui/const-generics/mgca/tuple_expr_arg_unbounded_assoc_const.rs +++ b/tests/ui/const-generics/mgca/tuple_expr_arg_unbounded_assoc_const.rs @@ -1,4 +1,4 @@ -#![feature(gca_min_const_items, gca_macroless_args, adt_const_params)] +#![feature(gca_adts, gca_min_const_items, gca_macroless_args, adt_const_params)] // Regression test for an ICE in privacy checking while walking the `T` qself // of `T::ASSOC` inside a tuple const argument. diff --git a/tests/ui/const-generics/mgca/tuple_expr_type_mismatch.rs b/tests/ui/const-generics/mgca/tuple_expr_type_mismatch.rs index e67a5a0a72e54..04533b8d96455 100644 --- a/tests/ui/const-generics/mgca/tuple_expr_type_mismatch.rs +++ b/tests/ui/const-generics/mgca/tuple_expr_type_mismatch.rs @@ -1,9 +1,9 @@ //! Regression test for -#![expect(incomplete_features)] #![feature( adt_const_params, - gca_min_const_items, + gca_adts, gca_macroless_args, + gca_min_const_items, unsized_const_params )] fn foo() {} diff --git a/tests/ui/const-generics/mgca/type_const-adt-expr-missing-field.rs b/tests/ui/const-generics/mgca/type_const-adt-expr-missing-field.rs index 46c661cfb96ce..e7f396543e66d 100644 --- a/tests/ui/const-generics/mgca/type_const-adt-expr-missing-field.rs +++ b/tests/ui/const-generics/mgca/type_const-adt-expr-missing-field.rs @@ -1,9 +1,13 @@ // Regression test for https://github.com/rust-lang/rust/issues/154632 -#![feature(generic_const_exprs)] -#![feature(gca_min_const_items)] -#![feature(gca_macroless_args)] -#![feature(generic_const_items)] +#![feature( + gca_adts, + gca_macroless_args, + gca_min_const_items, + generic_const_exprs, + generic_const_items, + min_adt_const_params +)] use std::gca; diff --git a/tests/ui/const-generics/mgca/type_const-adt-expr-missing-field.stderr b/tests/ui/const-generics/mgca/type_const-adt-expr-missing-field.stderr index 623ae087687f5..7135773752b55 100644 --- a/tests/ui/const-generics/mgca/type_const-adt-expr-missing-field.stderr +++ b/tests/ui/const-generics/mgca/type_const-adt-expr-missing-field.stderr @@ -1,5 +1,5 @@ error[E0573]: cannot find type `ADD1` in this scope - --> $DIR/type_const-adt-expr-missing-field.rs:12:22 + --> $DIR/type_const-adt-expr-missing-field.rs:16:22 | LL | const AliasFnUnused: ADD1 = gca!(ADD1::<{ Some:: {} }>); | ^^^^ not found in this scope @@ -7,7 +7,7 @@ LL | const AliasFnUnused: ADD1 = gca!(ADD1::<{ Some:: {} }>); = note: a constant named `ADD1` exists in another namespace error: unconstrained generic constant - --> $DIR/type_const-adt-expr-missing-field.rs:10:1 + --> $DIR/type_const-adt-expr-missing-field.rs:14:1 | LL | const ADD1: usize = gca!(const { N + 1 }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -18,7 +18,7 @@ LL | const ADD1: usize where [(); const { N + 1 }]: = gca!(const | ++++++++++++++++++++++++++++ error: struct expression with missing field initialiser for `0` - --> $DIR/type_const-adt-expr-missing-field.rs:12:43 + --> $DIR/type_const-adt-expr-missing-field.rs:16:43 | LL | const AliasFnUnused: ADD1 = gca!(ADD1::<{ Some:: {} }>); | ^^^^^^^^^^^^^^^^ diff --git a/tests/ui/const-generics/mgca/type_const-generic-param-in-type.nogate.stderr b/tests/ui/const-generics/mgca/type_const-generic-param-in-type.nogate.stderr index 46be38bb1a53d..aac10bc622b5d 100644 --- a/tests/ui/const-generics/mgca/type_const-generic-param-in-type.nogate.stderr +++ b/tests/ui/const-generics/mgca/type_const-generic-param-in-type.nogate.stderr @@ -1,53 +1,53 @@ error[E0770]: the type of const parameters must not depend on other generic parameters - --> $DIR/type_const-generic-param-in-type.rs:13:1 + --> $DIR/type_const-generic-param-in-type.rs:18:1 | LL | const FOO: [T; 0] = gca!([]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the type `[T; 0]` must not depend on other generic parameter error[E0770]: the type of const parameters must not depend on other generic parameters - --> $DIR/type_const-generic-param-in-type.rs:16:1 + --> $DIR/type_const-generic-param-in-type.rs:21:1 | LL | const BAR: StructWithConstParam = gca!(StructWithConstParam::); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the type `StructWithConstParam` must not depend on other generic parameter error[E0770]: the type of const parameters must not depend on other generic parameters - --> $DIR/type_const-generic-param-in-type.rs:19:1 + --> $DIR/type_const-generic-param-in-type.rs:24:1 | LL | const BAZ<'a>: [&'a (); 0] = gca!([]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ the type `[&'a (); 0]` must not depend on other generic parameter error[E0770]: the type of const parameters must not depend on other generic parameters - --> $DIR/type_const-generic-param-in-type.rs:37:5 + --> $DIR/type_const-generic-param-in-type.rs:42:5 | LL | const ASSOC: [T; 0] = gca!([]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the type `[T; 0]` must not depend on other generic parameter error[E0770]: the type of const parameters must not depend on other generic parameters - --> $DIR/type_const-generic-param-in-type.rs:40:5 + --> $DIR/type_const-generic-param-in-type.rs:45:5 | LL | const ASSOC_CONST: StructWithConstParam = gca!(StructWithConstParam::); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the type `StructWithConstParam` must not depend on other generic parameter error[E0770]: the type of const parameters must not depend on other generic parameters - --> $DIR/type_const-generic-param-in-type.rs:43:5 + --> $DIR/type_const-generic-param-in-type.rs:48:5 | LL | const ASSOC_LT<'a>: [&'a (); 0] = gca!([]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the type `[&'a (); 0]` must not depend on other generic parameter error[E0770]: the type of const parameters must not depend on other generic parameters - --> $DIR/type_const-generic-param-in-type.rs:24:5 + --> $DIR/type_const-generic-param-in-type.rs:29:5 | LL | const ASSOC: [T; 0]; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the type `[T; 0]` must not depend on other generic parameter error[E0770]: the type of const parameters must not depend on other generic parameters - --> $DIR/type_const-generic-param-in-type.rs:28:5 + --> $DIR/type_const-generic-param-in-type.rs:33:5 | LL | const ASSOC_CONST: StructWithConstParam; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the type `StructWithConstParam` must not depend on other generic parameter error[E0770]: the type of const parameters must not depend on other generic parameters - --> $DIR/type_const-generic-param-in-type.rs:32:5 + --> $DIR/type_const-generic-param-in-type.rs:37:5 | LL | const ASSOC_LT<'a>: [&'a (); 0]; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the type `[&'a (); 0]` must not depend on other generic parameter diff --git a/tests/ui/const-generics/mgca/type_const-generic-param-in-type.rs b/tests/ui/const-generics/mgca/type_const-generic-param-in-type.rs index 80293325d8800..b64853f37bb8f 100644 --- a/tests/ui/const-generics/mgca/type_const-generic-param-in-type.rs +++ b/tests/ui/const-generics/mgca/type_const-generic-param-in-type.rs @@ -1,7 +1,12 @@ //@ revisions: nogate gate //@ [gate] check-pass -#![expect(incomplete_features)] -#![feature(adt_const_params, unsized_const_params, gca_min_const_items, generic_const_items)] +#![feature( + adt_const_params, + gca_adts, + gca_min_const_items, + generic_const_items, + unsized_const_params +)] #![cfg_attr(gate, feature(generic_const_parameter_types))] use std::gca; diff --git a/tests/ui/const-generics/mgca/wrapped_array_elem_type_mismatch.rs b/tests/ui/const-generics/mgca/wrapped_array_elem_type_mismatch.rs index a80f602fbf96a..c3ba68ae5213d 100644 --- a/tests/ui/const-generics/mgca/wrapped_array_elem_type_mismatch.rs +++ b/tests/ui/const-generics/mgca/wrapped_array_elem_type_mismatch.rs @@ -1,5 +1,5 @@ #![expect(incomplete_features)] -#![feature(adt_const_params, gca_min_const_items, gca_macroless_args)] +#![feature(adt_const_params, gca_min_const_items, gca_macroless_args, gca_adts)] struct ArrWrap; diff --git a/tests/ui/const-generics/mgca/wrong_type_const_arr_diag.rs b/tests/ui/const-generics/mgca/wrong_type_const_arr_diag.rs index f5d3c06d7bc8e..3ca3c9bcb7536 100644 --- a/tests/ui/const-generics/mgca/wrong_type_const_arr_diag.rs +++ b/tests/ui/const-generics/mgca/wrong_type_const_arr_diag.rs @@ -1,7 +1,6 @@ // This test causes ERROR: mismatched types [E0308] // and makes rustc to print array from const arguments -#![feature(gca_min_const_items, gca_macroless_args, adt_const_params)] -#![allow(incomplete_features)] +#![feature(gca_adts, gca_min_const_items, gca_macroless_args, adt_const_params)] struct TakesArr; diff --git a/tests/ui/const-generics/mgca/wrong_type_const_arr_diag.stderr b/tests/ui/const-generics/mgca/wrong_type_const_arr_diag.stderr index a2e212b28ec6f..3e2ee8c5f718a 100644 --- a/tests/ui/const-generics/mgca/wrong_type_const_arr_diag.stderr +++ b/tests/ui/const-generics/mgca/wrong_type_const_arr_diag.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/wrong_type_const_arr_diag.rs:9:32 + --> $DIR/wrong_type_const_arr_diag.rs:8:32 | LL | let _: TakesArr<{ [N] }> = TakesArr::<{ [1] }>; | ----------------- ^^^^^^^^^^^^^^^^^^^ expected `[N]`, found `*b"\x01"` diff --git a/tests/ui/const-generics/mgca/wrong_type_const_arr_diag_trait.rs b/tests/ui/const-generics/mgca/wrong_type_const_arr_diag_trait.rs index 225d3ef226cba..ee91819204859 100644 --- a/tests/ui/const-generics/mgca/wrong_type_const_arr_diag_trait.rs +++ b/tests/ui/const-generics/mgca/wrong_type_const_arr_diag_trait.rs @@ -1,7 +1,6 @@ // This test causes ERROR: mismatched types [E0308] // and makes rustc to print array from const arguments -#![feature(gca_min_const_items, gca_macroless_args, adt_const_params, trivial_bounds)] -#![allow(incomplete_features)] +#![feature(gca_adts, gca_min_const_items, gca_macroless_args, adt_const_params, trivial_bounds)] trait Trait { #[rustc_always_gca] diff --git a/tests/ui/const-generics/mgca/wrong_type_const_arr_diag_trait.stderr b/tests/ui/const-generics/mgca/wrong_type_const_arr_diag_trait.stderr index 66de4a4d6a864..5338a7bfbd973 100644 --- a/tests/ui/const-generics/mgca/wrong_type_const_arr_diag_trait.stderr +++ b/tests/ui/const-generics/mgca/wrong_type_const_arr_diag_trait.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/wrong_type_const_arr_diag_trait.rs:17:51 + --> $DIR/wrong_type_const_arr_diag_trait.rs:16:51 | LL | let _: TakesArr<{ [::ASSOC] }> = TakesArr::<{ [1] }>; | ------------------------------------ ^^^^^^^^^^^^^^^^^^^ expected `[::ASSOC]`, found `*b"\x01"` diff --git a/tests/ui/const-generics/min_adt_const_params/tuple_with_infer_arg.rs b/tests/ui/const-generics/min_adt_const_params/tuple_with_infer_arg.rs index 675cecc68f884..e6d8e362dd191 100644 --- a/tests/ui/const-generics/min_adt_const_params/tuple_with_infer_arg.rs +++ b/tests/ui/const-generics/min_adt_const_params/tuple_with_infer_arg.rs @@ -1,6 +1,6 @@ //@ check-pass -#![feature(min_adt_const_params, gca_min_const_items, gca_macroless_args)] +#![feature(min_adt_const_params, gca_adts, gca_min_const_items, gca_macroless_args)] struct S; diff --git a/tests/ui/feature-gates/feature-gate-gca-adts.rs b/tests/ui/feature-gates/feature-gate-gca-adts.rs new file mode 100644 index 0000000000000..6b77771fc4cdc --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-gca-adts.rs @@ -0,0 +1,24 @@ +//@ revisions: without without_with_min_items with +//@[with] check-pass + +#![feature(min_adt_const_params)] +#![cfg_attr(with, feature(gca_adts))] +// gca_min_const_items enables the gca! macro as well, which is vaguely interesting to test too +#![cfg_attr(without_with_min_items, feature(gca_min_const_items))] + +use std::gca; +//[without]~^ ERROR use of unstable library feature `gca_min_const_items` + +struct S; + +fn main() { + let _: S = S::; + //[without_with_min_items]~^ ERROR complex const arguments must be placed inside of a `const` block + //[without_with_min_items]~| ERROR complex const arguments must be placed inside of a `const` block + //[without]~^^^ ERROR use of unstable library feature `gca_min_const_items` + //[without]~| ERROR use of unstable library feature `gca_min_const_items` + //[without]~| ERROR expected type, found `gca!()` constant + //[without]~| ERROR expected type, found `gca!()` constant + //[without]~| ERROR type provided when a constant was expected + //[without]~| ERROR type provided when a constant was expected +} diff --git a/tests/ui/feature-gates/feature-gate-gca-adts.without.stderr b/tests/ui/feature-gates/feature-gate-gca-adts.without.stderr new file mode 100644 index 0000000000000..9076e2313db09 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-gca-adts.without.stderr @@ -0,0 +1,58 @@ +error[E0658]: use of unstable library feature `gca_min_const_items` + --> $DIR/feature-gate-gca-adts.rs:15:14 + | +LL | let _: S = S::; + | ^^^ + | + = note: see issue #132980 for more information + = help: add `#![feature(gca_min_const_items)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: use of unstable library feature `gca_min_const_items` + --> $DIR/feature-gate-gca-adts.rs:15:34 + | +LL | let _: S = S::; + | ^^^ + | + = note: see issue #132980 for more information + = help: add `#![feature(gca_min_const_items)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: expected type, found `gca!()` constant + --> $DIR/feature-gate-gca-adts.rs:15:14 + | +LL | let _: S = S::; + | ^^^^^^^^^^^^ + +error: expected type, found `gca!()` constant + --> $DIR/feature-gate-gca-adts.rs:15:34 + | +LL | let _: S = S::; + | ^^^^^^^^^^^^ + +error[E0658]: use of unstable library feature `gca_min_const_items` + --> $DIR/feature-gate-gca-adts.rs:9:5 + | +LL | use std::gca; + | ^^^^^^^^ + | + = note: see issue #132980 for more information + = help: add `#![feature(gca_min_const_items)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0747]: type provided when a constant was expected + --> $DIR/feature-gate-gca-adts.rs:15:14 + | +LL | let _: S = S::; + | ^^^^^^^^^^^^ + +error[E0747]: type provided when a constant was expected + --> $DIR/feature-gate-gca-adts.rs:15:34 + | +LL | let _: S = S::; + | ^^^^^^^^^^^^ + +error: aborting due to 7 previous errors + +Some errors have detailed explanations: E0658, E0747. +For more information about an error, try `rustc --explain E0658`. diff --git a/tests/ui/feature-gates/feature-gate-gca-adts.without_with_min_items.stderr b/tests/ui/feature-gates/feature-gate-gca-adts.without_with_min_items.stderr new file mode 100644 index 0000000000000..84c64b828b3fc --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-gca-adts.without_with_min_items.stderr @@ -0,0 +1,14 @@ +error: complex const arguments must be placed inside of a `const` block + --> $DIR/feature-gate-gca-adts.rs:15:19 + | +LL | let _: S = S::; + | ^^^^^^ + +error: complex const arguments must be placed inside of a `const` block + --> $DIR/feature-gate-gca-adts.rs:15:39 + | +LL | let _: S = S::; + | ^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/tests/ui/transmutability/non_scalar_alignment_value.rs b/tests/ui/transmutability/non_scalar_alignment_value.rs index 24e97a2a55356..937fa39876303 100644 --- a/tests/ui/transmutability/non_scalar_alignment_value.rs +++ b/tests/ui/transmutability/non_scalar_alignment_value.rs @@ -1,4 +1,10 @@ -#![feature(gca_min_const_items, gca_macroless_args, transmutability)] +#![feature( + gca_adts, + gca_macroless_args, + gca_min_const_items, + min_adt_const_params, + transmutability +)] mod assert { use std::mem::{Assume, TransmuteFrom}; diff --git a/tests/ui/transmutability/non_scalar_alignment_value.stderr b/tests/ui/transmutability/non_scalar_alignment_value.stderr index 80a125d540a8f..020a557573bda 100644 --- a/tests/ui/transmutability/non_scalar_alignment_value.stderr +++ b/tests/ui/transmutability/non_scalar_alignment_value.stderr @@ -1,23 +1,23 @@ error: struct expression with missing field initialiser for `alignment` - --> $DIR/non_scalar_alignment_value.rs:12:32 + --> $DIR/non_scalar_alignment_value.rs:18:32 | LL | alignment: Assume {}, | ^^^^^^^^^ error: struct expression with missing field initialiser for `lifetimes` - --> $DIR/non_scalar_alignment_value.rs:12:32 + --> $DIR/non_scalar_alignment_value.rs:18:32 | LL | alignment: Assume {}, | ^^^^^^^^^ error: struct expression with missing field initialiser for `safety` - --> $DIR/non_scalar_alignment_value.rs:12:32 + --> $DIR/non_scalar_alignment_value.rs:18:32 | LL | alignment: Assume {}, | ^^^^^^^^^ error: struct expression with missing field initialiser for `validity` - --> $DIR/non_scalar_alignment_value.rs:12:32 + --> $DIR/non_scalar_alignment_value.rs:18:32 | LL | alignment: Assume {}, | ^^^^^^^^^ diff --git a/tests/ui/unpretty/struct-exprs-tuple-call-pretty-printing.rs b/tests/ui/unpretty/struct-exprs-tuple-call-pretty-printing.rs index a155d9ade5cf3..9895ccbfd0926 100644 --- a/tests/ui/unpretty/struct-exprs-tuple-call-pretty-printing.rs +++ b/tests/ui/unpretty/struct-exprs-tuple-call-pretty-printing.rs @@ -1,8 +1,7 @@ //@ compile-flags: -Zunpretty=hir //@ check-pass -#![feature(gca_min_const_items, gca_macroless_args, adt_const_params)] -#![expect(incomplete_features)] +#![feature(adt_const_params, gca_adts, gca_min_const_items, gca_macroless_args)] #![allow(dead_code)] use std::marker::ConstParamTy; diff --git a/tests/ui/unpretty/struct-exprs-tuple-call-pretty-printing.stdout b/tests/ui/unpretty/struct-exprs-tuple-call-pretty-printing.stdout index c2c79249a6bd4..f117acc810606 100644 --- a/tests/ui/unpretty/struct-exprs-tuple-call-pretty-printing.stdout +++ b/tests/ui/unpretty/struct-exprs-tuple-call-pretty-printing.stdout @@ -1,10 +1,9 @@ //@ compile-flags: -Zunpretty=hir //@ check-pass -#![expect(incomplete_features)] #![allow(dead_code)] -#![attr = Feature([gca_min_const_items#0, gca_macroless_args#0, -adt_const_params#0])] +#![attr = Feature([adt_const_params#0, gca_adts#0, gca_min_const_items#0, +gca_macroless_args#0])] extern crate std; #[attr = PreludeImport] use ::std::prelude::rust_2015::*; From ceee44bc9df314e1b522e6e479764b96ad5db82d Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Mon, 28 Sep 2026 16:01:46 +0200 Subject: [PATCH 24/30] Force the correct type variable to never for method resolution on an adjusted never type --- compiler/rustc_hir_typeck/src/method/probe.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs index 178585984cf27..aee246a61fb1e 100644 --- a/compiler/rustc_hir_typeck/src/method/probe.rs +++ b/compiler/rustc_hir_typeck/src/method/probe.rs @@ -524,8 +524,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { span, MethodCallOnDivergingInferenceVariable, ); - let root_ty = Ty::new_var(self.tcx, ty_id); - self.demand_eqtype(span, root_ty, self.tcx.types.never); + self.demand_eqtype(span, ty, self.tcx.types.never); } else { let guar = match *ty.kind() { _ if let Some(guar) = self.tainted_by_errors() => guar, From f3b65028f7a6f4cf250929317375d06437b445a2 Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Mon, 28 Sep 2026 16:02:04 +0200 Subject: [PATCH 25/30] Run the `method-on-never` type on the next trait solver --- ....stderr => method-on-never.current.stderr} | 20 ++-- .../basic/method-on-never.next.stderr | 113 ++++++++++++++++++ tests/ui/never_type/basic/method-on-never.rs | 2 + 3 files changed, 125 insertions(+), 10 deletions(-) rename tests/ui/never_type/basic/{method-on-never.stderr => method-on-never.current.stderr} (93%) create mode 100644 tests/ui/never_type/basic/method-on-never.next.stderr diff --git a/tests/ui/never_type/basic/method-on-never.stderr b/tests/ui/never_type/basic/method-on-never.current.stderr similarity index 93% rename from tests/ui/never_type/basic/method-on-never.stderr rename to tests/ui/never_type/basic/method-on-never.current.stderr index f5903616be6f4..ec89b37bc403e 100644 --- a/tests/ui/never_type/basic/method-on-never.stderr +++ b/tests/ui/never_type/basic/method-on-never.current.stderr @@ -1,5 +1,5 @@ warning: method call on a diverging inference variable - --> $DIR/method-on-never.rs:42:7 + --> $DIR/method-on-never.rs:44:7 | LL | x.method(); | ^^^^^^ @@ -10,7 +10,7 @@ LL | x.method(); = note: `#[warn(method_call_on_diverging_infer_var)]` (part of `#[warn(future_incompatible)]`) on by default warning: method call on a diverging inference variable - --> $DIR/method-on-never.rs:46:17 + --> $DIR/method-on-never.rs:48:17 | LL | { loop {} }.method(); | ^^^^^^ @@ -20,7 +20,7 @@ LL | { loop {} }.method(); = note: for more information, see issue #156047 warning: method call on a diverging inference variable - --> $DIR/method-on-never.rs:51:16 + --> $DIR/method-on-never.rs:53:16 | LL | y => y.method(), | ^^^^^^ @@ -30,7 +30,7 @@ LL | y => y.method(), = note: for more information, see issue #156047 warning: method call on a diverging inference variable - --> $DIR/method-on-never.rs:57:27 + --> $DIR/method-on-never.rs:59:27 | LL | error => (&error).anyhow_kind().new(error), | ^^^^^^^^^^^ @@ -40,7 +40,7 @@ LL | error => (&error).anyhow_kind().new(error), = note: for more information, see issue #156047 warning: method call on a diverging inference variable - --> $DIR/method-on-never.rs:63:9 + --> $DIR/method-on-never.rs:65:9 | LL | res.method(); | ^^^^^^ @@ -53,7 +53,7 @@ warning: 5 warnings emitted Future incompatibility report: Future breakage diagnostic: warning: method call on a diverging inference variable - --> $DIR/method-on-never.rs:42:7 + --> $DIR/method-on-never.rs:44:7 | LL | x.method(); | ^^^^^^ @@ -65,7 +65,7 @@ LL | x.method(); Future breakage diagnostic: warning: method call on a diverging inference variable - --> $DIR/method-on-never.rs:46:17 + --> $DIR/method-on-never.rs:48:17 | LL | { loop {} }.method(); | ^^^^^^ @@ -77,7 +77,7 @@ LL | { loop {} }.method(); Future breakage diagnostic: warning: method call on a diverging inference variable - --> $DIR/method-on-never.rs:51:16 + --> $DIR/method-on-never.rs:53:16 | LL | y => y.method(), | ^^^^^^ @@ -89,7 +89,7 @@ LL | y => y.method(), Future breakage diagnostic: warning: method call on a diverging inference variable - --> $DIR/method-on-never.rs:57:27 + --> $DIR/method-on-never.rs:59:27 | LL | error => (&error).anyhow_kind().new(error), | ^^^^^^^^^^^ @@ -101,7 +101,7 @@ LL | error => (&error).anyhow_kind().new(error), Future breakage diagnostic: warning: method call on a diverging inference variable - --> $DIR/method-on-never.rs:63:9 + --> $DIR/method-on-never.rs:65:9 | LL | res.method(); | ^^^^^^ diff --git a/tests/ui/never_type/basic/method-on-never.next.stderr b/tests/ui/never_type/basic/method-on-never.next.stderr new file mode 100644 index 0000000000000..ec89b37bc403e --- /dev/null +++ b/tests/ui/never_type/basic/method-on-never.next.stderr @@ -0,0 +1,113 @@ +warning: method call on a diverging inference variable + --> $DIR/method-on-never.rs:44:7 + | +LL | x.method(); + | ^^^^^^ + | + = help: consider providing a type annotation + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #156047 + = note: `#[warn(method_call_on_diverging_infer_var)]` (part of `#[warn(future_incompatible)]`) on by default + +warning: method call on a diverging inference variable + --> $DIR/method-on-never.rs:48:17 + | +LL | { loop {} }.method(); + | ^^^^^^ + | + = help: consider providing a type annotation + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #156047 + +warning: method call on a diverging inference variable + --> $DIR/method-on-never.rs:53:16 + | +LL | y => y.method(), + | ^^^^^^ + | + = help: consider providing a type annotation + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #156047 + +warning: method call on a diverging inference variable + --> $DIR/method-on-never.rs:59:27 + | +LL | error => (&error).anyhow_kind().new(error), + | ^^^^^^^^^^^ + | + = help: consider providing a type annotation + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #156047 + +warning: method call on a diverging inference variable + --> $DIR/method-on-never.rs:65:9 + | +LL | res.method(); + | ^^^^^^ + | + = help: consider providing a type annotation + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #156047 + +warning: 5 warnings emitted + +Future incompatibility report: Future breakage diagnostic: +warning: method call on a diverging inference variable + --> $DIR/method-on-never.rs:44:7 + | +LL | x.method(); + | ^^^^^^ + | + = help: consider providing a type annotation + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #156047 + = note: `#[warn(method_call_on_diverging_infer_var)]` (part of `#[warn(future_incompatible)]`) on by default + +Future breakage diagnostic: +warning: method call on a diverging inference variable + --> $DIR/method-on-never.rs:48:17 + | +LL | { loop {} }.method(); + | ^^^^^^ + | + = help: consider providing a type annotation + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #156047 + = note: `#[warn(method_call_on_diverging_infer_var)]` (part of `#[warn(future_incompatible)]`) on by default + +Future breakage diagnostic: +warning: method call on a diverging inference variable + --> $DIR/method-on-never.rs:53:16 + | +LL | y => y.method(), + | ^^^^^^ + | + = help: consider providing a type annotation + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #156047 + = note: `#[warn(method_call_on_diverging_infer_var)]` (part of `#[warn(future_incompatible)]`) on by default + +Future breakage diagnostic: +warning: method call on a diverging inference variable + --> $DIR/method-on-never.rs:59:27 + | +LL | error => (&error).anyhow_kind().new(error), + | ^^^^^^^^^^^ + | + = help: consider providing a type annotation + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #156047 + = note: `#[warn(method_call_on_diverging_infer_var)]` (part of `#[warn(future_incompatible)]`) on by default + +Future breakage diagnostic: +warning: method call on a diverging inference variable + --> $DIR/method-on-never.rs:65:9 + | +LL | res.method(); + | ^^^^^^ + | + = help: consider providing a type annotation + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #156047 + = note: `#[warn(method_call_on_diverging_infer_var)]` (part of `#[warn(future_incompatible)]`) on by default + diff --git a/tests/ui/never_type/basic/method-on-never.rs b/tests/ui/never_type/basic/method-on-never.rs index 4194884ade9ea..ac928a0e9d071 100644 --- a/tests/ui/never_type/basic/method-on-never.rs +++ b/tests/ui/never_type/basic/method-on-never.rs @@ -1,3 +1,5 @@ +//@ revisions: current next +//@[next] compile-flags: -Znext-solver //@ check-pass // Regression test for https://github.com/rust-lang/rust/issues/143349 From 773ad198b6b28ba21cd319607d264618420b3060 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Mon, 28 Sep 2026 15:39:32 +0200 Subject: [PATCH 26/30] Add `stable_rustc` helper in `run-make-support` --- .../src/external_deps/rustc.rs | 24 ++++++++++++++++--- src/tools/run-make-support/src/lib.rs | 4 +++- .../const-destruct-stable-toolchain/rmake.rs | 6 ++--- .../const-trait-stable-toolchain/rmake.rs | 9 +++---- .../issue-149402-suggest-unresolve/rmake.rs | 10 ++------ .../missing-unstable-trait-bound/rmake.rs | 6 ++--- .../rmake.rs | 11 +++------ 7 files changed, 36 insertions(+), 34 deletions(-) diff --git a/src/tools/run-make-support/src/external_deps/rustc.rs b/src/tools/run-make-support/src/external_deps/rustc.rs index 8b28105abe5ed..fab7f2098ea1c 100644 --- a/src/tools/run-make-support/src/external_deps/rustc.rs +++ b/src/tools/run-make-support/src/external_deps/rustc.rs @@ -22,6 +22,15 @@ pub fn bare_rustc() -> Rustc { Rustc::bare() } +/// Construct a plain `rustc` invocation with no flags set that behaves like a stable compiler, +/// i.e. rejects unstable flags. +/// Note that [`set_host_compiler_dylib_path`] still presets the environment variable +/// `HOST_RUSTC_DYLIB_PATH` by default. +#[track_caller] +pub fn stable_bare_rustc() -> Rustc { + Rustc::bare_stable() +} + /// Construct a `rustc` invocation for building `minicore`. /// /// This function: @@ -76,9 +85,6 @@ pub fn rustc_path() -> String { fn setup_common() -> Command { let mut cmd = Command::new(rustc_path()); set_host_compiler_dylib_path(&mut cmd); - if let Ok(codegen_backend) = std::env::var("RUSTC_CODEGEN_BACKEND") { - cmd.arg(format!("-Zcodegen-backend={codegen_backend}")); - } cmd } @@ -100,6 +106,10 @@ impl Rustc { cmd.arg("-Ctarget-feature=-crt-static"); } + if let Ok(codegen_backend) = std::env::var("RUSTC_CODEGEN_BACKEND") { + cmd.arg(format!("-Zcodegen-backend={codegen_backend}")); + } + // Automatically default to cross-compilation Self { cmd, target: Some(target()) } } @@ -111,6 +121,14 @@ impl Rustc { Self { cmd, target: None } } + /// Construct a bare `rustc` invocation with no flags set that acts like a stable compiler. + #[track_caller] + pub fn bare_stable() -> Self { + let mut cmd = setup_common(); + cmd.env("RUSTC_BOOTSTRAP", "-1"); + Self { cmd, target: None } + } + // Argument provider methods /// Configure the compilation environment. diff --git a/src/tools/run-make-support/src/lib.rs b/src/tools/run-make-support/src/lib.rs index d3c2115e745e0..b9717bda1e2c7 100644 --- a/src/tools/run-make-support/src/lib.rs +++ b/src/tools/run-make-support/src/lib.rs @@ -79,7 +79,9 @@ pub use crate::external_deps::llvm::{ llvm_readobj, }; pub use crate::external_deps::python::python_command; -pub use crate::external_deps::rustc::{self, Rustc, bare_rustc, rustc, rustc_minicore, rustc_path}; +pub use crate::external_deps::rustc::{ + self, Rustc, bare_rustc, rustc, rustc_minicore, rustc_path, stable_bare_rustc, +}; pub use crate::external_deps::rustdoc::{Rustdoc, bare_rustdoc, rustdoc}; // Path-related helpers. pub use crate::path_helpers::{ diff --git a/tests/run-make/const-destruct-stable-toolchain/rmake.rs b/tests/run-make/const-destruct-stable-toolchain/rmake.rs index 0f36d71f2c874..c4990582d40ba 100644 --- a/tests/run-make/const-destruct-stable-toolchain/rmake.rs +++ b/tests/run-make/const-destruct-stable-toolchain/rmake.rs @@ -1,17 +1,15 @@ //@ needs-target-std -// FIXME: Once GCC backend is fixed, remove this `ignore-backends`. //@ ignore-backends: gcc // Test that the suggestion to constrain a type parameter that is dropped in a const // function with a `[const] Destruct` bound is only offered on nightly, since the bound // requires an unstable feature. -use run_make_support::{diff, rustc}; +use run_make_support::{diff, rustc, stable_bare_rustc}; fn main() { - let out = rustc() + let out = stable_bare_rustc() .input("const-drop.rs") - .env("RUSTC_BOOTSTRAP", "-1") .run_fail() .assert_stderr_not_contains("consider restricting type parameter `T`") .stderr_utf8(); diff --git a/tests/run-make/const-trait-stable-toolchain/rmake.rs b/tests/run-make/const-trait-stable-toolchain/rmake.rs index e2ec58f03db19..c778873463063 100644 --- a/tests/run-make/const-trait-stable-toolchain/rmake.rs +++ b/tests/run-make/const-trait-stable-toolchain/rmake.rs @@ -1,16 +1,14 @@ //@ needs-target-std -// FIXME: Once GCC backend is fixed, remove this `ignore-backends`. //@ ignore-backends: gcc // Test output of const super trait errors in both stable and nightly. // We don't want to provide suggestions on stable that only make sense in nightly. -use run_make_support::{diff, rustc}; +use run_make_support::{diff, rustc, stable_bare_rustc}; fn main() { - let out = rustc() + let out = stable_bare_rustc() .input("const-super-trait.rs") - .env("RUSTC_BOOTSTRAP", "-1") .cfg("feature_enabled") .run_fail() .assert_stderr_not_contains("as `const` to allow it to have `const` implementations") @@ -35,9 +33,8 @@ fn main() { .expected_file("const-super-trait-nightly-enabled.stderr") .actual_text("(rustc)", &out) .run(); - let out = rustc() + let out = stable_bare_rustc() .input("const-super-trait.rs") - .env("RUSTC_BOOTSTRAP", "-1") .run_fail() .assert_stderr_not_contains("enable `#![feature(const_trait_impl)]` in your crate and mark") .assert_stderr_not_contains("as `const` to allow it to have `const` implementations") diff --git a/tests/run-make/issue-149402-suggest-unresolve/rmake.rs b/tests/run-make/issue-149402-suggest-unresolve/rmake.rs index 78348b914d1e6..a8d92606ee893 100644 --- a/tests/run-make/issue-149402-suggest-unresolve/rmake.rs +++ b/tests/run-make/issue-149402-suggest-unresolve/rmake.rs @@ -4,18 +4,12 @@ //! //@ only-nightly //@ needs-target-std -// FIXME: Once GCC backend is fixed, remove this `ignore-backends`. //@ ignore-backends: gcc -use run_make_support::{diff, rustc, similar}; +use run_make_support::{diff, rustc, similar, stable_bare_rustc}; fn main() { - let stable_like = rustc() - .env("RUSTC_BOOTSTRAP", "-1") - .edition("2024") - .input("foo.rs") - .run_fail() - .stderr_utf8(); + let stable_like = stable_bare_rustc().edition("2024").input("foo.rs").run_fail().stderr_utf8(); assert!(!stable_like.contains("CoroutineState::Complete")); diff().expected_file("stable.err").actual_text("stable_like", &stable_like).run(); diff --git a/tests/run-make/missing-unstable-trait-bound/rmake.rs b/tests/run-make/missing-unstable-trait-bound/rmake.rs index 753f4abcf32fd..52a011285c95c 100644 --- a/tests/run-make/missing-unstable-trait-bound/rmake.rs +++ b/tests/run-make/missing-unstable-trait-bound/rmake.rs @@ -2,17 +2,15 @@ //@ ignore-wasm32 //@ ignore-wasm64 // ignore-tidy-linelength -// FIXME: Once GCC backend is fixed, remove this `ignore-backends`. //@ ignore-backends: gcc // Ensure that on stable we don't suggest restricting with an unsafe trait and we continue // mentioning the rest of the obligation chain. -use run_make_support::{diff, rustc}; +use run_make_support::{diff, stable_bare_rustc}; fn main() { - let out = rustc() - .env("RUSTC_BOOTSTRAP", "-1") + let out = stable_bare_rustc() .input("missing-bound.rs") .run_fail() .assert_stderr_not_contains("help: consider restricting type parameter `T`") diff --git a/tests/run-make/print-request-help-stable-unstable/rmake.rs b/tests/run-make/print-request-help-stable-unstable/rmake.rs index 62cf0483f320d..01572a5aad942 100644 --- a/tests/run-make/print-request-help-stable-unstable/rmake.rs +++ b/tests/run-make/print-request-help-stable-unstable/rmake.rs @@ -2,18 +2,13 @@ //! //! Issue: -// FIXME: Once GCC backend is fixed, remove this `ignore-backends`. //@ ignore-backends: gcc -use run_make_support::{diff, rustc, similar}; +use run_make_support::{diff, rustc, similar, stable_bare_rustc}; fn main() { - let stable_invalid_print_request_help = rustc() - .env("RUSTC_BOOTSTRAP", "-1") - .cfg("force_stable") - .print("xxx") - .run_fail() - .stderr_utf8(); + let stable_invalid_print_request_help = + stable_bare_rustc().print("xxx").run_fail().stderr_utf8(); assert!(!stable_invalid_print_request_help.contains("all-target-specs-json")); diff() .expected_file("stable-invalid-print-request-help.err") From 62ce017dc8d51dee67e7bc021c202ab564f507c0 Mon Sep 17 00:00:00 2001 From: aerooneqq Date: Mon, 28 Sep 2026 17:21:14 +0300 Subject: [PATCH 27/30] Allow using different index types when reading and writing to tables --- compiler/rustc_metadata/src/rmeta/decoder.rs | 4 +-- compiler/rustc_metadata/src/rmeta/encoder.rs | 12 ++++--- compiler/rustc_metadata/src/rmeta/mod.rs | 34 ++++++++++++-------- compiler/rustc_metadata/src/rmeta/table.rs | 32 +++++++++++------- 4 files changed, 49 insertions(+), 33 deletions(-) diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs index 27b36c23daf46..7dd920c7e39bd 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder.rs @@ -189,7 +189,7 @@ pub(super) trait LazyDecoder: BlobDecoder { self.read_lazy_offset_then(|pos| LazyArray::from_position_and_num_elems(pos, len)) } - fn read_lazy_table(&mut self, width: usize, len: usize) -> LazyTable { + fn read_lazy_table(&mut self, width: usize, len: usize) -> LazyTable { self.read_lazy_offset_then(|pos| LazyTable::from_position_and_encoded_size(pos, width, len)) } @@ -667,7 +667,7 @@ impl Decodable for LazyArray { } } -impl Decodable for LazyTable { +impl Decodable for LazyTable { fn decode(decoder: &mut D) -> Self { let width = decoder.read_usize(); let len = decoder.read_usize(); diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index afa5646bea80e..40b61a52d2310 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -133,7 +133,7 @@ impl<'a, 'tcx, T> Encodable> for LazyArray { } } -impl<'a, 'tcx, I, T> Encodable> for LazyTable { +impl<'a, 'tcx, Ie, Id, T> Encodable> for LazyTable { fn encode(&self, e: &mut EncodeContext<'a, 'tcx>) { e.emit_usize(self.width); e.emit_usize(self.len); @@ -547,7 +547,9 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { self.lazy(DefPathHashMapRef::BorrowedFromTcx(self.tcx.def_path_hash_to_def_index_map())) } - fn encode_source_map(&mut self) -> LazyTable>> { + fn encode_source_map( + &mut self, + ) -> LazyTable>> { let source_map = self.tcx.sess.source_map(); let all_source_files = source_map.files(); @@ -2004,9 +2006,9 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { } fn encode_hygiene(&mut self) -> (SyntaxContextTable, ExpnDataTable, ExpnHashTable) { - let mut syntax_contexts: TableBuilder<_, _> = Default::default(); - let mut expn_data_table: TableBuilder<_, _> = Default::default(); - let mut expn_hash_table: TableBuilder<_, _> = Default::default(); + let mut syntax_contexts: TableBuilder<_, _, _> = Default::default(); + let mut expn_data_table: TableBuilder<_, _, _> = Default::default(); + let mut expn_hash_table: TableBuilder<_, _, _> = Default::default(); HygieneEncodeContext::encode( &Rc::clone(&self.hygiene_ctxt), diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs index e3f3257acc137..b70151604f679 100644 --- a/compiler/rustc_metadata/src/rmeta/mod.rs +++ b/compiler/rustc_metadata/src/rmeta/mod.rs @@ -48,6 +48,7 @@ use rustc_target::spec::{PanicStrategy, TargetTuple}; use table::TableBuilder; use crate::eii::EiiMapEncodedKeyValue; +use crate::rmeta::table::TableBuilderSingleIdx; mod decoder; mod def_path_hash_map; @@ -143,22 +144,27 @@ impl LazyArray { /// Random-access table (i.e. offering constant-time `get`/`set`), similar to /// `LazyArray`, but without requiring encoding or decoding all the values /// eagerly and in-order. -struct LazyTable { +/// +/// `IdxEncode` - is a type of index that is used to write to the table, +/// `IdxDecode` - is a type of index that is used to read from the table. +struct LazyTable { position: NonZero, /// The encoded size of the elements of a table is selected at runtime to drop /// trailing zeroes. This is the number of bytes used for each table element. width: usize, /// How many elements are in the table. len: usize, - _marker: PhantomData T>, + _marker: PhantomData T>, } -impl LazyTable { +type LazyTableSingleIdx = LazyTable; + +impl LazyTable { fn from_position_and_encoded_size( position: NonZero, width: usize, len: usize, - ) -> LazyTable { + ) -> LazyTable { LazyTable { position, width, len, _marker: PhantomData } } } @@ -177,8 +183,8 @@ impl Clone for LazyArray { } } -impl Copy for LazyTable {} -impl Clone for LazyTable { +impl Copy for LazyTable {} +impl Clone for LazyTable { fn clone(&self) -> Self { *self } @@ -199,9 +205,9 @@ enum LazyState { Previous(NonZero), } -type SyntaxContextTable = LazyTable>>; -type ExpnDataTable = LazyTable>>; -type ExpnHashTable = LazyTable>>; +type SyntaxContextTable = LazyTableSingleIdx>>; +type ExpnDataTable = LazyTableSingleIdx>>; +type ExpnHashTable = LazyTableSingleIdx>>; #[derive(MetadataEncodable, LazyDecodable)] pub(crate) struct ProcMacroData { @@ -305,7 +311,7 @@ pub(crate) struct CrateRoot { def_path_hash_map: LazyValue>, - source_map: LazyTable>>, + source_map: LazyTableSingleIdx>>, target_modifiers: LazyArray, denied_partial_mitigations: LazyArray, @@ -389,14 +395,14 @@ macro_rules! define_tables { ) => { #[derive(MetadataEncodable, LazyDecodable)] pub(crate) struct LazyTables { - $($name1: LazyTable<$IDX1, $T1>,)+ - $($name2: LazyTable<$IDX2, Option<$T2>>,)+ + $($name1: LazyTableSingleIdx<$IDX1, $T1>,)+ + $($name2: LazyTableSingleIdx<$IDX2, Option<$T2>>,)+ } #[derive(Default)] struct TableBuilders { - $($name1: TableBuilder<$IDX1, $T1>,)+ - $($name2: TableBuilder<$IDX2, Option<$T2>>,)+ + $($name1: TableBuilderSingleIdx<$IDX1, $T1>,)+ + $($name2: TableBuilderSingleIdx<$IDX2, Option<$T2>>,)+ } impl TableBuilders { diff --git a/compiler/rustc_metadata/src/rmeta/table.rs b/compiler/rustc_metadata/src/rmeta/table.rs index 7b53ebba8adfc..c023b61d73571 100644 --- a/compiler/rustc_metadata/src/rmeta/table.rs +++ b/compiler/rustc_metadata/src/rmeta/table.rs @@ -427,34 +427,38 @@ impl FixedSizeEncoding for Option> { } /// Helper for constructing a table's serialization (also see `Table`). -pub(super) struct TableBuilder { +pub(super) struct TableBuilder { width: usize, - blocks: IndexVec, - _marker: PhantomData, + blocks: IndexVec, + _marker: PhantomData<(IdxDecode, T)>, } -impl Default for TableBuilder { +pub(super) type TableBuilderSingleIdx = TableBuilder; + +impl Default for TableBuilder { fn default() -> Self { TableBuilder { width: 0, blocks: Default::default(), _marker: PhantomData } } } -impl TableBuilder> +impl TableBuilder> where Option: FixedSizeEncoding, { - pub(crate) fn set_some(&mut self, i: I, value: T) { + pub(crate) fn set_some(&mut self, i: Ie, value: T) { self.set(i, Some(value)) } } -impl> TableBuilder { +impl> + TableBuilder +{ /// Sets the table value if it is not default. /// ATTENTION: For optimization default values are simply ignored by this function, because /// right now metadata tables never need to reset non-default values to default. If such need /// arises in the future then a new method (e.g. `clear` or `reset`) will need to be introduced /// for doing that explicitly. - pub(crate) fn set(&mut self, i: I, value: T) { + pub(crate) fn set(&mut self, i: Ie, value: T) { #[cfg(debug_assertions)] { debug_assert!( @@ -477,7 +481,7 @@ impl> TableBui } } - pub(crate) fn encode(&self, buf: &mut FileEncoder<'_>) -> LazyTable { + pub(crate) fn encode(&self, buf: &mut FileEncoder<'_>) -> LazyTable { let pos = buf.position(); let width = self.width; @@ -500,13 +504,17 @@ fn trailing_zeros(x: &[u8]) -> usize { x.iter().rev().take_while(|b| **b == 0).count() } -impl + ParameterizedOverTcx> - LazyTable +impl< + Ie: Idx, + Id: Idx, + const N: usize, + T: FixedSizeEncoding + ParameterizedOverTcx, +> LazyTable where for<'tcx> T::Value<'tcx>: FixedSizeEncoding, { /// Given the metadata, extract out the value at a particular index (if any). - pub(super) fn get<'a, 'tcx, M: MetaBlob<'a>>(&self, metadata: M, i: I) -> T::Value<'tcx> { + pub(super) fn get<'a, 'tcx, M: MetaBlob<'a>>(&self, metadata: M, i: Id) -> T::Value<'tcx> { // Access past the end of the table returns a Default if i.index() >= self.len { return Default::default(); From b6d0fb14576c72e11f0754c6f457ab0341817a8b Mon Sep 17 00:00:00 2001 From: dianne Date: Fri, 11 Sep 2026 09:39:50 -0700 Subject: [PATCH 28/30] select: prefer impl candidates over where clauses --- .../rustc_trait_selection/src/solve/select.rs | 15 ++++++++-- ...tin-impl-over-where-clause-in-const-pat.rs | 26 +++++++++++++++++ ...ser-impl-over-where-clause-in-const-pat.rs | 29 +++++++++++++++++++ 3 files changed, 67 insertions(+), 3 deletions(-) create mode 100644 tests/ui/trait-bounds/prefer-builtin-impl-over-where-clause-in-const-pat.rs create mode 100644 tests/ui/trait-bounds/prefer-user-impl-over-where-clause-in-const-pat.rs diff --git a/compiler/rustc_trait_selection/src/solve/select.rs b/compiler/rustc_trait_selection/src/solve/select.rs index 53b999e4e5244..f41808df8a5b2 100644 --- a/compiler/rustc_trait_selection/src/solve/select.rs +++ b/compiler/rustc_trait_selection/src/solve/select.rs @@ -2,7 +2,7 @@ use std::ops::ControlFlow; use rustc_infer::infer::InferCtxt; use rustc_infer::traits::solve::inspect::ProbeKind; -use rustc_infer::traits::solve::{CandidateSource, Certainty, Goal}; +use rustc_infer::traits::solve::{CandidateSource, Certainty, Goal, ParamEnvSource}; use rustc_infer::traits::{ BuiltinImplSource, ImplSource, ImplSourceUserDefinedData, Obligation, ObligationCause, PolyTraitObligation, Selection, SelectionError, SelectionResult, @@ -93,8 +93,8 @@ fn candidate_should_be_dropped_in_favor_of<'tcx>( victim: &inspect::InspectCandidate<'_, 'tcx>, other: &inspect::InspectCandidate<'_, 'tcx>, ) -> bool { - // Don't winnow until `Certainty::Yes` -- we don't need to winnow until - // codegen, and only on the good path. + // Don't winnow until `Certainty::Yes` -- we don't need to winnow until constant evaluation or + // codegen. if matches!(other.result().unwrap(), Certainty::Maybe(_)) { return false; } @@ -137,6 +137,15 @@ fn candidate_should_be_dropped_in_favor_of<'tcx>( victim.goal().infcx().tcx.specializes((other_def_id, victim_def_id)) } + // Prefer impl candidates over global where clause candidates. Unless `generic_const_args` + // is enabled, we currently don't use an empty environment when resolving and evaluating + // constants to lower them to patterns. If we don't drop where clause candidates here, we + // can fail to select impl candidates (#162331). + ( + CandidateSource::ParamEnv(ParamEnvSource::Global), + CandidateSource::Impl(_) | CandidateSource::BuiltinImpl(_), + ) => true, + _ => false, } } diff --git a/tests/ui/trait-bounds/prefer-builtin-impl-over-where-clause-in-const-pat.rs b/tests/ui/trait-bounds/prefer-builtin-impl-over-where-clause-in-const-pat.rs new file mode 100644 index 0000000000000..e774bef50c123 --- /dev/null +++ b/tests/ui/trait-bounds/prefer-builtin-impl-over-where-clause-in-const-pat.rs @@ -0,0 +1,26 @@ +//! Regression test adjacent to +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver +//@ check-pass + +#![feature(const_trait_impl)] +#![feature(const_clone)] + +// At the time of writing, `ZERO` is evaluated in an environment containing `g`'s `(u8,): Clone` +// clause. In the new solver, this wasn't dropped in favor of the built-in `(u8,): Clone` impl when +// resolving an instance for `<(u8,) as Clone>::clone`, which resulted in ambiguity. + +const ZERO: (u8,) = (0,).clone(); + +fn g() +where + (u8,): Clone, +{ + match (0,) { + ZERO => {} + _ => {} + } +} + +fn main() {} diff --git a/tests/ui/trait-bounds/prefer-user-impl-over-where-clause-in-const-pat.rs b/tests/ui/trait-bounds/prefer-user-impl-over-where-clause-in-const-pat.rs new file mode 100644 index 0000000000000..e2c27d4f90e11 --- /dev/null +++ b/tests/ui/trait-bounds/prefer-user-impl-over-where-clause-in-const-pat.rs @@ -0,0 +1,29 @@ +//! Regression test for +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver +//@ check-pass + +// At the time of writing, when resolving an instance for `::N`, the environment +// contains `f`'s `u8: Trait` clause. The old solver dropped the where clause candidate in favor of +// the `impl Trait for u8` candidate, but the new solver didn't, which resulted in ambiguity. + +pub trait Trait { + const N: usize; +} + +impl Trait for u8 { + const N: usize = 0; +} + +pub fn f() +where + u8: Trait, +{ + match 0 { + ::N => {} + _ => {} + } +} + +fn main() {} From 48b2592ad8bdbcba6a5585dd39d560bf4a2e3ca1 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 28 Sep 2026 10:31:49 +0200 Subject: [PATCH 29/30] do not complain about unstable target features on nightly --- .../rustc_codegen_ssa/src/target_features.rs | 6 +- compiler/rustc_session/src/session.rs | 4 ++ src/doc/rustc-dev-guide/src/tests/misc.md | 13 ++-- .../run-make/unstable-target-feature/rmake.rs | 20 ++++++ .../unstable-target-feature.rs | 2 + .../unstable-target-feature.stderr | 8 +++ ...cv-discoverability-guidance.riscv32.stderr | 12 +--- ...cv-discoverability-guidance.riscv64.stderr | 12 +--- .../ui/abi/riscv-discoverability-guidance.rs | 5 -- tests/ui/abi/sparcv8plus.rs | 1 - tests/ui/abi/sparcv8plus.sparc.stderr | 2 +- tests/ui/abi/sparcv8plus.sparc_cpu_v9.stderr | 2 +- ...cv8plus.sparc_cpu_v9_feature_v8plus.stderr | 10 +-- .../sparcv8plus.sparc_feature_v8plus.stderr | 10 +-- tests/ui/abi/sparcv8plus.sparcv8plus.stderr | 2 +- tests/ui/asm/hexagon-register-pairs.rs | 2 - tests/ui/asm/hexagon-register-pairs.stderr | 12 +--- tests/ui/asm/hexagon/bad-reg.rs | 2 - tests/ui/asm/hexagon/bad-reg.stderr | 64 +++++++++---------- tests/ui/asm/mips/reg-conflict.mips32.stderr | 18 +----- .../ui/asm/mips/reg-conflict.mips32r6.stderr | 18 +----- tests/ui/asm/mips/reg-conflict.mips64.stderr | 18 +----- .../ui/asm/mips/reg-conflict.mips64r6.stderr | 18 +----- tests/ui/asm/mips/reg-conflict.rs | 2 - ...-irrelevant-target-feature-flag-disable.rs | 2 - ...elevant-target-feature-flag-disable.stderr | 8 --- ...bi-required-target-feature-flag-disable.rs | 1 - ...red-target-feature-flag-disable.x86.stderr | 8 +-- .../feature-hierarchy.hexagon-hvxv66.stderr | 8 --- .../feature-hierarchy.hexagon-v60.stderr | 8 --- .../feature-hierarchy.hexagon-v68.stderr | 8 --- tests/ui/target-feature/feature-hierarchy.rs | 4 -- .../packedstack-combinations.rs | 2 - ...edstack-combinations.with_softfloat.stderr | 8 --- tests/ui/target-feature/unstable-feature.rs | 8 --- .../ui/target-feature/unstable-feature.stderr | 8 --- 36 files changed, 100 insertions(+), 236 deletions(-) create mode 100644 tests/run-make/unstable-target-feature/rmake.rs create mode 100644 tests/run-make/unstable-target-feature/unstable-target-feature.rs create mode 100644 tests/run-make/unstable-target-feature/unstable-target-feature.stderr delete mode 100644 tests/ui/target-feature/abi-irrelevant-target-feature-flag-disable.stderr delete mode 100644 tests/ui/target-feature/feature-hierarchy.hexagon-hvxv66.stderr delete mode 100644 tests/ui/target-feature/feature-hierarchy.hexagon-v60.stderr delete mode 100644 tests/ui/target-feature/feature-hierarchy.hexagon-v68.stderr delete mode 100644 tests/ui/target-feature/packedstack-combinations.with_softfloat.stderr delete mode 100644 tests/ui/target-feature/unstable-feature.rs delete mode 100644 tests/ui/target-feature/unstable-feature.stderr diff --git a/compiler/rustc_codegen_ssa/src/target_features.rs b/compiler/rustc_codegen_ssa/src/target_features.rs index b630155c144ea..301195efb9a8f 100644 --- a/compiler/rustc_codegen_ssa/src/target_features.rs +++ b/compiler/rustc_codegen_ssa/src/target_features.rs @@ -383,8 +383,10 @@ pub fn internal_target_features<'a, const N: usize>( } else { sess.dcx().emit_warn(diag); } - } else if stability.requires_nightly(/* in_cfg */ false).is_some() { - // An unstable feature. Warn about using it. It makes little sense + } else if stability.requires_nightly(/* in_cfg */ false).is_some() + && !sess.is_nightly_build() + { + // An unstable feature. Warn about using it on stable. It makes little sense // to hard-error here since we just warn about fully unknown // features above. let note = if stability.is_cfg_stable_toggle_unstable() { diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 060e614e82180..3180d8ca5d10c 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -427,6 +427,10 @@ impl EarlySession { pub fn merge_functions(&self) -> MergeFunctions { self.opts.unstable_opts.merge_functions.unwrap_or(self.target.merge_functions) } + + pub fn is_nightly_build(&self) -> bool { + self.opts.unstable_features.is_nightly_build() + } } /// Some info about the backend, returned by `CodegenBackend::init` and put into the `Session`. diff --git a/src/doc/rustc-dev-guide/src/tests/misc.md b/src/doc/rustc-dev-guide/src/tests/misc.md index 34f9faa9cc722..8fe49c82e9873 100644 --- a/src/doc/rustc-dev-guide/src/tests/misc.md +++ b/src/doc/rustc-dev-guide/src/tests/misc.md @@ -12,15 +12,10 @@ This is a bootstrap/compiler implementation detail, but it can also be useful fo This is useful because some behaviors of the compiler (e.g. diagnostics) can differ depending on whether the compiler is nightly or not. -In `ui` tests and other test suites that support `//@ rustc-env`, you can specify - -```rust,ignore -// Force unstable features to be usable on stable rustc -//@ rustc-env:RUSTC_BOOTSTRAP=1 - -// Or force nightly rustc to pretend it is a stable rustc -//@ rustc-env:RUSTC_BOOTSTRAP=-1 -``` +Note that setting `RUSTC_BOOTSTRAP` in ui tests (e.g. via `//@ rustc-env`) is pointless since ui +tests inherit the bootstrap env var from bootstrap (so it's set to `1` by default), and since +compiletest itself passes `-Z` flags (so `-1` breaks the test). To test stable-only behavior, you +need to write a `run-make` test instead. For `run-make`/`run-make-cargo` tests, `//@ rustc-env` is not supported. You can do something like the following for individual `rustc` invocations. diff --git a/tests/run-make/unstable-target-feature/rmake.rs b/tests/run-make/unstable-target-feature/rmake.rs new file mode 100644 index 0000000000000..3fefe11ff791e --- /dev/null +++ b/tests/run-make/unstable-target-feature/rmake.rs @@ -0,0 +1,20 @@ +//@ only-x86_64 +// FIXME: Once GCC backend is fixed, remove this `ignore-backends`. +//@ ignore-backends: gcc + +// Ensure that on stable we don't suggest restricting with an unsafe trait and we continue +// mentioning the rest of the obligation chain. + +use run_make_support::{diff, rustc}; + +fn main() { + let out = rustc() + .env("RUSTC_BOOTSTRAP", "-1") + .input("unstable-target-feature.rs") + .args(&["-Ctarget-feature=+x87", "--crate-type=rlib"]) + .run() + .assert_stderr_not_contains("help: consider restricting type parameter `T`") + .assert_stderr_contains("unstable feature") + .stderr_utf8(); + diff().expected_file("unstable-target-feature.stderr").actual_text("(stable rustc)", &out).run() +} diff --git a/tests/run-make/unstable-target-feature/unstable-target-feature.rs b/tests/run-make/unstable-target-feature/unstable-target-feature.rs new file mode 100644 index 0000000000000..ec7318829e77e --- /dev/null +++ b/tests/run-make/unstable-target-feature/unstable-target-feature.rs @@ -0,0 +1,2 @@ +/// Tidy is unhappy if we leave the file empty. +pub fn empty() {} diff --git a/tests/run-make/unstable-target-feature/unstable-target-feature.stderr b/tests/run-make/unstable-target-feature/unstable-target-feature.stderr new file mode 100644 index 0000000000000..e4b2717f911f3 --- /dev/null +++ b/tests/run-make/unstable-target-feature/unstable-target-feature.stderr @@ -0,0 +1,8 @@ +warning: unstable feature specified for `-Ctarget-feature`: `x87` + | + = note: this feature is not stably supported; its behavior can change in the future + = note: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #162235 + +warning: 1 warning emitted + diff --git a/tests/ui/abi/riscv-discoverability-guidance.riscv32.stderr b/tests/ui/abi/riscv-discoverability-guidance.riscv32.stderr index c618e5f63546a..0378b9daafe79 100644 --- a/tests/ui/abi/riscv-discoverability-guidance.riscv32.stderr +++ b/tests/ui/abi/riscv-discoverability-guidance.riscv32.stderr @@ -1,11 +1,5 @@ -warning: unstable feature specified for `-Ctarget-feature`: `unaligned-scalar-mem` - | - = note: this feature is not stably supported; its behavior can change in the future - = note: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #162235 - error[E0703]: invalid ABI: found `riscv-interrupt` - --> $DIR/riscv-discoverability-guidance.rs:21:8 + --> $DIR/riscv-discoverability-guidance.rs:16:8 | LL | extern "riscv-interrupt" fn isr() {} | ^^^^^^^^^^^^^^^^^ invalid ABI @@ -17,7 +11,7 @@ LL | extern "riscv-interrupt-m" fn isr() {} | ++ error[E0703]: invalid ABI: found `riscv-interrupt-u` - --> $DIR/riscv-discoverability-guidance.rs:26:8 + --> $DIR/riscv-discoverability-guidance.rs:21:8 | LL | extern "riscv-interrupt-u" fn isr_U() {} | ^^^^^^^^^^^^^^^^^^^ invalid ABI @@ -29,6 +23,6 @@ LL - extern "riscv-interrupt-u" fn isr_U() {} LL + extern "riscv-interrupt-m" fn isr_U() {} | -error: aborting due to 2 previous errors; 1 warning emitted +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0703`. diff --git a/tests/ui/abi/riscv-discoverability-guidance.riscv64.stderr b/tests/ui/abi/riscv-discoverability-guidance.riscv64.stderr index c618e5f63546a..0378b9daafe79 100644 --- a/tests/ui/abi/riscv-discoverability-guidance.riscv64.stderr +++ b/tests/ui/abi/riscv-discoverability-guidance.riscv64.stderr @@ -1,11 +1,5 @@ -warning: unstable feature specified for `-Ctarget-feature`: `unaligned-scalar-mem` - | - = note: this feature is not stably supported; its behavior can change in the future - = note: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #162235 - error[E0703]: invalid ABI: found `riscv-interrupt` - --> $DIR/riscv-discoverability-guidance.rs:21:8 + --> $DIR/riscv-discoverability-guidance.rs:16:8 | LL | extern "riscv-interrupt" fn isr() {} | ^^^^^^^^^^^^^^^^^ invalid ABI @@ -17,7 +11,7 @@ LL | extern "riscv-interrupt-m" fn isr() {} | ++ error[E0703]: invalid ABI: found `riscv-interrupt-u` - --> $DIR/riscv-discoverability-guidance.rs:26:8 + --> $DIR/riscv-discoverability-guidance.rs:21:8 | LL | extern "riscv-interrupt-u" fn isr_U() {} | ^^^^^^^^^^^^^^^^^^^ invalid ABI @@ -29,6 +23,6 @@ LL - extern "riscv-interrupt-u" fn isr_U() {} LL + extern "riscv-interrupt-m" fn isr_U() {} | -error: aborting due to 2 previous errors; 1 warning emitted +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0703`. diff --git a/tests/ui/abi/riscv-discoverability-guidance.rs b/tests/ui/abi/riscv-discoverability-guidance.rs index 9937c28030f89..86e34a44ec59a 100644 --- a/tests/ui/abi/riscv-discoverability-guidance.rs +++ b/tests/ui/abi/riscv-discoverability-guidance.rs @@ -10,11 +10,6 @@ #![no_core] #![feature(no_core, lang_items, abi_riscv_interrupt)] -//~? WARN unstable feature specified for `-Ctarget-feature` -//~? NOTE this feature is not stably supported; its behavior can change in the future -//~? NOTE previously accepted -//~? NOTE for more information, see issue - extern crate minicore; use minicore::*; diff --git a/tests/ui/abi/sparcv8plus.rs b/tests/ui/abi/sparcv8plus.rs index 93e8871c642ee..ec64eb414f2ec 100644 --- a/tests/ui/abi/sparcv8plus.rs +++ b/tests/ui/abi/sparcv8plus.rs @@ -12,7 +12,6 @@ //@[sparc_cpu_v9_feature_v8plus] needs-llvm-components: sparc //@ ignore-backends: gcc -//[sparc_feature_v8plus,sparc_cpu_v9_feature_v8plus]~? WARN unstable feature specified for `-Ctarget-feature` //[sparc_feature_v8plus,sparc_cpu_v9_feature_v8plus]~? WARN `v8plus` must be disabled #![crate_type = "rlib"] diff --git a/tests/ui/abi/sparcv8plus.sparc.stderr b/tests/ui/abi/sparcv8plus.sparc.stderr index 779bf597d6f66..718ff1ab0af52 100644 --- a/tests/ui/abi/sparcv8plus.sparc.stderr +++ b/tests/ui/abi/sparcv8plus.sparc.stderr @@ -1,5 +1,5 @@ error: -v8plus,-v9 - --> $DIR/sparcv8plus.rs:31:1 + --> $DIR/sparcv8plus.rs:30:1 | LL | compile_error!("-v8plus,-v9"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/abi/sparcv8plus.sparc_cpu_v9.stderr b/tests/ui/abi/sparcv8plus.sparc_cpu_v9.stderr index edf75ab70db15..5e5e108d74dfc 100644 --- a/tests/ui/abi/sparcv8plus.sparc_cpu_v9.stderr +++ b/tests/ui/abi/sparcv8plus.sparc_cpu_v9.stderr @@ -1,5 +1,5 @@ error: -v8plus,+v9 - --> $DIR/sparcv8plus.rs:44:1 + --> $DIR/sparcv8plus.rs:43:1 | LL | compile_error!("-v8plus,+v9"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/abi/sparcv8plus.sparc_cpu_v9_feature_v8plus.stderr b/tests/ui/abi/sparcv8plus.sparc_cpu_v9_feature_v8plus.stderr index 7e1d9edff230f..27545025828b5 100644 --- a/tests/ui/abi/sparcv8plus.sparc_cpu_v9_feature_v8plus.stderr +++ b/tests/ui/abi/sparcv8plus.sparc_cpu_v9_feature_v8plus.stderr @@ -1,19 +1,13 @@ -warning: unstable feature specified for `-Ctarget-feature`: `v8plus` - | - = note: this feature is not stably supported; its behavior can change in the future - = note: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #162235 - warning: target feature `v8plus` must be disabled to ensure that the ABI of the current target can be implemented correctly | = note: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #162235 error: +v8plus,+v9 - --> $DIR/sparcv8plus.rs:35:1 + --> $DIR/sparcv8plus.rs:34:1 | LL | compile_error!("+v8plus,+v9"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 1 previous error; 2 warnings emitted +error: aborting due to 1 previous error; 1 warning emitted diff --git a/tests/ui/abi/sparcv8plus.sparc_feature_v8plus.stderr b/tests/ui/abi/sparcv8plus.sparc_feature_v8plus.stderr index 3e9e7ff7c4a5c..5fdbdea580fef 100644 --- a/tests/ui/abi/sparcv8plus.sparc_feature_v8plus.stderr +++ b/tests/ui/abi/sparcv8plus.sparc_feature_v8plus.stderr @@ -1,19 +1,13 @@ -warning: unstable feature specified for `-Ctarget-feature`: `v8plus` - | - = note: this feature is not stably supported; its behavior can change in the future - = note: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #162235 - warning: target feature `v8plus` must be disabled to ensure that the ABI of the current target can be implemented correctly | = note: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #162235 error: +v8plus,-v9 (FIXME) - --> $DIR/sparcv8plus.rs:40:1 + --> $DIR/sparcv8plus.rs:39:1 | LL | compile_error!("+v8plus,-v9 (FIXME)"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 1 previous error; 2 warnings emitted +error: aborting due to 1 previous error; 1 warning emitted diff --git a/tests/ui/abi/sparcv8plus.sparcv8plus.stderr b/tests/ui/abi/sparcv8plus.sparcv8plus.stderr index 37a03abae9b4b..5e1e1fa5c798f 100644 --- a/tests/ui/abi/sparcv8plus.sparcv8plus.stderr +++ b/tests/ui/abi/sparcv8plus.sparcv8plus.stderr @@ -1,5 +1,5 @@ error: +v8plus,+v9 - --> $DIR/sparcv8plus.rs:35:1 + --> $DIR/sparcv8plus.rs:34:1 | LL | compile_error!("+v8plus,+v9"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/asm/hexagon-register-pairs.rs b/tests/ui/asm/hexagon-register-pairs.rs index 75f2960778455..e14a80355cc14 100644 --- a/tests/ui/asm/hexagon-register-pairs.rs +++ b/tests/ui/asm/hexagon-register-pairs.rs @@ -7,8 +7,6 @@ #![crate_type = "lib"] #![no_core] -//~? WARN unstable feature specified for `-Ctarget-feature`: `hvx-length128b` - extern crate minicore; use minicore::*; diff --git a/tests/ui/asm/hexagon-register-pairs.stderr b/tests/ui/asm/hexagon-register-pairs.stderr index d59d46363bdb8..6de55c34a8bc2 100644 --- a/tests/ui/asm/hexagon-register-pairs.stderr +++ b/tests/ui/asm/hexagon-register-pairs.stderr @@ -1,11 +1,5 @@ -warning: unstable feature specified for `-Ctarget-feature`: `hvx-length128b` - | - = note: this feature is not stably supported; its behavior can change in the future - = note: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #162235 - error: avoid using named labels in inline assembly - --> $DIR/hexagon-register-pairs.rs:32:15 + --> $DIR/hexagon-register-pairs.rs:30:15 | LL | asm!("label1: r7:6 = combine(#2, #3)"); | ^^^^^^ @@ -15,7 +9,7 @@ LL | asm!("label1: r7:6 = combine(#2, #3)"); = note: `#[deny(named_asm_labels)]` on by default error: avoid using named labels in inline assembly - --> $DIR/hexagon-register-pairs.rs:35:15 + --> $DIR/hexagon-register-pairs.rs:33:15 | LL | asm!("hexagon_label: nop"); | ^^^^^^^^^^^^^ @@ -23,5 +17,5 @@ LL | asm!("hexagon_label: nop"); = help: only local labels of the form `:` should be used in inline asm = note: see the asm section of Rust By Example for more information -error: aborting due to 2 previous errors; 1 warning emitted +error: aborting due to 2 previous errors diff --git a/tests/ui/asm/hexagon/bad-reg.rs b/tests/ui/asm/hexagon/bad-reg.rs index 6d3e1521a26cc..cbff6d3c521da 100644 --- a/tests/ui/asm/hexagon/bad-reg.rs +++ b/tests/ui/asm/hexagon/bad-reg.rs @@ -3,8 +3,6 @@ //@ needs-llvm-components: hexagon //@ ignore-backends: gcc -//~? WARN unstable feature specified for `-Ctarget-feature`: `hvx-length128b` - #![crate_type = "lib"] #![feature(no_core, asm_experimental_arch)] #![no_core] diff --git a/tests/ui/asm/hexagon/bad-reg.stderr b/tests/ui/asm/hexagon/bad-reg.stderr index ce8f46e1b8498..5fcdd03c6b93e 100644 --- a/tests/ui/asm/hexagon/bad-reg.stderr +++ b/tests/ui/asm/hexagon/bad-reg.stderr @@ -1,95 +1,89 @@ -warning: unstable feature specified for `-Ctarget-feature`: `hvx-length128b` - | - = note: this feature is not stably supported; its behavior can change in the future - = note: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #162235 - error: invalid register `r19`: r19 is used internally by LLVM and cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:20:18 + --> $DIR/bad-reg.rs:18:18 | LL | asm!("", out("r19") _); | ^^^^^^^^^^^^ error: invalid register `sp`: the stack pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:22:18 + --> $DIR/bad-reg.rs:20:18 | LL | asm!("", out("sp") _); | ^^^^^^^^^^^ error: invalid register `r29`: the stack pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:24:18 + --> $DIR/bad-reg.rs:22:18 | LL | asm!("", out("r29") _); | ^^^^^^^^^^^^ error: invalid register `r30`: the frame register cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:26:18 + --> $DIR/bad-reg.rs:24:18 | LL | asm!("", out("r30") _); | ^^^^^^^^^^^^ error: invalid register `fr`: the frame register cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:28:18 + --> $DIR/bad-reg.rs:26:18 | LL | asm!("", out("fr") _); | ^^^^^^^^^^^ error: invalid register `r31`: the link register cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:30:18 + --> $DIR/bad-reg.rs:28:18 | LL | asm!("", out("r31") _); | ^^^^^^^^^^^^ error: invalid register `lr`: the link register cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:32:18 + --> $DIR/bad-reg.rs:30:18 | LL | asm!("", out("lr") _); | ^^^^^^^^^^^ error: invalid register `r19:18`: r19 is used internally by LLVM and cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:36:18 + --> $DIR/bad-reg.rs:34:18 | LL | asm!("", out("r19:18") _); | ^^^^^^^^^^^^^^^ error: invalid register `r29:28`: the stack pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:38:18 + --> $DIR/bad-reg.rs:36:18 | LL | asm!("", out("r29:28") _); | ^^^^^^^^^^^^^^^ error: invalid register `r31:30`: the frame register and link register cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:40:18 + --> $DIR/bad-reg.rs:38:18 | LL | asm!("", out("r31:30") _); | ^^^^^^^^^^^^^^^ error: register class `preg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:45:18 + --> $DIR/bad-reg.rs:43:18 | LL | asm!("", in("p0") x); | ^^^^^^^^^^ error: register class `preg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:48:18 + --> $DIR/bad-reg.rs:46:18 | LL | asm!("", out("p0") x); | ^^^^^^^^^^^ error: register class `qreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:54:18 + --> $DIR/bad-reg.rs:52:18 | LL | asm!("", in("q0") x); | ^^^^^^^^^^ error: register class `qreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:57:18 + --> $DIR/bad-reg.rs:55:18 | LL | asm!("", out("q0") x); | ^^^^^^^^^^^ error: register `r1:0` conflicts with register `r0` - --> $DIR/bad-reg.rs:99:31 + --> $DIR/bad-reg.rs:97:31 | LL | asm!("", out("r0") _, out("r1:0") _); | ----------- ^^^^^^^^^^^^^ register `r1:0` @@ -97,7 +91,7 @@ LL | asm!("", out("r0") _, out("r1:0") _); | register `r0` error: register `r1:0` conflicts with register `r1` - --> $DIR/bad-reg.rs:101:31 + --> $DIR/bad-reg.rs:99:31 | LL | asm!("", out("r1") _, out("r1:0") _); | ----------- ^^^^^^^^^^^^^ register `r1:0` @@ -105,7 +99,7 @@ LL | asm!("", out("r1") _, out("r1:0") _); | register `r1` error: register `v1:0` conflicts with register `v0` - --> $DIR/bad-reg.rs:105:31 + --> $DIR/bad-reg.rs:103:31 | LL | asm!("", out("v0") _, out("v1:0") _); | ----------- ^^^^^^^^^^^^^ register `v1:0` @@ -113,7 +107,7 @@ LL | asm!("", out("v0") _, out("v1:0") _); | register `v0` error: register `v1:0` conflicts with register `v1` - --> $DIR/bad-reg.rs:107:31 + --> $DIR/bad-reg.rs:105:31 | LL | asm!("", out("v1") _, out("v1:0") _); | ----------- ^^^^^^^^^^^^^ register `v1:0` @@ -121,7 +115,7 @@ LL | asm!("", out("v1") _, out("v1:0") _); | register `v1` error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:45:27 + --> $DIR/bad-reg.rs:43:27 | LL | asm!("", in("p0") x); | ^ @@ -129,7 +123,7 @@ LL | asm!("", in("p0") x); = note: register class `preg` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:48:28 + --> $DIR/bad-reg.rs:46:28 | LL | asm!("", out("p0") x); | ^ @@ -137,7 +131,7 @@ LL | asm!("", out("p0") x); = note: register class `preg` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:54:27 + --> $DIR/bad-reg.rs:52:27 | LL | asm!("", in("q0") x); | ^ @@ -145,7 +139,7 @@ LL | asm!("", in("q0") x); = note: register class `qreg` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:57:28 + --> $DIR/bad-reg.rs:55:28 | LL | asm!("", out("q0") x); | ^ @@ -153,7 +147,7 @@ LL | asm!("", out("q0") x); = note: register class `qreg` supports these types: error: type `i64` cannot be used with this register class - --> $DIR/bad-reg.rs:62:34 + --> $DIR/bad-reg.rs:60:34 | LL | asm!("/* {} */", in(reg) y); | ^ @@ -161,7 +155,7 @@ LL | asm!("/* {} */", in(reg) y); = note: register class `reg` supports these types: i8, i16, i32, f32 error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:66:39 + --> $DIR/bad-reg.rs:64:39 | LL | asm!("/* {} */", in(reg_pair) x); | ^ @@ -169,7 +163,7 @@ LL | asm!("/* {} */", in(reg_pair) x); = note: register class `reg_pair` supports these types: i64, f64 error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:70:35 + --> $DIR/bad-reg.rs:68:35 | LL | asm!("/* {} */", in(vreg) x); | ^ @@ -177,7 +171,7 @@ LL | asm!("/* {} */", in(vreg) x); = note: register class `vreg` supports these types: i32x16, i32x32 error: type `i64` cannot be used with this register class - --> $DIR/bad-reg.rs:72:35 + --> $DIR/bad-reg.rs:70:35 | LL | asm!("/* {} */", in(vreg) y); | ^ @@ -185,7 +179,7 @@ LL | asm!("/* {} */", in(vreg) y); = note: register class `vreg` supports these types: i32x16, i32x32 error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:76:40 + --> $DIR/bad-reg.rs:74:40 | LL | asm!("/* {} */", in(vreg_pair) x); | ^ @@ -193,12 +187,12 @@ LL | asm!("/* {} */", in(vreg_pair) x); = note: register class `vreg_pair` supports these types: i32x32, i32x64 error: type `i64` cannot be used with this register class - --> $DIR/bad-reg.rs:78:40 + --> $DIR/bad-reg.rs:76:40 | LL | asm!("/* {} */", in(vreg_pair) y); | ^ | = note: register class `vreg_pair` supports these types: i32x32, i32x64 -error: aborting due to 28 previous errors; 1 warning emitted +error: aborting due to 28 previous errors diff --git a/tests/ui/asm/mips/reg-conflict.mips32.stderr b/tests/ui/asm/mips/reg-conflict.mips32.stderr index 0bb1148e94bd2..7e7857662cca2 100644 --- a/tests/ui/asm/mips/reg-conflict.mips32.stderr +++ b/tests/ui/asm/mips/reg-conflict.mips32.stderr @@ -5,20 +5,8 @@ warning: unknown and unstable feature specified for `-Ctarget-feature`: `mips32r = note: for more information, see issue #162235 = help: consider filing a feature request -warning: unstable feature specified for `-Ctarget-feature`: `fp64` - | - = note: this feature is not stably supported; its behavior can change in the future - = note: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #162235 - -warning: unstable feature specified for `-Ctarget-feature`: `msa` - | - = note: this feature is not stably supported; its behavior can change in the future - = note: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #162235 - error: register `$f4` conflicts with register `$w4` - --> $DIR/reg-conflict.rs:28:33 + --> $DIR/reg-conflict.rs:26:33 | LL | asm!("", in("$w4") 0.0, in("$f4") 0.0); | ------------- ^^^^^^^^^^^^^ register `$f4` @@ -26,12 +14,12 @@ LL | asm!("", in("$w4") 0.0, in("$f4") 0.0); | register `$w4` error: register `$f25` conflicts with register `$w25` - --> $DIR/reg-conflict.rs:30:34 + --> $DIR/reg-conflict.rs:28:34 | LL | asm!("", in("$w25") 0.0, in("$f25") 0.0); | -------------- ^^^^^^^^^^^^^^ register `$f25` | | | register `$w25` -error: aborting due to 2 previous errors; 3 warnings emitted +error: aborting due to 2 previous errors; 1 warning emitted diff --git a/tests/ui/asm/mips/reg-conflict.mips32r6.stderr b/tests/ui/asm/mips/reg-conflict.mips32r6.stderr index ee8f6ee07bf8a..a2adedeef3f77 100644 --- a/tests/ui/asm/mips/reg-conflict.mips32r6.stderr +++ b/tests/ui/asm/mips/reg-conflict.mips32r6.stderr @@ -1,17 +1,5 @@ -warning: unstable feature specified for `-Ctarget-feature`: `fp64` - | - = note: this feature is not stably supported; its behavior can change in the future - = note: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #162235 - -warning: unstable feature specified for `-Ctarget-feature`: `msa` - | - = note: this feature is not stably supported; its behavior can change in the future - = note: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #162235 - error: register `$f4` conflicts with register `$w4` - --> $DIR/reg-conflict.rs:28:33 + --> $DIR/reg-conflict.rs:26:33 | LL | asm!("", in("$w4") 0.0, in("$f4") 0.0); | ------------- ^^^^^^^^^^^^^ register `$f4` @@ -19,12 +7,12 @@ LL | asm!("", in("$w4") 0.0, in("$f4") 0.0); | register `$w4` error: register `$f25` conflicts with register `$w25` - --> $DIR/reg-conflict.rs:30:34 + --> $DIR/reg-conflict.rs:28:34 | LL | asm!("", in("$w25") 0.0, in("$f25") 0.0); | -------------- ^^^^^^^^^^^^^^ register `$f25` | | | register `$w25` -error: aborting due to 2 previous errors; 2 warnings emitted +error: aborting due to 2 previous errors diff --git a/tests/ui/asm/mips/reg-conflict.mips64.stderr b/tests/ui/asm/mips/reg-conflict.mips64.stderr index 1deb3ae1a4b0d..b8cadec4d4102 100644 --- a/tests/ui/asm/mips/reg-conflict.mips64.stderr +++ b/tests/ui/asm/mips/reg-conflict.mips64.stderr @@ -5,20 +5,8 @@ warning: unknown and unstable feature specified for `-Ctarget-feature`: `mips64r = note: for more information, see issue #162235 = help: consider filing a feature request -warning: unstable feature specified for `-Ctarget-feature`: `fp64` - | - = note: this feature is not stably supported; its behavior can change in the future - = note: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #162235 - -warning: unstable feature specified for `-Ctarget-feature`: `msa` - | - = note: this feature is not stably supported; its behavior can change in the future - = note: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #162235 - error: register `$f4` conflicts with register `$w4` - --> $DIR/reg-conflict.rs:28:33 + --> $DIR/reg-conflict.rs:26:33 | LL | asm!("", in("$w4") 0.0, in("$f4") 0.0); | ------------- ^^^^^^^^^^^^^ register `$f4` @@ -26,12 +14,12 @@ LL | asm!("", in("$w4") 0.0, in("$f4") 0.0); | register `$w4` error: register `$f25` conflicts with register `$w25` - --> $DIR/reg-conflict.rs:30:34 + --> $DIR/reg-conflict.rs:28:34 | LL | asm!("", in("$w25") 0.0, in("$f25") 0.0); | -------------- ^^^^^^^^^^^^^^ register `$f25` | | | register `$w25` -error: aborting due to 2 previous errors; 3 warnings emitted +error: aborting due to 2 previous errors; 1 warning emitted diff --git a/tests/ui/asm/mips/reg-conflict.mips64r6.stderr b/tests/ui/asm/mips/reg-conflict.mips64r6.stderr index ee8f6ee07bf8a..a2adedeef3f77 100644 --- a/tests/ui/asm/mips/reg-conflict.mips64r6.stderr +++ b/tests/ui/asm/mips/reg-conflict.mips64r6.stderr @@ -1,17 +1,5 @@ -warning: unstable feature specified for `-Ctarget-feature`: `fp64` - | - = note: this feature is not stably supported; its behavior can change in the future - = note: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #162235 - -warning: unstable feature specified for `-Ctarget-feature`: `msa` - | - = note: this feature is not stably supported; its behavior can change in the future - = note: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #162235 - error: register `$f4` conflicts with register `$w4` - --> $DIR/reg-conflict.rs:28:33 + --> $DIR/reg-conflict.rs:26:33 | LL | asm!("", in("$w4") 0.0, in("$f4") 0.0); | ------------- ^^^^^^^^^^^^^ register `$f4` @@ -19,12 +7,12 @@ LL | asm!("", in("$w4") 0.0, in("$f4") 0.0); | register `$w4` error: register `$f25` conflicts with register `$w25` - --> $DIR/reg-conflict.rs:30:34 + --> $DIR/reg-conflict.rs:28:34 | LL | asm!("", in("$w25") 0.0, in("$f25") 0.0); | -------------- ^^^^^^^^^^^^^^ register `$f25` | | | register `$w25` -error: aborting due to 2 previous errors; 2 warnings emitted +error: aborting due to 2 previous errors diff --git a/tests/ui/asm/mips/reg-conflict.rs b/tests/ui/asm/mips/reg-conflict.rs index f0c9b5e6fd091..7fdf799b9d2d3 100644 --- a/tests/ui/asm/mips/reg-conflict.rs +++ b/tests/ui/asm/mips/reg-conflict.rs @@ -17,8 +17,6 @@ //[mips32]~? WARN unknown and unstable feature specified for `-Ctarget-feature`: `mips32r5` //[mips64]~? WARN unknown and unstable feature specified for `-Ctarget-feature`: `mips64r5` -//~? WARN unstable feature specified for `-Ctarget-feature`: `fp64` -//~? WARN unstable feature specified for `-Ctarget-feature`: `msa` extern crate minicore; use minicore::*; diff --git a/tests/ui/target-feature/abi-irrelevant-target-feature-flag-disable.rs b/tests/ui/target-feature/abi-irrelevant-target-feature-flag-disable.rs index 688f51dad58cb..38b35c67ba6ff 100644 --- a/tests/ui/target-feature/abi-irrelevant-target-feature-flag-disable.rs +++ b/tests/ui/target-feature/abi-irrelevant-target-feature-flag-disable.rs @@ -12,5 +12,3 @@ extern crate minicore; use minicore::*; - -//~? WARN unstable feature specified for `-Ctarget-feature`: `x87` diff --git a/tests/ui/target-feature/abi-irrelevant-target-feature-flag-disable.stderr b/tests/ui/target-feature/abi-irrelevant-target-feature-flag-disable.stderr deleted file mode 100644 index c7623519db2ed..0000000000000 --- a/tests/ui/target-feature/abi-irrelevant-target-feature-flag-disable.stderr +++ /dev/null @@ -1,8 +0,0 @@ -warning: unstable feature specified for `-Ctarget-feature`: `x87` - | - = note: this feature is not stably supported; its behavior can change in the future - = note: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #162235 - -warning: 1 warning emitted - diff --git a/tests/ui/target-feature/abi-required-target-feature-flag-disable.rs b/tests/ui/target-feature/abi-required-target-feature-flag-disable.rs index 4faef8d02d673..f15aa5313ba43 100644 --- a/tests/ui/target-feature/abi-required-target-feature-flag-disable.rs +++ b/tests/ui/target-feature/abi-required-target-feature-flag-disable.rs @@ -26,4 +26,3 @@ extern crate minicore; use minicore::*; //~? WARN must be enabled to ensure that the ABI of the current target can be implemented correctly -//[x86]~? WARN unstable feature specified for `-Ctarget-feature` diff --git a/tests/ui/target-feature/abi-required-target-feature-flag-disable.x86.stderr b/tests/ui/target-feature/abi-required-target-feature-flag-disable.x86.stderr index 52611d230af55..d7f4b3d670b73 100644 --- a/tests/ui/target-feature/abi-required-target-feature-flag-disable.x86.stderr +++ b/tests/ui/target-feature/abi-required-target-feature-flag-disable.x86.stderr @@ -1,13 +1,7 @@ -warning: unstable feature specified for `-Ctarget-feature`: `x87` - | - = note: this feature is not stably supported; its behavior can change in the future - = note: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #162235 - warning: target feature `x87` must be enabled to ensure that the ABI of the current target can be implemented correctly | = note: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #162235 -warning: 2 warnings emitted +warning: 1 warning emitted diff --git a/tests/ui/target-feature/feature-hierarchy.hexagon-hvxv66.stderr b/tests/ui/target-feature/feature-hierarchy.hexagon-hvxv66.stderr deleted file mode 100644 index a48e3f28bc683..0000000000000 --- a/tests/ui/target-feature/feature-hierarchy.hexagon-hvxv66.stderr +++ /dev/null @@ -1,8 +0,0 @@ -warning: unstable feature specified for `-Ctarget-feature`: `hvxv66` - | - = note: this feature is not stably supported; its behavior can change in the future - = note: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #162235 - -warning: 1 warning emitted - diff --git a/tests/ui/target-feature/feature-hierarchy.hexagon-v60.stderr b/tests/ui/target-feature/feature-hierarchy.hexagon-v60.stderr deleted file mode 100644 index 83ce776cb1c97..0000000000000 --- a/tests/ui/target-feature/feature-hierarchy.hexagon-v60.stderr +++ /dev/null @@ -1,8 +0,0 @@ -warning: unstable feature specified for `-Ctarget-feature`: `v60` - | - = note: this feature is not stably supported; its behavior can change in the future - = note: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #162235 - -warning: 1 warning emitted - diff --git a/tests/ui/target-feature/feature-hierarchy.hexagon-v68.stderr b/tests/ui/target-feature/feature-hierarchy.hexagon-v68.stderr deleted file mode 100644 index 2e62c87d7c9d8..0000000000000 --- a/tests/ui/target-feature/feature-hierarchy.hexagon-v68.stderr +++ /dev/null @@ -1,8 +0,0 @@ -warning: unstable feature specified for `-Ctarget-feature`: `v68` - | - = note: this feature is not stably supported; its behavior can change in the future - = note: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #162235 - -warning: 1 warning emitted - diff --git a/tests/ui/target-feature/feature-hierarchy.rs b/tests/ui/target-feature/feature-hierarchy.rs index 65f7a1e6d3db4..2629f5b000f80 100644 --- a/tests/ui/target-feature/feature-hierarchy.rs +++ b/tests/ui/target-feature/feature-hierarchy.rs @@ -63,10 +63,6 @@ fn check_sve2_includes_neon() { assert!(cfg!(target_feature = "sve2")); } -//[hexagon-v60]~? WARN unstable feature specified for `-Ctarget-feature`: `v60` -//[hexagon-v68]~? WARN unstable feature specified for `-Ctarget-feature`: `v68` -//[hexagon-hvxv66]~? WARN unstable feature specified for `-Ctarget-feature`: `hvxv66` - #[cfg(hexagon_v60)] fn check_v60_not_v68() { // Enabling v60 should not jump up the scalar feature hierarchy. diff --git a/tests/ui/target-feature/packedstack-combinations.rs b/tests/ui/target-feature/packedstack-combinations.rs index 5dc0f6adf812e..dc45ac1ff5e83 100644 --- a/tests/ui/target-feature/packedstack-combinations.rs +++ b/tests/ui/target-feature/packedstack-combinations.rs @@ -38,7 +38,5 @@ pub fn test() { } //[wrong_arch]~? ERROR `-Zpacked-stack` is only supported on s390x -//[backchain_cli]~? WARN unstable feature specified for `-Ctarget-feature`: `backchain` //[backchain_cli]~? ERROR `-Zpacked-stack` is incompatible with `backchain` target feature //[backchain_attr]~? ERROR `-Zpacked-stack` is incompatible with `backchain` target feature -//[with_softfloat]~? WARN unstable feature specified for `-Ctarget-feature`: `backchain` diff --git a/tests/ui/target-feature/packedstack-combinations.with_softfloat.stderr b/tests/ui/target-feature/packedstack-combinations.with_softfloat.stderr deleted file mode 100644 index eeec785448bc3..0000000000000 --- a/tests/ui/target-feature/packedstack-combinations.with_softfloat.stderr +++ /dev/null @@ -1,8 +0,0 @@ -warning: unstable feature specified for `-Ctarget-feature`: `backchain` - | - = note: this feature is not stably supported; its behavior can change in the future - = note: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #162235 - -warning: 1 warning emitted - diff --git a/tests/ui/target-feature/unstable-feature.rs b/tests/ui/target-feature/unstable-feature.rs deleted file mode 100644 index a79ad4696033a..0000000000000 --- a/tests/ui/target-feature/unstable-feature.rs +++ /dev/null @@ -1,8 +0,0 @@ -//@ compile-flags: -Ctarget-feature=+x87 --crate-type=rlib --target=x86_64-unknown-linux-gnu -//@ build-pass -//@ needs-llvm-components: x86 - -#![feature(no_core)] -#![no_core] - -//~? WARN unstable feature specified for `-Ctarget-feature`: `x87` diff --git a/tests/ui/target-feature/unstable-feature.stderr b/tests/ui/target-feature/unstable-feature.stderr deleted file mode 100644 index c7623519db2ed..0000000000000 --- a/tests/ui/target-feature/unstable-feature.stderr +++ /dev/null @@ -1,8 +0,0 @@ -warning: unstable feature specified for `-Ctarget-feature`: `x87` - | - = note: this feature is not stably supported; its behavior can change in the future - = note: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #162235 - -warning: 1 warning emitted - From 9495d190171feaddf952fad247c713a957918297 Mon Sep 17 00:00:00 2001 From: Crystal Durham Date: Mon, 31 Aug 2026 15:05:37 -0500 Subject: [PATCH 30/30] impl std::fs::{Home|Media}Dirs also includes: - std::env::split_paths_ref - std::os::{darwin|unix|windows}::{Home|Media}DirsExt --- library/std/src/env.rs | 51 ++ library/std/src/fs.rs | 7 + library/std/src/fs/dirs.rs | 572 +++++++++++++++++ library/std/src/os/darwin/fs.rs | 7 + library/std/src/os/darwin/fs/dirs.rs | 313 ++++++++++ library/std/src/os/unix/fs.rs | 7 + library/std/src/os/unix/fs/dirs.rs | 585 ++++++++++++++++++ library/std/src/os/windows/fs.rs | 7 + library/std/src/os/windows/fs/dirs.rs | 309 +++++++++ library/std/src/sys/fs/common.rs | 8 + library/std/src/sys/fs/hermit.rs | 2 +- library/std/src/sys/fs/mod.rs | 7 +- library/std/src/sys/fs/motor.rs | 2 +- library/std/src/sys/fs/solid.rs | 2 +- library/std/src/sys/fs/uefi.rs | 2 +- library/std/src/sys/fs/unix.rs | 4 + library/std/src/sys/fs/unsupported.rs | 2 +- library/std/src/sys/fs/vexos.rs | 2 + library/std/src/sys/fs/windows.rs | 1 + library/std/src/sys/pal/windows/c.rs | 45 ++ .../std/src/sys/pal/windows/c/bindings.txt | 7 + .../std/src/sys/pal/windows/c/windows_sys.rs | 8 + library/std/src/sys/pal/windows/compat.rs | 108 +++- library/std/src/sys/paths/mod.rs | 18 +- library/std/src/sys/paths/uefi.rs | 14 + library/std/src/sys/paths/unix.rs | 24 +- library/std/src/sys/paths/unsupported.rs | 14 +- library/std/src/sys/paths/windows.rs | 14 + 28 files changed, 2124 insertions(+), 18 deletions(-) create mode 100644 library/std/src/fs/dirs.rs create mode 100644 library/std/src/os/darwin/fs/dirs.rs create mode 100644 library/std/src/os/unix/fs/dirs.rs create mode 100644 library/std/src/os/windows/fs/dirs.rs diff --git a/library/std/src/env.rs b/library/std/src/env.rs index 762ad071e967c..6e401113cb26e 100644 --- a/library/std/src/env.rs +++ b/library/std/src/env.rs @@ -502,6 +502,57 @@ impl fmt::Debug for SplitPaths<'_> { } } +/// An iterator that splits an environment variable into paths according to +/// platform-specific conventions. +/// +/// The iterator element type is &[Path]. +/// +/// This structure is created by [`env::split_paths_ref()`]. See its +/// documentation for more. +/// +/// [`env::split_paths_ref()`]: split_paths_ref +#[must_use = "iterators are lazy and do nothing unless consumed"] +#[unstable(feature = "env_split_paths_ref", issue = "none")] +pub struct SplitPathsRef<'a> { + inner: paths_imp::SplitPathsRef<'a>, +} + +/// Parses input according to platform conventions for the `PATH` +/// environment variable. +/// +/// Unlike [`split_paths`], this function does not allocate and instead yields +/// [`Path`]s borrowed from `unparsed`. Returns `None` on platforms that may +/// require allocations to handle `PATH` splitting conventions. +/// +/// # Platform-specific behavior +/// +/// Returns `Some` on Unix platforms and `None` on all other platforms. +/// Note that this [may change in the future][changes]. +/// +/// [changes]: io#platform-specific-behavior +#[unstable(feature = "env_split_paths_ref", issue = "none")] +pub fn split_paths_ref + ?Sized>(unparsed: &T) -> Option> { + Some(SplitPathsRef { inner: paths_imp::split_paths_ref(unparsed.as_ref())? }) +} + +#[unstable(feature = "env_split_paths_ref", issue = "none")] +impl<'a> Iterator for SplitPathsRef<'a> { + type Item = &'a Path; + fn next(&mut self) -> Option<&'a Path> { + self.inner.next() + } + fn size_hint(&self) -> (usize, Option) { + self.inner.size_hint() + } +} + +#[unstable(feature = "env_split_paths_ref", issue = "none")] +impl fmt::Debug for SplitPathsRef<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("SplitPathsRef").finish_non_exhaustive() + } +} + /// The error type for operations on the `PATH` variable. Possibly returned from /// [`env::join_paths()`]. /// diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs index 8df809264a6dd..3a4f14c3306bc 100644 --- a/library/std/src/fs.rs +++ b/library/std/src/fs.rs @@ -49,6 +49,13 @@ use crate::sys::{AsInner, AsInnerMut, FromInner, IntoInner, fs as fs_imp}; use crate::time::SystemTime; use crate::{error, fmt}; +pub(crate) mod dirs; + +#[unstable(feature = "fs_home_dirs", issue = "162082")] +pub use self::dirs::HomeDirs; +#[unstable(feature = "fs_media_dirs", issue = "162083")] +pub use self::dirs::MediaDirs; + /// An object providing access to an open file on the filesystem. /// /// An instance of a `File` can be read and/or written depending on what options diff --git a/library/std/src/fs/dirs.rs b/library/std/src/fs/dirs.rs new file mode 100644 index 0000000000000..29ab5f69c42cb --- /dev/null +++ b/library/std/src/fs/dirs.rs @@ -0,0 +1,572 @@ +use crate::path::{Path, PathBuf}; +use crate::sys::fs::{ExtraHomeDirs, ExtraMediaDirs}; + +/// Common user directory paths used for user-specific application files. +/// +/// It is not required that the user directories are accessible by the current +/// user, nor that there is a directory at that path. A robust application +/// should handle the case where user directories are incorrectly configured. +/// +/// Even when configured correctly, multiple paths may point to the same location. +/// You should not assume that a file written relative to one directory will not +/// conflict with the same relative path in a different home directory. +/// +/// # Platform-specific behavior +/// +/// As the filesystem conventions for discovering directories varies between +/// operating systems, constructors for `HomeDirs` that use the host platform's +/// conventions are provided as extension traits under the `std::os` module. +#[unstable(feature = "fs_home_dirs", issue = "162082")] +#[derive(Debug, Clone)] +pub struct HomeDirs { + pub(crate) cache: Option, + pub(crate) config: Option, + pub(crate) data: Option, + pub(crate) state: Option, + #[cfg_attr(not(unix), expect(dead_code, reason = "no extra home dirs"))] + pub(crate) extra: ExtraHomeDirs, +} + +/// Common user directory paths used for user-specific media files. +/// +/// It is not required that the media directories are accessible by the current +/// user, nor that there is a directory at that path. A robust application +/// should handle the case where media directories are incorrectly configured. +/// +/// Even when configured correctly, multiple paths may point to the same location. +/// You should not assume that a file written relative to one directory will not +/// conflict with the same relative path in a different media directory. +/// +/// # Platform-specific behavior +/// +/// As the filesystem conventions for discovering directories varies between +/// operating systems, constructors for `MediaDirs` that use the host platform's +/// conventions are provided as extension traits under the `std::os` module. +#[unstable(feature = "fs_media_dirs", issue = "162083")] +#[derive(Debug, Clone)] +pub struct MediaDirs { + pub(crate) desktop: Option, + pub(crate) documents: Option, + pub(crate) downloads: Option, + pub(crate) music: Option, + pub(crate) pictures: Option, + pub(crate) videos: Option, + #[cfg_attr(not(unix), expect(dead_code, reason = "no extra media dirs"))] + pub(crate) extra: ExtraMediaDirs, +} + +// NB: HomeDirs and MediaDirs intentionally do not implement Default. Self::empty() +// is a logical default, but users may also intuit the default to use default +// platform conventions. Omitting Default pushes users to explicitly choose. + +impl HomeDirs { + /// Create a known user directory set with no known directories. + /// + /// This is useful with the builder `set_*` methods to create a `HomeDirs` + /// with exactly the directories you want, without any other defaults. + #[unstable(feature = "fs_home_dirs", issue = "162082")] + pub fn empty() -> Self { + Self { cache: None, config: None, data: None, state: None, extra: Default::default() } + } + + /// A base directory relative to which user-specific non-essential cache + /// data files should be stored. + /// + /// "Cache" files are temporary data that can be used to cache redundant + /// work of an application, but which can be discarded arbitrarily and + /// recreated as necessary. Files in this directory may potentially be + /// automatically purged any time they are not currently open, or they + /// may not, depending on system configuration. A robust application + /// should ensure that its caches do not grow without a reasonable bound. + /// + /// This is the same directory for all applications. Applications should + /// use a subdirectory for application-specific cache files. + /// + /// # Platform-specific behavior + /// + /// When constructed using platform-specific conventions, the value is: + /// + /// | OS | Path | + /// | -- | ---- | + /// | [XDG] (Linux) | `${XDG_CACHE_HOME:-$HOME/.cache}` | + /// | [Darwin] (macOS) | [`NSCachesDirectory`] (`$HOME/Library/Caches`) | + /// | [Windows] | [`{FOLDERID_LocalAppData}`] (`%LOCALAPPDATA%`) | + /// + /// Other paths can be configured via [`set_cache_home`](Self::set_cache_home). + /// + /// [XDG]: crate::os::unix::fs::HomeDirsExt + /// [Darwin]: crate::os::darwin::fs::HomeDirsExt + /// [Windows]: crate::os::windows::fs::HomeDirsExt + /// [`NSCachesDirectory`]: https://developer.apple.com/documentation/foundation/filemanager/searchpathdirectory/cachesdirectory?language=objc + /// [`{FOLDERID_LocalAppData}`]: https://learn.microsoft.com/en-us/windows/win32/shell/knownfolderid#folderid_localappdata + #[unstable(feature = "fs_home_dirs", issue = "162082")] + pub fn cache_home(&self) -> Option<&Path> { + self.cache.as_deref() + } + + /// A base directory relative to which user-specific configuration files + /// should be stored. + /// + /// "Config" files are configuration managed by the user, either by editing + /// the files directly or through a managing application. Configuration is + /// generally expected to be meaningful to the user and portable enough to + /// back up and synchronize across the same user's account on multiple + /// systems. + /// + /// This is the same directory for all applications. Applications should + /// use a subdirectory for application-specific configuration files. + /// + /// # Platform-specific behavior + /// + /// When constructed using platform-specific conventions, the value is: + /// + /// | OS | Path | + /// | -- | ---- | + /// | [XDG] (Linux) | `${XDG_CONFIG_HOME:-$HOME/.config}` | + /// | [Darwin] (macOS) | [`NSApplicationSupportDirectory`] (`$HOME/Library/Application Support`) | + /// | [Windows] | [`{FOLDERID_RoamingAppData}`] (`%APPDATA%`) | + /// + /// Other paths can be configured via [`set_config_home`](Self::set_config_home). + /// + /// [XDG]: crate::os::unix::fs::HomeDirsExt + /// [Darwin]: crate::os::darwin::fs::HomeDirsExt + /// [Windows]: crate::os::windows::fs::HomeDirsExt + /// [`NSApplicationSupportDirectory`]: https://developer.apple.com/documentation/foundation/filemanager/searchpathdirectory/applicationsupportdirectory?language=objc + /// [`{FOLDERID_RoamingAppData}`]: https://learn.microsoft.com/en-us/windows/win32/shell/knownfolderid#folderid_roamingappdata + #[unstable(feature = "fs_home_dirs", issue = "162082")] + pub fn config_home(&self) -> Option<&Path> { + self.config.as_deref() + } + + /// A base directory relative to which user-specific data files should be + /// stored. + /// + /// "Data" files are application-specific data that is meaningful to the + /// user in some way and does not implicitly rely on system configuration + /// or details of how the application is installed otherwise irrelevant to + /// the user. As such, data makes sense to back up and synchronize between + /// the same user's account on multiple systems. If a file is specific to + /// a single machine, it's probably [state](Self::state_home). + /// + /// This is the same directory for all applications. Applications should + /// use a subdirectory for application-specific data files. + /// + /// # Platform-specific behavior + /// + /// When constructed using platform-specific conventions, the value is: + /// + /// | OS | Path | + /// | -- | ---- | + /// | [XDG] (Linux) | `${XDG_DATA_HOME:-$HOME/.local/share}` | + /// | [Darwin] (macOS) | [`NSApplicationSupportDirectory`] (`$HOME/Library/Application Support`) | + /// | [Windows] | [`{FOLDERID_RoamingAppData}`] (`%APPDATA%`) | + /// + /// Other paths can be configured via [`set_data_home`](Self::set_data_home). + /// + /// [XDG]: crate::os::unix::fs::HomeDirsExt + /// [Darwin]: crate::os::darwin::fs::HomeDirsExt + /// [Windows]: crate::os::windows::fs::HomeDirsExt + /// [`NSApplicationSupportDirectory`]: https://developer.apple.com/documentation/foundation/filemanager/searchpathdirectory/applicationsupportdirectory?language=objc + /// [`{FOLDERID_RoamingAppData}`]: https://learn.microsoft.com/en-us/windows/win32/shell/knownfolderid#folderid_roamingappdata + #[unstable(feature = "fs_home_dirs", issue = "162082")] + pub fn data_home(&self) -> Option<&Path> { + self.data.as_deref() + } + + /// A base directory relative to which user-specific state files should be + /// stored. + /// + /// "State" files are data that should persist between application restarts, + /// but which is not important nor portable enough to the user to synchronize + /// between multiple systems like [data](Self::data_home) are. Common examples + /// include history (such as logs, recently used files, etc) and any current + /// state of the application that should be reused (such as view, layout, open + /// files, undo history, etc). + /// + /// This is the same directory for all applications. Applications should + /// use a subdirectory for application-specific state files. + /// + /// # Platform-specific behavior + /// + /// When constructed using platform-specific conventions, the value is: + /// + /// | OS | Path | + /// | -- | ---- | + /// | [XDG] (Linux) | `${XDG_STATE_HOME:-$HOME/.local/state}` | + /// | [Darwin] (macOS) | [`NSApplicationSupportDirectory`] (`$HOME/Library/Application Support`) | + /// | [Windows] | [`{FOLDERID_LocalAppData}`] (`%LOCALAPPDATA%`) | + /// + /// Other paths can be configured via [`set_state_home`](Self::set_state_home). + /// + /// [XDG]: crate::os::unix::fs::HomeDirsExt + /// [Darwin]: crate::os::darwin::fs::HomeDirsExt + /// [Windows]: crate::os::windows::fs::HomeDirsExt + /// [`NSApplicationSupportDirectory`]: https://developer.apple.com/documentation/foundation/filemanager/searchpathdirectory/applicationsupportdirectory?language=objc + /// [`{FOLDERID_LocalAppData}`]: https://learn.microsoft.com/en-us/windows/win32/shell/knownfolderid#folderid_localappdata + #[unstable(feature = "fs_home_dirs", issue = "162082")] + pub fn state_home(&self) -> Option<&Path> { + self.state.as_deref() + } +} + +impl MediaDirs { + /// Create a known user directory set with no known directories. + /// + /// This is useful with the builder `set_*` methods to create a `MediaDirs` + /// with exactly the directories you want, without any other defaults. + #[unstable(feature = "fs_media_dirs", issue = "162083")] + pub fn empty() -> Self { + Self { + desktop: None, + documents: None, + downloads: None, + music: None, + pictures: None, + videos: None, + extra: Default::default(), + } + } + + /// The OS-recognized user "Desktop" directory, often the `Desktop` + /// folder in the user's home directory. + /// + /// As a media directory, this should typically be used as a default path + /// for file selection dialogs, not for automatically accessed file paths. + /// + /// # Platform-specific behavior + /// + /// When constructed using platform-specific conventions, the value is: + /// + /// | OS | Path | + /// | -- | ---- | + /// | [XDG] (Linux) | `xdg-user-dir DESKTOP` (`$HOME/Desktop`) | + /// | [Darwin] (macOS) | [`NSDesktopDirectory`] (`$HOME/Desktop`) | + /// | [Windows] | [`{FOLDERID_Desktop}`] (`%USERPROFILE%\Desktop`) | + /// + /// Other paths can be configured via [`set_desktop`](Self::set_desktop). + /// + /// [XDG]: crate::os::unix::fs::MediaDirsExt + /// [Darwin]: crate::os::darwin::fs::MediaDirsExt + /// [Windows]: crate::os::windows::fs::MediaDirsExt + /// [`NSDesktopDirectory`]: https://developer.apple.com/documentation/foundation/filemanager/searchpathdirectory/desktopdirectory?language=objc + /// [`{FOLDERID_Desktop}`]: https://learn.microsoft.com/en-us/windows/win32/shell/knownfolderid#folderid_desktop + #[unstable(feature = "fs_media_dirs", issue = "162083")] + pub fn desktop(&self) -> Option<&Path> { + self.desktop.as_deref() + } + + /// The OS-recognized user "Documents" directory, often the `Documents` + /// folder in the user's home directory. + /// + /// As a media directory, this should typically be used as a default path + /// for file selection dialogs, not for automatically accessed file paths. + /// + /// # Platform-specific behavior + /// + /// When constructed using platform-specific conventions, the value is: + /// + /// | OS | Path | + /// | -- | ---- | + /// | [XDG] (Linux) | `xdg-user-dir DOCUMENTS` (`$HOME/Documents`) | + /// | [Darwin] (macOS) | [`NSDocumentDirectory`] (`$HOME/Documents`) | + /// | [Windows] | [`{FOLDERID_Documents}`] (`%USERPROFILE%\Documents`) | + /// + /// Other paths can be configured via [`set_documents`](Self::set_documents). + /// + /// [XDG]: crate::os::unix::fs::MediaDirsExt + /// [Darwin]: crate::os::darwin::fs::MediaDirsExt + /// [Windows]: crate::os::windows::fs::MediaDirsExt + /// [`NSDocumentDirectory`]: https://developer.apple.com/documentation/foundation/filemanager/searchpathdirectory/documentdirectory?language=objc + /// [`{FOLDERID_Documents}`]: https://learn.microsoft.com/en-us/windows/win32/shell/knownfolderid#folderid_documents + #[unstable(feature = "fs_media_dirs", issue = "162083")] + pub fn documents(&self) -> Option<&Path> { + self.documents.as_deref() + } + + /// The OS-recognized user "Downloads" directory, often the `Downloads` + /// folder in the user's home directory. + /// + /// As a media directory, this should typically be used as a default path + /// for file selection dialogs, not for automatically accessed file paths. + /// + /// # Platform-specific behavior + /// + /// When constructed using platform-specific conventions, the value is: + /// + /// | OS | Path | + /// | -- | ---- | + /// | [XDG] (Linux) | `xdg-user-dir DOWNLOAD` (`$HOME/Downloads`) | + /// | [Darwin] (macOS) | [`NSDownloadsDirectory`] (`$HOME/Downloads`) | + /// | [Windows] | [`{FOLDERID_Downloads}`] (`%USERPROFILE%\Downloads`) | + /// + /// Other paths can be configured via [`set_downloads`](Self::set_downloads). + /// + /// [XDG]: crate::os::unix::fs::MediaDirsExt + /// [Darwin]: crate::os::darwin::fs::MediaDirsExt + /// [Windows]: crate::os::windows::fs::MediaDirsExt + /// [`NSDownloadsDirectory`]: https://developer.apple.com/documentation/foundation/filemanager/searchpathdirectory/downloadsdirectory?language=objc + /// [`{FOLDERID_Downloads}`]: https://learn.microsoft.com/en-us/windows/win32/shell/knownfolderid#folderid_downloads + #[unstable(feature = "fs_media_dirs", issue = "162083")] + pub fn downloads(&self) -> Option<&Path> { + self.downloads.as_deref() + } + + /// The OS-recognized user "Music" directory, often the `Music` + /// folder in the user's home directory. + /// + /// As a media directory, this should typically be used as a default path + /// for file selection dialogs, not for automatically accessed file paths. + /// + /// # Platform-specific behavior + /// + /// When constructed using platform-specific conventions, the value is: + /// + /// | OS | Path | + /// | -- | ---- | + /// | [XDG] (Linux) | `xdg-user-dir MUSIC` (`$HOME/Music`) | + /// | [Darwin] (macOS) | [`NSMusicDirectory`] (`$HOME/Music`) | + /// | [Windows] | [`{FOLDERID_Music}`] (`%USERPROFILE%\Music`) | + /// + /// Other paths can be configured via [`set_music`](Self::set_music). + /// + /// [XDG]: crate::os::unix::fs::MediaDirsExt + /// [Darwin]: crate::os::darwin::fs::MediaDirsExt + /// [Windows]: crate::os::windows::fs::MediaDirsExt + /// [`NSMusicDirectory`]: https://developer.apple.com/documentation/foundation/filemanager/searchpathdirectory/musicdirectory?language=objc + /// [`{FOLDERID_Music}`]: https://learn.microsoft.com/en-us/windows/win32/shell/knownfolderid#folderid_music + #[unstable(feature = "fs_media_dirs", issue = "162083")] + pub fn music(&self) -> Option<&Path> { + self.music.as_deref() + } + + /// The OS-recognized user "Pictures" directory, often the `Pictures` + /// folder in the user's home directory. + /// + /// As a media directory, this should typically be used as a default path + /// for file selection dialogs, not for automatically accessed file paths. + /// + /// # Platform-specific behavior + /// + /// When constructed using platform-specific conventions, the value is: + /// + /// | OS | Path | + /// | -- | ---- | + /// | [XDG] (Linux) | `xdg-user-dir PICTURES` (`$HOME/Pictures`) | + /// | [Darwin] (macOS) | [`NSPicturesDirectory`] (`$HOME/Pictures`) | + /// | [Windows] | [`{FOLDERID_Pictures}`] (`%USERPROFILE%\Pictures`) | + /// + /// Other paths can be configured via [`set_pictures`](Self::set_pictures). + /// + /// [XDG]: crate::os::unix::fs::MediaDirsExt + /// [Darwin]: crate::os::darwin::fs::MediaDirsExt + /// [Windows]: crate::os::windows::fs::MediaDirsExt + /// [`NSPicturesDirectory`]: https://developer.apple.com/documentation/foundation/filemanager/searchpathdirectory/picturesdirectory?language=objc + /// [`{FOLDERID_Pictures}`]: https://learn.microsoft.com/en-us/windows/win32/shell/knownfolderid#folderid_pictures + #[unstable(feature = "fs_media_dirs", issue = "162083")] + pub fn pictures(&self) -> Option<&Path> { + self.pictures.as_deref() + } + + /// The OS-recognized user "Videos" directory, often the `Videos` + /// folder in the user's home directory. + /// + /// As a media directory, this should typically be used as a default path + /// for file selection dialogs, not for automatically accessed file paths. + /// + /// # Platform-specific behavior + /// + /// When constructed using platform-specific conventions, the value is: + /// + /// | OS | Path | + /// | -- | ---- | + /// | [XDG] (Linux) | `xdg-user-dir VIDEOS` (`$HOME/Videos`) | + /// | [Darwin] (macOS) | [`NSMoviesDirectory`] (`$HOME/Movies`) | + /// | [Windows] | [`{FOLDERID_Videos}`] (`%USERPROFILE%\Videos`) | + /// + /// Other paths can be configured via [`set_videos`](Self::set_videos). + /// + /// [XDG]: crate::os::unix::fs::MediaDirsExt + /// [Darwin]: crate::os::darwin::fs::MediaDirsExt + /// [Windows]: crate::os::windows::fs::MediaDirsExt + /// [`NSMoviesDirectory`]: https://developer.apple.com/documentation/foundation/filemanager/searchpathdirectory/moviesdirectory?language=objc + /// [`{FOLDERID_Videos}`]: https://learn.microsoft.com/en-us/windows/win32/shell/knownfolderid#folderid_videos + #[unstable(feature = "fs_media_dirs", issue = "162083")] + pub fn videos(&self) -> Option<&Path> { + self.videos.as_deref() + } +} + +impl HomeDirs { + /// Set the path for [Self::cache_home]. + /// + /// # Panics + /// + /// Panics if the provided path is not absolute. + #[unstable(feature = "fs_home_dirs", issue = "162082")] + pub fn set_cache_home(&mut self, path: PathBuf) -> &mut Self { + assert!(path.is_absolute(), "cache home directory path must be absolute"); + self.cache = Some(path); + self + } + + /// Set the path for [Self::config_home]. + /// + /// # Panics + /// + /// Panics if the provided path is not absolute. + #[unstable(feature = "fs_home_dirs", issue = "162082")] + pub fn set_config_home(&mut self, path: PathBuf) -> &mut Self { + assert!(path.is_absolute(), "config home directory path must be absolute"); + self.config = Some(path); + self + } + + /// Set the path for [Self::data_home]. + /// + /// # Panics + /// + /// Panics if the provided path is not absolute. + #[unstable(feature = "fs_home_dirs", issue = "162082")] + pub fn set_data_home(&mut self, path: PathBuf) -> &mut Self { + assert!(path.is_absolute(), "data home directory path must be absolute"); + self.data = Some(path); + self + } + + /// Set the path for [Self::state_home]. + /// + /// # Panics + /// + /// Panics if the provided path is not absolute. + #[unstable(feature = "fs_home_dirs", issue = "162082")] + pub fn set_state_home(&mut self, path: PathBuf) -> &mut Self { + assert!(path.is_absolute(), "state home directory path must be absolute"); + self.state = Some(path); + self + } +} + +impl MediaDirs { + /// Set the path for [Self::desktop]. + /// + /// # Panics + /// + /// Panics if the provided path is not absolute. + #[unstable(feature = "fs_media_dirs", issue = "162083")] + pub fn set_desktop(&mut self, path: PathBuf) -> &mut Self { + assert!(path.is_absolute(), "desktop directory path must be absolute"); + self.desktop = Some(path); + self + } + + /// Set the path for [Self::documents]. + /// + /// # Panics + /// + /// Panics if the provided path is not absolute. + #[unstable(feature = "fs_media_dirs", issue = "162083")] + pub fn set_documents(&mut self, path: PathBuf) -> &mut Self { + assert!(path.is_absolute(), "documents directory path must be absolute"); + self.documents = Some(path); + self + } + + /// Set the path for [Self::downloads]. + /// + /// # Panics + /// + /// Panics if the provided path is not absolute. + #[unstable(feature = "fs_media_dirs", issue = "162083")] + pub fn set_downloads(&mut self, path: PathBuf) -> &mut Self { + assert!(path.is_absolute(), "downloads directory path must be absolute"); + self.downloads = Some(path); + self + } + + /// Set the path for [Self::music]. + /// + /// # Panics + /// + /// Panics if the provided path is not absolute. + #[unstable(feature = "fs_media_dirs", issue = "162083")] + pub fn set_music(&mut self, path: PathBuf) -> &mut Self { + assert!(path.is_absolute(), "music directory path must be absolute"); + self.music = Some(path); + self + } + + /// Set the path for [Self::pictures]. + /// + /// # Panics + /// + /// Panics if the provided path is not absolute. + #[unstable(feature = "fs_media_dirs", issue = "162083")] + pub fn set_pictures(&mut self, path: PathBuf) -> &mut Self { + assert!(path.is_absolute(), "pictures directory path must be absolute"); + self.pictures = Some(path); + self + } + + /// Set the path for [Self::videos]. + /// + /// # Panics + /// + /// Panics if the provided path is not absolute. + #[unstable(feature = "fs_media_dirs", issue = "162083")] + pub fn set_videos(&mut self, path: PathBuf) -> &mut Self { + assert!(path.is_absolute(), "videos directory path must be absolute"); + self.videos = Some(path); + self + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_home_dirs_field_hookup_matches() { + let mut dirs = HomeDirs::empty(); + + assert_eq!(dirs.config_home(), None); + assert_eq!(dirs.data_home(), None); + assert_eq!(dirs.state_home(), None); + assert_eq!(dirs.cache_home(), None); + + dirs.set_config_home("/config".into()); + dirs.set_data_home("/data".into()); + dirs.set_state_home("/state".into()); + dirs.set_cache_home("/cache".into()); + + assert_eq!(dirs.config_home(), Some("/config".as_ref())); + assert_eq!(dirs.data_home(), Some("/data".as_ref())); + assert_eq!(dirs.state_home(), Some("/state".as_ref())); + assert_eq!(dirs.cache_home(), Some("/cache".as_ref())); + } + + #[test] + fn test_media_dirs_field_hookup_matches() { + let mut dirs = MediaDirs::empty(); + + assert_eq!(dirs.desktop(), None); + assert_eq!(dirs.documents(), None); + assert_eq!(dirs.downloads(), None); + assert_eq!(dirs.music(), None); + assert_eq!(dirs.pictures(), None); + assert_eq!(dirs.videos(), None); + + dirs.set_desktop("/desktop".into()); + dirs.set_documents("/documents".into()); + dirs.set_downloads("/downloads".into()); + dirs.set_music("/music".into()); + dirs.set_pictures("/pictures".into()); + dirs.set_videos("/videos".into()); + + assert_eq!(dirs.desktop(), Some("/desktop".as_ref())); + assert_eq!(dirs.documents(), Some("/documents".as_ref())); + assert_eq!(dirs.downloads(), Some("/downloads".as_ref())); + assert_eq!(dirs.music(), Some("/music".as_ref())); + assert_eq!(dirs.pictures(), Some("/pictures".as_ref())); + assert_eq!(dirs.videos(), Some("/videos".as_ref())); + } +} diff --git a/library/std/src/os/darwin/fs.rs b/library/std/src/os/darwin/fs.rs index fc2869d6b13f6..b41391f19dccf 100644 --- a/library/std/src/os/darwin/fs.rs +++ b/library/std/src/os/darwin/fs.rs @@ -7,6 +7,13 @@ use crate::fs::{self, Metadata}; use crate::sys::{AsInner, AsInnerMut, IntoInner}; use crate::time::SystemTime; +mod dirs; + +#[unstable(feature = "fs_home_dirs", issue = "162082")] +pub use dirs::HomeDirsExt; +#[unstable(feature = "fs_media_dirs", issue = "162083")] +pub use dirs::MediaDirsExt; + /// OS-specific extensions to [`fs::Metadata`]. /// /// [`fs::Metadata`]: crate::fs::Metadata diff --git a/library/std/src/os/darwin/fs/dirs.rs b/library/std/src/os/darwin/fs/dirs.rs new file mode 100644 index 0000000000000..6aaa910981b0b --- /dev/null +++ b/library/std/src/os/darwin/fs/dirs.rs @@ -0,0 +1,313 @@ +use crate::env; +use crate::fs::{HomeDirs, MediaDirs}; +use crate::io::{self, ErrorKind, const_error}; +use crate::path::PathBuf; + +/// Darwin-specific extensions to [`fs::HomeDirs`](HomeDirs). +#[unstable(feature = "fs_home_dirs", issue = "162082")] +pub impl(self) trait HomeDirsExt: Sized { + /// Load the standard user directory paths for the current user. + /// + /// On iOS, tvOS, watchOS, visionOS, and sandboxed macOS applications, + /// these directories are within the application's container. Outside + /// the sandbox, these are subdirectories of the `~/Library` directory on + /// macOS. + /// + /// The produced directory paths are not guaranteed to be the canonical + /// paths to the directories; they are allowed to be sandbox-redirected + /// paths as long as the directory is accessible there. + /// + /// The loaded common directories are: + /// + /// | `HomeDirs` | [`NSSearchPathDirectory`] | + /// | ---------- | ----------------------- | + /// | [`cache_home`] | [`NSCachesDirectory`] (`~/Library/Caches`) | + /// | [`config_home`] | [`NSApplicationSupportDirectory`] (`~/Library/Application Support`) | + /// | [`data_home`] | [`NSApplicationSupportDirectory`] (`~/Library/Application Support`) | + /// | [`state_home`] | [`NSApplicationSupportDirectory`] (`~/Library/Application Support`) | + /// + /// Note that the Application Support directory is used for the config, + /// data, and state directories. It is always possible for multiple user + /// directories to be configured to the same path, but this is the common + /// configuration on Apple platforms, making it even more important to not + /// assume files in different user directories cannot alias each other. + /// + /// # Errors + /// + /// Errors if the [user home](env::home_dir) cannot be determined. + // Errors due to the underlying sysdir(3) API should never occur, as + // - the user domain only has one directory for each search path; + // - the user domain always returns subdirectory paths of `~`; and + // - the username and OS defined path segments are always valid UTF-8. + /// + /// # Implementation-specific behavior + /// + /// Uses the `sysdir(3)` API from `libSystem` to discover the standard + /// user directories. + /// + /// This behavior may change in the future. One example change that we + /// explicitly reserve the right to make is to load additional common + /// directories not currently in this list. + /// + /// [`cache_home`]: HomeDirs::cache_home + /// [`config_home`]: HomeDirs::config_home + /// [`data_home`]: HomeDirs::data_home + /// [`state_home`]: HomeDirs::state_home + /// + /// [`NSSearchPathDirectory`]: https://developer.apple.com/documentation/foundation/filemanager/searchpathdirectory?language=objc + /// [`NSCachesDirectory`]: https://developer.apple.com/documentation/foundation/filemanager/searchpathdirectory/cachesdirectory?language=objc + /// [`NSApplicationSupportDirectory`]: https://developer.apple.com/documentation/foundation/filemanager/searchpathdirectory/applicationsupportdirectory?language=objc + #[unstable(feature = "fs_home_dirs", issue = "162082")] + fn sysdir() -> io::Result; +} + +/// Darwin-specific extensions to [`fs::MediaDirs`](MediaDirs). +#[unstable(feature = "fs_media_dirs", issue = "162083")] +pub impl(self) trait MediaDirsExt: Sized { + /// Load the standard user directory paths for the current user. + /// + /// The produced directory paths are not guaranteed to be the canonical + /// paths to the directories; they are allowed to be sandbox-redirected + /// paths as long as the directory is accessible there. + /// + /// The loaded common directories are: + /// + /// | `MediaDirs` | [`NSSearchPathDirectory`] | + /// | ---------- | ----------------------- | + /// | [`desktop`] | [`NSDesktopDirectory`] (`~/Desktop`) | + /// | [`documents`] | [`NSDocumentDirectory`] (`~/Documents`) | + /// | [`downloads`] | [`NSDownloadsDirectory`] (`~/Downloads`) | + /// | [`music`] | [`NSMusicDirectory`] (`~/Music`) | + /// | [`pictures`] | [`NSPicturesDirectory`] (`~/Pictures`) | + /// | [`videos`] | [`NSMoviesDirectory`] (`~/Movies`) | + /// + /// # Errors + /// + /// Errors if the the [user home](env::home_dir) cannot be determined. + // Errors due to the underlying sysdir(3) API should never occur, as + // - the user domain only has one directory for each search path; + // - the user domain always returns subdirectory paths of `~`; + // - the username plus OS defined path segments cannot exceed PATH_MAX; and + // - the username and OS defined path segments are always valid UTF-8. + /// + /// # Implementation-specific behavior + /// + /// Uses the `sysdir(3)` API from `libSystem` to discover the standard + /// user directories. + /// + /// This behavior may change in the future. One example change that we + /// explicitly reserve the right to make is to load additional common + /// directories not currently in this list. + /// + /// [`desktop`]: MediaDirs::desktop + /// [`documents`]: MediaDirs::documents + /// [`downloads`]: MediaDirs::downloads + /// [`music`]: MediaDirs::music + /// [`pictures`]: MediaDirs::pictures + /// [`videos`]: MediaDirs::videos + /// + /// [`NSSearchPathDirectory`]: https://developer.apple.com/documentation/foundation/filemanager/searchpathdirectory?language=objc + /// [`NSDesktopDirectory`]: https://developer.apple.com/documentation/foundation/filemanager/searchpathdirectory/desktopdirectory?language=objc + /// [`NSDocumentDirectory`]: https://developer.apple.com/documentation/foundation/filemanager/searchpathdirectory/documentdirectory?language=objc + /// [`NSDownloadsDirectory`]: https://developer.apple.com/documentation/foundation/filemanager/searchpathdirectory/downloadsdirectory?language=objc + /// [`NSMusicDirectory`]: https://developer.apple.com/documentation/foundation/filemanager/searchpathdirectory/musicdirectory?language=objc + /// [`NSPicturesDirectory`]: https://developer.apple.com/documentation/foundation/filemanager/searchpathdirectory/picturesdirectory?language=objc + /// [`NSMoviesDirectory`]: https://developer.apple.com/documentation/foundation/filemanager/searchpathdirectory/moviesdirectory?language=objc + #[unstable(feature = "fs_media_dirs", issue = "162083")] + fn sysdir() -> io::Result; +} + +fn user_home() -> io::Result { + env::home_dir() + .filter(|p| p.is_absolute()) + .ok_or(const_error!(ErrorKind::InvalidData, "home path not absolute")) +} + +#[unstable(feature = "fs_home_dirs", issue = "162082")] +#[cfg(target_vendor = "apple")] +impl HomeDirsExt for HomeDirs { + fn sysdir() -> io::Result { + use libc::sysdir_search_path_directory_t::*; + + let mut dirs = HomeDirs::empty(); + let home = user_home()?; + + let caches = sys::get_user_dir(&home, SYSDIR_DIRECTORY_CACHES)?; + let application_support = sys::get_user_dir(&home, SYSDIR_DIRECTORY_APPLICATION_SUPPORT)?; + + dirs.cache = caches; + // Apple puts config/data/state all in Application Support + dirs.config = application_support.clone(); + dirs.data = application_support.clone(); + dirs.state = application_support; + + Ok(dirs) + } +} + +#[unstable(feature = "fs_media_dirs", issue = "162083")] +#[cfg(target_vendor = "apple")] +impl MediaDirsExt for MediaDirs { + fn sysdir() -> io::Result { + use libc::sysdir_search_path_directory_t::*; + + let mut dirs = MediaDirs::empty(); + let home = user_home()?; + + let desktop = sys::get_user_dir(&home, SYSDIR_DIRECTORY_DESKTOP)?; + let documents = sys::get_user_dir(&home, SYSDIR_DIRECTORY_DOCUMENT)?; + let downloads = sys::get_user_dir(&home, SYSDIR_DIRECTORY_DOWNLOADS)?; + let movies = sys::get_user_dir(&home, SYSDIR_DIRECTORY_MOVIES)?; + let music = sys::get_user_dir(&home, SYSDIR_DIRECTORY_MUSIC)?; + let pictures = sys::get_user_dir(&home, SYSDIR_DIRECTORY_PICTURES)?; + + dirs.desktop = desktop; + dirs.documents = documents; + dirs.downloads = downloads; + dirs.music = music; + dirs.pictures = pictures; + dirs.videos = movies; + + Ok(dirs) + } +} + +/// Safer wrapper around the sysdir(3) API +#[cfg(target_vendor = "apple")] +mod sys { + use crate::ffi::{CStr, c_char}; + use crate::io::{self, ErrorKind, const_error}; + use crate::path::{Path, PathBuf}; + + /// Get the path for a system directory using `sysdir(3)`. + pub fn get_user_dir( + home: &Path, + kind: libc::sysdir_search_path_directory_t, + ) -> io::Result> { + use libc::sysdir_search_path_domain_mask_t::SYSDIR_DOMAIN_MASK_USER; + + // SAFETY: SYSDIR_DOMAIN_MASK_USER < SYSDIR_DOMAIN_MASK_ALL + let mut iter = unsafe { Iter::new(home, kind, SYSDIR_DOMAIN_MASK_USER) }; + let Some(path) = iter.next() else { + return Ok(None); + }; + let path = path?; + + if iter.next().is_some() { + // more than one path returned (shouldn't happen for SYSDIR_DOMAIN_MASK_USER) + return Err(const_error!( + ErrorKind::InvalidData, + "multiple paths returned for standard user directory", + )); + } + + Ok(Some(path)) + } + + struct Iter<'a> { + home: &'a Path, + state: libc::sysdir_search_path_enumeration_state, + } + + impl Drop for Iter<'_> { + fn drop(&mut self) { + for _ in self {} + } + } + + impl<'a> Iter<'a> { + // SAFETY: `mask` must be <= `SYSDIR_DOMAIN_MASK_ALL` + pub unsafe fn new( + home: &'a Path, + kind: libc::sysdir_search_path_directory_t, + mask: libc::sysdir_search_path_domain_mask_t, + ) -> Self { + // SAFETY: forwarded to the caller + let state = unsafe { libc::sysdir_start_search_path_enumeration(kind, mask) }; + Self { home, state } + } + } + + impl Iterator for Iter<'_> { + type Item = io::Result; + + fn next(&mut self) -> Option> { + let mut buf = [0u8; libc::PATH_MAX as usize]; + if self.state != 0 { + // SAFETY: `self.state` is nonzero and comes from prior sysdir_{start|get_next}_search_path_enumeration call + // SAFETY: sysdir_get_next_search_path_enumeration will write at most `PATH_MAX` bytes to `path` + self.state = unsafe { + libc::sysdir_get_next_search_path_enumeration( + self.state, + buf.as_mut_ptr() as *mut c_char, + ) + }; + } + + if self.state == 0 { + // exhausted + return None; + } + + let Ok(path) = CStr::from_bytes_until_nul(&buf) else { + // should be impossible given home-relative paths, but be defensive + return Some(Err(const_error!( + ErrorKind::InvalidData, + "standard user directory path too long", + ))); + }; + + let Ok(path) = path.to_str() else { + // should be impossible on a working system, but be defensive + return Some(Err(const_error!( + ErrorKind::InvalidData, + "standard user directory path not valid UTF-8", + ))); + }; + + // expand `~` shorthand + Some(match path { + "~" => Ok(self.home.into()), + _ if path.starts_with("~/") => Ok(self.home.join(&path[2..])), + _ if path.starts_with("~") => Err(const_error!( + ErrorKind::InvalidData, + "standard user directory relative to different user", + )), + _ => { + let path = PathBuf::from(path); + if path.is_relative() { + Err(const_error!( + ErrorKind::InvalidData, + "standard user directory path not absolute", + )) + } else { + Ok(path) + } + } + }) + } + } +} + +#[cfg(test)] +#[cfg(target_vendor = "apple")] +mod tests { + use super::*; + + #[test] + fn can_fetch_sysdir_paths() { + let dirs = HomeDirs::sysdir().unwrap(); + assert!(dirs.cache_home().is_some()); + assert!(dirs.config_home().is_some()); + assert!(dirs.data_home().is_some()); + assert!(dirs.state_home().is_some()); + + let dirs = MediaDirs::sysdir().unwrap(); + assert!(dirs.desktop().is_some()); + assert!(dirs.documents().is_some()); + assert!(dirs.downloads().is_some()); + assert!(dirs.music().is_some()); + assert!(dirs.pictures().is_some()); + assert!(dirs.videos().is_some()); + } +} diff --git a/library/std/src/os/unix/fs.rs b/library/std/src/os/unix/fs.rs index b93f94225070a..fcafe1c29797f 100644 --- a/library/std/src/os/unix/fs.rs +++ b/library/std/src/os/unix/fs.rs @@ -22,6 +22,13 @@ use crate::{io, sys}; #[cfg(test)] mod tests; +pub(crate) mod dirs; + +#[unstable(feature = "fs_media_dirs", issue = "162083")] +pub use dirs::MediaDirsExt; +#[unstable(feature = "fs_home_dirs", issue = "162082")] +pub use dirs::{HomeDirsExt, XdgDirs}; + /// Unix-specific extensions to [`fs::File`]. #[stable(feature = "file_offset", since = "1.15.0")] pub trait FileExt { diff --git a/library/std/src/os/unix/fs/dirs.rs b/library/std/src/os/unix/fs/dirs.rs new file mode 100644 index 0000000000000..5d8e13c7a730a --- /dev/null +++ b/library/std/src/os/unix/fs/dirs.rs @@ -0,0 +1,585 @@ +use crate::env::{self, SplitPathsRef}; +use crate::ffi::{OsStr, OsString}; +use crate::fs::{self, HomeDirs, MediaDirs}; +use crate::io::{self, ErrorKind, const_error}; +use crate::path::{Path, PathBuf}; + +#[derive(Debug, Default, Clone)] +#[non_exhaustive] +pub struct ExtraHomeDirs { + runtime: Option, + config_path: Option, + data_path: Option, +} + +#[derive(Debug, Default, Clone)] +#[non_exhaustive] +pub struct ExtraMediaDirs { + templates: Option, +} + +/// XDG-specific extensions to [`fs::HomeDirs`](HomeDirs). +/// +/// The XDG conventions are defined by the Freedesktop.org project in the +/// [XDG Base Directory Specification][xdg-basedir]. These conventions have +/// been largely adopted by Linux distributions. +/// +/// The XDG conventions are written to be usable on any Unix-like filesystem, +/// thus this extension being provided in `os::unix` rather than `os::linux`. +/// However, while some tooling does use XDG conventions on macOS, note that +/// macOS has its own separate conventions for user directories. Consider +/// carefully what conventions your users will expect your application to +/// follow along with any legacy path compatibility you might need to support. +/// +/// [xdg-basedir]: https://specifications.freedesktop.org/basedir/ +#[unstable(feature = "fs_home_dirs", issue = "162082")] +pub impl(self) trait HomeDirsExt: Sized { + /// Load the user directory paths according to the + /// [XDG Base Directory Specification][xdg-basedir]. + /// + /// Each base directory path is set to the value of its corresponding + /// `XDG_*` environment variable (if it is set and non-empty), else to + /// the default value defined by the specification. + /// + /// | Field | Environment Variable | Default Value | + /// | ----- | -------------------- | ------------- | + /// | [`cache_home`] | `XDG_CACHE_HOME` | `$HOME/.cache` | + /// | [`config_home`] | `XDG_CONFIG_HOME` | `$HOME/.config` | + /// | [`data_home`] | `XDG_DATA_HOME` | `$HOME/.local/share` | + /// | [`state_home`] | `XDG_STATE_HOME` | `$HOME/.local/state` | + /// | [`runtime_home`] | `XDG_RUNTIME_DIR` | (see method docs) | + /// | [`config_dirs`] | `XDG_CONFIG_DIRS` | `/etc/xdg` | + /// | [`data_dirs`] | `XDG_DATA_DIRS` | `/usr/local/share/`, `/usr/share/` | + /// + /// Note that `$HOME` here means [`env::home_dir`], which uses + /// `$HOME` if set and non-empty, but falls back to the system password + /// database if it isn't set. + /// + /// All paths are required to be absolute. If a relative path is configured + /// by the environment, it is ignored and the default value is used instead. + /// + /// `config_dirs` and `data_dirs` are a list of delimited paths using the + /// [`env::split_paths`] delimiter. If some but not all paths in the list are + /// relative, those relative paths are ignored and the remaining absolute + /// paths are used. If there are no valid absolute paths, the default value + /// is used instead. + /// + /// # Errors + /// + /// Errors if the user's home directory cannot be determined. + /// + /// [xdg-basedir]: https://specifications.freedesktop.org/basedir/ + /// [`cache_home`]: HomeDirs::cache_home + /// [`config_home`]: HomeDirs::config_home + /// [`data_home`]: HomeDirs::data_home + /// [`state_home`]: HomeDirs::state_home + /// [`runtime_home`]: HomeDirsExt::runtime_home + /// [`config_dirs`]: HomeDirsExt::config_dirs + /// [`data_dirs`]: HomeDirsExt::data_dirs + /// [`env::split_paths`]: crate::env::split_paths + #[unstable(feature = "fs_home_dirs", issue = "162082")] + fn xdg() -> io::Result; + + /// A base directory relative to which user-specific runtime files + /// (such as sockets, named pipes, etc) should be stored. + /// + /// Files in this directory may be subjected to periodic clean-up. + /// Larger files should not be placed here, since it might reside in + /// runtime memory and cannot necessarily be swapped out to disk. + /// + /// This path does not have a default if not set. If it isn't set, + /// applications should fall back to a replacement directory with + /// similar capabilities and print a warning message. + #[unstable(feature = "fs_home_dirs", issue = "162082")] + fn runtime_home(&self) -> Option<&Path>; + + /// A preference-ordered list of base directories to search for config + /// files *in addition to* [`config_home`]. + /// + /// The order of directories denotes their importance; the first directory + /// is the most important. Information defined relative to the more + /// important base directory takes precedent. [`config_home`] is not + /// necessarily present in this list, and is considered more important + /// than any base directory in this list. + /// + /// [`config_home`]: HomeDirs::config_home + #[unstable(feature = "fs_home_dirs", issue = "162082")] + fn config_dirs(&self) -> Option>; + + /// A preference-ordered list of base directories to search for data + /// files *in addition to* [`data_home`]. + /// + /// The order of directories denotes their importance; the first directory + /// is the most important. Information defined relative to the more + /// important base directory takes precedent. [`data_home`] is not + /// necessarily present in this list, and is considered more important + /// than any base directory in this list. + /// + /// [`data_home`]: HomeDirs::data_home + #[unstable(feature = "fs_home_dirs", issue = "162082")] + fn data_dirs(&self) -> Option>; + + /// Set the path for [Self::runtime_home]. + /// + /// # Panics + /// + /// Panics if the provided path is not absolute. + #[unstable(feature = "fs_home_dirs", issue = "162082")] + fn set_runtime_home(&mut self, path: PathBuf) -> &mut Self; + + /// Set the paths for [Self::config_dirs]. + /// + /// Takes one or more paths joined appropriately for the `PATH` environment + /// variable, as by [`env::join_paths`]. + /// + /// # Panics + /// + /// Panics if any of the provided paths are not absolute. + #[unstable(feature = "fs_home_dirs", issue = "162082")] + fn set_config_dirs(&mut self, paths: OsString) -> &mut Self; + + /// Set the paths for [Self::data_dirs]. + /// + /// Takes one or more paths joined appropriately for the `PATH` environment + /// variable, as by [`env::join_paths`]. + /// + /// # Panics + /// + /// Panics if any of the provided paths are not absolute. + #[unstable(feature = "fs_home_dirs", issue = "162082")] + fn set_data_dirs(&mut self, paths: OsString) -> &mut Self; +} + +/// XDG-specific extensions to [`fs::MediaDirs`](MediaDirs). +/// +/// The XDG conventions are defined by the Freedesktop.org project through the +/// [xdg-user-dirs] tool. This configuration is generally present on desktop +/// Linux distributions, although adoption is less widespread than the base +/// directory specification. +/// +/// The XDG conventions are written to be usable on any Unix-like filesystem, +/// thus this extension being provided in `os::unix` rather than `os::linux`. +/// However, while some tooling does use XDG conventions on macOS, note that +/// macOS has its own separate conventions for user directories. Consider +/// carefully what conventions your users will expect your application to +/// follow along with any legacy path compatibility you might need to support. +/// +/// [xdg-user-dirs]: https://www.freedesktop.org/wiki/Software/xdg-user-dirs/ +#[unstable(feature = "fs_media_dirs", issue = "162083")] +pub impl(self) trait MediaDirsExt: Sized { + /// Load the user directory paths according to the [xdg-user-dirs] tool. + /// + /// This directly reads and parses the `$XDG_CONFIG_HOME/user-dirs.dirs` + /// file as defined and maintained by the [xdg-user-dirs] tool. + /// + /// # Errors + /// + /// Errors if the user's home directory cannot be determined or if the + /// `$XDG_CONFIG_HOME/user-dirs.dirs` file cannot be read. + /// + /// # Implementation-specific behavior + /// + /// Only the format maintained by xdg-user-dirs-update is supported. Any + /// configuration that does not match the expected format will result in + /// loading an unspecified path or `None` for that directory. To be more + /// specific: + /// + /// - Any line not in the format of `XDG_{NAME}_DIR={path}` where `{NAME}` + /// is one of `DESKTOP`, `DOWNLOAD`, `TEMPLATES`, `PUBLICSHARE`, + /// `DOCUMENTS`, `MUSIC`, `PICTURES`, or `VIDEOS` is ignored. + /// - `{path}` must be a `"`-quoted shell-escaped path. + /// - `{path}` may only start with `/` or `$HOME/`. A home-relative path + /// is returned relative to [`env::home_dir`]; shell expansion is not + /// performed. + /// - A directory set to just `$HOME` marks it as removed, and results in + /// a `None` value for that path. + /// - If shell expansion syntax other than a leading `$HOME` is present, + /// the produced directory path is unspecified. This is invalid config + /// according to the xdg-user-dirs tooling. + /// + /// This behavior may change in the future. One example change that we + /// explicitly reserve the right to make is to load paths that we currently + /// ignore, such as path formats that are not canonically supported by + /// xdg-user-dirs but which may occur in manually-edited `user-dirs.dirs`. + /// + /// [xdg-user-dirs]: https://www.freedesktop.org/wiki/Software/xdg-user-dirs/ + #[unstable(feature = "fs_media_dirs", issue = "162083")] + fn xdg() -> io::Result; + + /// The OS-privileged user "Templates" directory, often the `Templates` + /// folder in the user's home directory. + /// + /// As a media directory, this should typically be used as a default path + /// for file selection dialogs, not for automatically accessed file paths. + #[unstable(feature = "fs_media_dirs", issue = "162083")] + fn templates(&self) -> Option<&Path>; + + /// Set the paths for [Self::templates]. + /// + /// # Panics + /// + /// Panics if the provided path is not absolute. + #[unstable(feature = "fs_media_dirs", issue = "162083")] + fn set_templates(&mut self, path: PathBuf) -> &mut Self; +} + +#[unstable(feature = "fs_home_dirs", issue = "162082")] +#[derive(Debug)] +pub struct XdgDirs<'a>(SplitPathsRef<'a>); + +#[unstable(feature = "fs_home_dirs", issue = "162082")] +impl HomeDirsExt for HomeDirs { + fn xdg() -> io::Result { + let mut dirs = HomeDirs::empty(); + let user_home = xdg::user_home()?; + + dirs.set_cache_home(xdg::dir_or_else(|| user_home.join(".cache"), "XDG_CACHE_HOME")); + dirs.set_config_home(xdg::dir_or_else(|| user_home.join(".config"), "XDG_CONFIG_HOME")); + dirs.set_data_home(xdg::dir_or_else(|| user_home.join(".local/share"), "XDG_DATA_HOME")); + dirs.set_state_home(xdg::dir_or_else(|| user_home.join(".local/state"), "XDG_STATE_HOME")); + if let Some(runtime) = xdg::dir("XDG_RUNTIME_DIR") { + dirs.set_runtime_home(runtime); + } + + dirs.set_config_dirs(xdg::dirs_or("/etc/xdg", "XDG_CONFIG_DIRS")); + dirs.set_data_dirs(xdg::dirs_or("/usr/local/share/:/usr/share/", "XDG_DATA_DIRS")); + + Ok(dirs) + } + + fn runtime_home(&self) -> Option<&Path> { + self.extra.runtime.as_deref() + } + + fn config_dirs(&self) -> Option> { + self.extra.config_path.as_deref().map(XdgDirs::new) + } + + fn data_dirs(&self) -> Option> { + self.extra.data_path.as_deref().map(XdgDirs::new) + } + + fn set_runtime_home(&mut self, path: PathBuf) -> &mut Self { + assert!(path.is_absolute(), "runtime directory path must be absolute"); + self.extra.runtime = Some(path); + self + } + + fn set_config_dirs(&mut self, paths: OsString) -> &mut Self { + for path in split_paths_ref(&paths) { + assert!(path.is_absolute(), "config directory paths must be absolute"); + } + self.extra.config_path = Some(paths); + self + } + + fn set_data_dirs(&mut self, paths: OsString) -> &mut Self { + for path in split_paths_ref(&paths) { + assert!(path.is_absolute(), "data directory paths must be absolute"); + } + self.extra.data_path = Some(paths); + self + } +} + +#[unstable(feature = "fs_media_dirs", issue = "162083")] +impl MediaDirsExt for MediaDirs { + fn xdg() -> io::Result { + let user_home = xdg::user_home()?; + let config_home = xdg::dir_or_else(|| user_home.join(".config"), "XDG_CONFIG_HOME"); + + let spec = match fs::read(config_home.join("user-dirs.dirs")) { + Ok(spec) => spec, + Err(e) if e.kind() == ErrorKind::NotFound => { + return Err(const_error!( + ErrorKind::NotFound, + "missing `$XDG_CONFIG_HOME/user-dirs.dirs`", + )); + } + Err(e) => return Err(e), + }; + + Ok(xdg::parse_user_dirs(&spec, &user_home)) + } + + fn templates(&self) -> Option<&Path> { + self.extra.templates.as_deref() + } + + fn set_templates(&mut self, path: PathBuf) -> &mut Self { + assert!(path.is_absolute(), "templates directory path must be absolute"); + self.extra.templates = Some(path); + self + } +} + +impl<'a> XdgDirs<'a> { + fn new(paths: &'a OsStr) -> Self { + XdgDirs(split_paths_ref(paths)) + } +} + +#[unstable(feature = "fs_home_dirs", issue = "162082")] +impl<'a> Iterator for XdgDirs<'a> { + type Item = &'a Path; + + fn next(&mut self) -> Option { + self.0.next() + } +} + +fn split_paths_ref<'a>(paths: &'a OsStr) -> SplitPathsRef<'a> { + // returns Some on cfg(unix) + env::split_paths_ref(paths).unwrap() +} + +mod xdg { + use super::{MediaDirs, split_paths_ref}; + use crate::env::{self, join_paths, var_os}; + use crate::ffi::OsString; + use crate::io::{self, ErrorKind, const_error}; + use crate::ops::Deref; + use crate::os::unix::ffi::{OsStrExt, OsStringExt}; + use crate::path::{Path, PathBuf}; + + pub fn user_home() -> io::Result { + env::home_dir() + .filter(|p| p.is_absolute()) + .ok_or(const_error!(ErrorKind::InvalidData, "user home directory path not absolute")) + } + + pub fn dir(env: &str) -> Option { + var_os(env).filter(|s| !s.is_empty()).map(PathBuf::from).filter(|path| path.is_absolute()) + } + + pub fn dir_or_else(fallback: impl FnOnce() -> PathBuf, env: &str) -> PathBuf { + dir(env).unwrap_or_else(fallback) + } + + fn dirs(env: &str) -> Option { + let dirs = var_os(env).filter(|s| !s.is_empty())?; + if split_paths_ref(&dirs).all(|p| p.is_absolute()) { + return Some(dirs); + } + + struct DerefAsRef(pub T); + impl>, U: ?Sized> AsRef for DerefAsRef { + fn as_ref(&self) -> &U { + (*self.0).as_ref() + } + } + + let paths = split_paths_ref(&dirs).filter(|p| p.is_absolute()).map(DerefAsRef); + join_paths(paths).ok().filter(|s| !s.is_empty()) + } + + pub fn dirs_or(fallback: impl Into, env: &str) -> OsString { + dirs(env).unwrap_or_else(|| fallback.into()) + } + + pub fn parse_user_dirs<'a>(spec: &'a [u8], user_home: &Path) -> MediaDirs { + let mut dirs = MediaDirs::empty(); + + for (xdg, path) in + spec.split(|&b| b == b'\n').flat_map(|line| parse_user_dirs_line(line, user_home)) + { + // load the known user directories + match xdg { + b"XDG_DESKTOP_DIR" => dirs.desktop = path, + b"XDG_DOCUMENTS_DIR" => dirs.documents = path, + b"XDG_DOWNLOAD_DIR" => dirs.downloads = path, + b"XDG_MUSIC_DIR" => dirs.music = path, + b"XDG_PICTURES_DIR" => dirs.pictures = path, + b"XDG_VIDEOS_DIR" => dirs.videos = path, + b"XDG_TEMPLATES_DIR" => dirs.extra.templates = path, + b"XDG_PUBLICSHARE_DIR" => { + // we don't expose this directory yet as Windows also has a "public" directory + // and it's not yet clear if we want to expose this at the target-agnostic level + } + _ => { + // ignore unknown variable assignment + } + } + } + + dirs + } + + fn parse_user_dirs_line<'a>( + line: &'a [u8], + user_home: &Path, + ) -> Option<(&'a [u8], Option)> { + // trim whitespace + let trimmed = line.trim_ascii(); + // skip empty lines and comments + if trimmed.is_empty() || trimmed.starts_with(&[b'#']) { + return None; + } + + // only variable assignment lines are allowed; split on `=` + let mut split = trimmed.splitn(2, |&b| b == b'='); + // extract assignment parts; ignore lines not in this format + let var = split.next()?; + let val = split.next()?; + debug_assert_eq!(split.next(), None); + + // the path value is quoted; unquote it + let path = unquote(val, user_home)?; + + let path = Some(path) + // ignore non-absolute paths + .filter(|path| path.is_absolute()) + // setting to the home dir disables the directory configuration + .filter(|path| path != user_home); + + Some((var, path)) + } + + fn unquote(bytes: &[u8], user_home: &Path) -> Option { + let [b'"', rest @ .., b'"'] = bytes else { return None }; + + // setting to the home dir disables the directory configuration; + // if done symbolically, return None here and bypass later work + if matches!(rest, b"$HOME" | b"$HOME/") { + return None; + } + + let mut rest = rest; + let mut s = Vec::with_capacity(rest.len()); + + // expand leading $HOME + if rest.starts_with(b"$HOME/") { + s.extend_from_slice(user_home.as_os_str().as_bytes()); + if !user_home.has_trailing_sep() { + s.push(b'/'); + } + rest = &rest[6..]; + } + + loop { + let i = rest + .iter() + .position(|&b| matches!(b, b'"' | b'\\' | b'$' | b'`')) + .unwrap_or(rest.len()); + s.extend_from_slice(&rest[..i]); + match &rest[i..] { + [] => break, + [b'\\', c @ (b'"' | b'\\' | b'$' | b'`'), tail @ ..] => { + // supported escapes + s.push(*c); + rest = tail; + } + [b'"' | b'\\' | b'$' | b'`', ..] => { + // unsupported shell syntax + return None; + } + _ => { + if cfg!(debug_assertions) { + unreachable!() + } else { + return None; + } + } + } + } + + Some(PathBuf::from(OsString::from_vec(s))) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn can_fetch_xdg_base_dirs() { + let dirs = HomeDirs::xdg().unwrap(); + + assert!(dirs.cache_home().is_some()); + assert!(dirs.config_home().is_some()); + assert!(dirs.data_home().is_some()); + assert!(dirs.state_home().is_some()); + // dirs.runtime() may not exist + assert!(dirs.config_dirs().is_some()); + assert!(dirs.data_dirs().is_some()); + } + + #[test] + fn can_fetch_xdg_media_dirs() { + let dirs = match MediaDirs::xdg() { + Ok(dirs) => dirs, + Err(e) if e.kind() == ErrorKind::NotFound => { + // xdg-user-dirs not initialized on this system, skip the test + return; + } + Err(e) => panic!("failed to fetch xdg user dirs: {e:?}"), + }; + + // even when user-dirs.dirs is present, directories may be unset, + // so we can't assert anything about the dir paths here + let _ = dirs; + } + + #[test] + fn test_well_user_dirs_parsing() { + const TEST_USERDIRS: &'static str = r#" +# This file is written by xdg-user-dirs-update +# If you want to change or add directories, just edit the line you're +# interested in. All local changes will be retained on the next run. +# Format is XDG_xxx_DIR="$HOME/yyy", where yyy is a shell-escaped +# homedir-relative path, or XDG_xxx_DIR="/yyy", where /yyy is an +# absolute path. No other format is supported. +# +XDG_DESKTOP_DIR="$HOME/Desktop" +XDG_DOWNLOAD_DIR="$HOME/Downloads" +XDG_TEMPLATES_DIR="/pub/Templates" +XDG_PUBLICSHARE_DIR="/pub" +XDG_DOCUMENTS_DIR="$HOME/Documents" +XDG_MUSIC_DIR="$HOME/My \"Music\"" +XDG_PICTURES_DIR="$HOME/Pictures" +XDG_VIDEOS_DIR="$HOME" +"#; + + let dirs = xdg::parse_user_dirs(TEST_USERDIRS.as_bytes(), Path::new("/home/ferris")); + + assert_eq!(dirs.desktop(), Some("/home/ferris/Desktop".as_ref())); + assert_eq!(dirs.downloads(), Some("/home/ferris/Downloads".as_ref())); + assert_eq!(dirs.templates(), Some("/pub/Templates".as_ref())); + assert_eq!(dirs.documents(), Some("/home/ferris/Documents".as_ref())); + assert_eq!(dirs.music(), Some("/home/ferris/My \"Music\"".as_ref())); + assert_eq!(dirs.pictures(), Some("/home/ferris/Pictures".as_ref())); + assert_eq!(dirs.videos(), None); + } + + #[test] + fn test_ill_user_dirs_parsing() { + const TEST_USERDIRS: &'static str = r#" +# unquoted +XDG_DESKTOP_DIR=$HOME/Desktop +# mixed quoting +XDG_DOWNLOAD_DIR="$HOME"/"Downloads" +# non-HOME env +XDG_TEMPLATES_DIR="/home/$USER/Templates" +# missing PUBLICSHARE +# command substitution +XDG_DOCUMENTS_DIR="`pwd`/Documents" +# non-symbolic homedir disabled +XDG_MUSIC_DIR="/home/ferris" +# set multiple times +XDG_PICTURES_DIR="$HOME/Pictures1" +XDG_PICTURES_DIR="$HOME/Pictures2" +# relative path +XDG_VIDEOS_DIR="Videos" +"#; + + let dirs = xdg::parse_user_dirs(TEST_USERDIRS.as_bytes(), Path::new("/home/ferris")); + + assert_eq!(dirs.desktop(), None); + assert_eq!(dirs.downloads(), None); + assert_eq!(dirs.templates(), None); + assert_eq!(dirs.documents(), None); + assert_eq!(dirs.music(), None); + assert_eq!(dirs.pictures(), Some("/home/ferris/Pictures2".as_ref())); + assert_eq!(dirs.videos(), None); + } +} diff --git a/library/std/src/os/windows/fs.rs b/library/std/src/os/windows/fs.rs index 21560638c1d0f..f452de6de449f 100644 --- a/library/std/src/os/windows/fs.rs +++ b/library/std/src/os/windows/fs.rs @@ -11,6 +11,13 @@ use crate::sys::{AsInner, AsInnerMut, FromInner, IntoInner}; use crate::time::SystemTime; use crate::{io, sys}; +mod dirs; + +#[unstable(feature = "fs_home_dirs", issue = "162082")] +pub use dirs::HomeDirsExt; +#[unstable(feature = "fs_media_dirs", issue = "162083")] +pub use dirs::MediaDirsExt; + /// Windows-specific extensions to [`fs::File`]. #[stable(feature = "file_offset", since = "1.15.0")] pub trait FileExt { diff --git a/library/std/src/os/windows/fs/dirs.rs b/library/std/src/os/windows/fs/dirs.rs new file mode 100644 index 0000000000000..52710375e6eb2 --- /dev/null +++ b/library/std/src/os/windows/fs/dirs.rs @@ -0,0 +1,309 @@ +use crate::fs::{HomeDirs, MediaDirs}; +use crate::io::{ErrorKind, const_error}; +use crate::path::PathBuf; +use crate::{env, io}; + +/// Windows-specific extensions to [`fs::HomeDirs`](HomeDirs). +#[unstable(feature = "fs_home_dirs", issue = "162082")] +pub impl(self) trait HomeDirsExt: Sized { + /// Load the known user folder paths from environment variables. + /// + /// The loaded known folders are: + /// + /// | `HomeDirs` | Environment Variable | + /// | ---------- | -------------------- | + /// | [`cache_home`] | `%LOCALAPPDATA%` (`%USERPROFILE%\AppData\Local`) | + /// | [`config_home`] | `%APPDATA%` (`%USERPROFILE%\AppData\Roaming`) | + /// | [`data_home`] | `%APPDATA%` (`%USERPROFILE%\AppData\Roaming`) | + /// | [`state_home`] | `%LOCALAPPDATA%` (`%USERPROFILE%\AppData\Local`) | + /// + /// Note that caches/state are both put in `AppData\Local`, and config/data + /// in `AppData\Roaming`. It is always possible for multiple user directories + /// to be configured to the same path, but this is the common configuration + /// on Windows platforms, making it even more important to not assume files + /// in different user directories cannot alias each other. + /// + /// # Errors + /// + /// Errors if `%APPDATA%` or `%LOCALAPPDATA%` are not set to absolute paths. + /// + /// # Implementation-specific behavior + /// + /// Windows keeps these environment variables updated to contain the paths + /// to the configured folder path, but it is possible for the environment + /// variables to not match the underlying system, such as when the user or + /// a program modifies the environment directly, or if the configuration + /// changed after the environment block was copied from the system. + /// + /// Unlike [`known_folders`](Self::known_folders), this does not require + /// `Shell32.dll` and thus does not require the overhead of linking in + /// DLLs that may result in Windows considering the application as a + /// graphical application. + /// + /// This behavior may change in the future. One example change that we + /// explicitly reserve the right to make is to load additional common + /// directories not currently in this list. + /// + /// [`cache_home`]: HomeDirs::cache_home + /// [`config_home`]: HomeDirs::config_home + /// [`data_home`]: HomeDirs::data_home + /// [`state_home`]: HomeDirs::state_home + #[unstable(feature = "fs_home_dirs", issue = "162082")] + fn appdata_env() -> io::Result; + + /// Load the known user folder paths using the [Known Folders] API. + /// + /// The loaded known folders are: + /// + /// | `HomeDirs` | [`KNOWNFOLDERID`] | + /// | ---------- | ----------------- | + /// | [`cache_home`] | [`FOLDERID_LocalAppData`] (`%LOCALAPPDATA%`) | + /// | [`config_home`] | [`FOLDERID_RoamingAppData`] (`%APPDATA%`) | + /// | [`data_home`] | [`FOLDERID_RoamingAppData`] (`%APPDATA%`) | + /// | [`state_home`] | [`FOLDERID_LocalAppData`] (`%LOCALAPPDATA%`) | + /// + /// Note that caches/state are both put in LocalAppData, and config/data + /// in RoamingAppData. It is always possible for multiple user directories + /// to be configured to the same path, but this is the common configuration + /// on Windows platforms, making it even more important to not assume files + /// in different user directories cannot alias each other. + /// + /// # Errors + /// + /// Errors if the underlying system discovery API returns an error. The + /// lack of a configured path is not considered an error and results in a + /// `None` value. + /// + /// # Implementation-specific behavior + /// + /// Calls [`SHGetKnownFolderPath`] for the current user once for each known + /// folder. Does not create the folder if missing. + /// + /// COM should be initialized on the thread that calls this function or + /// else it may return unexpected errors. + /// + /// This behavior may change in the future. One example change that we + /// explicitly reserve the right to make is to load additional common + /// directories not currently in this list. + /// + /// [Known Folders]: https://learn.microsoft.com/en-us/windows/win32/shell/known-folders + /// [`SHGetKnownFolderPath`]: https://learn.microsoft.com/en-us/windows/win32/api/shlobj_core/nf-shlobj_core-shgetknownfolderpath + /// [`KNOWNFOLDERID`]: https://learn.microsoft.com/en-us/windows/win32/shell/knownfolderid + /// [`FOLDERID_LocalAppData`]: https://learn.microsoft.com/en-us/windows/win32/shell/knownfolderid#folderid_localappdata + /// [`FOLDERID_RoamingAppData`]: https://learn.microsoft.com/en-us/windows/win32/shell/knownfolderid#folderid_roamingappdata + /// [`cache_home`]: HomeDirs::cache_home + /// [`config_home`]: HomeDirs::config_home + /// [`data_home`]: HomeDirs::data_home + /// [`state_home`]: HomeDirs::state_home + #[unstable(feature = "fs_home_dirs", issue = "162082")] + fn known_folders() -> io::Result; +} + +/// Windows-specific extensions to [`fs::MediaDirs`](MediaDirs). +#[unstable(feature = "fs_media_dirs", issue = "162083")] +pub impl(self) trait MediaDirsExt: Sized { + /// Load the known user folder paths using the [Known Folders] API. + /// + /// The loaded known folders are: + /// + /// | `MediaDirs` | [`KNOWNFOLDERID`] | + /// | ---------- | ----------------- | + /// | [`desktop`] | [`FOLDERID_Desktop`] (`%USERPROFILE%\Desktop`) | + /// | [`documents`] | [`FOLDERID_Documents`] (`%USERPROFILE%\Documents`) | + /// | [`downloads`] | [`FOLDERID_Downloads`] (`%USERPROFILE%\Downloads`) | + /// | [`music`] | [`FOLDERID_Music`] (`%USERPROFILE%\Music`) | + /// | [`pictures`] | [`FOLDERID_Pictures`] (`%USERPROFILE%\Pictures`) | + /// | [`videos`] | [`FOLDERID_Videos`] (`%USERPROFILE%\Videos`) | + /// + /// # Errors + /// + /// Errors if the underlying system discovery API returns an error. The + /// lack of a configured path is not considered an error and results in a + /// `None` value. + /// + /// # Implementation-specific behavior + /// + /// Calls [`SHGetKnownFolderPath`] for the current user once for each known + /// folder. Does not create the folder if missing. + /// + /// COM should be initialized on the thread that calls this function or + /// else it may return unexpected errors. + /// + /// This behavior may change in the future. One example change that we + /// explicitly reserve the right to make is to load additional common + /// directories not currently in this list. + /// + /// [Known Folders]: https://learn.microsoft.com/en-us/windows/win32/shell/known-folders + /// [`SHGetKnownFolderPath`]: https://learn.microsoft.com/en-us/windows/win32/api/shlobj_core/nf-shlobj_core-shgetknownfolderpath + /// [`KNOWNFOLDERID`]: https://learn.microsoft.com/en-us/windows/win32/shell/knownfolderid + /// [`FOLDERID_Desktop`]: https://learn.microsoft.com/en-us/windows/win32/shell/knownfolderid#folderid_desktop + /// [`FOLDERID_Documents`]: https://learn.microsoft.com/en-us/windows/win32/shell/knownfolderid#folderid_documents + /// [`FOLDERID_Downloads`]: https://learn.microsoft.com/en-us/windows/win32/shell/knownfolderid#folderid_downloads + /// [`FOLDERID_Music`]: https://learn.microsoft.com/en-us/windows/win32/shell/knownfolderid#folderid_music + /// [`FOLDERID_Pictures`]: https://learn.microsoft.com/en-us/windows/win32/shell/knownfolderid#folderid_pictures + /// [`FOLDERID_Videos`]: https://learn.microsoft.com/en-us/windows/win32/shell/knownfolderid#folderid_videos + /// [`desktop`]: MediaDirs::desktop + /// [`documents`]: MediaDirs::documents + /// [`downloads`]: MediaDirs::downloads + /// [`music`]: MediaDirs::music + /// [`pictures`]: MediaDirs::pictures + /// [`videos`]: MediaDirs::videos + #[unstable(feature = "fs_media_dirs", issue = "162083")] + fn known_folders() -> io::Result; +} + +#[cfg(windows)] +#[unstable(feature = "fs_home_dirs", issue = "162082")] +impl HomeDirsExt for HomeDirs { + fn appdata_env() -> io::Result { + let roaming_app_data = env::var_os("APPDATA") + .map(PathBuf::from) + .filter(|p| p.is_absolute()) + .ok_or(const_error!(ErrorKind::InvalidData, "non-absolute %APPDATA%"))?; + let local_app_data = env::var_os("LOCALAPPDATA") + .map(PathBuf::from) + .filter(|p| p.is_absolute()) + .ok_or(const_error!(ErrorKind::InvalidData, "non-absolute %LOCALAPPDATA%"))?; + + // AppData/Local -- system-local, doesn't make sense to sync to another + // AppData/Roaming -- data that makes sense to sync across machines + + let mut dirs = HomeDirs::empty(); + + dirs.cache = Some(local_app_data.clone()); + dirs.config = Some(roaming_app_data.clone()); + dirs.data = Some(roaming_app_data); + dirs.state = Some(local_app_data); + + Ok(dirs) + } + + fn known_folders() -> io::Result { + use crate::sys::c; + + let local_app_data = sys::get_known_folder_path(&c::FOLDERID_LocalAppData)?; + let roaming_app_data = sys::get_known_folder_path(&c::FOLDERID_RoamingAppData)?; + + // AppData/Local -- system-local, doesn't make sense to sync to another + // AppData/Roaming -- data that makes sense to sync across machines + + let mut dirs = HomeDirs::empty(); + + dirs.cache = local_app_data.clone(); + dirs.config = roaming_app_data.clone(); + dirs.data = roaming_app_data; + dirs.state = local_app_data; + + Ok(dirs) + } +} + +#[cfg(windows)] +#[unstable(feature = "fs_media_dirs", issue = "162083")] +impl MediaDirsExt for MediaDirs { + fn known_folders() -> io::Result { + use crate::sys::c; + + let desktop = sys::get_known_folder_path(&c::FOLDERID_Desktop)?; + let documents = sys::get_known_folder_path(&c::FOLDERID_Documents)?; + let downloads = sys::get_known_folder_path(&c::FOLDERID_Downloads)?; + let music = sys::get_known_folder_path(&c::FOLDERID_Music)?; + let pictures = sys::get_known_folder_path(&c::FOLDERID_Pictures)?; + let videos = sys::get_known_folder_path(&c::FOLDERID_Videos)?; + + let mut dirs = MediaDirs::empty(); + + dirs.desktop = desktop; + dirs.documents = documents; + dirs.downloads = downloads; + dirs.music = music; + dirs.pictures = pictures; + dirs.videos = videos; + + Ok(dirs) + } +} + +#[cfg(windows)] +mod sys { + use crate::io::{self, ErrorKind, const_error}; + use crate::path::PathBuf; + use crate::sys::{c, os2path}; + use crate::{ptr, slice}; + + /// Retrieve a known folder path from the Windows API. + pub fn get_known_folder_path(id: &c::GUID) -> io::Result> { + // Get the known folder path. hToken = NULL requests the current user + // scope, and we set KF_FLAG_DONT_VERIFY because it's a bit faster and + // we don't guarantee that the directories at the paths exist. + let mut pszPath = ptr::null_mut(); + // SAFETY: rfid/ppszPath are valid pointers, flags are appropriate, and + // a NULL hToken is supported by SHGetKnownFolderPath. + let hr = unsafe { + c::SHGetKnownFolderPath( + /* rfid */ id, + /* dwFlags */ c::KF_FLAG_DONT_VERIFY as _, + /* hToken */ ptr::null_mut(), + /* ppszPath */ &mut pszPath, + ) + }; + + let result = match hr { + c::S_OK => { + // SAFETY: pszPath was populated by a successful call to SHGetKnownFolderPath + // and is valid up to and including its nul terminator + let len = unsafe { c::lstrlenW(pszPath) }; + // SAFETY: *pszPath is valid up to and including its nul terminator + Ok(Some(os2path(unsafe { slice::from_raw_parts(pszPath, len as usize) }))) + } + c::E_FAIL => { + // This known folder id exists but does not have a path + if cfg!(debug_assertions) { + unreachable!("should not call get_known_folder_path on a virtual folder"); + } else { + Err(const_error!( + ErrorKind::InvalidInput, + "virtual known folders do not have paths" + )) + } + } + c::E_INVALIDARG => { + // This known folder id is not present on the system + Ok(None) + } + _ => { + // Miscellaneous error + Err(io::Error::from_raw_os_error(hr)) + } + }; + + // SAFETY: The caller is responsible for freeing the path returned by + // SHGetKnownFolderPath by calling CoTaskMemFree, whether it + // succeeds or not. + unsafe { c::CoTaskMemFree(pszPath.cast()) }; + + result + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn can_fetch_known_folder_paths() { + let dirs = HomeDirs::known_folders().unwrap(); + assert!(dirs.cache_home().is_some()); + assert!(dirs.config_home().is_some()); + assert!(dirs.data_home().is_some()); + assert!(dirs.state_home().is_some()); + + let dirs = MediaDirs::known_folders().unwrap(); + assert!(dirs.desktop().is_some()); + assert!(dirs.documents().is_some()); + assert!(dirs.downloads().is_some()); + assert!(dirs.music().is_some()); + assert!(dirs.pictures().is_some()); + assert!(dirs.videos().is_some()); + } +} diff --git a/library/std/src/sys/fs/common.rs b/library/std/src/sys/fs/common.rs index 17b98a4506544..6f16946b79914 100644 --- a/library/std/src/sys/fs/common.rs +++ b/library/std/src/sys/fs/common.rs @@ -111,3 +111,11 @@ impl fmt::Debug for Dir { f.debug_struct("Dir").field("path", &self.path).finish() } } + +#[derive(Debug, Clone, Default)] +#[non_exhaustive] +pub struct ExtraHomeDirs {} + +#[derive(Debug, Clone, Default)] +#[non_exhaustive] +pub struct ExtraMediaDirs {} diff --git a/library/std/src/sys/fs/hermit.rs b/library/std/src/sys/fs/hermit.rs index 33feee4fc1c97..89f6989b9adbe 100644 --- a/library/std/src/sys/fs/hermit.rs +++ b/library/std/src/sys/fs/hermit.rs @@ -11,7 +11,7 @@ use crate::os::hermit::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, Raw use crate::path::{Path, PathBuf}; use crate::sync::Arc; use crate::sys::fd::FileDesc; -pub use crate::sys::fs::common::{Dir, copy, exists}; +pub use crate::sys::fs::common::{Dir, ExtraHomeDirs, ExtraMediaDirs, copy, exists}; use crate::sys::helpers::run_path_with_cstr; use crate::sys::io::DEFAULT_BUF_SIZE; use crate::sys::time::SystemTime; diff --git a/library/std/src/sys/fs/mod.rs b/library/std/src/sys/fs/mod.rs index b2666eb2a3da9..93b05b9859d28 100644 --- a/library/std/src/sys/fs/mod.rs +++ b/library/std/src/sys/fs/mod.rs @@ -47,6 +47,9 @@ cfg_select! { mod vexos; use vexos as imp; } + target_vendor = "apple" => { + mod darwin; + } _ => { mod unsupported; use unsupported as imp; @@ -61,8 +64,8 @@ pub fn with_native_path(path: &Path, f: &dyn Fn(&Path) -> io::Result) -> i } pub use imp::{ - Dir, DirBuilder, DirEntry, File, FileAttr, FilePermissions, FileTimes, FileType, OpenOptions, - ReadDir, + Dir, DirBuilder, DirEntry, ExtraHomeDirs, ExtraMediaDirs, File, FileAttr, FilePermissions, + FileTimes, FileType, OpenOptions, ReadDir, }; pub fn read_dir(path: &Path) -> io::Result { diff --git a/library/std/src/sys/fs/motor.rs b/library/std/src/sys/fs/motor.rs index 938d1537790a1..91cd90a6b0cb3 100644 --- a/library/std/src/sys/fs/motor.rs +++ b/library/std/src/sys/fs/motor.rs @@ -4,7 +4,7 @@ use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, SeekFrom}; use crate::os::fd::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd}; use crate::path::{Path, PathBuf}; use crate::sys::fd::FileDesc; -pub use crate::sys::fs::common::{Dir, exists}; +pub use crate::sys::fs::common::{Dir, ExtraHomeDirs, ExtraMediaDirs, exists}; use crate::sys::time::SystemTime; use crate::sys::{ AsInner, AsInnerMut, FromInner, IntoInner, io_slices, io_slices_mut, map_motor_error, diff --git a/library/std/src/sys/fs/solid.rs b/library/std/src/sys/fs/solid.rs index bd963b1d2f038..dfdcacdcdf277 100644 --- a/library/std/src/sys/fs/solid.rs +++ b/library/std/src/sys/fs/solid.rs @@ -9,7 +9,7 @@ use crate::os::raw::{c_int, c_short}; use crate::os::solid::ffi::OsStrExt; use crate::path::{Path, PathBuf}; use crate::sync::Arc; -pub use crate::sys::fs::common::{Dir, exists}; +pub use crate::sys::fs::common::{Dir, ExtraHomeDirs, ExtraMediaDirs, exists}; use crate::sys::helpers::ignore_notfound; use crate::sys::pal::{abi, error}; use crate::sys::time::SystemTime; diff --git a/library/std/src/sys/fs/uefi.rs b/library/std/src/sys/fs/uefi.rs index 08473e245cc8e..5d0c219586e0e 100644 --- a/library/std/src/sys/fs/uefi.rs +++ b/library/std/src/sys/fs/uefi.rs @@ -6,7 +6,7 @@ use crate::fs::TryLockError; use crate::hash::Hash; use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, SeekFrom}; use crate::path::{Path, PathBuf}; -pub use crate::sys::fs::common::{Dir, copy, remove_dir_all}; +pub use crate::sys::fs::common::{Dir, ExtraHomeDirs, ExtraMediaDirs, copy, remove_dir_all}; use crate::sys::pal::{helpers, unsupported}; use crate::sys::time::SystemTime; diff --git a/library/std/src/sys/fs/unix.rs b/library/std/src/sys/fs/unix.rs index 613397e6903c1..691c491c6f7e5 100644 --- a/library/std/src/sys/fs/unix.rs +++ b/library/std/src/sys/fs/unix.rs @@ -50,6 +50,8 @@ use crate::fmt::{self, Write as _}; use crate::fs::TryLockError; use crate::io::{self, BorrowedCursor, Error, IoSlice, IoSliceMut, SeekFrom}; use crate::os::fd::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd}; +#[cfg(not(target_os = "wasi"))] +pub use crate::os::unix::fs::dirs::{ExtraHomeDirs, ExtraMediaDirs}; #[cfg(target_family = "unix")] use crate::os::unix::prelude::*; #[cfg(target_os = "wasi")] @@ -58,6 +60,8 @@ use crate::path::{Path, PathBuf}; use crate::sync::Arc; use crate::sys::fd::FileDesc; pub use crate::sys::fs::common::exists; +#[cfg(target_os = "wasi")] +pub use crate::sys::fs::common::{ExtraHomeDirs, ExtraMediaDirs}; use crate::sys::helpers::run_path_with_cstr; use crate::sys::time::SystemTime; #[cfg(all(target_os = "linux", target_env = "gnu"))] diff --git a/library/std/src/sys/fs/unsupported.rs b/library/std/src/sys/fs/unsupported.rs index 512af27401dfe..26677c7c7410a 100644 --- a/library/std/src/sys/fs/unsupported.rs +++ b/library/std/src/sys/fs/unsupported.rs @@ -4,7 +4,7 @@ use crate::fs::TryLockError; use crate::hash::{Hash, Hasher}; use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, SeekFrom}; use crate::path::{Path, PathBuf}; -pub use crate::sys::fs::common::Dir; +pub use crate::sys::fs::common::{Dir, ExtraHomeDirs, ExtraMediaDirs}; use crate::sys::time::SystemTime; use crate::sys::unsupported; diff --git a/library/std/src/sys/fs/vexos.rs b/library/std/src/sys/fs/vexos.rs index 1357787c44c77..5043a4daa83b4 100644 --- a/library/std/src/sys/fs/vexos.rs +++ b/library/std/src/sys/fs/vexos.rs @@ -16,6 +16,8 @@ pub use unsupported_fs::{ symlink, unlink, }; +pub use crate::sys::fs::common::{ExtraHomeDirs, ExtraMediaDirs}; + /// VEXos file descriptor. /// /// This stores an opaque pointer to a [FatFs file object structure] managed by VEXos diff --git a/library/std/src/sys/fs/windows.rs b/library/std/src/sys/fs/windows.rs index b703ad9bd0ae8..0db5a8b9f9bfb 100644 --- a/library/std/src/sys/fs/windows.rs +++ b/library/std/src/sys/fs/windows.rs @@ -11,6 +11,7 @@ use crate::os::windows::io::{AsHandle, BorrowedHandle}; use crate::os::windows::prelude::*; use crate::path::{Path, PathBuf}; use crate::sync::Arc; +pub use crate::sys::fs::common::{ExtraHomeDirs, ExtraMediaDirs}; use crate::sys::handle::Handle; use crate::sys::pal::api::{self, WinError, set_file_information_by_handle}; use crate::sys::pal::{IoResult, fill_utf16_buf, to_u16s, truncate_utf16_at_nul}; diff --git a/library/std/src/sys/pal/windows/c.rs b/library/std/src/sys/pal/windows/c.rs index 432cd0425ac11..25031968a7e02 100644 --- a/library/std/src/sys/pal/windows/c.rs +++ b/library/std/src/sys/pal/windows/c.rs @@ -154,6 +154,9 @@ pub const MB_ERR_INVALID_CHARS: u32 = cu(windows_sys::MB_ERR_INVALID_CHARS); pub const CREATE_WAITABLE_TIMER_HIGH_RESOLUTION: u32 = cu(windows_sys::CREATE_WAITABLE_TIMER_HIGH_RESOLUTION); +// dwFlags --LoadLibraryExA +pub const LOAD_LIBRARY_SEARCH_SYSTEM32: u32 = cu(windows_sys::LOAD_LIBRARY_SEARCH_SYSTEM32); + // dwCreationDisposition -- CreateFile pub const CREATE_NEW: u32 = cu(windows_sys::CREATE_NEW); pub const OPEN_ALWAYS: u32 = cu(windows_sys::OPEN_ALWAYS); @@ -197,6 +200,16 @@ pub const WAIT_OBJECT_0: u32 = windows_sys::WAIT_OBJECT_0.cast_unsigned(); // LPPROGRESS_ROUTINE return value (DWORD) pub const PROGRESS_CONTINUE: u32 = cu(windows_sys::PROGRESS_CONTINUE); +// KNOWNFOLDERIDs (GUID) +pub const FOLDERID_Desktop: GUID = GUID::from_u128(0xb4bfcc3a_db2c_424c_b029_7fe99a87c641); +pub const FOLDERID_Documents: GUID = GUID::from_u128(0xfdd39ad0_238f_46af_adb4_6c85480369c7); +pub const FOLDERID_Downloads: GUID = GUID::from_u128(0x374de290_123f_4565_9164_39c4925e467b); +pub const FOLDERID_LocalAppData: GUID = GUID::from_u128(0xf1b32785_6fba_4fcf_9d55_7b8e7f157091); +pub const FOLDERID_Music: GUID = GUID::from_u128(0x4bd8d571_6d19_48d3_be97_422220080e43); +pub const FOLDERID_Pictures: GUID = GUID::from_u128(0x33e28130_4e1e_4676_835a_98395c3bc3bb); +pub const FOLDERID_RoamingAppData: GUID = GUID::from_u128(0x3eb685db_65f9_4cf6_a03a_e3ef65729f3d); +pub const FOLDERID_Videos: GUID = GUID::from_u128(0x18989b1d_99b5_455b_841c_ab7c74e4ddfc); + // Errors pub const ERROR_SUCCESS: u32 = cu(windows_sys::ERROR_SUCCESS); pub const ERROR_ACCESS_DENIED: u32 = cu(windows_sys::ERROR_ACCESS_DENIED); @@ -280,6 +293,17 @@ pub fn nt_success(status: NTSTATUS) -> bool { status >= 0 } +impl GUID { + pub const fn from_u128(uuid: u128) -> Self { + Self { + data1: (uuid >> 96) as u32, + data2: (uuid >> 80 & 0xffff) as u16, + data3: (uuid >> 64 & 0xffff) as u16, + data4: (uuid as u64).to_be_bytes(), + } + } +} + impl OBJECT_ATTRIBUTES { pub fn with_length() -> Self { Self { @@ -459,3 +483,24 @@ cfg_select! { unsafe extern "C" { pub fn atexit(cb: unsafe extern "C" fn()) -> c_int; } + +// Functions in DLLs that we want to lazily load +compat_fn_with_fallback! { + // Avoid eagerly linking shell32.dll, which marks the loading application as graphical. + #[lazy] + pub static SHELL32: &CStr = c"shell32"; + + pub fn SHGetKnownFolderPath(rfid : *const KNOWNFOLDERID, dwflags : u32, htoken : HANDLE, ppszpath : *mut PWSTR) -> HRESULT { + unsafe { SetLastError(ERROR_CALL_NOT_IMPLEMENTED as u32); E_NOTIMPL } + } +} + +compat_fn_with_fallback! { + // Only used with SHGetKnownFolderPath, so avoid eager load overhead. + #[lazy] + pub static OLE32: &CStr = c"ole32"; + + pub fn CoTaskMemFree(pv : *mut core::ffi::c_void) -> () { + // without OLE32 there's no COM alloc, so no-op COM free is fine + } +} diff --git a/library/std/src/sys/pal/windows/c/bindings.txt b/library/std/src/sys/pal/windows/c/bindings.txt index ebe4b8e128352..70af36f27907e 100644 --- a/library/std/src/sys/pal/windows/c/bindings.txt +++ b/library/std/src/sys/pal/windows/c/bindings.txt @@ -252,6 +252,8 @@ DNS_ERROR_ZONESCOPE_IS_REFERENCED DUPLICATE_CLOSE_SOURCE DUPLICATE_SAME_ACCESS DuplicateHandle +E_FAIL +E_INVALIDARG E_NOTIMPL ENABLE_AUTO_POSITION ENABLE_ECHO_INPUT @@ -2260,8 +2262,12 @@ IPV6_MREQ IPV6_MULTICAST_LOOP IPV6_V6ONLY IsThreadAFiber +KF_FLAG_DONT_VERIFY +KNOWNFOLDERID LINGER listen +LOAD_LIBRARY_SEARCH_SYSTEM32 +LoadLibraryExA LocalFree LOCKFILE_EXCLUSIVE_LOCK LOCKFILE_FAIL_IMMEDIATELY @@ -2350,6 +2356,7 @@ ReleaseSRWLockShared RemoveDirectoryW RtlGenRandom RtlNtStatusToDosError +S_OK SD_BOTH SD_RECEIVE SD_SEND diff --git a/library/std/src/sys/pal/windows/c/windows_sys.rs b/library/std/src/sys/pal/windows/c/windows_sys.rs index 34c4407296dcf..0c865494a6e7c 100644 --- a/library/std/src/sys/pal/windows/c/windows_sys.rs +++ b/library/std/src/sys/pal/windows/c/windows_sys.rs @@ -71,6 +71,7 @@ windows_link::link!("kernel32.dll" "system" fn InitOnceBeginInitialize(lpinitonc windows_link::link!("kernel32.dll" "system" fn InitOnceComplete(lpinitonce : LPINIT_ONCE, dwflags : u32, lpcontext : *const core::ffi::c_void) -> BOOL); windows_link::link!("kernel32.dll" "system" fn InitializeProcThreadAttributeList(lpattributelist : *mut _PROC_THREAD_ATTRIBUTE_LIST, dwattributecount : u32, dwflags : u32, lpsize : *mut usize) -> BOOL); windows_link::link!("kernel32.dll" "system" fn IsThreadAFiber() -> BOOL); +windows_link::link!("kernel32.dll" "system" fn LoadLibraryExA(lplibfilename : PCSTR, hfile : HANDLE, dwflags : u32) -> HMODULE); windows_link::link!("kernel32.dll" "system" fn LocalFree(hmem : HLOCAL) -> HLOCAL); windows_link::link!("kernel32.dll" "system" fn LockFileEx(hfile : HANDLE, dwflags : u32, dwreserved : u32, nnumberofbytestolocklow : u32, nnumberofbytestolockhigh : u32, lpoverlapped : *mut OVERLAPPED) -> BOOL); windows_link::link!("kernel32.dll" "system" fn MoveFileExW(lpexistingfilename : PCWSTR, lpnewfilename : PCWSTR, dwflags : u32) -> BOOL); @@ -2433,6 +2434,8 @@ impl Default for EXCEPTION_RECORD { } pub const EXCEPTION_STACK_OVERFLOW: i32 = -1073741571; pub const EXTENDED_STARTUPINFO_PRESENT: i32 = 524288; +pub const E_FAIL: HRESULT = 0x80004005_u32 as _; +pub const E_INVALIDARG: HRESULT = 0x80070057_u32 as _; pub const E_NOTIMPL: HRESULT = 0x80004001_u32 as _; pub const ExceptionCollidedUnwind: EXCEPTION_DISPOSITION = 3; pub const ExceptionContinueExecution: EXCEPTION_DISPOSITION = 0; @@ -2925,7 +2928,11 @@ impl Default for IP_MREQ { pub const IP_MULTICAST_LOOP: i32 = 11; pub const IP_MULTICAST_TTL: i32 = 10; pub const IP_TTL: i32 = 4; +pub const KF_FLAG_DONT_VERIFY: KNOWN_FOLDER_FLAG = 16384; +pub type KNOWNFOLDERID = GUID; +pub type KNOWN_FOLDER_FLAG = u32; pub type LINGER = linger; +pub const LOAD_LIBRARY_SEARCH_SYSTEM32: i32 = 2048; pub const LOCKFILE_EXCLUSIVE_LOCK: i32 = 2; pub const LOCKFILE_FAIL_IMMEDIATELY: i32 = 1; pub type LPBYTE = *mut u8; @@ -3295,6 +3302,7 @@ pub struct SYSTEM_INFO_0_0 { pub wProcessorArchitecture: u16, pub wReserved: u16, } +pub const S_OK: HRESULT = 0x0_u32 as _; pub const TCP_NODELAY: i32 = 1; pub const TIMER_ALL_ACCESS: i32 = 2031619; pub const TIMER_MODIFY_STATE: i32 = 2; diff --git a/library/std/src/sys/pal/windows/compat.rs b/library/std/src/sys/pal/windows/compat.rs index c465ceb2301ce..7e9f5bd108729 100644 --- a/library/std/src/sys/pal/windows/compat.rs +++ b/library/std/src/sys/pal/windows/compat.rs @@ -20,7 +20,8 @@ //! importing the same function unnecessarily. use crate::ffi::{CStr, c_void}; -use crate::ptr::NonNull; +use crate::ptr::{self, NonNull}; +use crate::sync::LazyLock; use crate::sys::c; // This uses a static initializer to preload some imported functions. @@ -118,6 +119,23 @@ impl Module { } } + /// Try to load a module handle. + /// + /// # SAFETY + /// + /// This should only be use for modules that exist for the lifetime of std. + pub unsafe fn load(name: &CStr) -> Option { + // SAFETY: A CStr is always null terminated. + unsafe { + let module = c::LoadLibraryExA( + /* lpLibFileName */ name.as_ptr().cast::(), + /* hReserved */ ptr::null_mut(), + /* dwFlags */ c::LOAD_LIBRARY_SEARCH_SYSTEM32, + ); + NonNull::new(module).map(Self) + } + } + // Try to get the address of a function. pub fn proc_address(self, name: &CStr) -> Option> { unsafe { @@ -131,6 +149,33 @@ impl Module { } } +unsafe impl Send for Module {} +unsafe impl Sync for Module {} + +type LazyLoadModule = impl FnOnce() -> Option + Send + Sync + 'static; + +#[define_opaque(LazyLoadModule)] +const fn lazy_load_module(name: &'static CStr) -> LazyLoadModule { + move || unsafe { Module::load(name) } +} + +/// Represents a loaded module. +/// +/// Note that the modules std depends on must not be unloaded. +/// Therefore a `Module` is always valid for the lifetime of std. +pub(in crate::sys) struct LazyModule(LazyLock, LazyLoadModule>); +impl LazyModule { + /// Create a lazy-loaded handle to a module. + pub const unsafe fn new(name: &'static CStr) -> Self { + Self(LazyLock::new(lazy_load_module(name))) + } + + /// Get the module handle, loading it if necessary. + pub fn load(&self) -> Option { + *LazyLock::force(&self.0) + } +} + /// Load a function or use a fallback implementation if that fails. macro_rules! compat_fn_with_fallback { (pub static $module:ident: &CStr = $name:expr; $( @@ -193,6 +238,67 @@ macro_rules! compat_fn_with_fallback { #[allow(unused)] $(#[$meta])* $vis use $symbol::call as $symbol; + )*); + (#[lazy] pub static $module:ident: &CStr = $name:expr; $( + $(#[$meta:meta])* + $vis:vis fn $symbol:ident($($argname:ident: $argtype:ty),*) -> $rettype:ty $fallback_body:block + )*) => ( + pub static $module: &CStr = $name; + $( + $(#[$meta])* + pub mod $symbol { + #[allow(unused_imports)] + use super::*; + use crate::mem; + use crate::ffi::CStr; + use crate::sync::atomic::{Atomic, AtomicPtr, Ordering}; + use crate::sys::compat::{LazyModule, Module}; + + type F = unsafe extern "system" fn($($argtype),*) -> $rettype; + + /// `PTR` contains a function pointer to one of three functions. + /// It starts with the `load` function. + /// When that is called it attempts to load the requested symbol. + /// If it succeeds, `PTR` is set to the address of that symbol. + /// If it fails, then `PTR` is set to `fallback`. + static PTR: Atomic<*mut c_void> = AtomicPtr::new(load as unsafe extern "system" fn($($argname: $argtype),*) -> $rettype as *mut _); + + unsafe extern "system" fn load($($argname: $argtype),*) -> $rettype { + static MODULE: LazyModule = unsafe { LazyModule::new($module) }; + unsafe { + let func = load_from_module(MODULE.load()); + func($($argname),*) + } + } + + fn load_from_module(module: Option) -> F { + unsafe { + static SYMBOL_NAME: &CStr = ansi_str!(sym $symbol); + if let Some(f) = module.and_then(|m| m.proc_address(SYMBOL_NAME)) { + PTR.store(f.as_ptr(), Ordering::Relaxed); + mem::transmute(f) + } else { + PTR.store(fallback as unsafe extern "system" fn($($argname: $argtype),*) -> $rettype as *mut _, Ordering::Relaxed); + fallback + } + } + } + + #[allow(unused_variables)] + unsafe extern "system" fn fallback($($argname: $argtype),*) -> $rettype { + $fallback_body + } + + #[inline(always)] + pub unsafe fn call($($argname: $argtype),*) -> $rettype { + unsafe { + let func: F = mem::transmute(PTR.load(Ordering::Relaxed)); + func($($argname),*) + } + } + } + $(#[$meta])* + $vis use $symbol::call as $symbol; )*) } diff --git a/library/std/src/sys/paths/mod.rs b/library/std/src/sys/paths/mod.rs index 69c814ef06c06..865d06ae8644e 100644 --- a/library/std/src/sys/paths/mod.rs +++ b/library/std/src/sys/paths/mod.rs @@ -6,7 +6,8 @@ cfg_select! { mod imp { pub use super::hermit::{getcwd, temp_dir}; pub use super::unsupported::{ - JoinPathsError, SplitPaths, chdir, current_exe, home_dir, join_paths, split_paths, + JoinPathsError, SplitPaths, SplitPathsRef, chdir, current_exe, home_dir, + join_paths, split_paths, split_paths_ref, }; } } @@ -16,8 +17,8 @@ cfg_select! { mod unsupported; mod imp { pub use super::motor::{ - JoinPathsError, SplitPaths, chdir, current_exe, getcwd, home_dir, join_paths, - split_paths, temp_dir, + JoinPathsError, SplitPaths, SplitPathsRef, chdir, current_exe, getcwd, home_dir, + join_paths, split_paths, split_paths_ref, temp_dir, }; } } @@ -28,8 +29,8 @@ cfg_select! { mod imp { pub use super::sgx::chdir; pub use super::unsupported::{ - JoinPathsError, SplitPaths, current_exe, getcwd, home_dir, join_paths, split_paths, - temp_dir, + JoinPathsError, SplitPaths, SplitPathsRef, current_exe, getcwd, home_dir, + join_paths, split_paths, split_paths_ref, temp_dir, }; } } @@ -47,7 +48,8 @@ cfg_select! { mod unsupported; mod imp { pub use super::unsupported::{ - JoinPathsError, SplitPaths, current_exe, home_dir, join_paths, split_paths, + JoinPathsError, SplitPaths, SplitPathsRef, current_exe, home_dir, join_paths, + split_paths, split_paths_ref, }; pub use super::wasi::{chdir, getcwd, temp_dir}; } @@ -63,6 +65,6 @@ cfg_select! { } pub use imp::{ - JoinPathsError, SplitPaths, chdir, current_exe, getcwd, home_dir, join_paths, split_paths, - temp_dir, + JoinPathsError, SplitPaths, SplitPathsRef, chdir, current_exe, getcwd, home_dir, join_paths, + split_paths, split_paths_ref, temp_dir, }; diff --git a/library/std/src/sys/paths/uefi.rs b/library/std/src/sys/paths/uefi.rs index 7fddcfdff7724..d9dcd45267935 100644 --- a/library/std/src/sys/paths/uefi.rs +++ b/library/std/src/sys/paths/uefi.rs @@ -1,6 +1,7 @@ use r_efi::efi::protocols::{device_path, loaded_image_device_path}; use crate::ffi::{OsStr, OsString}; +use crate::marker::PhantomData; use crate::os::uefi::ffi::{OsStrExt, OsStringExt}; use crate::path::{self, PathBuf}; use crate::sys::pal::{helpers, unsupported_err}; @@ -71,6 +72,19 @@ impl<'a> Iterator for SplitPaths<'a> { } } +pub struct SplitPathsRef<'a>(!, PhantomData<&'a ()>); + +pub fn split_paths_ref(_unparsed: &OsStr) -> Option> { + None +} + +impl<'a> Iterator for SplitPathsRef<'a> { + type Item = &'a path::Path; + fn next(&mut self) -> Option<&'a path::Path> { + self.0 + } +} + #[derive(Debug)] pub struct JoinPathsError; diff --git a/library/std/src/sys/paths/unix.rs b/library/std/src/sys/paths/unix.rs index e023451b13c8c..cee799c36c79b 100644 --- a/library/std/src/sys/paths/unix.rs +++ b/library/std/src/sys/paths/unix.rs @@ -6,7 +6,7 @@ use libc::{c_char, c_int, c_void}; use crate::ffi::{CStr, OsStr, OsString}; use crate::os::unix::prelude::*; -use crate::path::{self, PathBuf}; +use crate::path::{self, Path, PathBuf}; use crate::sys::helpers::run_path_with_cstr; use crate::sys::pal::cvt; use crate::{fmt, io, iter, mem, ptr, slice, str}; @@ -76,6 +76,28 @@ pub fn split_paths(unparsed: &OsStr) -> SplitPaths<'_> { unparsed.as_bytes().split(is_separator).map(into_pathbuf) } +pub type SplitPathsRef<'a> = iter::Map< + slice::Split<'a, u8, impl FnMut(&u8) -> bool + 'static>, + impl FnMut(&[u8]) -> &'_ Path + 'static, +>; + +pub fn split_paths_ref(unparsed: &OsStr) -> Option> { + #[define_opaque(SplitPathsRef)] + fn split_paths_ref(unparsed: &OsStr) -> SplitPathsRef<'_> { + fn is_separator(&b: &u8) -> bool { + b == PATH_SEPARATOR + } + + fn into_path(part: &[u8]) -> &Path { + Path::new(OsStr::from_bytes(part)) + } + + unparsed.as_bytes().split(is_separator).map(into_path) + } + + Some(split_paths_ref(unparsed)) +} + #[derive(Debug)] pub struct JoinPathsError; diff --git a/library/std/src/sys/paths/unsupported.rs b/library/std/src/sys/paths/unsupported.rs index 024830a254fd7..204d6b0897e20 100644 --- a/library/std/src/sys/paths/unsupported.rs +++ b/library/std/src/sys/paths/unsupported.rs @@ -1,6 +1,6 @@ use crate::ffi::{OsStr, OsString}; use crate::marker::PhantomData; -use crate::path::{self, PathBuf}; +use crate::path::{self, Path, PathBuf}; use crate::sys::pal::unsupported; use crate::{fmt, io}; @@ -13,11 +13,16 @@ pub fn chdir(_: &path::Path) -> io::Result<()> { } pub struct SplitPaths<'a>(!, PhantomData<&'a ()>); +pub struct SplitPathsRef<'a>(!, PhantomData<&'a ()>); pub fn split_paths(_unparsed: &OsStr) -> SplitPaths<'_> { panic!("unsupported") } +pub fn split_paths_ref(_unparsed: &OsStr) -> Option> { + None +} + impl<'a> Iterator for SplitPaths<'a> { type Item = PathBuf; fn next(&mut self) -> Option { @@ -25,6 +30,13 @@ impl<'a> Iterator for SplitPaths<'a> { } } +impl<'a> Iterator for SplitPathsRef<'a> { + type Item = &'a Path; + fn next(&mut self) -> Option<&'a Path> { + self.0 + } +} + #[derive(Debug)] pub struct JoinPathsError; diff --git a/library/std/src/sys/paths/windows.rs b/library/std/src/sys/paths/windows.rs index 9b9316b2658c0..350a942124694 100644 --- a/library/std/src/sys/paths/windows.rs +++ b/library/std/src/sys/paths/windows.rs @@ -3,6 +3,7 @@ #![allow(nonstandard_style)] use crate::ffi::{OsStr, OsString}; +use crate::marker::PhantomData; use crate::os::windows::ffi::EncodeWide; use crate::os::windows::prelude::*; use crate::path::{self, PathBuf}; @@ -56,6 +57,19 @@ impl<'a> Iterator for SplitPaths<'a> { } } +pub struct SplitPathsRef<'a>(!, PhantomData<&'a ()>); + +pub fn split_paths_ref(_unparsed: &OsStr) -> Option> { + None +} + +impl<'a> Iterator for SplitPathsRef<'a> { + type Item = &'a path::Path; + fn next(&mut self) -> Option<&'a path::Path> { + self.0 + } +} + #[derive(Debug)] pub struct JoinPathsError;