From aa11bf6f0a1ba4e6eb92f31423feb831fae2b7d2 Mon Sep 17 00:00:00 2001 From: simonschoening Date: Wed, 14 Jun 2023 22:51:51 +0200 Subject: [PATCH 1/7] Extending filesystem support for hermit-os --- library/std/src/sys/pal/hermit/fd.rs | 5 + library/std/src/sys/pal/hermit/fs.rs | 306 +++++++++++++++++-------- library/std/src/sys/pal/hermit/time.rs | 10 + 3 files changed, 225 insertions(+), 96 deletions(-) diff --git a/library/std/src/sys/pal/hermit/fd.rs b/library/std/src/sys/pal/hermit/fd.rs index 5eb828fea1f9e..962577bb1ed83 100644 --- a/library/std/src/sys/pal/hermit/fd.rs +++ b/library/std/src/sys/pal/hermit/fd.rs @@ -48,6 +48,11 @@ impl FileDesc { pub fn set_nonblocking(&self, _nonblocking: bool) -> io::Result<()> { unsupported() } + + pub fn fstat(&self, stat: *mut abi::stat) -> io::Result<()> { + cvt(unsafe { abi::fstat(self.fd.as_raw_fd(), stat) })?; + Ok(()) + } } impl<'a> Read for &'a FileDesc { diff --git a/library/std/src/sys/pal/hermit/fs.rs b/library/std/src/sys/pal/hermit/fs.rs index d4da53fd3df9e..306e4d2210ac6 100644 --- a/library/std/src/sys/pal/hermit/fs.rs +++ b/library/std/src/sys/pal/hermit/fs.rs @@ -2,11 +2,14 @@ use super::abi::{self, O_APPEND, O_CREAT, O_EXCL, O_RDONLY, O_RDWR, O_TRUNC, O_W use super::fd::FileDesc; use crate::ffi::{CStr, OsString}; use crate::fmt; -use crate::hash::{Hash, Hasher}; use crate::io::{self, Error, ErrorKind}; use crate::io::{BorrowedCursor, IoSlice, IoSliceMut, SeekFrom}; +use crate::mem; +use crate::os::hermit::ffi::OsStringExt; use crate::os::hermit::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd}; use crate::path::{Path, PathBuf}; +use crate::ptr; +use crate::sync::Arc; use crate::sys::common::small_c_string::run_path_with_cstr; use crate::sys::cvt; use crate::sys::time::SystemTime; @@ -18,12 +21,44 @@ pub use crate::sys_common::fs::{copy, try_exists}; #[derive(Debug)] pub struct File(FileDesc); +#[derive(Clone)] +pub struct FileAttr { + stat_val: stat_struct, +} -pub struct FileAttr(!); +impl FileAttr { + fn from_stat(stat_val: stat_struct) -> Self { + Self { stat_val } + } +} -pub struct ReadDir(!); +// all DirEntry's will have a reference to this struct +struct InnerReadDir { + dirp: FileDesc, + root: PathBuf, +} + +pub struct ReadDir { + inner: Arc, + end_of_stream: bool, +} -pub struct DirEntry(!); +impl ReadDir { + fn new(inner: InnerReadDir) -> Self { + Self { inner: Arc::new(inner), end_of_stream: false } + } +} + +pub struct DirEntry { + dir: Arc, + entry: dirent_min, + name: OsString, +} + +struct dirent_min { + d_ino: u64, + d_type: u32, +} #[derive(Clone, Debug)] pub struct OpenOptions { @@ -41,72 +76,78 @@ pub struct OpenOptions { #[derive(Copy, Clone, Debug, Default)] pub struct FileTimes {} -pub struct FilePermissions(!); - -pub struct FileType(!); +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct FilePermissions { + mode: u32, +} -#[derive(Debug)] -pub struct DirBuilder {} +#[derive(Copy, Clone, Eq, Debug)] +pub struct FileType { + mode: u32, +} -impl FileAttr { - pub fn size(&self) -> u64 { - self.0 +impl PartialEq for FileType { + fn eq(&self, other: &Self) -> bool { + self.mode == other.mode } +} - pub fn perm(&self) -> FilePermissions { - self.0 +impl core::hash::Hash for FileType { + fn hash(&self, state: &mut H) { + self.mode.hash(state); } +} - pub fn file_type(&self) -> FileType { - self.0 - } +#[derive(Debug)] +pub struct DirBuilder { + mode: u32, +} +impl FileAttr { pub fn modified(&self) -> io::Result { - self.0 + Ok(SystemTime::new(self.stat_val.st_mtime, self.stat_val.st_mtime_nsec)) } pub fn accessed(&self) -> io::Result { - self.0 + Ok(SystemTime::new(self.stat_val.st_atime, self.stat_val.st_atime_nsec)) } pub fn created(&self) -> io::Result { - self.0 + Ok(SystemTime::new(self.stat_val.st_ctime, self.stat_val.st_ctime_nsec)) } -} -impl Clone for FileAttr { - fn clone(&self) -> FileAttr { - self.0 + pub fn size(&self) -> u64 { + self.stat_val.st_size as u64 } -} - -impl FilePermissions { - pub fn readonly(&self) -> bool { - self.0 + pub fn perm(&self) -> FilePermissions { + FilePermissions { mode: (self.stat_val.st_mode) } } - pub fn set_readonly(&mut self, _readonly: bool) { - self.0 + pub fn file_type(&self) -> FileType { + let masked_mode = self.stat_val.st_mode & S_IFMT; + let mode = match masked_mode { + S_IFDIR => DT_DIR, + S_IFLNK => DT_LNK, + S_IFREG => DT_REG, + _ => DT_UNKNOWN, + }; + FileType { mode: mode } } } -impl Clone for FilePermissions { - fn clone(&self) -> FilePermissions { - self.0 +impl FilePermissions { + pub fn readonly(&self) -> bool { + // check if any class (owner, group, others) has write permission + self.mode & 0o222 == 0 } -} -impl PartialEq for FilePermissions { - fn eq(&self, _other: &FilePermissions) -> bool { - self.0 + pub fn set_readonly(&mut self, _readonly: bool) { + unimplemented!() } -} - -impl Eq for FilePermissions {} -impl fmt::Debug for FilePermissions { - fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.0 + #[allow(dead_code)] + pub fn mode(&self) -> u32 { + self.mode as u32 } } @@ -117,75 +158,127 @@ impl FileTimes { impl FileType { pub fn is_dir(&self) -> bool { - self.0 + self.mode == DT_DIR } - pub fn is_file(&self) -> bool { - self.0 + self.mode == DT_REG } - pub fn is_symlink(&self) -> bool { - self.0 + self.mode == DT_LNK } } -impl Clone for FileType { - fn clone(&self) -> FileType { - self.0 +impl fmt::Debug for ReadDir { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // This will only be called from std::fs::ReadDir, which will add a "ReadDir()" frame. + // Thus the result will be e g 'ReadDir("/home")' + fmt::Debug::fmt(&*self.inner.root, f) } } -impl Copy for FileType {} +impl Iterator for ReadDir { + type Item = io::Result; -impl PartialEq for FileType { - fn eq(&self, _other: &FileType) -> bool { - self.0 - } -} + fn next(&mut self) -> Option> { + if self.end_of_stream { + return None; + } -impl Eq for FileType {} + unsafe { + loop { + // As of POSIX.1-2017, readdir() is not required to be thread safe; only + // readdir_r() is. However, readdir_r() cannot correctly handle platforms + // with unlimited or variable NAME_MAX. Many modern platforms guarantee + // thread safety for readdir() as long an individual DIR* is not accessed + // concurrently, which is sufficient for Rust. + let entry_ptr = match abi::readdir(self.inner.dirp.as_raw_fd()) { + abi::DirectoryEntry::Invalid(e) => { + // We either encountered an error, or reached the end. Either way, + // the next call to next() should return None. + self.end_of_stream = true; + + return Some(Err(Error::from_raw_os_error(e))); + } + abi::DirectoryEntry::Valid(ptr) => { + if ptr.is_null() { + return None; + } + + ptr + } + }; + + macro_rules! offset_ptr { + ($entry_ptr:expr, $field:ident) => {{ + const OFFSET: isize = { + let delusion = MaybeUninit::::uninit(); + let entry_ptr = delusion.as_ptr(); + unsafe { + ptr::addr_of!((*entry_ptr).$field) + .cast::() + .offset_from(entry_ptr.cast::()) + } + }; + if true { + // Cast to the same type determined by the else branch. + $entry_ptr.byte_offset(OFFSET).cast::<_>() + } else { + #[allow(deref_nullptr)] + { + ptr::addr_of!((*ptr::null::()).$field) + } + } + }}; + } -impl Hash for FileType { - fn hash(&self, _h: &mut H) { - self.0 - } -} + // d_name is NOT guaranteed to be null-terminated. + let name_bytes = core::slice::from_raw_parts( + offset_ptr!(entry_ptr, d_name) as *const u8, + *offset_ptr!(entry_ptr, d_namelen) as usize, + ) + .to_vec(); -impl fmt::Debug for FileType { - fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.0 - } -} + if name_bytes == b"." || name_bytes == b".." { + continue; + } -impl fmt::Debug for ReadDir { - fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.0 - } -} + let name = OsString::from_vec(name_bytes); -impl Iterator for ReadDir { - type Item = io::Result; + let entry = dirent_min { + d_ino: *offset_ptr!(entry_ptr, d_ino), + d_type: *offset_ptr!(entry_ptr, d_type), + }; - fn next(&mut self) -> Option> { - self.0 + return Some(Ok(DirEntry { entry, name: name, dir: Arc::clone(&self.inner) })); + } + } } } impl DirEntry { pub fn path(&self) -> PathBuf { - self.0 + self.dir.root.join(self.file_name_os_str()) } pub fn file_name(&self) -> OsString { - self.0 + self.file_name_os_str().to_os_string() } pub fn metadata(&self) -> io::Result { - self.0 + lstat(&self.path()) } pub fn file_type(&self) -> io::Result { - self.0 + Ok(FileType { mode: self.entry.d_type }) + } + + #[allow(dead_code)] + pub fn ino(&self) -> u64 { + self.entry.d_ino + } + + pub fn file_name_os_str(&self) -> &OsStr { + self.name.as_os_str() } } @@ -288,7 +381,9 @@ impl File { } pub fn file_attr(&self) -> io::Result { - Err(Error::from_raw_os_error(22)) + let mut stat_val: stat_struct = unsafe { mem::zeroed() }; + self.0.fstat(&mut stat_val)?; + Ok(FileAttr::from_stat(stat_val)) } pub fn fsync(&self) -> io::Result<()> { @@ -357,11 +452,18 @@ impl File { impl DirBuilder { pub fn new() -> DirBuilder { - DirBuilder {} + DirBuilder { mode: 0o777 } } - pub fn mkdir(&self, _p: &Path) -> io::Result<()> { - unsupported() + pub fn mkdir(&self, path: &Path) -> io::Result<()> { + run_path_with_cstr(path, |path| { + cvt(unsafe { abi::mkdir(path.as_ptr(), self.mode) }).map(|_| ()) + }) + } + + #[allow(dead_code)] + pub fn set_mode(&mut self, mode: u32) { + self.mode = mode as u32; } } @@ -416,8 +518,12 @@ impl FromRawFd for File { } } -pub fn readdir(_p: &Path) -> io::Result { - unsupported() +pub fn readdir(path: &Path) -> io::Result { + let fd_raw = run_path_with_cstr(path, |path| cvt(unsafe { abi::opendir(path.as_ptr()) }))?; + let fd = unsafe { FileDesc::from_raw_fd(fd_raw as i32) }; + let root = path.to_path_buf(); + let inner = InnerReadDir { dirp: fd, root }; + Ok(ReadDir::new(inner)) } pub fn unlink(path: &Path) -> io::Result<()> { @@ -428,12 +534,12 @@ pub fn rename(_old: &Path, _new: &Path) -> io::Result<()> { unsupported() } -pub fn set_perm(_p: &Path, perm: FilePermissions) -> io::Result<()> { - match perm.0 {} +pub fn set_perm(_p: &Path, _perm: FilePermissions) -> io::Result<()> { + Err(Error::from_raw_os_error(22)) } -pub fn rmdir(_p: &Path) -> io::Result<()> { - unsupported() +pub fn rmdir(path: &Path) -> io::Result<()> { + run_path_with_cstr(path, |path| cvt(unsafe { abi::rmdir(path.as_ptr()) }).map(|_| ())) } pub fn remove_dir_all(_path: &Path) -> io::Result<()> { @@ -453,12 +559,20 @@ pub fn link(_original: &Path, _link: &Path) -> io::Result<()> { unsupported() } -pub fn stat(_p: &Path) -> io::Result { - unsupported() +pub fn stat(path: &Path) -> io::Result { + run_path_with_cstr(path, |path| { + let mut stat_val: stat_struct = unsafe { mem::zeroed() }; + cvt(unsafe { abi::stat(path.as_ptr(), &mut stat_val) })?; + Ok(FileAttr::from_stat(stat_val)) + }) } -pub fn lstat(_p: &Path) -> io::Result { - unsupported() +pub fn lstat(path: &Path) -> io::Result { + run_path_with_cstr(path, |path| { + let mut stat_val: stat_struct = unsafe { mem::zeroed() }; + cvt(unsafe { abi::lstat(path.as_ptr(), &mut stat_val) })?; + Ok(FileAttr::from_stat(stat_val)) + }) } pub fn canonicalize(_p: &Path) -> io::Result { diff --git a/library/std/src/sys/pal/hermit/time.rs b/library/std/src/sys/pal/hermit/time.rs index f289dafd8bc56..fa10b7638ec20 100644 --- a/library/std/src/sys/pal/hermit/time.rs +++ b/library/std/src/sys/pal/hermit/time.rs @@ -18,6 +18,12 @@ impl Timespec { Timespec { t: timespec { tv_sec: 0, tv_nsec: 0 } } } + const fn new(tv_sec: i64, tv_nsec: i64) -> Timespec { + assert!(tv_nsec >= 0 && tv_nsec < NSEC_PER_SEC as i64); + // SAFETY: The assert above checks tv_nsec is within the valid range + Timespec { t: timespec { tv_sec: tv_sec, tv_nsec: tv_nsec } } + } + fn sub_timespec(&self, other: &Timespec) -> Result { if self >= other { Ok(if self.t.tv_nsec >= other.t.tv_nsec { @@ -195,6 +201,10 @@ pub struct SystemTime(Timespec); pub const UNIX_EPOCH: SystemTime = SystemTime(Timespec::zero()); impl SystemTime { + pub fn new(tv_sec: i64, tv_nsec: i64) -> SystemTime { + SystemTime(Timespec::new(tv_sec, tv_nsec)) + } + pub fn now() -> SystemTime { let mut time: Timespec = Timespec::zero(); let _ = unsafe { abi::clock_gettime(CLOCK_REALTIME, core::ptr::addr_of_mut!(time.t)) }; From b1c7804c684301213775a4f490605302aeec7789 Mon Sep 17 00:00:00 2001 From: Stefan Lankes Date: Wed, 17 Jan 2024 20:19:16 +0100 Subject: [PATCH 2/7] revise interface to read directory entries The new interface has some similarities to Linux system call getdents64. The system call reads several dirent64 structures. At the end of each dirent64 is stored the name of the file. The length of file name is implictly part of dirent64 because d_reclen contains size of dirent64 plus the length of the file name. --- library/std/src/sys/pal/hermit/fs.rs | 213 +++++++++++++++------------ 1 file changed, 116 insertions(+), 97 deletions(-) diff --git a/library/std/src/sys/pal/hermit/fs.rs b/library/std/src/sys/pal/hermit/fs.rs index 306e4d2210ac6..6519cc22f1f6f 100644 --- a/library/std/src/sys/pal/hermit/fs.rs +++ b/library/std/src/sys/pal/hermit/fs.rs @@ -1,6 +1,9 @@ -use super::abi::{self, O_APPEND, O_CREAT, O_EXCL, O_RDONLY, O_RDWR, O_TRUNC, O_WRONLY}; +use super::abi::{ + self, dirent64, stat as stat_struct, DT_DIR, DT_LNK, DT_REG, DT_UNKNOWN, O_APPEND, O_CREAT, + O_EXCL, O_RDONLY, O_RDWR, O_TRUNC, O_WRONLY, S_IFDIR, S_IFLNK, S_IFMT, S_IFREG, +}; use super::fd::FileDesc; -use crate::ffi::{CStr, OsString}; +use crate::ffi::{CStr, OsStr, OsString}; use crate::fmt; use crate::io::{self, Error, ErrorKind}; use crate::io::{BorrowedCursor, IoSlice, IoSliceMut, SeekFrom}; @@ -8,7 +11,6 @@ use crate::mem; use crate::os::hermit::ffi::OsStringExt; use crate::os::hermit::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd}; use crate::path::{Path, PathBuf}; -use crate::ptr; use crate::sync::Arc; use crate::sys::common::small_c_string::run_path_with_cstr; use crate::sys::cvt; @@ -17,7 +19,6 @@ use crate::sys::unsupported; use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner}; pub use crate::sys_common::fs::{copy, try_exists}; -//pub use crate::sys_common::fs::remove_dir_all; #[derive(Debug)] pub struct File(FileDesc); @@ -34,32 +35,38 @@ impl FileAttr { // all DirEntry's will have a reference to this struct struct InnerReadDir { - dirp: FileDesc, root: PathBuf, + dir: Vec, +} + +impl InnerReadDir { + pub fn new(root: PathBuf, dir: Vec) -> Self { + Self { root, dir } + } } pub struct ReadDir { inner: Arc, - end_of_stream: bool, + pos: i64, } impl ReadDir { fn new(inner: InnerReadDir) -> Self { - Self { inner: Arc::new(inner), end_of_stream: false } + Self { inner: Arc::new(inner), pos: 0 } } } pub struct DirEntry { - dir: Arc, - entry: dirent_min, + /// path to the entry + root: PathBuf, + /// 64-bit inode number + ino: u64, + /// File type + type_: u32, + /// name of the entry name: OsString, } -struct dirent_min { - d_ino: u64, - d_type: u32, -} - #[derive(Clone, Debug)] pub struct OpenOptions { // generic @@ -105,15 +112,24 @@ pub struct DirBuilder { impl FileAttr { pub fn modified(&self) -> io::Result { - Ok(SystemTime::new(self.stat_val.st_mtime, self.stat_val.st_mtime_nsec)) + Ok(SystemTime::new( + self.stat_val.st_mtime.try_into().unwrap(), + self.stat_val.st_mtime_nsec.try_into().unwrap(), + )) } pub fn accessed(&self) -> io::Result { - Ok(SystemTime::new(self.stat_val.st_atime, self.stat_val.st_atime_nsec)) + Ok(SystemTime::new( + self.stat_val.st_atime.try_into().unwrap(), + self.stat_val.st_atime_nsec.try_into().unwrap(), + )) } pub fn created(&self) -> io::Result { - Ok(SystemTime::new(self.stat_val.st_ctime, self.stat_val.st_ctime_nsec)) + Ok(SystemTime::new( + self.stat_val.st_ctime.try_into().unwrap(), + self.stat_val.st_ctime_nsec.try_into().unwrap(), + )) } pub fn size(&self) -> u64 { @@ -171,7 +187,7 @@ impl FileType { impl fmt::Debug for ReadDir { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { // This will only be called from std::fs::ReadDir, which will add a "ReadDir()" frame. - // Thus the result will be e g 'ReadDir("/home")' + // Thus the result will be e.g. 'ReadDir("/home")' fmt::Debug::fmt(&*self.inner.root, f) } } @@ -180,84 +196,55 @@ impl Iterator for ReadDir { type Item = io::Result; fn next(&mut self) -> Option> { - if self.end_of_stream { - return None; - } + let mut counter: usize = 0; + let mut offset: i64 = 0; + + // loop over all directory entries and search the entry for the current position + loop { + // leave function, if the loop reaches the of the buffer (with all entries) + if offset >= self.inner.dir.len().try_into().unwrap() { + return None; + } - unsafe { - loop { - // As of POSIX.1-2017, readdir() is not required to be thread safe; only - // readdir_r() is. However, readdir_r() cannot correctly handle platforms - // with unlimited or variable NAME_MAX. Many modern platforms guarantee - // thread safety for readdir() as long an individual DIR* is not accessed - // concurrently, which is sufficient for Rust. - let entry_ptr = match abi::readdir(self.inner.dirp.as_raw_fd()) { - abi::DirectoryEntry::Invalid(e) => { - // We either encountered an error, or reached the end. Either way, - // the next call to next() should return None. - self.end_of_stream = true; - - return Some(Err(Error::from_raw_os_error(e))); - } - abi::DirectoryEntry::Valid(ptr) => { - if ptr.is_null() { - return None; - } - - ptr - } + let dir = unsafe { + &*(self.inner.dir.as_ptr().offset(offset.try_into().unwrap()) as *const dirent64) + }; + + if counter == self.pos.try_into().unwrap() { + self.pos += 1; + + // After dirent64, the file name is stored. d_reclen represents the length of the dirent64 + // plus the length of the file name. Consequently, file name has a size of d_reclen minus + // the size of dirent64. The file name is always a C string and terminated by `\0`. + // Consequently, we are able to ignore the last byte. + let name_bytes = unsafe { + core::slice::from_raw_parts( + &dir.d_name as *const _ as *const u8, + dir.d_reclen as usize - core::mem::size_of::() - 1, + ) + .to_vec() }; - - macro_rules! offset_ptr { - ($entry_ptr:expr, $field:ident) => {{ - const OFFSET: isize = { - let delusion = MaybeUninit::::uninit(); - let entry_ptr = delusion.as_ptr(); - unsafe { - ptr::addr_of!((*entry_ptr).$field) - .cast::() - .offset_from(entry_ptr.cast::()) - } - }; - if true { - // Cast to the same type determined by the else branch. - $entry_ptr.byte_offset(OFFSET).cast::<_>() - } else { - #[allow(deref_nullptr)] - { - ptr::addr_of!((*ptr::null::()).$field) - } - } - }}; - } - - // d_name is NOT guaranteed to be null-terminated. - let name_bytes = core::slice::from_raw_parts( - offset_ptr!(entry_ptr, d_name) as *const u8, - *offset_ptr!(entry_ptr, d_namelen) as usize, - ) - .to_vec(); - - if name_bytes == b"." || name_bytes == b".." { - continue; - } - - let name = OsString::from_vec(name_bytes); - - let entry = dirent_min { - d_ino: *offset_ptr!(entry_ptr, d_ino), - d_type: *offset_ptr!(entry_ptr, d_type), + let entry = DirEntry { + root: self.inner.root.clone(), + ino: dir.d_ino, + type_: dir.d_type as u32, + name: OsString::from_vec(name_bytes), }; - return Some(Ok(DirEntry { entry, name: name, dir: Arc::clone(&self.inner) })); + return Some(Ok(entry)); } + + counter += 1; + + // move to the next dirent64, which is directly stored after the previous one + offset = offset + dir.d_off; } } } impl DirEntry { pub fn path(&self) -> PathBuf { - self.dir.root.join(self.file_name_os_str()) + self.root.join(self.file_name_os_str()) } pub fn file_name(&self) -> OsString { @@ -265,16 +252,18 @@ impl DirEntry { } pub fn metadata(&self) -> io::Result { - lstat(&self.path()) + let mut path = self.path(); + path.set_file_name(self.file_name_os_str()); + lstat(&path) } pub fn file_type(&self) -> io::Result { - Ok(FileType { mode: self.entry.d_type }) + Ok(FileType { mode: self.type_ as u32 }) } #[allow(dead_code)] pub fn ino(&self) -> u64 { - self.entry.d_ino + self.ino } pub fn file_name_os_str(&self) -> &OsStr { @@ -456,7 +445,7 @@ impl DirBuilder { } pub fn mkdir(&self, path: &Path) -> io::Result<()> { - run_path_with_cstr(path, |path| { + run_path_with_cstr(path, &|path| { cvt(unsafe { abi::mkdir(path.as_ptr(), self.mode) }).map(|_| ()) }) } @@ -519,11 +508,42 @@ impl FromRawFd for File { } pub fn readdir(path: &Path) -> io::Result { - let fd_raw = run_path_with_cstr(path, |path| cvt(unsafe { abi::opendir(path.as_ptr()) }))?; + let fd_raw = run_path_with_cstr(path, &|path| cvt(unsafe { abi::opendir(path.as_ptr()) }))?; let fd = unsafe { FileDesc::from_raw_fd(fd_raw as i32) }; let root = path.to_path_buf(); - let inner = InnerReadDir { dirp: fd, root }; - Ok(ReadDir::new(inner)) + + // read all director entries + let mut vec: Vec = Vec::new(); + let mut sz = 512; + loop { + // reserve memory to receive all directory entries + vec.resize(sz, 0); + + let readlen = + unsafe { abi::getdents64(fd.as_raw_fd(), vec.as_mut_ptr() as *mut dirent64, sz) }; + if readlen > 0 { + // shrink down to the minimal size + vec.resize(readlen.try_into().unwrap(), 0); + break; + } + + // if the buffer is too small, getdents64 returns EINVAL + // otherwise, getdents64 returns an error number + if readlen != (-abi::errno::EINVAL).into() { + return Err(Error::from_raw_os_error(readlen.try_into().unwrap())); + } + + // we don't have enough memory => try to increase the vector size + sz = sz * 2; + + // 1 MB for directory entries should be enough + // stop here to avoid an endless loop + if sz > 0x100000 { + return Err(Error::from(ErrorKind::Uncategorized)); + } + } + + Ok(ReadDir::new(InnerReadDir::new(root, vec))) } pub fn unlink(path: &Path) -> io::Result<()> { @@ -539,12 +559,11 @@ pub fn set_perm(_p: &Path, _perm: FilePermissions) -> io::Result<()> { } pub fn rmdir(path: &Path) -> io::Result<()> { - run_path_with_cstr(path, |path| cvt(unsafe { abi::rmdir(path.as_ptr()) }).map(|_| ())) + run_path_with_cstr(path, &|path| cvt(unsafe { abi::rmdir(path.as_ptr()) }).map(|_| ())) } pub fn remove_dir_all(_path: &Path) -> io::Result<()> { - //unsupported() - Ok(()) + unsupported() } pub fn readlink(_p: &Path) -> io::Result { @@ -560,7 +579,7 @@ pub fn link(_original: &Path, _link: &Path) -> io::Result<()> { } pub fn stat(path: &Path) -> io::Result { - run_path_with_cstr(path, |path| { + run_path_with_cstr(path, &|path| { let mut stat_val: stat_struct = unsafe { mem::zeroed() }; cvt(unsafe { abi::stat(path.as_ptr(), &mut stat_val) })?; Ok(FileAttr::from_stat(stat_val)) @@ -568,7 +587,7 @@ pub fn stat(path: &Path) -> io::Result { } pub fn lstat(path: &Path) -> io::Result { - run_path_with_cstr(path, |path| { + run_path_with_cstr(path, &|path| { let mut stat_val: stat_struct = unsafe { mem::zeroed() }; cvt(unsafe { abi::lstat(path.as_ptr(), &mut stat_val) })?; Ok(FileAttr::from_stat(stat_val)) From 95ec17a793d396fca2d03fb3ce5a53e137e04af4 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Sat, 20 Jan 2024 00:22:25 +0300 Subject: [PATCH 3/7] privacy: Stabilize lint `unnameable_types` --- compiler/rustc_feature/src/accepted.rs | 2 ++ compiler/rustc_feature/src/unstable.rs | 2 -- compiler/rustc_lint/src/builtin.rs | 5 +++-- compiler/rustc_lint_defs/src/builtin.rs | 7 +++++-- .../feature-gate-type_privacy_lints.rs | 4 ---- .../feature-gate-type_privacy_lints.stderr | 14 -------------- tests/ui/privacy/unnameable_types.rs | 1 - tests/ui/privacy/unnameable_types.stderr | 8 ++++---- 8 files changed, 14 insertions(+), 29 deletions(-) delete mode 100644 tests/ui/feature-gates/feature-gate-type_privacy_lints.rs delete mode 100644 tests/ui/feature-gates/feature-gate-type_privacy_lints.stderr diff --git a/compiler/rustc_feature/src/accepted.rs b/compiler/rustc_feature/src/accepted.rs index 7e5197f16f91e..2debfb96eaab0 100644 --- a/compiler/rustc_feature/src/accepted.rs +++ b/compiler/rustc_feature/src/accepted.rs @@ -351,6 +351,8 @@ declare_features! ( (accepted, type_alias_enum_variants, "1.37.0", Some(49683)), /// Allows macros to appear in the type position. (accepted, type_macros, "1.13.0", Some(27245)), + /// Allows using type privacy lints (`private_interfaces`, `private_bounds`, `unnameable_types`). + (accepted, type_privacy_lints, "CURRENT_RUSTC_VERSION", Some(48054)), /// Allows `const _: TYPE = VALUE`. (accepted, underscore_const_names, "1.37.0", Some(54912)), /// Allows `use path as _;` and `extern crate c as _;`. diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index 52421fce86738..60ecfec50aca1 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -605,8 +605,6 @@ declare_features! ( /// Allows creation of instances of a struct by moving fields that have /// not changed from prior instances of the same struct (RFC #2528) (unstable, type_changing_struct_update, "1.58.0", Some(86555)), - /// Allows using type privacy lints (`private_interfaces`, `private_bounds`, `unnameable_types`). - (unstable, type_privacy_lints, "1.72.0", Some(48054)), /// Enables rustc to generate code that instructs libstd to NOT ignore SIGPIPE. (unstable, unix_sigpipe, "1.65.0", Some(97889)), /// Allows unnamed fields of struct and union type diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index 595dc08b081ed..19a5ef6a86481 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -1337,8 +1337,9 @@ impl<'tcx> LateLintPass<'tcx> for UngatedAsyncFnTrackCaller { } declare_lint! { - /// The `unreachable_pub` lint triggers for `pub` items not reachable from - /// the crate root. + /// The `unreachable_pub` lint triggers for `pub` items not reachable from other crates - that + /// means neither directly accessible, nor reexported, nor leaked through things like return + /// types. /// /// ### Example /// diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index f2560b35aa2e1..cc6c5b6921767 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -4248,7 +4248,6 @@ declare_lint! { /// ### Example /// /// ```rust,compile_fail - /// # #![feature(type_privacy_lints)] /// # #![allow(unused)] /// #![deny(unnameable_types)] /// mod m { @@ -4265,10 +4264,14 @@ declare_lint! { /// /// It is often expected that if you can obtain an object of type `T`, then /// you can name the type `T` as well, this lint attempts to enforce this rule. + /// The recommended action is to either reexport the type properly to make it nameable, + /// or document that users are not supposed to be able to name it for one reason or another. + /// + /// Besides types, this lint applies to traits because traits can also leak through signatures, + /// and you may obtain objects of their `dyn Trait` or `impl Trait` types. pub UNNAMEABLE_TYPES, Allow, "effective visibility of a type is larger than the area in which it can be named", - @feature_gate = sym::type_privacy_lints; } declare_lint! { diff --git a/tests/ui/feature-gates/feature-gate-type_privacy_lints.rs b/tests/ui/feature-gates/feature-gate-type_privacy_lints.rs deleted file mode 100644 index c537fc419f68d..0000000000000 --- a/tests/ui/feature-gates/feature-gate-type_privacy_lints.rs +++ /dev/null @@ -1,4 +0,0 @@ -//@ check-pass - -#![warn(unnameable_types)] //~ WARN unknown lint -fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-type_privacy_lints.stderr b/tests/ui/feature-gates/feature-gate-type_privacy_lints.stderr deleted file mode 100644 index 72ac3792fff50..0000000000000 --- a/tests/ui/feature-gates/feature-gate-type_privacy_lints.stderr +++ /dev/null @@ -1,14 +0,0 @@ -warning: unknown lint: `unnameable_types` - --> $DIR/feature-gate-type_privacy_lints.rs:3:1 - | -LL | #![warn(unnameable_types)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: the `unnameable_types` lint is unstable - = note: see issue #48054 for more information - = help: add `#![feature(type_privacy_lints)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - = note: `#[warn(unknown_lints)]` on by default - -warning: 1 warning emitted - diff --git a/tests/ui/privacy/unnameable_types.rs b/tests/ui/privacy/unnameable_types.rs index c6c5561c3c4be..e82bfd0477f74 100644 --- a/tests/ui/privacy/unnameable_types.rs +++ b/tests/ui/privacy/unnameable_types.rs @@ -1,4 +1,3 @@ -#![feature(type_privacy_lints)] #![deny(unnameable_types)] mod m { diff --git a/tests/ui/privacy/unnameable_types.stderr b/tests/ui/privacy/unnameable_types.stderr index d68a11c9728f6..a1bc60ef9c260 100644 --- a/tests/ui/privacy/unnameable_types.stderr +++ b/tests/ui/privacy/unnameable_types.stderr @@ -1,23 +1,23 @@ error: struct `PubStruct` is reachable but cannot be named - --> $DIR/unnameable_types.rs:5:5 + --> $DIR/unnameable_types.rs:4:5 | LL | pub struct PubStruct(pub i32); | ^^^^^^^^^^^^^^^^^^^^ reachable at visibility `pub`, but can only be named at visibility `pub(crate)` | note: the lint level is defined here - --> $DIR/unnameable_types.rs:2:9 + --> $DIR/unnameable_types.rs:1:9 | LL | #![deny(unnameable_types)] | ^^^^^^^^^^^^^^^^ error: enum `PubE` is reachable but cannot be named - --> $DIR/unnameable_types.rs:7:5 + --> $DIR/unnameable_types.rs:6:5 | LL | pub enum PubE { | ^^^^^^^^^^^^^ reachable at visibility `pub`, but can only be named at visibility `pub(crate)` error: trait `PubTr` is reachable but cannot be named - --> $DIR/unnameable_types.rs:11:5 + --> $DIR/unnameable_types.rs:10:5 | LL | pub trait PubTr { | ^^^^^^^^^^^^^^^ reachable at visibility `pub`, but can only be named at visibility `pub(crate)` From 19758714cadc29fcd2d0e411f3088669d0dbc819 Mon Sep 17 00:00:00 2001 From: Dan Date: Wed, 3 Apr 2024 19:03:12 -0400 Subject: [PATCH 4/7] update messages --- compiler/rustc_driver_impl/messages.ftl | 2 +- compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs | 2 +- compiler/rustc_llvm/llvm-wrapper/ArchiveWrapper.cpp | 2 +- src/tools/clippy/clippy_lints/src/misc.rs | 2 +- src/tools/clippy/tests/ui-internal/custom_ice_message.stderr | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_driver_impl/messages.ftl b/compiler/rustc_driver_impl/messages.ftl index 1b69a6e2fbecc..62391daecd053 100644 --- a/compiler/rustc_driver_impl/messages.ftl +++ b/compiler/rustc_driver_impl/messages.ftl @@ -2,7 +2,7 @@ driver_impl_ice = the compiler unexpectedly panicked. this is a bug. driver_impl_ice_bug_report = we would appreciate a bug report: {$bug_report_url} driver_impl_ice_bug_report_internal_feature = using internal features is not supported and expected to cause internal compiler errors when used incorrectly driver_impl_ice_bug_report_outdated = - it seems that this compiler `{$version}` is outdated, a newer nightly should have been released in the mean time + it seems that this compiler `{$version}` is outdated, a newer nightly should have been released in the meantime .update = please consider running `rustup update nightly` to update the nightly channel and check if this problem still persists .url = if the problem still persists, we would appreciate a bug report: {$bug_report_url} driver_impl_ice_exclude_cargo_defaults = some of the compiler flags provided by cargo are hidden diff --git a/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs b/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs index cf825be7a55ea..ec7954536bec4 100644 --- a/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs +++ b/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs @@ -20,7 +20,7 @@ declare_lint! { /// This functionality was removed in #97346, but then rolled back in #99860 /// because it caused regressions. /// - /// We plan on reintroducing this as a hard error, but in the mean time, + /// We plan on reintroducing this as a hard error, but in the meantime, /// this lint serves to warn and suggest fixes for any use-cases which rely /// on this behavior. /// diff --git a/compiler/rustc_llvm/llvm-wrapper/ArchiveWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/ArchiveWrapper.cpp index 40723ff9f5ef2..64e6c18092ffe 100644 --- a/compiler/rustc_llvm/llvm-wrapper/ArchiveWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/ArchiveWrapper.cpp @@ -147,7 +147,7 @@ LLVMRustArchiveChildName(LLVMRustArchiveChildConstRef Child, size_t *Size) { Expected NameOrErr = Child->getName(); if (!NameOrErr) { // rustc_codegen_llvm currently doesn't use this error string, but it might be - // useful in the future, and in the mean time this tells LLVM that the + // useful in the future, and in the meantime this tells LLVM that the // error was not ignored and that it shouldn't abort the process. LLVMRustSetLastError(toString(NameOrErr.takeError()).c_str()); return nullptr; diff --git a/src/tools/clippy/clippy_lints/src/misc.rs b/src/tools/clippy/clippy_lints/src/misc.rs index ea6e662b4be36..be1a35f8fb210 100644 --- a/src/tools/clippy/clippy_lints/src/misc.rs +++ b/src/tools/clippy/clippy_lints/src/misc.rs @@ -66,7 +66,7 @@ declare_clippy_lint! { /// /// ### Known problems /// The lint does not work properly with desugaring and - /// macro, it has been allowed in the mean time. + /// macro, it has been allowed in the meantime. /// /// ### Example /// ```no_run diff --git a/src/tools/clippy/tests/ui-internal/custom_ice_message.stderr b/src/tools/clippy/tests/ui-internal/custom_ice_message.stderr index b84f4e87e073f..763ce59ba1d93 100644 --- a/src/tools/clippy/tests/ui-internal/custom_ice_message.stderr +++ b/src/tools/clippy/tests/ui-internal/custom_ice_message.stderr @@ -4,7 +4,7 @@ note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace error: the compiler unexpectedly panicked. this is a bug. -note: it seems that this compiler is outdated, a newer nightly should have been released in the mean time +note: it seems that this compiler is outdated, a newer nightly should have been released in the meantime | = note: please consider running `rustup update nightly` to update the nightly channel and check if this problem still persists = note: if the problem still persists, we would appreciate a bug report: https://github.com/rust-lang/rust-clippy/issues/new?template=ice.yml From 8f6ebf608db7759d0ca673b24bc9c7810484f169 Mon Sep 17 00:00:00 2001 From: BALAGANESH <85802233+balaganesh102004@users.noreply.github.com> Date: Sun, 7 Apr 2024 15:07:25 +0000 Subject: [PATCH 5/7] Made changes in documentation --- library/core/src/ops/try_trait.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/core/src/ops/try_trait.rs b/library/core/src/ops/try_trait.rs index 3f8c8efd416f3..483f55b207093 100644 --- a/library/core/src/ops/try_trait.rs +++ b/library/core/src/ops/try_trait.rs @@ -310,7 +310,7 @@ pub trait FromResidual::Residual> { /// This should be implemented consistently with the `branch` method such /// that applying the `?` operator will get back an equivalent residual: /// `FromResidual::from_residual(r).branch() --> ControlFlow::Break(r)`. - /// (It must not be an *identical* residual when interconversion is involved.) + /// (The residual is not mandated to be *identical* when interconversion is involved.) /// /// # Examples /// From 7a2678de7d3c0caa169542bd56a4fbf41b73a5b5 Mon Sep 17 00:00:00 2001 From: Philippe-Cholet <44676486+Philippe-Cholet@users.noreply.github.com> Date: Mon, 8 Apr 2024 12:12:13 +0200 Subject: [PATCH 6/7] Add invariant to VecDeque::pop_* that len < cap if pop successful Similar to #114370 for VecDeque instead of Vec. It now uses `core::hint::assert_unchecked`. --- .../alloc/src/collections/vec_deque/mod.rs | 10 ++- tests/codegen/vecdeque_pop_push.rs | 67 +++++++++++++++++++ 2 files changed, 75 insertions(+), 2 deletions(-) create mode 100644 tests/codegen/vecdeque_pop_push.rs diff --git a/library/alloc/src/collections/vec_deque/mod.rs b/library/alloc/src/collections/vec_deque/mod.rs index e2fc320f280a5..693ecb0b6b48d 100644 --- a/library/alloc/src/collections/vec_deque/mod.rs +++ b/library/alloc/src/collections/vec_deque/mod.rs @@ -1614,7 +1614,10 @@ impl VecDeque { let old_head = self.head; self.head = self.to_physical_idx(1); self.len -= 1; - Some(unsafe { self.buffer_read(old_head) }) + unsafe { + core::hint::assert_unchecked(self.len < self.capacity()); + Some(self.buffer_read(old_head)) + } } } @@ -1638,7 +1641,10 @@ impl VecDeque { None } else { self.len -= 1; - Some(unsafe { self.buffer_read(self.to_physical_idx(self.len)) }) + unsafe { + core::hint::assert_unchecked(self.len < self.capacity()); + Some(self.buffer_read(self.to_physical_idx(self.len))) + } } } diff --git a/tests/codegen/vecdeque_pop_push.rs b/tests/codegen/vecdeque_pop_push.rs new file mode 100644 index 0000000000000..040d5a279dcab --- /dev/null +++ b/tests/codegen/vecdeque_pop_push.rs @@ -0,0 +1,67 @@ +//@ compile-flags: -O + +#![crate_type = "lib"] + +use std::collections::VecDeque; + +#[no_mangle] +// CHECK-LABEL: @noop_back( +pub fn noop_back(v: &mut VecDeque) { + // CHECK-NOT: grow + // CHECK: tail call void @llvm.assume + // CHECK-NOT: grow + // CHECK: ret + if let Some(x) = v.pop_back() { + v.push_back(x); + } +} + +#[no_mangle] +// CHECK-LABEL: @noop_front( +pub fn noop_front(v: &mut VecDeque) { + // CHECK-NOT: grow + // CHECK: tail call void @llvm.assume + // CHECK-NOT: grow + // CHECK: ret + if let Some(x) = v.pop_front() { + v.push_front(x); + } +} + +#[no_mangle] +// CHECK-LABEL: @move_byte_front_to_back( +pub fn move_byte_front_to_back(v: &mut VecDeque) { + // CHECK-NOT: grow + // CHECK: tail call void @llvm.assume + // CHECK-NOT: grow + // CHECK: ret + if let Some(x) = v.pop_front() { + v.push_back(x); + } +} + +#[no_mangle] +// CHECK-LABEL: @move_byte_back_to_front( +pub fn move_byte_back_to_front(v: &mut VecDeque) { + // CHECK-NOT: grow + // CHECK: tail call void @llvm.assume + // CHECK-NOT: grow + // CHECK: ret + if let Some(x) = v.pop_back() { + v.push_front(x); + } +} + +#[no_mangle] +// CHECK-LABEL: @push_back_byte( +pub fn push_back_byte(v: &mut VecDeque) { + // CHECK: call {{.*}}grow + v.push_back(3); +} + +#[no_mangle] +// CHECK-LABEL: @push_front_byte( +pub fn push_front_byte(v: &mut VecDeque) { + // CHECK: call {{.*}}grow + v.push_front(3); +} From a9edbfda3287ea0012283f729c5893f5ff11d537 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Mon, 8 Apr 2024 10:36:15 +0000 Subject: [PATCH 7/7] Stop exporting `TypeckRootCtxt` and `FnCtxt`. While they have many convenient APIs, it is better to expose dedicated functions for them --- compiler/rustc_hir_typeck/src/cast.rs | 41 +++++++++++++++---- compiler/rustc_hir_typeck/src/coercion.rs | 14 +++++++ compiler/rustc_hir_typeck/src/expr.rs | 10 +---- compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs | 2 +- compiler/rustc_hir_typeck/src/lib.rs | 5 ++- .../rustc_hir_typeck/src/typeck_root_ctxt.rs | 2 +- .../src/methods/unnecessary_to_owned.rs | 5 +-- .../transmutes_expressible_as_ptr_casts.rs | 4 +- .../clippy_lints/src/transmute/utils.rs | 37 ----------------- 9 files changed, 57 insertions(+), 63 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/cast.rs b/compiler/rustc_hir_typeck/src/cast.rs index c948b6343b748..07d64918e8ba4 100644 --- a/compiler/rustc_hir_typeck/src/cast.rs +++ b/compiler/rustc_hir_typeck/src/cast.rs @@ -40,17 +40,19 @@ use rustc_middle::mir::Mutability; use rustc_middle::ty::adjustment::AllowTwoPhase; use rustc_middle::ty::cast::{CastKind, CastTy}; use rustc_middle::ty::error::TypeError; +use rustc_middle::ty::TyCtxt; use rustc_middle::ty::{self, Ty, TypeAndMut, TypeVisitableExt, VariantDef}; use rustc_session::lint; use rustc_span::def_id::{DefId, LOCAL_CRATE}; use rustc_span::symbol::sym; use rustc_span::Span; +use rustc_span::DUMMY_SP; use rustc_trait_selection::infer::InferCtxtExt; /// Reifies a cast check to be checked once we have full type information for /// a function context. #[derive(Debug)] -pub struct CastCheck<'tcx> { +pub(crate) struct CastCheck<'tcx> { /// The expression whose value is being casted expr: &'tcx hir::Expr<'tcx>, /// The source type for the cast expression @@ -60,8 +62,6 @@ pub struct CastCheck<'tcx> { cast_ty: Ty<'tcx>, cast_span: Span, span: Span, - /// whether the cast is made in a const context or not. - pub constness: hir::Constness, } /// The kind of pointer and associated metadata (thin, length or vtable) - we @@ -194,18 +194,45 @@ fn make_invalid_casting_error<'a, 'tcx>( ) } +/// If a cast from `from_ty` to `to_ty` is valid, returns a `Some` containing the kind +/// of the cast. +/// +/// This is a helper used from clippy. +pub fn check_cast<'tcx>( + tcx: TyCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + e: &'tcx hir::Expr<'tcx>, + from_ty: Ty<'tcx>, + to_ty: Ty<'tcx>, +) -> Option { + let hir_id = e.hir_id; + let local_def_id = hir_id.owner.def_id; + + let root_ctxt = crate::TypeckRootCtxt::new(tcx, local_def_id); + let fn_ctxt = FnCtxt::new(&root_ctxt, param_env, local_def_id); + + if let Ok(check) = CastCheck::new( + &fn_ctxt, e, from_ty, to_ty, + // We won't show any errors to the user, so the span is irrelevant here. + DUMMY_SP, DUMMY_SP, + ) { + check.do_check(&fn_ctxt).ok() + } else { + None + } +} + impl<'a, 'tcx> CastCheck<'tcx> { - pub fn new( + pub(crate) fn new( fcx: &FnCtxt<'a, 'tcx>, expr: &'tcx hir::Expr<'tcx>, expr_ty: Ty<'tcx>, cast_ty: Ty<'tcx>, cast_span: Span, span: Span, - constness: hir::Constness, ) -> Result, ErrorGuaranteed> { let expr_span = expr.span.find_ancestor_inside(span).unwrap_or(expr.span); - let check = CastCheck { expr, expr_ty, expr_span, cast_ty, cast_span, span, constness }; + let check = CastCheck { expr, expr_ty, expr_span, cast_ty, cast_span, span }; // For better error messages, check for some obviously unsized // cases now. We do a more thorough check at the end, once @@ -644,7 +671,7 @@ impl<'a, 'tcx> CastCheck<'tcx> { /// Checks a cast, and report an error if one exists. In some cases, this /// can return Ok and create type errors in the fcx rather than returning /// directly. coercion-cast is handled in check instead of here. - pub fn do_check(&self, fcx: &FnCtxt<'a, 'tcx>) -> Result { + fn do_check(&self, fcx: &FnCtxt<'a, 'tcx>) -> Result { use rustc_middle::ty::cast::CastTy::*; use rustc_middle::ty::cast::IntTy::*; diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index 44b19318d5d19..51d01afc4ebd8 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -1318,6 +1318,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } +/// Check whether `ty` can be coerced to `output_ty`. +/// Used from clippy. +pub fn can_coerce<'tcx>( + tcx: TyCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + body_id: LocalDefId, + ty: Ty<'tcx>, + output_ty: Ty<'tcx>, +) -> bool { + let root_ctxt = crate::typeck_root_ctxt::TypeckRootCtxt::new(tcx, body_id); + let fn_ctxt = FnCtxt::new(&root_ctxt, param_env, body_id); + fn_ctxt.can_coerce(ty, output_ty) +} + /// CoerceMany encapsulates the pattern you should use when you have /// many expressions that are all getting coerced to a common /// type. This arises, for example, when you have a match (the result diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 4da45303d1277..d3df3dd38853e 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -1390,15 +1390,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } else { // Defer other checks until we're done type checking. let mut deferred_cast_checks = self.deferred_cast_checks.borrow_mut(); - match cast::CastCheck::new( - self, - e, - t_expr, - t_cast, - t.span, - expr.span, - hir::Constness::NotConst, - ) { + match cast::CastCheck::new(self, e, t_expr, t_cast, t.span, expr.span) { Ok(cast_check) => { debug!( "check_expr_cast: deferring cast from {:?} to {:?}: {:?}", diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs index 05e7c5b2b4188..0b69c7a243173 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs @@ -38,7 +38,7 @@ use std::ops::Deref; /// /// [`ItemCtxt`]: rustc_hir_analysis::collect::ItemCtxt /// [`InferCtxt`]: infer::InferCtxt -pub struct FnCtxt<'a, 'tcx> { +pub(crate) struct FnCtxt<'a, 'tcx> { pub(super) body_id: LocalDefId, /// The parameter environment used for proving trait obligations diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs index 80fd4be53e1a2..700dde184f2d9 100644 --- a/compiler/rustc_hir_typeck/src/lib.rs +++ b/compiler/rustc_hir_typeck/src/lib.rs @@ -42,8 +42,9 @@ mod typeck_root_ctxt; mod upvar; mod writeback; -pub use fn_ctxt::FnCtxt; -pub use typeck_root_ctxt::TypeckRootCtxt; +pub use coercion::can_coerce; +use fn_ctxt::FnCtxt; +use typeck_root_ctxt::TypeckRootCtxt; use crate::check::check_fn; use crate::coercion::DynamicCoerceMany; diff --git a/compiler/rustc_hir_typeck/src/typeck_root_ctxt.rs b/compiler/rustc_hir_typeck/src/typeck_root_ctxt.rs index e493e6a0a7ed9..88777bf5b02d0 100644 --- a/compiler/rustc_hir_typeck/src/typeck_root_ctxt.rs +++ b/compiler/rustc_hir_typeck/src/typeck_root_ctxt.rs @@ -27,7 +27,7 @@ use std::ops::Deref; /// `bar()` will each have their own `FnCtxt`, but they will /// share the inference context, will process obligations together, /// can access each other's local types (scoping permitted), etc. -pub struct TypeckRootCtxt<'tcx> { +pub(crate) struct TypeckRootCtxt<'tcx> { pub(super) infcx: InferCtxt<'tcx>, pub(super) typeck_results: RefCell>, diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs index 23fc323446e0a..d3347466be982 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs @@ -12,7 +12,6 @@ use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::{BorrowKind, Expr, ExprKind, ItemKind, LangItem, Node}; -use rustc_hir_typeck::{FnCtxt, TypeckRootCtxt}; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::LateContext; use rustc_middle::mir::Mutability; @@ -437,9 +436,7 @@ fn can_change_type<'a>(cx: &LateContext<'a>, mut expr: &'a Expr<'a>, mut ty: Ty< Node::Item(item) => { if let ItemKind::Fn(_, _, body_id) = &item.kind && let output_ty = return_ty(cx, item.owner_id) - && let root_ctxt = TypeckRootCtxt::new(cx.tcx, item.owner_id.def_id) - && let fn_ctxt = FnCtxt::new(&root_ctxt, cx.param_env, item.owner_id.def_id) - && fn_ctxt.can_coerce(ty, output_ty) + && rustc_hir_typeck::can_coerce(cx.tcx, cx.param_env, item.owner_id.def_id, ty, output_ty) { if has_lifetime(output_ty) && has_lifetime(ty) { return false; diff --git a/src/tools/clippy/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs b/src/tools/clippy/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs index 6f5ac625e3575..a6a6e9a3bac37 100644 --- a/src/tools/clippy/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs +++ b/src/tools/clippy/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs @@ -1,4 +1,4 @@ -use super::utils::check_cast; +use rustc_hir_typeck::cast::check_cast; use super::TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::sugg::Sugg; @@ -22,7 +22,7 @@ pub(super) fn check<'tcx>( ) -> bool { use CastKind::{AddrPtrCast, ArrayPtrCast, FnPtrAddrCast, FnPtrPtrCast, PtrAddrCast, PtrPtrCast}; let mut app = Applicability::MachineApplicable; - let mut sugg = match check_cast(cx, e, from_ty, to_ty) { + let mut sugg = match check_cast(cx.tcx, cx.param_env, e, from_ty, to_ty) { Some(FnPtrAddrCast | PtrAddrCast) if const_context => return false, Some(PtrPtrCast | AddrPtrCast | ArrayPtrCast | FnPtrPtrCast | FnPtrAddrCast) => { Sugg::hir_with_context(cx, arg, e.span.ctxt(), "..", &mut app) diff --git a/src/tools/clippy/clippy_lints/src/transmute/utils.rs b/src/tools/clippy/clippy_lints/src/transmute/utils.rs index 15f1890aa39ea..e8ccd35b4daf6 100644 --- a/src/tools/clippy/clippy_lints/src/transmute/utils.rs +++ b/src/tools/clippy/clippy_lints/src/transmute/utils.rs @@ -1,10 +1,5 @@ -use rustc_hir as hir; -use rustc_hir::Expr; -use rustc_hir_typeck::{cast, FnCtxt, TypeckRootCtxt}; use rustc_lint::LateContext; -use rustc_middle::ty::cast::CastKind; use rustc_middle::ty::Ty; -use rustc_span::DUMMY_SP; // check if the component types of the transmuted collection and the result have different ABI, // size or alignment @@ -20,35 +15,3 @@ pub(super) fn is_layout_incompatible<'tcx>(cx: &LateContext<'tcx>, from: Ty<'tcx false } } - -/// If a cast from `from_ty` to `to_ty` is valid, returns an Ok containing the kind of -/// the cast. In certain cases, including some invalid casts from array references -/// to pointers, this may cause additional errors to be emitted and/or ICE error -/// messages. This function will panic if that occurs. -pub(super) fn check_cast<'tcx>( - cx: &LateContext<'tcx>, - e: &'tcx Expr<'_>, - from_ty: Ty<'tcx>, - to_ty: Ty<'tcx>, -) -> Option { - let hir_id = e.hir_id; - let local_def_id = hir_id.owner.def_id; - - let root_ctxt = TypeckRootCtxt::new(cx.tcx, local_def_id); - let fn_ctxt = FnCtxt::new(&root_ctxt, cx.param_env, local_def_id); - - if let Ok(check) = cast::CastCheck::new( - &fn_ctxt, - e, - from_ty, - to_ty, - // We won't show any error to the user, so we don't care what the span is here. - DUMMY_SP, - DUMMY_SP, - hir::Constness::NotConst, - ) { - check.do_check(&fn_ctxt).ok() - } else { - None - } -}