From 1e772f9681d4828cb10e0df3a37fc18594bfe992 Mon Sep 17 00:00:00 2001 From: lcnr Date: Thu, 2 Oct 2025 12:09:54 +0200 Subject: [PATCH 01/19] check for empty heads after adding parent --- compiler/rustc_type_ir/src/search_graph/mod.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_type_ir/src/search_graph/mod.rs b/compiler/rustc_type_ir/src/search_graph/mod.rs index 7aa58d096d5c0..6d3da27acf9bd 100644 --- a/compiler/rustc_type_ir/src/search_graph/mod.rs +++ b/compiler/rustc_type_ir/src/search_graph/mod.rs @@ -969,10 +969,6 @@ impl, X: Cx> SearchGraph { return true; }; - let Some(new_highest_head_index) = heads.opt_highest_cycle_head_index() else { - return false; - }; - // We're rebasing an entry `e` over a head `p`. This head // has a number of own heads `h` it depends on. // @@ -1046,6 +1042,10 @@ impl, X: Cx> SearchGraph { }; } + let Some(new_highest_head_index) = heads.opt_highest_cycle_head_index() else { + return false; + }; + // We now care about the path from the next highest cycle head to the // provisional cache entry. *path_from_head = path_from_head.extend(Self::cycle_path_kind( From 36283bcb7e37b6e1cdf0c971f514a8212164a593 Mon Sep 17 00:00:00 2001 From: lcnr Date: Thu, 2 Oct 2025 12:10:58 +0200 Subject: [PATCH 02/19] fix `RebaseReason::Ambiguity` we should not get the certainty from the current result of the provisional cache entry --- .../src/solve/search_graph.rs | 14 +++--- compiler/rustc_type_ir/src/interner.rs | 3 +- .../rustc_type_ir/src/search_graph/mod.rs | 26 +++++++---- .../forced_ambiguity-use-head-maybe-cause.rs | 44 +++++++++++++++++++ ...rced_ambiguity-use-head-maybe-cause.stderr | 27 ++++++++++++ 5 files changed, 99 insertions(+), 15 deletions(-) create mode 100644 tests/ui/traits/next-solver/cycles/forced_ambiguity-use-head-maybe-cause.rs create mode 100644 tests/ui/traits/next-solver/cycles/forced_ambiguity-use-head-maybe-cause.stderr diff --git a/compiler/rustc_next_trait_solver/src/solve/search_graph.rs b/compiler/rustc_next_trait_solver/src/solve/search_graph.rs index 109c8476ccb16..ea45d50969909 100644 --- a/compiler/rustc_next_trait_solver/src/solve/search_graph.rs +++ b/compiler/rustc_next_trait_solver/src/solve/search_graph.rs @@ -99,19 +99,23 @@ where response_no_constraints(cx, input, Certainty::overflow(false)) } - fn is_ambiguous_result(result: QueryResult) -> bool { - result.is_ok_and(|response| { - has_no_inference_or_external_constraints(response) + fn is_ambiguous_result(result: QueryResult) -> Option { + result.ok().and_then(|response| { + if has_no_inference_or_external_constraints(response) && matches!(response.value.certainty, Certainty::Maybe { .. }) + { + Some(response.value.certainty) + } else { + None + } }) } fn propagate_ambiguity( cx: I, for_input: CanonicalInput, - from_result: QueryResult, + certainty: Certainty, ) -> QueryResult { - let certainty = from_result.unwrap().value.certainty; response_no_constraints(cx, for_input, certainty) } diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs index 886d1a78bcbfd..322be31f4cb10 100644 --- a/compiler/rustc_type_ir/src/interner.rs +++ b/compiler/rustc_type_ir/src/interner.rs @@ -11,7 +11,7 @@ use crate::inherent::*; use crate::ir_print::IrPrint; use crate::lang_items::{SolverAdtLangItem, SolverLangItem, SolverTraitLangItem}; use crate::relate::Relate; -use crate::solve::{CanonicalInput, ExternalConstraintsData, QueryResult, inspect}; +use crate::solve::{CanonicalInput, Certainty, ExternalConstraintsData, QueryResult, inspect}; use crate::visit::{Flags, TypeVisitable}; use crate::{self as ty, CanonicalParamEnvCacheEntry, search_graph}; @@ -548,6 +548,7 @@ impl CollectAndApply for Result { impl search_graph::Cx for I { type Input = CanonicalInput; type Result = QueryResult; + type AmbiguityInfo = Certainty; type DepNodeIndex = I::DepNodeIndex; type Tracked = I::Tracked; diff --git a/compiler/rustc_type_ir/src/search_graph/mod.rs b/compiler/rustc_type_ir/src/search_graph/mod.rs index 6d3da27acf9bd..ad8b643d976c7 100644 --- a/compiler/rustc_type_ir/src/search_graph/mod.rs +++ b/compiler/rustc_type_ir/src/search_graph/mod.rs @@ -40,6 +40,7 @@ pub use global_cache::GlobalCache; pub trait Cx: Copy { type Input: Debug + Eq + Hash + Copy; type Result: Debug + Eq + Hash + Copy; + type AmbiguityInfo: Debug + Eq + Hash + Copy; type DepNodeIndex; type Tracked: Debug; @@ -96,11 +97,13 @@ pub trait Delegate: Sized { input: ::Input, ) -> ::Result; - fn is_ambiguous_result(result: ::Result) -> bool; + fn is_ambiguous_result( + result: ::Result, + ) -> Option<::AmbiguityInfo>; fn propagate_ambiguity( cx: Self::Cx, for_input: ::Input, - from_result: ::Result, + ambiguity_info: ::AmbiguityInfo, ) -> ::Result; fn compute_goal( @@ -913,9 +916,9 @@ impl, X: Cx> SearchGraph { /// heads from the stack. This may not necessarily mean that we've actually /// reached a fixpoint for that cycle head, which impacts the way we rebase /// provisional cache entries. -enum RebaseReason { +enum RebaseReason { NoCycleUsages, - Ambiguity, + Ambiguity(X::AmbiguityInfo), Overflow, /// We've actually reached a fixpoint. /// @@ -951,7 +954,7 @@ impl, X: Cx> SearchGraph { &mut self, cx: X, stack_entry: &StackEntry, - rebase_reason: RebaseReason, + rebase_reason: RebaseReason, ) { let popped_head_index = self.stack.next_index(); #[allow(rustc::potential_query_instability)] @@ -1029,8 +1032,8 @@ impl, X: Cx> SearchGraph { // is not actually equal to the final provisional result. We // need to discard the provisional cache entry in this case. RebaseReason::NoCycleUsages => return false, - RebaseReason::Ambiguity => { - *result = D::propagate_ambiguity(cx, input, *result); + RebaseReason::Ambiguity(info) => { + *result = D::propagate_ambiguity(cx, input, info); } RebaseReason::Overflow => *result = D::fixpoint_overflow_result(cx, input), RebaseReason::ReachedFixpoint(None) => {} @@ -1268,6 +1271,7 @@ impl, X: Cx> SearchGraph { } /// Whether we've reached a fixpoint when evaluating a cycle head. + #[instrument(level = "trace", skip(self, stack_entry), ret)] fn reached_fixpoint( &mut self, stack_entry: &StackEntry, @@ -1355,8 +1359,12 @@ impl, X: Cx> SearchGraph { // As we only get to this branch if we haven't yet reached a fixpoint, // we also taint all provisional cache entries which depend on the // current goal. - if D::is_ambiguous_result(result) { - self.rebase_provisional_cache_entries(cx, &stack_entry, RebaseReason::Ambiguity); + if let Some(info) = D::is_ambiguous_result(result) { + self.rebase_provisional_cache_entries( + cx, + &stack_entry, + RebaseReason::Ambiguity(info), + ); return EvaluationResult::finalize(stack_entry, encountered_overflow, result); }; diff --git a/tests/ui/traits/next-solver/cycles/forced_ambiguity-use-head-maybe-cause.rs b/tests/ui/traits/next-solver/cycles/forced_ambiguity-use-head-maybe-cause.rs new file mode 100644 index 0000000000000..aa5893d621cdb --- /dev/null +++ b/tests/ui/traits/next-solver/cycles/forced_ambiguity-use-head-maybe-cause.rs @@ -0,0 +1,44 @@ +//@ compile-flags: -Znext-solver +#![feature(rustc_attrs)] +#![rustc_no_implicit_bounds] + +// A regression test making sure that when forcing dependent +// provisional cache entries to ambiguous, we use the `MaybeCause` +// of the cycle head. We ended up trying to use the current result +// of the provisional cache entry, which is incorrect and caused an +// ICE when trying to unwrap it. + +struct Root(T); +struct Head(T); +struct Error(T); +struct NotImplemented(T); + +#[rustc_coinductive] +trait Trait {} +impl Trait for Root +where + Head: Trait, +{} + +impl Trait for Head +where + Root: Trait, + T: Trait, // ambiguous +{} + +impl Trait for Head +where + Error: Trait, + NotImplemented: Trait, +{} + +impl Trait for Error +where + Head: Trait, + NotImplemented: Trait, +{} + +fn impls_trait() {} +fn main() { + impls_trait::>() //~ ERROR type annotations needed +} diff --git a/tests/ui/traits/next-solver/cycles/forced_ambiguity-use-head-maybe-cause.stderr b/tests/ui/traits/next-solver/cycles/forced_ambiguity-use-head-maybe-cause.stderr new file mode 100644 index 0000000000000..dd4049a66d0b4 --- /dev/null +++ b/tests/ui/traits/next-solver/cycles/forced_ambiguity-use-head-maybe-cause.stderr @@ -0,0 +1,27 @@ +error[E0283]: type annotations needed + --> $DIR/forced_ambiguity-use-head-maybe-cause.rs:43:19 + | +LL | impls_trait::>() + | ^^^^^^^ cannot infer type for struct `Head<_>` + | + = note: cannot satisfy `Head<_>: Trait` + = help: the trait `Trait` is implemented for `Head` +note: required for `Root<_>` to implement `Trait` + --> $DIR/forced_ambiguity-use-head-maybe-cause.rs:18:9 + | +LL | impl Trait for Root + | ^^^^^ ^^^^^^^ +LL | where +LL | Head: Trait, + | ----- unsatisfied trait bound introduced here + = note: 8 redundant requirements hidden + = note: required for `Root<_>` to implement `Trait` +note: required by a bound in `impls_trait` + --> $DIR/forced_ambiguity-use-head-maybe-cause.rs:41:19 + | +LL | fn impls_trait() {} + | ^^^^^ required by this bound in `impls_trait` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0283`. From 1e6b444df7f1242ad9b0cc58620f101f34514734 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Tue, 7 Oct 2025 13:07:23 -0700 Subject: [PATCH 03/19] library: fs: Factor out a `file_time_to_timespec` function in preparation for reusing it --- library/std/src/sys/fs/unix.rs | 49 ++++++++++++++++++---------------- 1 file changed, 26 insertions(+), 23 deletions(-) diff --git a/library/std/src/sys/fs/unix.rs b/library/std/src/sys/fs/unix.rs index 33a1e7ff5e40e..bcd5ea6494447 100644 --- a/library/std/src/sys/fs/unix.rs +++ b/library/std/src/sys/fs/unix.rs @@ -1604,24 +1604,6 @@ impl File { } pub fn set_times(&self, times: FileTimes) -> io::Result<()> { - #[cfg(not(any( - target_os = "redox", - target_os = "espidf", - target_os = "horizon", - target_os = "nuttx", - )))] - let to_timespec = |time: Option| match time { - Some(time) if let Some(ts) = time.t.to_timespec() => Ok(ts), - Some(time) if time > crate::sys::time::UNIX_EPOCH => Err(io::const_error!( - io::ErrorKind::InvalidInput, - "timestamp is too large to set as a file time", - )), - Some(_) => Err(io::const_error!( - io::ErrorKind::InvalidInput, - "timestamp is too small to set as a file time", - )), - None => Ok(libc::timespec { tv_sec: 0, tv_nsec: libc::UTIME_OMIT as _ }), - }; cfg_select! { any(target_os = "redox", target_os = "espidf", target_os = "horizon", target_os = "nuttx") => { // Redox doesn't appear to support `UTIME_OMIT`. @@ -1639,17 +1621,17 @@ impl File { let mut attrlist: libc::attrlist = unsafe { mem::zeroed() }; attrlist.bitmapcount = libc::ATTR_BIT_MAP_COUNT; if times.created.is_some() { - buf[num_times].write(to_timespec(times.created)?); + buf[num_times].write(file_time_to_timespec(times.created)?); num_times += 1; attrlist.commonattr |= libc::ATTR_CMN_CRTIME; } if times.modified.is_some() { - buf[num_times].write(to_timespec(times.modified)?); + buf[num_times].write(file_time_to_timespec(times.modified)?); num_times += 1; attrlist.commonattr |= libc::ATTR_CMN_MODTIME; } if times.accessed.is_some() { - buf[num_times].write(to_timespec(times.accessed)?); + buf[num_times].write(file_time_to_timespec(times.accessed)?); num_times += 1; attrlist.commonattr |= libc::ATTR_CMN_ACCTIME; } @@ -1663,7 +1645,7 @@ impl File { Ok(()) } target_os = "android" => { - let times = [to_timespec(times.accessed)?, to_timespec(times.modified)?]; + let times = [file_time_to_timespec(times.accessed)?, file_time_to_timespec(times.modified)?]; // futimens requires Android API level 19 cvt(unsafe { weak!( @@ -1697,7 +1679,7 @@ impl File { return Ok(()); } } - let times = [to_timespec(times.accessed)?, to_timespec(times.modified)?]; + let times = [file_time_to_timespec(times.accessed)?, file_time_to_timespec(times.modified)?]; cvt(unsafe { libc::futimens(self.as_raw_fd(), times.as_ptr()) })?; Ok(()) } @@ -1705,6 +1687,27 @@ impl File { } } +#[cfg(not(any( + target_os = "redox", + target_os = "espidf", + target_os = "horizon", + target_os = "nuttx", +)))] +fn file_time_to_timespec(time: Option) -> io::Result { + match time { + Some(time) if let Some(ts) = time.t.to_timespec() => Ok(ts), + Some(time) if time > crate::sys::time::UNIX_EPOCH => Err(io::const_error!( + io::ErrorKind::InvalidInput, + "timestamp is too large to set as a file time", + )), + Some(_) => Err(io::const_error!( + io::ErrorKind::InvalidInput, + "timestamp is too small to set as a file time", + )), + None => Ok(libc::timespec { tv_sec: 0, tv_nsec: libc::UTIME_OMIT as _ }), + } +}; + impl DirBuilder { pub fn new() -> DirBuilder { DirBuilder { mode: 0o777 } From 1bf555c947296016d78de926e989d306b4cfde45 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Tue, 7 Oct 2025 13:18:38 -0700 Subject: [PATCH 04/19] library: fs: Factor out the Apple file time to attrlist code for reuse --- library/std/src/sys/fs/unix.rs | 74 +++++++++++++++++++++++----------- 1 file changed, 51 insertions(+), 23 deletions(-) diff --git a/library/std/src/sys/fs/unix.rs b/library/std/src/sys/fs/unix.rs index bcd5ea6494447..bed9ea9139834 100644 --- a/library/std/src/sys/fs/unix.rs +++ b/library/std/src/sys/fs/unix.rs @@ -1616,30 +1616,12 @@ impl File { )) } target_vendor = "apple" => { - let mut buf = [mem::MaybeUninit::::uninit(); 3]; - let mut num_times = 0; - let mut attrlist: libc::attrlist = unsafe { mem::zeroed() }; - attrlist.bitmapcount = libc::ATTR_BIT_MAP_COUNT; - if times.created.is_some() { - buf[num_times].write(file_time_to_timespec(times.created)?); - num_times += 1; - attrlist.commonattr |= libc::ATTR_CMN_CRTIME; - } - if times.modified.is_some() { - buf[num_times].write(file_time_to_timespec(times.modified)?); - num_times += 1; - attrlist.commonattr |= libc::ATTR_CMN_MODTIME; - } - if times.accessed.is_some() { - buf[num_times].write(file_time_to_timespec(times.accessed)?); - num_times += 1; - attrlist.commonattr |= libc::ATTR_CMN_ACCTIME; - } + let ta = TimesAttrlist::from_times(×)?; cvt(unsafe { libc::fsetattrlist( self.as_raw_fd(), - (&raw const attrlist).cast::().cast_mut(), - buf.as_ptr().cast::().cast_mut(), - num_times * size_of::(), + ta.attrlist(), + ta.times_buf(), + ta.times_buf_size(), 0 ) })?; Ok(()) @@ -1706,7 +1688,53 @@ fn file_time_to_timespec(time: Option) -> io::Result )), None => Ok(libc::timespec { tv_sec: 0, tv_nsec: libc::UTIME_OMIT as _ }), } -}; +} + +#[cfg(target_vendor = "apple")] +struct TimesAttrlist { + buf: [mem::MaybeUninit; 3], + attrlist: libc::attrlist, + num_times: usize, +} + +#[cfg(target_vendor = "apple")] +impl TimesAttrlist { + fn from_times(times: &FileTimes) -> io::Result { + let mut this = Self { + buf: [mem::MaybeUninit::::uninit(); 3], + attrlist: unsafe { mem::zeroed() }, + num_times: 0, + }; + this.attrlist.bitmapcount = libc::ATTR_BIT_MAP_COUNT; + if times.created.is_some() { + this.buf[this.num_times].write(file_time_to_timespec(times.created)?); + this.num_times += 1; + attrlist.commonattr |= libc::ATTR_CMN_CRTIME; + } + if times.modified.is_some() { + this.buf[this.num_times].write(file_time_to_timespec(times.modified)?); + this.num_times += 1; + attrlist.commonattr |= libc::ATTR_CMN_MODTIME; + } + if times.accessed.is_some() { + this.buf[this.num_times].write(file_time_to_timespec(times.accessed)?); + this.num_times += 1; + attrlist.commonattr |= libc::ATTR_CMN_ACCTIME; + } + } + + fn attrlist(&self) -> *mut libc::c_void { + (&raw const self.attrlist).cast::().cast_mut() + } + + fn times_buf(&self) -> *mut libc::c_void { + self.buf.as_ptr().cast::().cast_mut() + } + + fn times_buf_size(&self) -> usize { + self.num_times * size_of::() + } +} impl DirBuilder { pub fn new() -> DirBuilder { From 0355358e6cd106f771392eadae3c0e3f49b17e98 Mon Sep 17 00:00:00 2001 From: cyrgani Date: Wed, 8 Oct 2025 13:49:32 +0200 Subject: [PATCH 05/19] run zero-size assertion in `const {}` --- library/proc_macro/src/bridge/selfless_reify.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/proc_macro/src/bridge/selfless_reify.rs b/library/proc_macro/src/bridge/selfless_reify.rs index b06434a5ffee2..a53550e0b9e0c 100644 --- a/library/proc_macro/src/bridge/selfless_reify.rs +++ b/library/proc_macro/src/bridge/selfless_reify.rs @@ -50,7 +50,7 @@ macro_rules! define_reify_functions { >(f: F) -> $(extern $abi)? fn($($arg_ty),*) -> $ret_ty { // FIXME(eddyb) describe the `F` type (e.g. via `type_name::`) once panic // formatting becomes possible in `const fn`. - assert!(size_of::() == 0, "selfless_reify: closure must be zero-sized"); + const { assert!(size_of::() == 0, "selfless_reify: closure must be zero-sized"); } $(extern $abi)? fn wrapper< $($($param,)*)? From e3d2016a2264b704a7f6f1c18e45ce21732b627b Mon Sep 17 00:00:00 2001 From: cyrgani Date: Wed, 8 Oct 2025 14:33:31 +0200 Subject: [PATCH 06/19] inline constants in generated `enum` `Encode` impls --- library/proc_macro/src/bridge/rpc.rs | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/library/proc_macro/src/bridge/rpc.rs b/library/proc_macro/src/bridge/rpc.rs index 7f4f5fc3a97d5..6e5afe282d683 100644 --- a/library/proc_macro/src/bridge/rpc.rs +++ b/library/proc_macro/src/bridge/rpc.rs @@ -58,16 +58,11 @@ macro_rules! rpc_encode_decode { fn encode(self, w: &mut Writer, s: &mut S) { // HACK(eddyb): `Tag` enum duplicated between the // two impls as there's no other place to stash it. - #[allow(non_upper_case_globals)] - mod tag { - #[repr(u8)] enum Tag { $($variant),* } - - $(pub(crate) const $variant: u8 = Tag::$variant as u8;)* - } + #[repr(u8)] enum Tag { $($variant),* } match self { $($name::$variant $(($field))* => { - tag::$variant.encode(w, s); + (Tag::$variant as u8).encode(w, s); $($field.encode(w, s);)* })* } From 722545427f46d670228c345a03ab9c0c9cf74241 Mon Sep 17 00:00:00 2001 From: yukang Date: Wed, 8 Oct 2025 16:39:53 +0800 Subject: [PATCH 07/19] Implement fs api set_times and set_times_nofollow --- library/std/src/fs.rs | 74 +++++++++ library/std/src/fs/tests.rs | 219 ++++++++++++++++++++++++++ library/std/src/sys/fs/hermit.rs | 8 + library/std/src/sys/fs/mod.rs | 8 + library/std/src/sys/fs/solid.rs | 11 ++ library/std/src/sys/fs/uefi.rs | 8 + library/std/src/sys/fs/unix.rs | 127 +++++++++++++++ library/std/src/sys/fs/unsupported.rs | 8 + library/std/src/sys/fs/vexos.rs | 8 + library/std/src/sys/fs/wasi.rs | 12 ++ library/std/src/sys/fs/windows.rs | 17 ++ 11 files changed, 500 insertions(+) diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs index 28b2c7173d321..e97190e69d68f 100644 --- a/library/std/src/fs.rs +++ b/library/std/src/fs.rs @@ -387,6 +387,80 @@ pub fn write, C: AsRef<[u8]>>(path: P, contents: C) -> io::Result inner(path.as_ref(), contents.as_ref()) } +/// Changes the timestamps of the file or directory at the specified path. +/// +/// This function will attempt to set the access and modification times +/// to the times specified. If the path refers to a symbolic link, this function +/// will follow the link and change the timestamps of the target file. +/// +/// # Platform-specific behavior +/// +/// This function currently corresponds to the `utimensat` function on Unix platforms +/// and the `SetFileTime` function on Windows. +/// +/// # Errors +/// +/// This function will return an error if the user lacks permission to change timestamps on the +/// target file or symlink. It may also return an error if the OS does not support it. +/// +/// # Examples +/// +/// ```no_run +/// #![feature(fs_set_times)] +/// use std::fs::{self, FileTimes}; +/// use std::time::SystemTime; +/// +/// fn main() -> std::io::Result<()> { +/// let now = SystemTime::now(); +/// let times = FileTimes::new() +/// .set_accessed(now) +/// .set_modified(now); +/// fs::set_times("foo.txt", times)?; +/// Ok(()) +/// } +/// ``` +#[unstable(feature = "fs_set_times", issue = "147455")] +pub fn set_times>(path: P, times: FileTimes) -> io::Result<()> { + fs_imp::set_times(path.as_ref(), times.0) +} + +/// Changes the timestamps of the file or symlink at the specified path. +/// +/// This function will attempt to set the access and modification times +/// to the times specified. Differ from `set_times`, if the path refers to a symbolic link, +/// this function will change the timestamps of the symlink itself, not the target file. +/// +/// # Platform-specific behavior +/// +/// This function currently corresponds to the `utimensat` function with `AT_SYMLINK_NOFOLLOW` +/// on Unix platforms and the `SetFileTime` function on Windows after opening the symlink. +/// +/// # Errors +/// +/// This function will return an error if the user lacks permission to change timestamps on the +/// target file or symlink. It may also return an error if the OS does not support it. +/// +/// # Examples +/// +/// ```no_run +/// #![feature(fs_set_times)] +/// use std::fs::{self, FileTimes}; +/// use std::time::SystemTime; +/// +/// fn main() -> std::io::Result<()> { +/// let now = SystemTime::now(); +/// let times = FileTimes::new() +/// .set_accessed(now) +/// .set_modified(now); +/// fs::set_times_nofollow("symlink.txt", times)?; +/// Ok(()) +/// } +/// ``` +#[unstable(feature = "fs_set_times", issue = "147455")] +pub fn set_times_nofollow>(path: P, times: FileTimes) -> io::Result<()> { + fs_imp::set_times_nofollow(path.as_ref(), times.0) +} + #[stable(feature = "file_lock", since = "1.89.0")] impl error::Error for TryLockError {} diff --git a/library/std/src/fs/tests.rs b/library/std/src/fs/tests.rs index f8dfb0d633400..4d67ba9248998 100644 --- a/library/std/src/fs/tests.rs +++ b/library/std/src/fs/tests.rs @@ -2226,3 +2226,222 @@ fn test_open_options_invalid_combinations() { assert_eq!(err.kind(), ErrorKind::InvalidInput); assert_eq!(err.to_string(), "must specify at least one of read, write, or append access"); } + +#[test] +fn test_fs_set_times() { + #[cfg(target_vendor = "apple")] + use crate::os::darwin::fs::FileTimesExt; + #[cfg(windows)] + use crate::os::windows::fs::FileTimesExt; + + let tmp = tmpdir(); + let path = tmp.join("foo"); + File::create(&path).unwrap(); + + let mut times = FileTimes::new(); + let accessed = SystemTime::UNIX_EPOCH + Duration::from_secs(12345); + let modified = SystemTime::UNIX_EPOCH + Duration::from_secs(54321); + times = times.set_accessed(accessed).set_modified(modified); + + #[cfg(any(windows, target_vendor = "apple"))] + let created = SystemTime::UNIX_EPOCH + Duration::from_secs(32123); + #[cfg(any(windows, target_vendor = "apple"))] + { + times = times.set_created(created); + } + + match fs::set_times(&path, times) { + // Allow unsupported errors on platforms which don't support setting times. + #[cfg(not(any( + windows, + all( + unix, + not(any( + target_os = "android", + target_os = "redox", + target_os = "espidf", + target_os = "horizon" + )) + ) + )))] + Err(e) if e.kind() == ErrorKind::Unsupported => return, + Err(e) => panic!("error setting file times: {e:?}"), + Ok(_) => {} + } + + let metadata = fs::metadata(&path).unwrap(); + assert_eq!(metadata.accessed().unwrap(), accessed); + assert_eq!(metadata.modified().unwrap(), modified); + #[cfg(any(windows, target_vendor = "apple"))] + { + assert_eq!(metadata.created().unwrap(), created); + } +} + +#[test] +fn test_fs_set_times_follows_symlink() { + #[cfg(target_vendor = "apple")] + use crate::os::darwin::fs::FileTimesExt; + #[cfg(windows)] + use crate::os::windows::fs::FileTimesExt; + + let tmp = tmpdir(); + + // Create a target file + let target = tmp.join("target"); + File::create(&target).unwrap(); + + // Create a symlink to the target + #[cfg(unix)] + let link = tmp.join("link"); + #[cfg(unix)] + crate::os::unix::fs::symlink(&target, &link).unwrap(); + + #[cfg(windows)] + let link = tmp.join("link.txt"); + #[cfg(windows)] + crate::os::windows::fs::symlink_file(&target, &link).unwrap(); + + // Get the symlink's own modified time BEFORE calling set_times (to compare later) + // We don't check accessed time because reading metadata may update atime on some platforms. + let link_metadata_before = fs::symlink_metadata(&link).unwrap(); + let link_modified_before = link_metadata_before.modified().unwrap(); + + let mut times = FileTimes::new(); + let accessed = SystemTime::UNIX_EPOCH + Duration::from_secs(12345); + let modified = SystemTime::UNIX_EPOCH + Duration::from_secs(54321); + times = times.set_accessed(accessed).set_modified(modified); + + #[cfg(any(windows, target_vendor = "apple"))] + let created = SystemTime::UNIX_EPOCH + Duration::from_secs(32123); + #[cfg(any(windows, target_vendor = "apple"))] + { + times = times.set_created(created); + } + + // Call fs::set_times on the symlink - it should follow the link and modify the target + match fs::set_times(&link, times) { + // Allow unsupported errors on platforms which don't support setting times. + #[cfg(not(any( + windows, + all( + unix, + not(any( + target_os = "android", + target_os = "redox", + target_os = "espidf", + target_os = "horizon" + )) + ) + )))] + Err(e) if e.kind() == ErrorKind::Unsupported => return, + Err(e) => panic!("error setting file times through symlink: {e:?}"), + Ok(_) => {} + } + + // Verify that the TARGET file's times were changed (following the symlink) + let target_metadata = fs::metadata(&target).unwrap(); + assert_eq!( + target_metadata.accessed().unwrap(), + accessed, + "target file accessed time should match" + ); + assert_eq!( + target_metadata.modified().unwrap(), + modified, + "target file modified time should match" + ); + #[cfg(any(windows, target_vendor = "apple"))] + { + assert_eq!( + target_metadata.created().unwrap(), + created, + "target file created time should match" + ); + } + + // Also verify through the symlink (fs::metadata follows symlinks) + let link_followed_metadata = fs::metadata(&link).unwrap(); + assert_eq!(link_followed_metadata.accessed().unwrap(), accessed); + assert_eq!(link_followed_metadata.modified().unwrap(), modified); + + // Verify that the SYMLINK ITSELF was NOT modified + // Note: We only check modified time, not accessed time, because reading the symlink + // metadata may update its atime on some platforms (e.g., Linux). + let link_metadata_after = fs::symlink_metadata(&link).unwrap(); + assert_eq!( + link_metadata_after.modified().unwrap(), + link_modified_before, + "symlink's own modified time should not change" + ); +} + +#[test] +fn test_fs_set_times_nofollow() { + #[cfg(target_vendor = "apple")] + use crate::os::darwin::fs::FileTimesExt; + #[cfg(windows)] + use crate::os::windows::fs::FileTimesExt; + + let tmp = tmpdir(); + + // Create a target file and a symlink to it + let target = tmp.join("target"); + File::create(&target).unwrap(); + + #[cfg(unix)] + let link = tmp.join("link"); + #[cfg(unix)] + crate::os::unix::fs::symlink(&target, &link).unwrap(); + + #[cfg(windows)] + let link = tmp.join("link.txt"); + #[cfg(windows)] + crate::os::windows::fs::symlink_file(&target, &link).unwrap(); + + let mut times = FileTimes::new(); + let accessed = SystemTime::UNIX_EPOCH + Duration::from_secs(11111); + let modified = SystemTime::UNIX_EPOCH + Duration::from_secs(22222); + times = times.set_accessed(accessed).set_modified(modified); + + #[cfg(any(windows, target_vendor = "apple"))] + let created = SystemTime::UNIX_EPOCH + Duration::from_secs(33333); + #[cfg(any(windows, target_vendor = "apple"))] + { + times = times.set_created(created); + } + + // Set times on the symlink itself (not following it) + match fs::set_times_nofollow(&link, times) { + // Allow unsupported errors on platforms which don't support setting times. + #[cfg(not(any( + windows, + all( + unix, + not(any( + target_os = "android", + target_os = "redox", + target_os = "espidf", + target_os = "horizon" + )) + ) + )))] + Err(e) if e.kind() == ErrorKind::Unsupported => return, + Err(e) => panic!("error setting symlink times: {e:?}"), + Ok(_) => {} + } + + // Read symlink metadata (without following) + let metadata = fs::symlink_metadata(&link).unwrap(); + assert_eq!(metadata.accessed().unwrap(), accessed); + assert_eq!(metadata.modified().unwrap(), modified); + #[cfg(any(windows, target_vendor = "apple"))] + { + assert_eq!(metadata.created().unwrap(), created); + } + + // Verify that the target file's times were NOT changed + let target_metadata = fs::metadata(&target).unwrap(); + assert_ne!(target_metadata.accessed().unwrap(), accessed); + assert_ne!(target_metadata.modified().unwrap(), modified); +} diff --git a/library/std/src/sys/fs/hermit.rs b/library/std/src/sys/fs/hermit.rs index 175d919c289dd..21235bcfbd8c5 100644 --- a/library/std/src/sys/fs/hermit.rs +++ b/library/std/src/sys/fs/hermit.rs @@ -566,6 +566,14 @@ pub fn set_perm(_p: &Path, _perm: FilePermissions) -> io::Result<()> { Err(Error::from_raw_os_error(22)) } +pub fn set_times(_p: &Path, _times: FileTimes) -> io::Result<()> { + Err(Error::from_raw_os_error(22)) +} + +pub fn set_times_nofollow(_p: &Path, _times: FileTimes) -> io::Result<()> { + Err(Error::from_raw_os_error(22)) +} + pub fn rmdir(path: &Path) -> io::Result<()> { run_path_with_cstr(path, &|path| cvt(unsafe { hermit_abi::rmdir(path.as_ptr()) }).map(|_| ())) } diff --git a/library/std/src/sys/fs/mod.rs b/library/std/src/sys/fs/mod.rs index 64f5a6b36d3db..b498f9cb7ea72 100644 --- a/library/std/src/sys/fs/mod.rs +++ b/library/std/src/sys/fs/mod.rs @@ -161,3 +161,11 @@ pub fn exists(path: &Path) -> io::Result { #[cfg(windows)] with_native_path(path, &imp::exists) } + +pub fn set_times(path: &Path, times: FileTimes) -> io::Result<()> { + with_native_path(path, &|path| imp::set_times(path, times.clone())) +} + +pub fn set_times_nofollow(path: &Path, times: FileTimes) -> io::Result<()> { + with_native_path(path, &|path| imp::set_times_nofollow(path, times.clone())) +} diff --git a/library/std/src/sys/fs/solid.rs b/library/std/src/sys/fs/solid.rs index 808a95829114e..39bd9b3cdd70b 100644 --- a/library/std/src/sys/fs/solid.rs +++ b/library/std/src/sys/fs/solid.rs @@ -538,6 +538,17 @@ pub fn set_perm(p: &Path, perm: FilePermissions) -> io::Result<()> { Ok(()) } +pub fn set_times(_p: &Path, _times: FileTimes) -> io::Result<()> { + Err(io::const_error!(io::ErrorKind::Unsupported, "setting file times not supported",)) +} + +pub fn set_times_nofollow(_p: &Path, _times: FileTimes) -> io::Result<()> { + Err(io::const_error!( + io::ErrorKind::Unsupported, + "setting file times on symlinks not supported", + )) +} + pub fn rmdir(p: &Path) -> io::Result<()> { if stat(p)?.file_type().is_dir() { error::SolidError::err_if_negative(unsafe { abi::SOLID_FS_Unlink(cstr(p)?.as_ptr()) }) diff --git a/library/std/src/sys/fs/uefi.rs b/library/std/src/sys/fs/uefi.rs index 5763d7862f5ae..e4e7274ae8cb3 100644 --- a/library/std/src/sys/fs/uefi.rs +++ b/library/std/src/sys/fs/uefi.rs @@ -333,6 +333,14 @@ pub fn set_perm(_p: &Path, _perm: FilePermissions) -> io::Result<()> { unsupported() } +pub fn set_times(_p: &Path, _times: FileTimes) -> io::Result<()> { + unsupported() +} + +pub fn set_times_nofollow(_p: &Path, _times: FileTimes) -> io::Result<()> { + unsupported() +} + pub fn rmdir(_p: &Path) -> io::Result<()> { unsupported() } diff --git a/library/std/src/sys/fs/unix.rs b/library/std/src/sys/fs/unix.rs index bed9ea9139834..578b5f4a1d986 100644 --- a/library/std/src/sys/fs/unix.rs +++ b/library/std/src/sys/fs/unix.rs @@ -1195,6 +1195,55 @@ impl fmt::Debug for OpenOptions { } } +#[cfg(not(any( + target_os = "redox", + target_os = "espidf", + target_os = "horizon", + target_os = "nuttx", +)))] +fn to_timespec(time: Option) -> io::Result { + match time { + Some(time) if let Some(ts) = time.t.to_timespec() => Ok(ts), + Some(time) if time > crate::sys::time::UNIX_EPOCH => Err(io::const_error!( + io::ErrorKind::InvalidInput, + "timestamp is too large to set as a file time", + )), + Some(_) => Err(io::const_error!( + io::ErrorKind::InvalidInput, + "timestamp is too small to set as a file time", + )), + None => Ok(libc::timespec { tv_sec: 0, tv_nsec: libc::UTIME_OMIT as _ }), + } +} + +#[cfg(target_vendor = "apple")] +fn set_attrlist_with_times( + times: &FileTimes, +) -> io::Result<(libc::attrlist, [mem::MaybeUninit; 3], usize)> { + let mut buf = [mem::MaybeUninit::::uninit(); 3]; + let mut num_times = 0; + let mut attrlist: libc::attrlist = unsafe { mem::zeroed() }; + attrlist.bitmapcount = libc::ATTR_BIT_MAP_COUNT; + + if times.created.is_some() { + buf[num_times].write(to_timespec(times.created)?); + num_times += 1; + attrlist.commonattr |= libc::ATTR_CMN_CRTIME; + } + if times.modified.is_some() { + buf[num_times].write(to_timespec(times.modified)?); + num_times += 1; + attrlist.commonattr |= libc::ATTR_CMN_MODTIME; + } + if times.accessed.is_some() { + buf[num_times].write(to_timespec(times.accessed)?); + num_times += 1; + attrlist.commonattr |= libc::ATTR_CMN_ACCTIME; + } + + Ok((attrlist, buf, num_times)) +} + impl File { pub fn open(path: &Path, opts: &OpenOptions) -> io::Result { run_path_with_cstr(path, &|path| File::open_c(path, opts)) @@ -2112,6 +2161,84 @@ fn open_from(from: &Path) -> io::Result<(crate::fs::File, crate::fs::Metadata)> Ok((reader, metadata)) } +fn set_times_impl(p: &CStr, times: FileTimes, flags: c_int) -> io::Result<()> { + cfg_select! { + any(target_os = "redox", target_os = "espidf", target_os = "horizon", target_os = "nuttx") => { + let _ = (p, times, flags); + Err(io::const_error!( + io::ErrorKind::Unsupported, + "setting file times not supported", + )) + } + target_vendor = "apple" => { + // Apple platforms use setattrlist which supports setting times on symlinks + let (attrlist, buf, num_times) = set_attrlist_with_times(×)?; + let options = if flags == libc::AT_SYMLINK_NOFOLLOW { + libc::FSOPT_NOFOLLOW + } else { + 0 + }; + + cvt(unsafe { libc::setattrlist( + p.as_ptr(), + (&raw const attrlist).cast::().cast_mut(), + buf.as_ptr().cast::().cast_mut(), + num_times * size_of::(), + options as u32 + ) })?; + Ok(()) + } + target_os = "android" => { + let times = [to_timespec(times.accessed)?, to_timespec(times.modified)?]; + // utimensat requires Android API level 19 + cvt(unsafe { + weak!( + fn utimensat(dirfd: c_int, path: *const c_char, times: *const libc::timespec, flags: c_int) -> c_int; + ); + match utimensat.get() { + Some(utimensat) => utimensat(libc::AT_FDCWD, p.as_ptr(), times.as_ptr(), flags), + None => return Err(io::const_error!( + io::ErrorKind::Unsupported, + "setting file times requires Android API level >= 19", + )), + } + })?; + Ok(()) + } + _ => { + #[cfg(all(target_os = "linux", target_env = "gnu", target_pointer_width = "32", not(target_arch = "riscv32")))] + { + use crate::sys::{time::__timespec64, weak::weak}; + + // Added in glibc 2.34 + weak!( + fn __utimensat64(dirfd: c_int, path: *const c_char, times: *const __timespec64, flags: c_int) -> c_int; + ); + + if let Some(utimensat64) = __utimensat64.get() { + let to_timespec = |time: Option| time.map(|time| time.t.to_timespec64()) + .unwrap_or(__timespec64::new(0, libc::UTIME_OMIT as _)); + let times = [to_timespec(times.accessed), to_timespec(times.modified)]; + cvt(unsafe { utimensat64(libc::AT_FDCWD, p.as_ptr(), times.as_ptr(), flags) })?; + return Ok(()); + } + } + let times = [to_timespec(times.accessed)?, to_timespec(times.modified)?]; + cvt(unsafe { libc::utimensat(libc::AT_FDCWD, p.as_ptr(), times.as_ptr(), flags) })?; + Ok(()) + } + } +} + +pub fn set_times(p: &CStr, times: FileTimes) -> io::Result<()> { + // flags = 0 means follow symlinks + set_times_impl(p, times, 0) +} + +pub fn set_times_nofollow(p: &CStr, times: FileTimes) -> io::Result<()> { + set_times_impl(p, times, libc::AT_SYMLINK_NOFOLLOW) +} + #[cfg(target_os = "espidf")] fn open_to_and_set_permissions( to: &Path, diff --git a/library/std/src/sys/fs/unsupported.rs b/library/std/src/sys/fs/unsupported.rs index efaddb51b3751..659ea2a8fc276 100644 --- a/library/std/src/sys/fs/unsupported.rs +++ b/library/std/src/sys/fs/unsupported.rs @@ -312,6 +312,14 @@ pub fn set_perm(_p: &Path, perm: FilePermissions) -> io::Result<()> { match perm.0 {} } +pub fn set_times(_p: &Path, times: FileTimes) -> io::Result<()> { + match times {} +} + +pub fn set_times_nofollow(_p: &Path, times: FileTimes) -> io::Result<()> { + match times {} +} + pub fn rmdir(_p: &Path) -> io::Result<()> { unsupported() } diff --git a/library/std/src/sys/fs/vexos.rs b/library/std/src/sys/fs/vexos.rs index f642e7cb074ec..99b156d535768 100644 --- a/library/std/src/sys/fs/vexos.rs +++ b/library/std/src/sys/fs/vexos.rs @@ -492,6 +492,14 @@ pub fn set_perm(_p: &Path, _perm: FilePermissions) -> io::Result<()> { unsupported() } +pub fn set_times(_p: &Path, _times: FileTimes) -> io::Result<()> { + unsupported() +} + +pub fn set_times_nofollow(_p: &Path, _times: FileTimes) -> io::Result<()> { + unsupported() +} + pub fn exists(path: &Path) -> io::Result { run_path_with_cstr(path, &|path| Ok(unsafe { vex_sdk::vexFileStatus(path.as_ptr()) } != 0)) } diff --git a/library/std/src/sys/fs/wasi.rs b/library/std/src/sys/fs/wasi.rs index 0b65b9cb389df..1e6c0fad5b830 100644 --- a/library/std/src/sys/fs/wasi.rs +++ b/library/std/src/sys/fs/wasi.rs @@ -643,6 +643,18 @@ pub fn set_perm(_p: &Path, _perm: FilePermissions) -> io::Result<()> { unsupported() } +pub fn set_times(_p: &Path, _times: FileTimes) -> io::Result<()> { + // File times haven't been fully figured out in wasi yet, so this is + // likely temporary + unsupported() +} + +pub fn set_times_nofollow(_p: &Path, _times: FileTimes) -> io::Result<()> { + // File times haven't been fully figured out in wasi yet, so this is + // likely temporary + unsupported() +} + pub fn rmdir(p: &Path) -> io::Result<()> { let (dir, file) = open_parent(p)?; dir.remove_directory(osstr2str(file.as_ref())?) diff --git a/library/std/src/sys/fs/windows.rs b/library/std/src/sys/fs/windows.rs index ccfe410627f70..f2d325da35c7d 100644 --- a/library/std/src/sys/fs/windows.rs +++ b/library/std/src/sys/fs/windows.rs @@ -1514,6 +1514,23 @@ pub fn set_perm(p: &WCStr, perm: FilePermissions) -> io::Result<()> { } } +pub fn set_times(p: &WCStr, times: FileTimes) -> io::Result<()> { + let mut opts = OpenOptions::new(); + opts.write(true); + opts.custom_flags(c::FILE_FLAG_BACKUP_SEMANTICS); + let file = File::open_native(p, &opts)?; + file.set_times(times) +} + +pub fn set_times_nofollow(p: &WCStr, times: FileTimes) -> io::Result<()> { + let mut opts = OpenOptions::new(); + opts.write(true); + // `FILE_FLAG_OPEN_REPARSE_POINT` for no_follow behavior + opts.custom_flags(c::FILE_FLAG_BACKUP_SEMANTICS | c::FILE_FLAG_OPEN_REPARSE_POINT); + let file = File::open_native(p, &opts)?; + file.set_times(times) +} + fn get_path(f: &File) -> io::Result { fill_utf16_buf( |buf, sz| unsafe { From 3d40fa69587c60d8ba7bcf3d2a39e503a5ded07c Mon Sep 17 00:00:00 2001 From: Yukang Date: Thu, 9 Oct 2025 10:20:48 +0800 Subject: [PATCH 08/19] Update library/std/src/fs.rs Co-authored-by: Josh Triplett --- library/std/src/fs.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs index e97190e69d68f..f374f4155cfd2 100644 --- a/library/std/src/fs.rs +++ b/library/std/src/fs.rs @@ -432,8 +432,9 @@ pub fn set_times>(path: P, times: FileTimes) -> io::Result<()> { /// /// # Platform-specific behavior /// -/// This function currently corresponds to the `utimensat` function with `AT_SYMLINK_NOFOLLOW` -/// on Unix platforms and the `SetFileTime` function on Windows after opening the symlink. +/// This function currently corresponds to the `utimensat` function with `AT_SYMLINK_NOFOLLOW` on +/// Unix platforms, the `setattrlist` function with `FSOPT_NOFOLLOW` on Apple platforms, and the +/// `SetFileTime` function on Windows. /// /// # Errors /// From 6308e76cd7d9d6b4b3912c875eabd3eb695afc5d Mon Sep 17 00:00:00 2001 From: Yukang Date: Thu, 9 Oct 2025 10:21:06 +0800 Subject: [PATCH 09/19] Update library/std/src/fs.rs Co-authored-by: Josh Triplett --- library/std/src/fs.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs index f374f4155cfd2..60eefc36ae2be 100644 --- a/library/std/src/fs.rs +++ b/library/std/src/fs.rs @@ -395,8 +395,8 @@ pub fn write, C: AsRef<[u8]>>(path: P, contents: C) -> io::Result /// /// # Platform-specific behavior /// -/// This function currently corresponds to the `utimensat` function on Unix platforms -/// and the `SetFileTime` function on Windows. +/// This function currently corresponds to the `utimensat` function on Unix platforms, the +/// `setattrlist` function on Apple platforms, and the `SetFileTime` function on Windows. /// /// # Errors /// From 1c5c8caad2401c923c1c8c515f004c52c8e74ed8 Mon Sep 17 00:00:00 2001 From: yukang Date: Thu, 9 Oct 2025 10:23:34 +0800 Subject: [PATCH 10/19] use proper unsupported --- library/std/src/sys/fs/solid.rs | 7 ++----- library/std/src/sys/fs/unix.rs | 2 ++ 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/library/std/src/sys/fs/solid.rs b/library/std/src/sys/fs/solid.rs index 39bd9b3cdd70b..f6d5d3b784d3b 100644 --- a/library/std/src/sys/fs/solid.rs +++ b/library/std/src/sys/fs/solid.rs @@ -539,14 +539,11 @@ pub fn set_perm(p: &Path, perm: FilePermissions) -> io::Result<()> { } pub fn set_times(_p: &Path, _times: FileTimes) -> io::Result<()> { - Err(io::const_error!(io::ErrorKind::Unsupported, "setting file times not supported",)) + unsupported() } pub fn set_times_nofollow(_p: &Path, _times: FileTimes) -> io::Result<()> { - Err(io::const_error!( - io::ErrorKind::Unsupported, - "setting file times on symlinks not supported", - )) + unsupported() } pub fn rmdir(p: &Path) -> io::Result<()> { diff --git a/library/std/src/sys/fs/unix.rs b/library/std/src/sys/fs/unix.rs index 578b5f4a1d986..014b10f06176c 100644 --- a/library/std/src/sys/fs/unix.rs +++ b/library/std/src/sys/fs/unix.rs @@ -1201,6 +1201,7 @@ impl fmt::Debug for OpenOptions { target_os = "horizon", target_os = "nuttx", )))] +#[inline(always)] fn to_timespec(time: Option) -> io::Result { match time { Some(time) if let Some(ts) = time.t.to_timespec() => Ok(ts), @@ -1217,6 +1218,7 @@ fn to_timespec(time: Option) -> io::Result { } #[cfg(target_vendor = "apple")] +#[inline(always)] fn set_attrlist_with_times( times: &FileTimes, ) -> io::Result<(libc::attrlist, [mem::MaybeUninit; 3], usize)> { From 46c6f0aadee016c28a0eb3d3fa977f33111e38ab Mon Sep 17 00:00:00 2001 From: yukang Date: Thu, 9 Oct 2025 10:49:13 +0800 Subject: [PATCH 11/19] rebase #147504 --- library/std/src/sys/fs/unix.rs | 73 ++++++---------------------------- 1 file changed, 13 insertions(+), 60 deletions(-) diff --git a/library/std/src/sys/fs/unix.rs b/library/std/src/sys/fs/unix.rs index 014b10f06176c..63a6db77324a9 100644 --- a/library/std/src/sys/fs/unix.rs +++ b/library/std/src/sys/fs/unix.rs @@ -1195,57 +1195,6 @@ impl fmt::Debug for OpenOptions { } } -#[cfg(not(any( - target_os = "redox", - target_os = "espidf", - target_os = "horizon", - target_os = "nuttx", -)))] -#[inline(always)] -fn to_timespec(time: Option) -> io::Result { - match time { - Some(time) if let Some(ts) = time.t.to_timespec() => Ok(ts), - Some(time) if time > crate::sys::time::UNIX_EPOCH => Err(io::const_error!( - io::ErrorKind::InvalidInput, - "timestamp is too large to set as a file time", - )), - Some(_) => Err(io::const_error!( - io::ErrorKind::InvalidInput, - "timestamp is too small to set as a file time", - )), - None => Ok(libc::timespec { tv_sec: 0, tv_nsec: libc::UTIME_OMIT as _ }), - } -} - -#[cfg(target_vendor = "apple")] -#[inline(always)] -fn set_attrlist_with_times( - times: &FileTimes, -) -> io::Result<(libc::attrlist, [mem::MaybeUninit; 3], usize)> { - let mut buf = [mem::MaybeUninit::::uninit(); 3]; - let mut num_times = 0; - let mut attrlist: libc::attrlist = unsafe { mem::zeroed() }; - attrlist.bitmapcount = libc::ATTR_BIT_MAP_COUNT; - - if times.created.is_some() { - buf[num_times].write(to_timespec(times.created)?); - num_times += 1; - attrlist.commonattr |= libc::ATTR_CMN_CRTIME; - } - if times.modified.is_some() { - buf[num_times].write(to_timespec(times.modified)?); - num_times += 1; - attrlist.commonattr |= libc::ATTR_CMN_MODTIME; - } - if times.accessed.is_some() { - buf[num_times].write(to_timespec(times.accessed)?); - num_times += 1; - attrlist.commonattr |= libc::ATTR_CMN_ACCTIME; - } - - Ok((attrlist, buf, num_times)) -} - impl File { pub fn open(path: &Path, opts: &OpenOptions) -> io::Result { run_path_with_cstr(path, &|path| File::open_c(path, opts)) @@ -1760,18 +1709,19 @@ impl TimesAttrlist { if times.created.is_some() { this.buf[this.num_times].write(file_time_to_timespec(times.created)?); this.num_times += 1; - attrlist.commonattr |= libc::ATTR_CMN_CRTIME; + this.attrlist.commonattr |= libc::ATTR_CMN_CRTIME; } if times.modified.is_some() { this.buf[this.num_times].write(file_time_to_timespec(times.modified)?); this.num_times += 1; - attrlist.commonattr |= libc::ATTR_CMN_MODTIME; + this.attrlist.commonattr |= libc::ATTR_CMN_MODTIME; } if times.accessed.is_some() { this.buf[this.num_times].write(file_time_to_timespec(times.accessed)?); this.num_times += 1; - attrlist.commonattr |= libc::ATTR_CMN_ACCTIME; + this.attrlist.commonattr |= libc::ATTR_CMN_ACCTIME; } + Ok(this) } fn attrlist(&self) -> *mut libc::c_void { @@ -2174,7 +2124,8 @@ fn set_times_impl(p: &CStr, times: FileTimes, flags: c_int) -> io::Result<()> { } target_vendor = "apple" => { // Apple platforms use setattrlist which supports setting times on symlinks - let (attrlist, buf, num_times) = set_attrlist_with_times(×)?; + //let (attrlist, buf, num_times) = set_attrlist_with_times(×)?; + let ta = TimesAttrlist::from_times(×)?; let options = if flags == libc::AT_SYMLINK_NOFOLLOW { libc::FSOPT_NOFOLLOW } else { @@ -2183,15 +2134,15 @@ fn set_times_impl(p: &CStr, times: FileTimes, flags: c_int) -> io::Result<()> { cvt(unsafe { libc::setattrlist( p.as_ptr(), - (&raw const attrlist).cast::().cast_mut(), - buf.as_ptr().cast::().cast_mut(), - num_times * size_of::(), + ta.attrlist(), + ta.times_buf(), + ta.times_buf_size(), options as u32 ) })?; Ok(()) } target_os = "android" => { - let times = [to_timespec(times.accessed)?, to_timespec(times.modified)?]; + let times = [file_time_to_timespec(times.accessed)?, file_time_to_timespec(times.modified)?]; // utimensat requires Android API level 19 cvt(unsafe { weak!( @@ -2225,18 +2176,20 @@ fn set_times_impl(p: &CStr, times: FileTimes, flags: c_int) -> io::Result<()> { return Ok(()); } } - let times = [to_timespec(times.accessed)?, to_timespec(times.modified)?]; + let times = [file_time_to_timespec(times.accessed)?, file_time_to_timespec(times.modified)?]; cvt(unsafe { libc::utimensat(libc::AT_FDCWD, p.as_ptr(), times.as_ptr(), flags) })?; Ok(()) } } } +#[inline(always)] pub fn set_times(p: &CStr, times: FileTimes) -> io::Result<()> { // flags = 0 means follow symlinks set_times_impl(p, times, 0) } +#[inline(always)] pub fn set_times_nofollow(p: &CStr, times: FileTimes) -> io::Result<()> { set_times_impl(p, times, libc::AT_SYMLINK_NOFOLLOW) } From 2438df75fe8a954f54479ce1a79cb9863f05d926 Mon Sep 17 00:00:00 2001 From: yukang Date: Thu, 9 Oct 2025 11:08:30 +0800 Subject: [PATCH 12/19] support fs::set_times for wasi --- library/std/src/sys/fs/wasi.rs | 55 +++++++++++++++++++++++----------- 1 file changed, 37 insertions(+), 18 deletions(-) diff --git a/library/std/src/sys/fs/wasi.rs b/library/std/src/sys/fs/wasi.rs index 1e6c0fad5b830..92eb35317415f 100644 --- a/library/std/src/sys/fs/wasi.rs +++ b/library/std/src/sys/fs/wasi.rs @@ -536,17 +536,9 @@ impl File { } pub fn set_times(&self, times: FileTimes) -> io::Result<()> { - let to_timestamp = |time: Option| match time { - Some(time) if let Some(ts) = time.to_wasi_timestamp() => Ok(ts), - Some(_) => Err(io::const_error!( - io::ErrorKind::InvalidInput, - "timestamp is too large to set as a file time", - )), - None => Ok(0), - }; self.fd.filestat_set_times( - to_timestamp(times.accessed)?, - to_timestamp(times.modified)?, + to_wasi_timestamp_or_now(times.accessed)?, + to_wasi_timestamp_or_now(times.modified)?, times.accessed.map_or(0, |_| wasi::FSTFLAGS_ATIM) | times.modified.map_or(0, |_| wasi::FSTFLAGS_MTIM), ) @@ -643,16 +635,43 @@ pub fn set_perm(_p: &Path, _perm: FilePermissions) -> io::Result<()> { unsupported() } -pub fn set_times(_p: &Path, _times: FileTimes) -> io::Result<()> { - // File times haven't been fully figured out in wasi yet, so this is - // likely temporary - unsupported() +#[inline(always)] +pub fn set_times(p: &Path, times: FileTimes) -> io::Result<()> { + let (dir, file) = open_parent(p)?; + set_times_impl(&dir, &file, times, wasi::LOOKUPFLAGS_SYMLINK_FOLLOW) } -pub fn set_times_nofollow(_p: &Path, _times: FileTimes) -> io::Result<()> { - // File times haven't been fully figured out in wasi yet, so this is - // likely temporary - unsupported() +#[inline(always)] +pub fn set_times_nofollow(p: &Path, times: FileTimes) -> io::Result<()> { + let (dir, file) = open_parent(p)?; + set_times_impl(&dir, &file, times, 0) +} + +fn to_wasi_timestamp_or_now(time: Option) -> io::Result { + match time { + Some(time) if let Some(ts) = time.to_wasi_timestamp() => Ok(ts), + Some(_) => Err(io::const_error!( + io::ErrorKind::InvalidInput, + "timestamp is too large to set as a file time", + )), + None => Ok(0), + } +} + +fn set_times_impl( + fd: &WasiFd, + path: &Path, + times: FileTimes, + flags: wasi::Lookupflags, +) -> io::Result<()> { + fd.path_filestat_set_times( + flags, + osstr2str(path.as_ref())?, + to_wasi_timestamp_or_now(times.accessed)?, + to_wasi_timestamp_or_now(times.modified)?, + times.accessed.map_or(0, |_| wasi::FSTFLAGS_ATIM) + | times.modified.map_or(0, |_| wasi::FSTFLAGS_MTIM), + ) } pub fn rmdir(p: &Path) -> io::Result<()> { From 1dd5641d74ca6d0fd873e7de9f5a63d12046d870 Mon Sep 17 00:00:00 2001 From: Yukang Date: Thu, 9 Oct 2025 11:37:48 +0800 Subject: [PATCH 13/19] add doc alias for set_times_nofollow Co-authored-by: Josh Triplett --- library/std/src/fs.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs index 60eefc36ae2be..9841a246d6a5d 100644 --- a/library/std/src/fs.rs +++ b/library/std/src/fs.rs @@ -458,6 +458,9 @@ pub fn set_times>(path: P, times: FileTimes) -> io::Result<()> { /// } /// ``` #[unstable(feature = "fs_set_times", issue = "147455")] +#[doc(alias = "utimensat")] +#[doc(alias = "lutimens")] +#[doc(alias = "lutimes")] pub fn set_times_nofollow>(path: P, times: FileTimes) -> io::Result<()> { fs_imp::set_times_nofollow(path.as_ref(), times.0) } From 6fd1c2bd9456cfa69812c7b35132b6f4ce7569f0 Mon Sep 17 00:00:00 2001 From: Yukang Date: Thu, 9 Oct 2025 11:38:19 +0800 Subject: [PATCH 14/19] add doc alias for set_times Co-authored-by: Josh Triplett --- library/std/src/fs.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs index 9841a246d6a5d..b548eb4939d42 100644 --- a/library/std/src/fs.rs +++ b/library/std/src/fs.rs @@ -420,6 +420,9 @@ pub fn write, C: AsRef<[u8]>>(path: P, contents: C) -> io::Result /// } /// ``` #[unstable(feature = "fs_set_times", issue = "147455")] +#[doc(alias = "utimens")] +#[doc(alias = "utimes")] +#[doc(alias = "utime")] pub fn set_times>(path: P, times: FileTimes) -> io::Result<()> { fs_imp::set_times(path.as_ref(), times.0) } From 901366af1e673a260d1a20551b0199540ae4b455 Mon Sep 17 00:00:00 2001 From: yukang Date: Thu, 9 Oct 2025 16:42:54 +0800 Subject: [PATCH 15/19] fix c_char error in Android --- library/std/src/sys/fs/unix.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/std/src/sys/fs/unix.rs b/library/std/src/sys/fs/unix.rs index 63a6db77324a9..51849a31f61b5 100644 --- a/library/std/src/sys/fs/unix.rs +++ b/library/std/src/sys/fs/unix.rs @@ -2146,7 +2146,7 @@ fn set_times_impl(p: &CStr, times: FileTimes, flags: c_int) -> io::Result<()> { // utimensat requires Android API level 19 cvt(unsafe { weak!( - fn utimensat(dirfd: c_int, path: *const c_char, times: *const libc::timespec, flags: c_int) -> c_int; + fn utimensat(dirfd: c_int, path: *const libc::c_char, times: *const libc::timespec, flags: c_int) -> c_int; ); match utimensat.get() { Some(utimensat) => utimensat(libc::AT_FDCWD, p.as_ptr(), times.as_ptr(), flags), From f8118d88d7bdb5f697cf4712e01f6b46fe2bc742 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Thu, 9 Oct 2025 06:36:51 -0700 Subject: [PATCH 16/19] unsupported: Use `unsupported()` for `set_times` --- library/std/src/sys/fs/unsupported.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/std/src/sys/fs/unsupported.rs b/library/std/src/sys/fs/unsupported.rs index 659ea2a8fc276..7901bf5624d74 100644 --- a/library/std/src/sys/fs/unsupported.rs +++ b/library/std/src/sys/fs/unsupported.rs @@ -312,8 +312,8 @@ pub fn set_perm(_p: &Path, perm: FilePermissions) -> io::Result<()> { match perm.0 {} } -pub fn set_times(_p: &Path, times: FileTimes) -> io::Result<()> { - match times {} +pub fn set_times(_p: &Path, _times: FileTimes) -> io::Result<()> { + unsupported() } pub fn set_times_nofollow(_p: &Path, times: FileTimes) -> io::Result<()> { From d2f590a6d598dc75b09fd9d97c5bb32d23a6971a Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Thu, 9 Oct 2025 06:37:09 -0700 Subject: [PATCH 17/19] unsupported: Use `unsupported()` for `set_times_nofollow` --- library/std/src/sys/fs/unsupported.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/std/src/sys/fs/unsupported.rs b/library/std/src/sys/fs/unsupported.rs index 7901bf5624d74..f222151d18e25 100644 --- a/library/std/src/sys/fs/unsupported.rs +++ b/library/std/src/sys/fs/unsupported.rs @@ -316,8 +316,8 @@ pub fn set_times(_p: &Path, _times: FileTimes) -> io::Result<()> { unsupported() } -pub fn set_times_nofollow(_p: &Path, times: FileTimes) -> io::Result<()> { - match times {} +pub fn set_times_nofollow(_p: &Path, _times: FileTimes) -> io::Result<()> { + unsupported() } pub fn rmdir(_p: &Path) -> io::Result<()> { From 6a12470210aefa46d6078e27bcd20f6ef896c93a Mon Sep 17 00:00:00 2001 From: cyrgani Date: Fri, 10 Oct 2025 22:19:04 +0200 Subject: [PATCH 18/19] rename `DecodeMut` to `Decode` --- library/proc_macro/src/bridge/client.rs | 6 ++--- library/proc_macro/src/bridge/mod.rs | 4 +-- library/proc_macro/src/bridge/rpc.rs | 36 ++++++++++++------------- library/proc_macro/src/bridge/server.rs | 10 +++---- library/proc_macro/src/bridge/symbol.rs | 4 +-- 5 files changed, 30 insertions(+), 30 deletions(-) diff --git a/library/proc_macro/src/bridge/client.rs b/library/proc_macro/src/bridge/client.rs index 92558f2b7d9cc..bdaa865a998d6 100644 --- a/library/proc_macro/src/bridge/client.rs +++ b/library/proc_macro/src/bridge/client.rs @@ -58,7 +58,7 @@ macro_rules! define_client_handles { } } - impl DecodeMut<'_, '_, S> for $oty { + impl Decode<'_, '_, S> for $oty { fn decode(r: &mut Reader<'_>, s: &mut S) -> Self { $oty { handle: handle::Handle::decode(r, s), @@ -82,7 +82,7 @@ macro_rules! define_client_handles { } } - impl DecodeMut<'_, '_, S> for $ity { + impl Decode<'_, '_, S> for $ity { fn decode(r: &mut Reader<'_>, s: &mut S) -> Self { $ity { handle: handle::Handle::decode(r, s), @@ -276,7 +276,7 @@ fn maybe_install_panic_hook(force_show_panics: bool) { /// Client-side helper for handling client panics, entering the bridge, /// deserializing input and serializing output. // FIXME(eddyb) maybe replace `Bridge::enter` with this? -fn run_client DecodeMut<'a, 's, ()>, R: Encode<()>>( +fn run_client Decode<'a, 's, ()>, R: Encode<()>>( config: BridgeConfig<'_>, f: impl FnOnce(A) -> R, ) -> Buffer { diff --git a/library/proc_macro/src/bridge/mod.rs b/library/proc_macro/src/bridge/mod.rs index 582c43c78fcbb..b0ee9c0cc3027 100644 --- a/library/proc_macro/src/bridge/mod.rs +++ b/library/proc_macro/src/bridge/mod.rs @@ -143,7 +143,7 @@ mod symbol; use buffer::Buffer; pub use rpc::PanicMessage; -use rpc::{DecodeMut, Encode, Reader, Writer}; +use rpc::{Decode, Encode, Reader, Writer}; /// Configuration for establishing an active connection between a server and a /// client. The server creates the bridge config (`run_server` in `server.rs`), @@ -168,7 +168,7 @@ impl !Sync for BridgeConfig<'_> {} #[forbid(unsafe_code)] #[allow(non_camel_case_types)] mod api_tags { - use super::rpc::{DecodeMut, Encode, Reader, Writer}; + use super::rpc::{Decode, Encode, Reader, Writer}; macro_rules! declare_tags { ($($name:ident { diff --git a/library/proc_macro/src/bridge/rpc.rs b/library/proc_macro/src/bridge/rpc.rs index 6e5afe282d683..ed67674a74ab7 100644 --- a/library/proc_macro/src/bridge/rpc.rs +++ b/library/proc_macro/src/bridge/rpc.rs @@ -12,7 +12,7 @@ pub(super) trait Encode: Sized { pub(super) type Reader<'a> = &'a [u8]; -pub(super) trait DecodeMut<'a, 's, S>: Sized { +pub(super) trait Decode<'a, 's, S>: Sized { fn decode(r: &mut Reader<'a>, s: &'s mut S) -> Self; } @@ -24,7 +24,7 @@ macro_rules! rpc_encode_decode { } } - impl DecodeMut<'_, '_, S> for $ty { + impl Decode<'_, '_, S> for $ty { fn decode(r: &mut Reader<'_>, _: &mut S) -> Self { const N: usize = size_of::<$ty>(); @@ -43,12 +43,12 @@ macro_rules! rpc_encode_decode { } } - impl<'a, S, $($($T: for<'s> DecodeMut<'a, 's, S>),+)?> DecodeMut<'a, '_, S> + impl<'a, S, $($($T: for<'s> Decode<'a, 's, S>),+)?> Decode<'a, '_, S> for $name $(<$($T),+>)? { fn decode(r: &mut Reader<'a>, s: &mut S) -> Self { $name { - $($field: DecodeMut::decode(r, s)),* + $($field: Decode::decode(r, s)),* } } } @@ -69,7 +69,7 @@ macro_rules! rpc_encode_decode { } } - impl<'a, S, $($($T: for<'s> DecodeMut<'a, 's, S>),+)?> DecodeMut<'a, '_, S> + impl<'a, S, $($($T: for<'s> Decode<'a, 's, S>),+)?> Decode<'a, '_, S> for $name $(<$($T),+>)? { fn decode(r: &mut Reader<'a>, s: &mut S) -> Self { @@ -84,7 +84,7 @@ macro_rules! rpc_encode_decode { match u8::decode(r, s) { $(tag::$variant => { - $(let $field = DecodeMut::decode(r, s);)* + $(let $field = Decode::decode(r, s);)* $name::$variant $(($field))* })* _ => unreachable!(), @@ -98,7 +98,7 @@ impl Encode for () { fn encode(self, _: &mut Writer, _: &mut S) {} } -impl DecodeMut<'_, '_, S> for () { +impl Decode<'_, '_, S> for () { fn decode(_: &mut Reader<'_>, _: &mut S) -> Self {} } @@ -108,7 +108,7 @@ impl Encode for u8 { } } -impl DecodeMut<'_, '_, S> for u8 { +impl Decode<'_, '_, S> for u8 { fn decode(r: &mut Reader<'_>, _: &mut S) -> Self { let x = r[0]; *r = &r[1..]; @@ -125,7 +125,7 @@ impl Encode for bool { } } -impl DecodeMut<'_, '_, S> for bool { +impl Decode<'_, '_, S> for bool { fn decode(r: &mut Reader<'_>, s: &mut S) -> Self { match u8::decode(r, s) { 0 => false, @@ -141,7 +141,7 @@ impl Encode for char { } } -impl DecodeMut<'_, '_, S> for char { +impl Decode<'_, '_, S> for char { fn decode(r: &mut Reader<'_>, s: &mut S) -> Self { char::from_u32(u32::decode(r, s)).unwrap() } @@ -153,7 +153,7 @@ impl Encode for NonZero { } } -impl DecodeMut<'_, '_, S> for NonZero { +impl Decode<'_, '_, S> for NonZero { fn decode(r: &mut Reader<'_>, s: &mut S) -> Self { Self::new(u32::decode(r, s)).unwrap() } @@ -166,11 +166,11 @@ impl, B: Encode> Encode for (A, B) { } } -impl<'a, S, A: for<'s> DecodeMut<'a, 's, S>, B: for<'s> DecodeMut<'a, 's, S>> DecodeMut<'a, '_, S> +impl<'a, S, A: for<'s> Decode<'a, 's, S>, B: for<'s> Decode<'a, 's, S>> Decode<'a, '_, S> for (A, B) { fn decode(r: &mut Reader<'a>, s: &mut S) -> Self { - (DecodeMut::decode(r, s), DecodeMut::decode(r, s)) + (Decode::decode(r, s), Decode::decode(r, s)) } } @@ -181,7 +181,7 @@ impl Encode for &[u8] { } } -impl<'a, S> DecodeMut<'a, '_, S> for &'a [u8] { +impl<'a, S> Decode<'a, '_, S> for &'a [u8] { fn decode(r: &mut Reader<'a>, s: &mut S) -> Self { let len = usize::decode(r, s); let xs = &r[..len]; @@ -196,7 +196,7 @@ impl Encode for &str { } } -impl<'a, S> DecodeMut<'a, '_, S> for &'a str { +impl<'a, S> Decode<'a, '_, S> for &'a str { fn decode(r: &mut Reader<'a>, s: &mut S) -> Self { str::from_utf8(<&[u8]>::decode(r, s)).unwrap() } @@ -208,7 +208,7 @@ impl Encode for String { } } -impl DecodeMut<'_, '_, S> for String { +impl Decode<'_, '_, S> for String { fn decode(r: &mut Reader<'_>, s: &mut S) -> Self { <&str>::decode(r, s).to_string() } @@ -223,7 +223,7 @@ impl> Encode for Vec { } } -impl<'a, S, T: for<'s> DecodeMut<'a, 's, S>> DecodeMut<'a, '_, S> for Vec { +impl<'a, S, T: for<'s> Decode<'a, 's, S>> Decode<'a, '_, S> for Vec { fn decode(r: &mut Reader<'a>, s: &mut S) -> Self { let len = usize::decode(r, s); let mut vec = Vec::with_capacity(len); @@ -283,7 +283,7 @@ impl Encode for PanicMessage { } } -impl DecodeMut<'_, '_, S> for PanicMessage { +impl Decode<'_, '_, S> for PanicMessage { fn decode(r: &mut Reader<'_>, s: &mut S) -> Self { match Option::::decode(r, s) { Some(s) => PanicMessage::String(s), diff --git a/library/proc_macro/src/bridge/server.rs b/library/proc_macro/src/bridge/server.rs index 2850e1099b700..e9ef26c07f24f 100644 --- a/library/proc_macro/src/bridge/server.rs +++ b/library/proc_macro/src/bridge/server.rs @@ -32,7 +32,7 @@ macro_rules! define_server_handles { } } - impl DecodeMut<'_, '_, HandleStore>> + impl Decode<'_, '_, HandleStore>> for Marked { fn decode(r: &mut Reader<'_>, s: &mut HandleStore>) -> Self { @@ -40,7 +40,7 @@ macro_rules! define_server_handles { } } - impl<'s, S: Types> DecodeMut<'_, 's, HandleStore>> + impl<'s, S: Types> Decode<'_, 's, HandleStore>> for &'s Marked { fn decode(r: &mut Reader<'_>, s: &'s mut HandleStore>) -> Self { @@ -48,7 +48,7 @@ macro_rules! define_server_handles { } } - impl<'s, S: Types> DecodeMut<'_, 's, HandleStore>> + impl<'s, S: Types> Decode<'_, 's, HandleStore>> for &'s mut Marked { fn decode( @@ -67,7 +67,7 @@ macro_rules! define_server_handles { } } - impl DecodeMut<'_, '_, HandleStore>> + impl Decode<'_, '_, HandleStore>> for Marked { fn decode(r: &mut Reader<'_>, s: &mut HandleStore>) -> Self { @@ -355,7 +355,7 @@ pub trait MessagePipe: Sized { fn run_server< S: Server, I: Encode>>, - O: for<'a, 's> DecodeMut<'a, 's, HandleStore>>, + O: for<'a, 's> Decode<'a, 's, HandleStore>>, >( strategy: &impl ExecutionStrategy, handle_counters: &'static client::HandleCounters, diff --git a/library/proc_macro/src/bridge/symbol.rs b/library/proc_macro/src/bridge/symbol.rs index eb7d30f9a6cc9..0d6a725fddd98 100644 --- a/library/proc_macro/src/bridge/symbol.rs +++ b/library/proc_macro/src/bridge/symbol.rs @@ -102,7 +102,7 @@ impl Encode for Symbol { } } -impl DecodeMut<'_, '_, server::HandleStore>> +impl Decode<'_, '_, server::HandleStore>> for Marked { fn decode(r: &mut Reader<'_>, s: &mut server::HandleStore>) -> Self { @@ -118,7 +118,7 @@ impl Encode>> } } -impl DecodeMut<'_, '_, S> for Symbol { +impl Decode<'_, '_, S> for Symbol { fn decode(r: &mut Reader<'_>, s: &mut S) -> Self { Symbol::new(<&str>::decode(r, s)) } From 04da682613b1849893cff7cb31eda4429ff5633f Mon Sep 17 00:00:00 2001 From: Tropical <42101043+tropicaaal@users.noreply.github.com> Date: Sat, 11 Oct 2025 14:42:26 -0500 Subject: [PATCH 19/19] vexos: implement `pal::os::exit` --- library/std/src/sys/pal/vexos/mod.rs | 1 - library/std/src/sys/pal/vexos/os.rs | 19 +++++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 library/std/src/sys/pal/vexos/os.rs diff --git a/library/std/src/sys/pal/vexos/mod.rs b/library/std/src/sys/pal/vexos/mod.rs index 61a34b0f68a30..3a37c8f2d4885 100644 --- a/library/std/src/sys/pal/vexos/mod.rs +++ b/library/std/src/sys/pal/vexos/mod.rs @@ -1,4 +1,3 @@ -#[path = "../unsupported/os.rs"] pub mod os; #[path = "../unsupported/pipe.rs"] pub mod pipe; diff --git a/library/std/src/sys/pal/vexos/os.rs b/library/std/src/sys/pal/vexos/os.rs new file mode 100644 index 0000000000000..405f7c918f4a5 --- /dev/null +++ b/library/std/src/sys/pal/vexos/os.rs @@ -0,0 +1,19 @@ +#[expect(dead_code)] +#[path = "../unsupported/os.rs"] +mod unsupported_os; +pub use unsupported_os::{ + JoinPathsError, SplitPaths, chdir, current_exe, errno, error_string, getcwd, getpid, home_dir, + join_paths, split_paths, temp_dir, +}; + +pub use super::unsupported; + +pub fn exit(_code: i32) -> ! { + unsafe { + vex_sdk::vexSystemExitRequest(); + + loop { + vex_sdk::vexTasksRun(); + } + } +}