diff --git a/Cargo.lock b/Cargo.lock index e661d810eab82..bcb7dd1b25bcb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5263,9 +5263,9 @@ dependencies = [ [[package]] name = "stringdex" -version = "0.0.1-alpha10" +version = "0.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fa846a7d509d1828a4f90962dc09810e161abcada7fc6a921e92c168d0811d7" +checksum = "18b3bd4f10d15ef859c40291769f0d85209de6b0f1c30713ff9cdf45ac43ea36" dependencies = [ "stacker", ] diff --git a/compiler/rustc_codegen_ssa/src/mir/locals.rs b/compiler/rustc_codegen_ssa/src/mir/locals.rs index 93f0ab36f2a2f..5d586d97641d2 100644 --- a/compiler/rustc_codegen_ssa/src/mir/locals.rs +++ b/compiler/rustc_codegen_ssa/src/mir/locals.rs @@ -40,12 +40,12 @@ impl<'tcx, V> Locals<'tcx, V> { impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { pub(super) fn initialize_locals(&mut self, values: Vec>) { assert!(self.locals.values.is_empty()); + self.locals.values = IndexVec::from_raw(values); // FIXME(#115215): After #115025 get's merged this might not be necessary - for (local, value) in values.into_iter().enumerate() { + for (local, value) in self.locals.values.iter_enumerated() { match value { LocalRef::Place(_) | LocalRef::UnsizedPlace(_) | LocalRef::PendingOperand => (), LocalRef::Operand(op) => { - let local = mir::Local::from_usize(local); let expected_ty = self.monomorphize(self.mir.local_decls[local].ty); if expected_ty != op.layout.ty { warn!( @@ -56,7 +56,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } } } - self.locals.values.push(value); } } diff --git a/compiler/rustc_const_eval/src/check_consts/qualifs.rs b/compiler/rustc_const_eval/src/check_consts/qualifs.rs index b63e929387e61..5b65f1726f6c2 100644 --- a/compiler/rustc_const_eval/src/check_consts/qualifs.rs +++ b/compiler/rustc_const_eval/src/check_consts/qualifs.rs @@ -234,7 +234,7 @@ where Rvalue::Discriminant(place) => in_place::(cx, in_local, place.as_ref()), - Rvalue::CopyForDeref(_) => bug!("`CopyForDeref` in runtime MIR"), + Rvalue::CopyForDeref(place) => in_place::(cx, in_local, place.as_ref()), Rvalue::Use(operand) | Rvalue::Repeat(operand, _) diff --git a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs index 82a2a64f23078..6926546abf776 100644 --- a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs +++ b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs @@ -10,8 +10,8 @@ use rustc_hir as hir; use rustc_hir::LangItem; use rustc_middle::bug; use rustc_middle::ty::{ - self, ExistentialPredicateStableCmpExt as _, Instance, InstanceKind, IntTy, List, TraitRef, Ty, - TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt, UintTy, + self, ExistentialPredicateStableCmpExt as _, Instance, IntTy, List, TraitRef, Ty, TyCtxt, + TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt, UintTy, }; use rustc_span::def_id::DefId; use rustc_span::{DUMMY_SP, sym}; @@ -458,6 +458,30 @@ pub(crate) fn transform_instance<'tcx>( instance } +fn default_or_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) -> Option { + match instance.def { + ty::InstanceKind::Item(def_id) | ty::InstanceKind::FnPtrShim(def_id, _) => { + tcx.opt_associated_item(def_id).map(|item| item.def_id) + } + _ => None, + } +} + +/// Determines if an instance represents a trait method implementation and returns the necessary +/// information for type erasure. +/// +/// This function handles two main cases: +/// +/// * **Implementation in an `impl` block**: When the instance represents a concrete implementation +/// of a trait method in an `impl` block, it extracts the trait reference, method ID, and trait +/// ID from the implementation. The method ID is obtained from the `trait_item_def_id` field of +/// the associated item, which points to the original trait method definition. +/// +/// * **Provided method in a `trait` block or synthetic `shim`**: When the instance represents a +/// default implementation provided in the trait definition itself or a synthetic shim, it uses +/// the instance's own `def_id` as the method ID and determines the trait ID from the associated +/// item. +/// fn implemented_method<'tcx>( tcx: TyCtxt<'tcx>, instance: Instance<'tcx>, @@ -473,11 +497,9 @@ fn implemented_method<'tcx>( trait_method = tcx.associated_item(method_id); trait_id = trait_ref.skip_binder().def_id; impl_id - } else if let InstanceKind::Item(def_id) = instance.def - && let Some(trait_method_bound) = tcx.opt_associated_item(def_id) - { - // Provided method in a `trait` block - trait_method = trait_method_bound; + } else if let Some(trait_method_def_id) = default_or_shim(tcx, instance) { + // Provided method in a `trait` block or a synthetic `shim` + trait_method = tcx.associated_item(trait_method_def_id); method_id = instance.def_id(); trait_id = tcx.trait_of_assoc(method_id)?; trait_ref = ty::EarlyBinder::bind(TraitRef::from_assoc(tcx, trait_id, instance.args)); diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs index 28b2c7173d321..b548eb4939d42 100644 --- a/library/std/src/fs.rs +++ b/library/std/src/fs.rs @@ -387,6 +387,87 @@ 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, the +/// `setattrlist` function on Apple 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")] +#[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) +} + +/// 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, the `setattrlist` function with `FSOPT_NOFOLLOW` on Apple 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_nofollow("symlink.txt", times)?; +/// Ok(()) +/// } +/// ``` +#[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) +} + #[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 403bfb2d9b929..74354227d84a6 100644 --- a/library/std/src/sys/fs/mod.rs +++ b/library/std/src/sys/fs/mod.rs @@ -165,3 +165,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..f6d5d3b784d3b 100644 --- a/library/std/src/sys/fs/solid.rs +++ b/library/std/src/sys/fs/solid.rs @@ -538,6 +538,14 @@ pub fn set_perm(p: &Path, perm: FilePermissions) -> io::Result<()> { Ok(()) } +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<()> { 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 33a1e7ff5e40e..d9a7fcb0e2d39 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`. @@ -1634,36 +1616,18 @@ 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(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; - } + 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(()) } 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 +1661,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 +1669,74 @@ 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 _ }), + } +} + +#[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; + 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; + 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; + this.attrlist.commonattr |= libc::ATTR_CMN_ACCTIME; + } + Ok(this) + } + + 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 { DirBuilder { mode: 0o777 } @@ -2081,6 +2113,87 @@ fn open_from(from: &Path) -> io::Result<(crate::fs::File, crate::fs::Metadata)> Ok((reader, metadata)) } +fn set_times_impl(p: &CStr, times: FileTimes, follow_symlinks: bool) -> io::Result<()> { + cfg_select! { + any(target_os = "redox", target_os = "espidf", target_os = "horizon", target_os = "nuttx") => { + let _ = (p, times, follow_symlinks); + 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 ta = TimesAttrlist::from_times(×)?; + let options = if follow_symlinks { + 0 + } else { + libc::FSOPT_NOFOLLOW + }; + + cvt(unsafe { libc::setattrlist( + p.as_ptr(), + ta.attrlist(), + ta.times_buf(), + ta.times_buf_size(), + options as u32 + ) })?; + Ok(()) + } + target_os = "android" => { + let times = [file_time_to_timespec(times.accessed)?, file_time_to_timespec(times.modified)?]; + let flags = if follow_symlinks { 0 } else { libc::AT_SYMLINK_NOFOLLOW }; + // utimensat requires Android API level 19 + cvt(unsafe { + weak!( + 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), + None => return Err(io::const_error!( + io::ErrorKind::Unsupported, + "setting file times requires Android API level >= 19", + )), + } + })?; + Ok(()) + } + _ => { + let flags = if follow_symlinks { 0 } else { libc::AT_SYMLINK_NOFOLLOW }; + #[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 = [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<()> { + set_times_impl(p, times, true) +} + +#[inline(always)] +pub fn set_times_nofollow(p: &CStr, times: FileTimes) -> io::Result<()> { + set_times_impl(p, times, false) +} + #[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..f222151d18e25 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<()> { + 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/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..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,6 +635,45 @@ pub fn set_perm(_p: &Path, _perm: FilePermissions) -> io::Result<()> { 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) +} + +#[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<()> { 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 { diff --git a/src/bootstrap/Cargo.lock b/src/bootstrap/Cargo.lock index fe832652d25ce..5a2adb206b856 100644 --- a/src/bootstrap/Cargo.lock +++ b/src/bootstrap/Cargo.lock @@ -491,9 +491,9 @@ dependencies = [ [[package]] name = "object" -version = "0.36.5" +version = "0.37.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aedf0a2d09c573ed1d8d85b30c119153926a2b36dce0ab28322c09a117a4683e" +checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe" dependencies = [ "memchr", ] diff --git a/src/bootstrap/Cargo.toml b/src/bootstrap/Cargo.toml index 9a76a7dda2ac3..e1725db60cfca 100644 --- a/src/bootstrap/Cargo.toml +++ b/src/bootstrap/Cargo.toml @@ -41,7 +41,7 @@ clap_complete = "4.4" home = "0.5" ignore = "0.4" libc = "0.2" -object = { version = "0.36.3", default-features = false, features = ["archive", "coff", "read_core", "std", "unaligned"] } +object = { version = "0.37", default-features = false, features = ["archive", "coff", "read_core", "std", "unaligned"] } opener = "0.8" semver = "1.0" serde = "1.0" diff --git a/src/librustdoc/Cargo.toml b/src/librustdoc/Cargo.toml index f9c2465fb3c38..63412e2b9373d 100644 --- a/src/librustdoc/Cargo.toml +++ b/src/librustdoc/Cargo.toml @@ -21,7 +21,7 @@ rustdoc-json-types = { path = "../rustdoc-json-types" } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" smallvec = "1.8.1" -stringdex = { version = "0.0.1-alpha10" } +stringdex = "=0.0.2" tempfile = "3" threadpool = "1.8.1" tracing = "0.1" diff --git a/src/librustdoc/html/static/js/stringdex.js b/src/librustdoc/html/static/js/stringdex.js index 6299576d56439..d8b8c5bd70c13 100644 --- a/src/librustdoc/html/static/js/stringdex.js +++ b/src/librustdoc/html/static/js/stringdex.js @@ -1447,7 +1447,7 @@ function loadDatabase(hooks) { makeSearchTreeBranchesAlphaBitmapClass(LONG_ALPHABITMAP_CHARS, 4); /** - * @typedef {PrefixSearchTree|SuffixSearchTree} SearchTree + * @typedef {PrefixSearchTree|SuffixSearchTree|InlineNeighborsTree} SearchTree * @typedef {PrefixTrie|SuffixTrie} Trie */ @@ -1675,9 +1675,12 @@ function loadDatabase(hooks) { yield leaves; } } - /** @type {HashTable<[number, SearchTree][]>} */ + /** @type {HashTable<[number, PrefixSearchTree|SuffixSearchTree][]>} */ const subnodes = new HashTable(); - for await (const node of current_layer) { + for await (const nodeEncoded of current_layer) { + const node = nodeEncoded instanceof InlineNeighborsTree ? + nodeEncoded.decode() : + nodeEncoded; const branches = node.branches; const l = branches.subtrees.length; for (let i = 0; i < l; ++i) { @@ -1741,7 +1744,10 @@ function loadDatabase(hooks) { // we then yield the smallest ones (can't yield bigger ones // if we want to do them in order) for (const {node, len} of current_layer) { - const tree = await node; + const treeEncoded = await node; + const tree = treeEncoded instanceof InlineNeighborsTree ? + treeEncoded.decode() : + treeEncoded; if (!(tree instanceof PrefixSearchTree)) { continue; } @@ -1804,7 +1810,10 @@ function loadDatabase(hooks) { /** @type {HashTable<{byte: number, tree: PrefixSearchTree, len: number}[]>} */ const subnodes = new HashTable(); for await (const {node, len} of current_layer) { - const tree = await node; + const treeEncoded = await node; + const tree = treeEncoded instanceof InlineNeighborsTree ? + treeEncoded.decode() : + treeEncoded; if (!(tree instanceof PrefixSearchTree)) { continue; } @@ -2166,9 +2175,12 @@ function loadDatabase(hooks) { yield leaves; } } - /** @type {HashTable<[number, SearchTree][]>} */ + /** @type {HashTable<[number, PrefixSearchTree|SuffixSearchTree][]>} */ const subnodes = new HashTable(); - for await (const node of current_layer) { + for await (const nodeEncoded of current_layer) { + const node = nodeEncoded instanceof InlineNeighborsTree ? + nodeEncoded.decode() : + nodeEncoded; const branches = node.branches; const l = branches.subtrees.length; for (let i = 0; i < l; ++i) { @@ -2264,6 +2276,174 @@ function loadDatabase(hooks) { } } + /** + * Represents a subtree where all transitive leaves + * have a shared 16bit prefix and there are no sub-branches. + */ + class InlineNeighborsTree { + /** + * @param {Uint8Array} encoded + * @param {number} start + */ + constructor( + encoded, + start, + ) { + this.encoded = encoded; + this.start = start; + } + /** + * @return {PrefixSearchTree|SuffixSearchTree} + */ + decode() { + let i = this.start; + const encoded = this.encoded; + const has_branches = (encoded[i] & 0x04) !== 0; + /** @type {boolean} */ + const is_suffixes_only = (encoded[i] & 0x01) !== 0; + let leaves_count = ((encoded[i] >> 4) & 0x0f) + 1; + i += 1; + let branch_count = 0; + if (has_branches) { + branch_count = encoded[i] + 1; + i += 1; + } + const dlen = encoded[i] & 0x3f; + if ((encoded[i] & 0x80) !== 0) { + leaves_count = 0; + } + i += 1; + let data = EMPTY_UINT8; + if (!is_suffixes_only && dlen !== 0) { + data = encoded.subarray(i, i + dlen); + i += dlen; + } + const leaf_value_upper = encoded[i] | (encoded[i + 1] << 8); + i += 2; + /** @type {Promise[]} */ + const branch_nodes = []; + for (let j = 0; j < branch_count; j += 1) { + const branch_dlen = encoded[i] & 0x0f; + const branch_leaves_count = ((encoded[i] >> 4) & 0x0f) + 1; + i += 1; + let branch_data = EMPTY_UINT8; + if (!is_suffixes_only && branch_dlen !== 0) { + branch_data = encoded.subarray(i, i + branch_dlen); + i += branch_dlen; + } + const branch_leaves = new RoaringBitmap(null); + branch_leaves.keysAndCardinalities = Uint8Array.of( + leaf_value_upper & 0xff, + (leaf_value_upper >> 8) & 0xff, + (branch_leaves_count - 1) & 0xff, + ((branch_leaves_count - 1) >> 8) & 0xff, + ); + branch_leaves.containers = [ + new RoaringBitmapArray( + branch_leaves_count, + encoded.subarray(i, i + (branch_leaves_count * 2)), + ), + ]; + i += branch_leaves_count * 2; + branch_nodes.push(Promise.resolve( + is_suffixes_only ? + new SuffixSearchTree( + EMPTY_SEARCH_TREE_BRANCHES, + branch_dlen, + branch_leaves, + ) : + new PrefixSearchTree( + EMPTY_SEARCH_TREE_BRANCHES, + EMPTY_SEARCH_TREE_BRANCHES, + branch_data, + branch_leaves, + EMPTY_BITMAP, + ), + )); + } + /** @type {SearchTreeBranchesArray} */ + const branches = branch_count === 0 ? + EMPTY_SEARCH_TREE_BRANCHES : + new SearchTreeBranchesArray( + encoded.subarray(i, i + branch_count), + EMPTY_UINT8, + ); + i += branch_count; + branches.subtrees = branch_nodes; + let leaves = EMPTY_BITMAP; + if (leaves_count !== 0) { + leaves = new RoaringBitmap(null); + leaves.keysAndCardinalities = Uint8Array.of( + leaf_value_upper & 0xff, + (leaf_value_upper >> 8) & 0xff, + (leaves_count - 1) & 0xff, + ((leaves_count - 1) >> 8) & 0xff, + ); + leaves.containers = [ + new RoaringBitmapArray( + leaves_count, + encoded.subarray(i, i + (leaves_count * 2)), + ), + ]; + i += leaves_count * 2; + } + return is_suffixes_only ? + new SuffixSearchTree( + branches, + dlen, + leaves, + ) : + new PrefixSearchTree( + branches, + branches, + data, + leaves, + EMPTY_BITMAP, + ); + } + + /** + * Returns the Trie for the root node. + * + * A Trie pointer refers to a single node in a logical decompressed search tree + * (the real search tree is compressed). + * + * @param {DataColumn} dataColumn + * @param {Uint8ArraySearchPattern} searchPattern + * @return {Trie} + */ + trie(dataColumn, searchPattern) { + const tree = this.decode(); + return tree instanceof SuffixSearchTree ? + new SuffixTrie(tree, 0, dataColumn, searchPattern) : + new PrefixTrie(tree, 0, dataColumn, searchPattern); + } + + /** + * Return the trie representing `name` + * @param {Uint8Array|string} name + * @param {DataColumn} dataColumn + * @returns {Promise} + */ + search(name, dataColumn) { + return this.decode().search(name, dataColumn); + } + + /** + * @param {Uint8Array|string} name + * @param {DataColumn} dataColumn + * @returns {AsyncGenerator} + */ + searchLev(name, dataColumn) { + return this.decode().searchLev(name, dataColumn); + } + + /** @returns {RoaringBitmap} */ + getCurrentLeaves() { + return this.decode().getCurrentLeaves(); + } + } + class DataColumn { /** * Construct the wrapper object for a data column. @@ -2765,21 +2945,37 @@ function loadDatabase(hooks) { // because that's the canonical, hashed version of the data let compression_tag = input[i]; const is_pure_suffixes_only_node = (compression_tag & 0x01) !== 0; - let no_leaves_flag; - if (compression_tag > 1) { - // compressed node - const is_long_compressed = (compression_tag & 0x04) !== 0; - const is_data_compressed = (compression_tag & 0x08) !== 0; + const is_long_compressed = (compression_tag & 0x04) !== 0; + const is_data_compressed = (compression_tag & 0x08) !== 0; + i += 1; + if (is_long_compressed) { + compression_tag |= input[i] << 8; i += 1; - if (is_long_compressed) { - compression_tag |= input[i] << 8; - i += 1; - compression_tag |= input[i] << 16; - i += 1; - } - let dlen = input[i] & 0x7F; + } + /** @type {number} */ + let dlen; + /** @type {number} */ + let no_leaves_flag; + /** @type {number} */ + let inline_neighbors_flag; + if (is_data_compressed && is_pure_suffixes_only_node) { + dlen = 0; + no_leaves_flag = 0x80; + inline_neighbors_flag = 0; + } else { + dlen = input[i] & 0x3F; no_leaves_flag = input[i] & 0x80; + inline_neighbors_flag = input[i] & 0x40; i += 1; + } + if (inline_neighbors_flag !== 0) { + // node with packed leaves and common 16bit prefix + const leaves_count = no_leaves_flag !== 0 ? + 0 : + ((compression_tag >> 4) & 0x0f) + 1; + const branch_count = is_long_compressed ? + ((compression_tag >> 8) & 0xff) + 1 : + 0; if (is_data_compressed) { data = data_history[data_history.length - dlen - 1]; dlen = data.length; @@ -2791,6 +2987,72 @@ function loadDatabase(hooks) { new Uint8Array(input.buffer, i + input.byteOffset, dlen); i += dlen; } + const branches_start = i; + // leaf_value_upper + i += 2; + // branch_nodes + for (let j = 0; j < branch_count; j += 1) { + const branch_dlen = input[i] & 0x0f; + const branch_leaves_count = ((input[i] >> 4) & 0x0f) + 1; + i += 1; + if (!is_pure_suffixes_only_node) { + i += branch_dlen; + } + i += branch_leaves_count * 2; + } + // branch keys + i += branch_count; + // leaves + i += leaves_count * 2; + if (is_data_compressed) { + const clen = ( + 1 + // first compression header byte + (is_long_compressed ? 1 : 0) + // branch count + 1 + // data length and other flags + dlen + // data + (i - branches_start) // branches and leaves + ); + const canonical = new Uint8Array(clen); + let ci = 0; + canonical[ci] = input[start] ^ 0x08; + ci += 1; + if (is_long_compressed) { + canonical[ci] = input[start + ci]; + ci += 1; + } + canonical[ci] = dlen | no_leaves_flag | 0x40; + ci += 1; + for (let j = 0; j < dlen; j += 1) { + canonical[ci] = data[j]; + ci += 1; + } + for (let j = branches_start; j < i; j += 1) { + canonical[ci] = input[j]; + ci += 1; + } + tree = new InlineNeighborsTree(canonical, 0); + siphashOfBytes(canonical, 0, 0, 0, 0, hash); + } else { + tree = new InlineNeighborsTree(input, start); + siphashOfBytes(new Uint8Array( + input.buffer, + start + input.byteOffset, + i - start, + ), 0, 0, 0, 0, hash); + } + } else if (compression_tag > 1) { + // compressed node + if (is_pure_suffixes_only_node) { + data = EMPTY_UINT8; + } else if (is_data_compressed) { + data = data_history[data_history.length - dlen - 1]; + dlen = data.length; + } else { + data = dlen === 0 ? + EMPTY_UINT8 : + new Uint8Array(input.buffer, i + input.byteOffset, dlen); + i += dlen; + } const coffset = i; const { cpbranches, @@ -2820,19 +3082,27 @@ function loadDatabase(hooks) { suffix, ); const clen = ( - 3 + // lengths of children and data + // lengths of children and data + (is_data_compressed ? 2 : 3) + + // branches csnodes.length + csbranches.length + + // leaves suffix.consumed_len_bytes ); if (canonical.length < clen) { canonical = new Uint8Array(clen); } let ci = 0; - canonical[ci] = 1; - ci += 1; - canonical[ci] = dlen | no_leaves_flag; - ci += 1; + if (is_data_compressed) { + canonical[ci] = 0x09; + ci += 1; + } else { + canonical[ci] = 1; + ci += 1; + canonical[ci] = dlen | no_leaves_flag; + ci += 1; + } canonical[ci] = input[coffset]; // suffix child count ci += 1; canonical.set(csnodes, ci); @@ -2901,13 +3171,8 @@ function loadDatabase(hooks) { } siphashOfBytes(canonical.subarray(0, clen), 0, 0, 0, 0, hash); } - hash[2] &= 0x7f; } else { - i += 1; // uncompressed node - const dlen = input[i] & 0x7F; - no_leaves_flag = input[i] & 0x80; - i += 1; if (dlen === 0 || is_pure_suffixes_only_node) { data = EMPTY_UINT8; } else { @@ -2946,7 +3211,6 @@ function loadDatabase(hooks) { start + input.byteOffset, i - start, ), 0, 0, 0, 0, hash); - hash[2] &= 0x7f; tree = is_pure_suffixes_only_node ? new SuffixSearchTree( branches, @@ -2961,30 +3225,33 @@ function loadDatabase(hooks) { suffix, ); } + hash[2] &= 0x7f; hash_history.push({hash: truncatedHash.slice(), used: false}); if (data.length !== 0) { data_history.push(data); } - const tree_branch_nodeids = tree.branches.nodeids; - const tree_branch_subtrees = tree.branches.subtrees; - let j = 0; - let lb = tree.branches.subtrees.length; - while (j < lb) { - // node id with a 1 in its most significant bit is inlined, and, so - // it won't be in the stash - if ((tree_branch_nodeids[j * 6] & 0x80) === 0) { - const subtree = stash.getWithOffsetKey(tree_branch_nodeids, j * 6); - if (subtree !== undefined) { - tree_branch_subtrees[j] = Promise.resolve(subtree); + if (!(tree instanceof InlineNeighborsTree)) { + const tree_branch_nodeids = tree.branches.nodeids; + const tree_branch_subtrees = tree.branches.subtrees; + let j = 0; + const lb = tree.branches.subtrees.length; + while (j < lb) { + // node id with a 1 in its most significant bit is inlined, and, so + // it won't be in the stash + if ((tree_branch_nodeids[j * 6] & 0x80) === 0) { + const subtree = stash.getWithOffsetKey(tree_branch_nodeids, j * 6); + if (subtree !== undefined) { + tree_branch_subtrees[j] = Promise.resolve(subtree); + } } + j += 1; } - j += 1; } if (tree instanceof PrefixSearchTree) { const tree_mhp_branch_nodeids = tree.might_have_prefix_branches.nodeids; const tree_mhp_branch_subtrees = tree.might_have_prefix_branches.subtrees; - j = 0; - lb = tree.might_have_prefix_branches.subtrees.length; + let j = 0; + const lb = tree.might_have_prefix_branches.subtrees.length; while (j < lb) { // node id with a 1 in its most significant bit is inlined, and, so // it won't be in the stash diff --git a/tests/ui/consts/precise-drop-nested-deref-ice.rs b/tests/ui/consts/precise-drop-nested-deref-ice.rs new file mode 100644 index 0000000000000..3ac5bea6266a6 --- /dev/null +++ b/tests/ui/consts/precise-drop-nested-deref-ice.rs @@ -0,0 +1,15 @@ +//! Tests that multiple derefs in a projection does not cause an ICE +//! when checking const precise drops. +//! +//! Regression test for + +#![feature(const_precise_live_drops)] +struct Foo(u32); +impl Foo { + const fn get(self: Box<&Self>, f: &u32) -> u32 { + //~^ ERROR destructor of `Box<&Foo>` cannot be evaluated at compile-time + self.0 + } +} + +fn main() {} diff --git a/tests/ui/consts/precise-drop-nested-deref-ice.stderr b/tests/ui/consts/precise-drop-nested-deref-ice.stderr new file mode 100644 index 0000000000000..4e5af100f1dbe --- /dev/null +++ b/tests/ui/consts/precise-drop-nested-deref-ice.stderr @@ -0,0 +1,12 @@ +error[E0493]: destructor of `Box<&Foo>` cannot be evaluated at compile-time + --> $DIR/precise-drop-nested-deref-ice.rs:9:18 + | +LL | const fn get(self: Box<&Self>, f: &u32) -> u32 { + | ^^^^ the destructor for this type cannot be evaluated in constant functions +... +LL | } + | - value is dropped here + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0493`. diff --git a/tests/ui/sanitizer/cfi/fn-trait-objects.rs b/tests/ui/sanitizer/cfi/fn-trait-objects.rs new file mode 100644 index 0000000000000..977d4124fff0c --- /dev/null +++ b/tests/ui/sanitizer/cfi/fn-trait-objects.rs @@ -0,0 +1,32 @@ +// Verifies that types that implement the Fn, FnMut, or FnOnce traits can be +// called through their trait methods. +// +//@ needs-sanitizer-cfi +//@ only-linux +//@ ignore-backends: gcc +//@ compile-flags: -Ctarget-feature=-crt-static -Ccodegen-units=1 -Clto -Cprefer-dynamic=off -Copt-level=0 -Zsanitizer=cfi -Cunsafe-allow-abi-mismatch=sanitizer --test +//@ run-pass + +#![feature(fn_traits)] +#![feature(unboxed_closures)] + +fn foo(_a: u32) {} + +#[test] +fn test_fn_trait() { + let f: Box = Box::new(foo); + Fn::call(&f, (0,)); +} + +#[test] +fn test_fnmut_trait() { + let mut a = 0; + let mut f: Box = Box::new(|x| a += x); + FnMut::call_mut(&mut f, (1,)); +} + +#[test] +fn test_fnonce_trait() { + let f: Box = Box::new(foo); + FnOnce::call_once(f, (2,)); +} diff --git a/tests/ui/sanitizer/kcfi/fn-trait-objects.rs b/tests/ui/sanitizer/kcfi/fn-trait-objects.rs new file mode 100644 index 0000000000000..1745e7b6728e9 --- /dev/null +++ b/tests/ui/sanitizer/kcfi/fn-trait-objects.rs @@ -0,0 +1,32 @@ +// Verifies that types that implement the Fn, FnMut, or FnOnce traits can be +// called through their trait methods. +// +//@ needs-sanitizer-cfi +//@ only-linux +//@ ignore-backends: gcc +//@ compile-flags: -Ctarget-feature=-crt-static -Zpanic_abort_tests -Cpanic=abort -Cprefer-dynamic=off -Copt-level=0 -Zsanitizer=kcfi -Cunsafe-allow-abi-mismatch=sanitizer --test +//@ run-pass + +#![feature(fn_traits)] +#![feature(unboxed_closures)] + +fn foo(_a: u32) {} + +#[test] +fn test_fn_trait() { + let f: Box = Box::new(foo); + Fn::call(&f, (0,)); +} + +#[test] +fn test_fnmut_trait() { + let mut a = 0; + let mut f: Box = Box::new(|x| a += x); + FnMut::call_mut(&mut f, (1,)); +} + +#[test] +fn test_fnonce_trait() { + let f: Box = Box::new(foo); + FnOnce::call_once(f, (2,)); +}