diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index a5053c408b155..95f6348cfbdbb 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -3647,11 +3647,14 @@ pub enum Polonius { impl Default for Polonius { fn default() -> Self { - if option_env!("CFG_DEFAULT_POLONIUS_NEXT").is_some() { Self::Next } else { Self::Off } + Self::DEFAULT } } impl Polonius { + pub(crate) const DEFAULT: Self = + if option_env!("CFG_DEFAULT_POLONIUS_NEXT").is_some() { Self::Next } else { Self::Off }; + /// Returns whether the legacy version of polonius is enabled pub fn is_legacy_enabled(&self) -> bool { matches!(self, Polonius::Legacy) diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 90459090ced87..bab087249593a 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -510,7 +510,7 @@ macro_rules! options { $( { TARGET_MODIFIER: $tmod_variant:ident } )? $( { MITIGATION: $mitigation_variant:ident } )? , - $desc:literal + $desc:expr $(, removed: $removed:ident )? ), )* @@ -2350,6 +2350,12 @@ options! { // - src/doc/rustc/src/codegen-options/index.md } +const POLONIUS_HELP: &str = match Polonius::DEFAULT { + Polonius::Off => "enable polonius-based borrow-checker (default: no)", + Polonius::Next => "enable polonius-based borrow-checker (default: next)", + Polonius::Legacy => panic!("Polonius::Legacy is not a valid default value"), +}; + options! { UnstableOptions, UnstableOptionsTargetModifiers, Z_OPTIONS, dbopts, "Z", "unstable", @@ -2750,7 +2756,7 @@ options! { `vt-ptr-type-discrimination - incorporate type discrimination in authenticated vtable pointers Example: `-Zpointer-authentication=+calls,-init-fini`."), polonius: Polonius = (Polonius::default(), parse_polonius, [TRACKED], - "enable polonius-based borrow-checker (default: no)"), + POLONIUS_HELP), pre_link_arg: (/* redirected to pre_link_args */) = ((), parse_string_push, [UNTRACKED], "a single extra argument to prepend the linker invocation (can be used several times)"), pre_link_args: Vec = (Vec::new(), parse_list, [UNTRACKED], diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs index 6ad20b192fa5c..8df809264a6dd 100644 --- a/library/std/src/fs.rs +++ b/library/std/src/fs.rs @@ -3620,22 +3620,22 @@ pub fn set_permissions>(path: P, perm: Permissions) -> io::Result /// /// # Platform-specific behavior /// -/// This function currently corresponds to: -/// * `open` with `O_NOFOLLOW` flag enabled + `fchmod` on WASI -/// * `fchmodat` function with the flag `AT_SYMLINK_NOFOLLOW` enabled -/// on Unix platforms -/// * The flag `FILE_FLAG_OPEN_REPARSE_POINT` is enabled and then the -/// permissions of the file is set through `SetFileInformationByHandle` -/// on Windows. -/// * On all other platforms, the behavior remains the same with -/// [`fs::set_permissions`]. -/// -/// [`fs::set_permissions`]: crate::fs::set_permissions +/// This function currently corresponds to the following underlying operations: +/// * Android: returns [`Unsupported`] on all files. +/// * Linux, BSD-based platforms, QNX, NTO: `fchmodat` with `AT_SYMLINK_NOFOLLOW`. +/// If that is not supported, we fall back to: +/// * Unix-based platforms with symlinks: `open` with `O_NOFOLLOW` followed by +/// [`fs::set_permissions`]. +/// * Unix-based platforms without symlinks: `open` followed by [`fs::set_permissions`]. +/// * Windows: `CreateFileW` with `FILE_FLAG_OPEN_REPARSE_POINT` followed +/// by `SetFileInformationByHandle`. /// /// Note that, this [may change in the future][changes]. /// /// [changes]: io#platform-specific-behavior /// +/// [`fs::set_permissions`]: crate::fs::set_permissions +/// /// # Errors /// /// This function will return an error in the following situations, but is not @@ -3644,10 +3644,8 @@ pub fn set_permissions>(path: P, perm: Permissions) -> io::Result /// * `path` does not exist. /// * The user lacks the permission to change attributes of the file. /// -/// Note: On Linux, this will result in a [`Unsupported`] error -/// if the final element is a symlink. On BSD-based systems, the -/// behavior can vary from symlink permission bits changing or -/// there being no effects on symlinks +/// Note: On Linux and other Unix-based platforms with symlinks (non-BSD-based), +/// this will result in an [`Unsupported`] error if the final element is a symlink. /// /// [`Unsupported`]: crate::io::ErrorKind::Unsupported /// @@ -3660,8 +3658,8 @@ pub fn set_permissions>(path: P, perm: Permissions) -> io::Result /// fn main() -> std::io::Result<()> { /// let mut perms = fs::symlink_metadata("foo.txt")?.permissions(); /// perms.set_readonly(true); -/// // This should result in an error on certain platforms -/// // or succeed in modifying the permissions of a symlink +/// // This should result in an error on certain platforms or +/// // succeed in modifying the permissions of a symlink /// fs::set_permissions_nofollow("foo.txt", perms)?; /// Ok(()) /// } diff --git a/library/std/src/fs/tests.rs b/library/std/src/fs/tests.rs index 075814b379027..dab1df38aa1f5 100644 --- a/library/std/src/fs/tests.rs +++ b/library/std/src/fs/tests.rs @@ -613,6 +613,7 @@ fn set_get_unix_permissions() { assert_eq!(mask & metadata1.permissions().mode(), 0o0777); } +#[cfg(not(target_os = "android"))] #[test] fn set_get_permissions_nofollows() { let tmpdir = tmpdir(); @@ -649,56 +650,71 @@ fn set_get_permissions_nofollows() { // Only Windows and Unix support `fs::set_permissions_nofollow` #[test] -#[cfg(all(any(windows, unix), not(any(target_os = "espidf", target_os = "horizon"))))] +#[cfg(all( + any(windows, unix), + not(any(target_os = "espidf", target_os = "horizon", target_os = "wasi")) +))] fn set_get_permissions_nofollows_symlink() { #[cfg(not(windows))] - use crate::os::unix::fs::symlink as symlink_dir; + use crate::os::unix::fs::symlink as symlink_file; #[cfg(windows)] - use crate::os::windows::fs::symlink_dir; + use crate::os::windows::fs::symlink_file; let tmpdir = tmpdir(); let filename = tmpdir.join("set_get_unix_permissions_file"); let symlink_name = tmpdir.join("set_get_unix_permissions"); check!(File::create(&filename)); - check!(symlink_dir(&filename, &symlink_name)); + check!(symlink_file(&filename, &symlink_name)); - let sym_metadata = check!(fs::symlink_metadata(&symlink_name)); - let mut permission_bits = sym_metadata.permissions(); - permission_bits.set_readonly(true); - let result = fs::set_permissions_nofollow(&symlink_name, permission_bits); + let init_symlink_metadata = check!(fs::symlink_metadata(&symlink_name)); + let mut init_symlink_permissions = init_symlink_metadata.permissions(); + + let init_target_metadata = check!(fs::metadata(&symlink_name)); + let init_target_permissions = init_target_metadata.permissions(); + + // Set symlink permissions to readonly + init_symlink_permissions.set_readonly(true); + let result = fs::set_permissions_nofollow(&symlink_name, init_symlink_permissions); cfg_select! { any( windows, - target_os = "android", target_os = "macos", target_os = "freebsd", target_os = "openbsd", target_os = "netbsd", - target_os = "dragonfly" + target_os = "dragonfly", + target_os = "nto", + target_os = "qnx" ) => { assert_eq!(result.unwrap(), ()); - let metadata0 = check!(fs::symlink_metadata(&symlink_name)); - // So seems like BSD-based systems trying to set permissions - // on symlinks could lead to no effect, so we should expect - // there being no change to BSD-based systems. + + let after_target_metadata = check!(fs::metadata(&symlink_name)); + // We should expect the target file to not have its permission bits + // changed + assert_eq!(after_target_metadata.permissions(), init_target_permissions); + + let after_symlink_metadata = check!(fs::symlink_metadata(&symlink_name)); + // On these systems, it's confirmed the symlink itself is marked readonly // https://superuser.com/questions/1099634/change-permissions-symbolic-link-mac-os - #[cfg(windows)] - assert!(metadata0.permissions().readonly()); - #[cfg(not(windows))] - assert!(!metadata0.permissions().readonly()); + assert!(after_symlink_metadata.permissions().readonly()); // Reset the read-only bit under Windows 7: avoids the // `TempDir::drop` from crashing on a permission denial when // trying to delete the file that has it. #[cfg(all(windows, target_vendor = "win7"))] { - let mut permission_bits = metadata0.permissions(); - permission_bits.set_readonly(false); - check!(fs::set_permissions_nofollow(&symlink_name, permission_bits)); + let mut symlink_permission_bits = after_symlink_metadata.permissions(); + symlink_permission_bits.set_readonly(false); + check!(fs::set_permissions_nofollow(&symlink_name, symlink_permission_bits)); } } _ => { + let after_target_metadata = check!(fs::metadata(&symlink_name)); + // We should expect the target file to not have its permission bits + // changed + assert_eq!(after_target_metadata.permissions(), init_target_permissions); + let error_kind = result.unwrap_err().kind(); assert_eq!(error_kind, crate::io::ErrorKind::Unsupported); } @@ -1426,6 +1442,7 @@ fn fchmod_works() { check!(file.set_permissions(p)); } +#[cfg(not(target_os = "android"))] #[test] fn fchmodat_works() { let tmpdir = tmpdir(); diff --git a/library/std/src/path.rs b/library/std/src/path.rs index 0f527e902cfec..f2fc148551d2e 100644 --- a/library/std/src/path.rs +++ b/library/std/src/path.rs @@ -2377,7 +2377,7 @@ pub struct NormalizeError; impl Path { // The following (private!) function allows construction of a path from a u8 // slice, which is only safe when it is known to follow the OsStr encoding. - unsafe fn from_u8_slice(s: &[u8]) -> &Path { + pub(crate) unsafe fn from_u8_slice(s: &[u8]) -> &Path { unsafe { Path::new(OsStr::from_encoded_bytes_unchecked(s)) } } // The following (private!) function reveals the byte encoding used for OsStr. diff --git a/library/std/src/sys/fs/unix.rs b/library/std/src/sys/fs/unix.rs index b33ebadebe4ad..74b4322d027c5 100644 --- a/library/std/src/sys/fs/unix.rs +++ b/library/std/src/sys/fs/unix.rs @@ -1884,31 +1884,91 @@ pub fn set_perm(p: &CStr, perm: FilePermissions) -> io::Result<()> { cvt_r(|| unsafe { libc::chmod(p.as_ptr(), perm.mode) }).map(|_| ()) } +#[cfg(target_os = "android")] +pub fn set_perm_nofollow(_p: &CStr, _perm: FilePermissions) -> io::Result<()> { + // Currently Android seems to be having inconsistent behavior with fchmodat + // with `AT_SYMLINK_NOFOLLOW` or openat with `O_NOFOLLOW` + fchmod. + // See this issue here mentioning inconsistent behavior on fchmodat: + // https://github.com/android/ndk/issues/1258 + // On the arm-android CI job, using fchmodat with `AT_SYMLINK_NOFOLLOW` + + // fallback behavior on a symlink sets the target file's permissions, + // which is incorrect behavior. + Err(crate::io::ErrorKind::Unsupported.into()) +} + +#[cfg(not(target_os = "android"))] pub fn set_perm_nofollow(p: &CStr, perm: FilePermissions) -> io::Result<()> { - // ESP-IDF and Horizon do not support O_NOFOLLOW, so we skip setting it. - // Their filesystems do not have symbolic links, so no special handling is required. - cfg_select! { - // wasm32-wasip1 targets do not support fchmodat, so we fall down to - // open + fchmod - target_os = "wasi" => { - use crate::fs::{OpenOptions, Permissions}; - use crate::os::wasi::ffi::OsStrExt; - use crate::os::wasi::fs::OpenOptionsExt; + #[inline] + /// Helper function for fallback open with `O_NOFOLLOW` + `fchmod` behavior + fn open_and_set_permissions(p: &CStr, perm: FilePermissions) -> io::Result<()> { + use crate::fs::{OpenOptions, Permissions}; - let mut options = OpenOptions::new(); - options.custom_flags(libc::O_NOFOLLOW); + let mut options = OpenOptions::new(); - let bytes = p.to_bytes(); - let os_str = OsStr::from_bytes(bytes); - options.open(Path::new(os_str))?.set_permissions(Permissions::from_inner(perm)) + // ESP-IDF and Horizon do not support O_NOFOLLOW, so we skip setting it. + // Their filesystems do not have symbolic links, so no special handling is required. + #[cfg(not(any(target_os = "espidf", target_os = "horizon")))] + { + #[cfg(not(target_os = "wasi"))] + use crate::os::unix::fs::OpenOptionsExt; + #[cfg(target_os = "wasi")] + use crate::os::wasi::fs::OpenOptionsExt; + options.read(true).custom_flags(libc::O_NOFOLLOW); } - all(target_os = "linux", not(any(target_os = "espidf", target_os = "horizon"))) => { - cvt_r(|| unsafe { - libc::fchmodat(libc::AT_FDCWD, p.as_ptr(), perm.mode, libc::AT_SYMLINK_NOFOLLOW) - }) - .map(|_| ()) + + // SAFETY: Since this function is called with `with_native_path` + // and that successfully converted the `&Path` to a `CString`, + // it should be safe to convert the `&CStr` back to a `Path`. + let os_str = unsafe { OsStr::from_encoded_bytes_unchecked(p.to_bytes()) }; + options.open(Path::new(os_str))?.set_permissions(Permissions::from_inner(perm)) + } + + // This res value is modified for platforms that support the `fchmodat` syscall. + #[allow(unused)] + let mut res: Result<(), core::io::Error> = Err(crate::io::ErrorKind::Unsupported.into()); + + // These platforms support `fchmodat`, so utilize this syscall over `open` + `fchmod` + #[cfg(any( + target_os = "linux", + target_os = "macos", + target_os = "freebsd", + target_os = "openbsd", + target_os = "netbsd", + target_os = "dragonfly", + target_os = "nto", + target_os = "qnx" + ))] + { + res = cvt_r(|| unsafe { + libc::fchmodat(libc::AT_FDCWD, p.as_ptr(), perm.mode, libc::AT_SYMLINK_NOFOLLOW) + }) + .map(|_| ()); + } + + // If fchmodat fails with `ErrorKind::Unsupported` fallback to using open + fchmod. This is just in case + // for older systems like Ubuntu 20.04 where fchmodat fails with EOPNOTSUPP on both regular files and + // symlinks when AT_SYMLINK_NOFOLLOW is passed in. + match res { + Ok(_) => Ok(()), + Err(err) => { + if err.kind() == crate::io::ErrorKind::Unsupported { + match open_and_set_permissions(p, perm) { + Ok(_) => return Ok(()), + Err(e) => { + if e.kind() == crate::io::ErrorKind::FilesystemLoop { + // When open is used with O_NOFOLLOW flag, if the trailing component of + // a path is a symbolic link, it should fail with ELOOP error. Instead of + // returning `FilesystemLoop`, this returns `Unsupported` to keep it consistent + // with what `fchmodat` would return when chmoding a symlink using AT_SYMLINK_NOFOLLOW. + return Err(err); + } + return Err(e); + } + } + } + + Err(err) } - _ => cvt_r(|| unsafe { libc::fchmodat(libc::AT_FDCWD, p.as_ptr(), perm.mode, 0) }).map(|_| ()), } } diff --git a/tests/run-make/rustc-help/polonius-help-stable.stdout b/tests/run-make/rustc-help/polonius-help-stable.stdout new file mode 100644 index 0000000000000..9f56fb6de9481 --- /dev/null +++ b/tests/run-make/rustc-help/polonius-help-stable.stdout @@ -0,0 +1 @@ + -Z polonius=val -- enable polonius-based borrow-checker (default: no) diff --git a/tests/run-make/rustc-help/polonius-help.stdout b/tests/run-make/rustc-help/polonius-help.stdout new file mode 100644 index 0000000000000..b1dfd15c09958 --- /dev/null +++ b/tests/run-make/rustc-help/polonius-help.stdout @@ -0,0 +1 @@ + -Z polonius=val -- enable polonius-based borrow-checker (default: next) diff --git a/tests/run-make/rustc-help/rmake.rs b/tests/run-make/rustc-help/rmake.rs index 17811ef18449f..84f8c1b59b629 100644 --- a/tests/run-make/rustc-help/rmake.rs +++ b/tests/run-make/rustc-help/rmake.rs @@ -5,6 +5,7 @@ use run_make_support::{bare_rustc, diff, similar}; fn main() { // `rustc --help` let help = bare_rustc().arg("--help").run().stdout_utf8(); + diff().expected_file("help.stdout").actual_text("(rustc --help)", &help).run(); // `rustc` should be the same as `rustc --help` @@ -22,6 +23,20 @@ fn main() { // Check that all help options can be invoked at once let codegen_help = bare_rustc().arg("-Chelp").run().stdout_utf8(); let unstable_help = bare_rustc().arg("-Zhelp").run().stdout_utf8(); + + let polonius_help = + format!("{}\n", unstable_help.lines().find(|line| line.contains("polonius=val")).unwrap()); + let version = bare_rustc().arg("--version").run().stdout_utf8(); + let expected_file = if version.contains("-nightly") || version.contains("-dev") { + "polonius-help.stdout" + } else { + "polonius-help-stable.stdout" + }; + diff() + .expected_file(expected_file) + .actual_text("rustc -Zhelp (polonius)", &polonius_help) + .run(); + let lints_help = bare_rustc().arg("-Whelp").run().stdout_utf8(); let expected_all = format!("{help}{codegen_help}{unstable_help}{lints_help}"); let all_help = bare_rustc().args(["--help", "-Chelp", "-Zhelp", "-Whelp"]).run().stdout_utf8(); diff --git a/tests/ui/associated-consts/assoc-const-equality-unexpected-region.rs b/tests/ui/associated-consts/assoc-const-equality-unexpected-region.rs new file mode 100644 index 0000000000000..e8a3dcf47eea3 --- /dev/null +++ b/tests/ui/associated-consts/assoc-const-equality-unexpected-region.rs @@ -0,0 +1,14 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/143896. + +trait TraitA<'a> { + const K: usize = 0; +} + +impl TraitA<'_> for () {} +//~^ ERROR the type parameter `T` is not constrained + +impl dyn TraitA<'_> where (): TraitA<'a, K = 0> {} +//~^ ERROR use of undeclared lifetime name `'a` +//~| ERROR associated const equality is incomplete + +pub fn main() {} diff --git a/tests/ui/associated-consts/assoc-const-equality-unexpected-region.stderr b/tests/ui/associated-consts/assoc-const-equality-unexpected-region.stderr new file mode 100644 index 0000000000000..6e38e3e41acbf --- /dev/null +++ b/tests/ui/associated-consts/assoc-const-equality-unexpected-region.stderr @@ -0,0 +1,40 @@ +error[E0261]: use of undeclared lifetime name `'a` + --> $DIR/assoc-const-equality-unexpected-region.rs:10:38 + | +LL | impl dyn TraitA<'_> where (): TraitA<'a, K = 0> {} + | ^^ undeclared lifetime + | + = note: for more information on higher-ranked polymorphism, visit https://doc.rust-lang.org/nomicon/hrtb.html +help: consider making the bound lifetime-generic with a new `'a` lifetime + | +LL | impl dyn TraitA<'_> where (): for<'a> TraitA<'a, K = 0> {} + | +++++++ +help: consider making the bound lifetime-generic with a new `'a` lifetime + | +LL | impl dyn TraitA<'_> where for<'a> (): TraitA<'a, K = 0> {} + | +++++++ +help: consider introducing lifetime `'a` here + | +LL | impl<'a> dyn TraitA<'_> where (): TraitA<'a, K = 0> {} + | ++++ + +error[E0658]: associated const equality is incomplete + --> $DIR/assoc-const-equality-unexpected-region.rs:10:42 + | +LL | impl dyn TraitA<'_> where (): TraitA<'a, K = 0> {} + | ^^^^^ + | + = note: see issue #132980 for more information + = help: add `#![feature(min_generic_const_args)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0207]: the type parameter `T` is not constrained by the impl trait, self type, or predicates + --> $DIR/assoc-const-equality-unexpected-region.rs:7:6 + | +LL | impl TraitA<'_> for () {} + | ^ unconstrained type parameter + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0207, E0261, E0658. +For more information about an error, try `rustc --explain E0207`. diff --git a/tests/ui/lint/improper-ctypes/tait-extern-fn-next-solver-issue-156345.rs b/tests/ui/lint/improper-ctypes/tait-extern-fn-next-solver-issue-156345.rs new file mode 100644 index 0000000000000..fd8ba56b10be6 --- /dev/null +++ b/tests/ui/lint/improper-ctypes/tait-extern-fn-next-solver-issue-156345.rs @@ -0,0 +1,42 @@ +//@ compile-flags: -Znext-solver=globally +//@ edition: 2021 +//@ check-pass + +// Regression test for . +// An `extern "C" fn` taking a type alias impl trait argument used to ICE with +// the new solver, leaving an entry in the `OpaqueTypeStorage`. Only the new +// solver was affected. + +#![feature(type_alias_impl_trait)] +#![allow(improper_ctypes_definitions)] + +struct Foo { + field: String, +} + +type Tait = impl Sized; + +#[define_opaque(Tait)] +extern "C" fn ice_cold(beverage: Tait) { + let Foo { field } = beverage; + let _ = field; +} + +// A second reproducer from the same issue, with the opaque type in return +// position behind a higher-ranked closure bound. +struct Parser(H); + +impl Parser +where + H: for<'a> Fn(&'a str) -> T, +{ + fn new(handler: H) -> Parser { + Parser(handler) + } + + extern "C" fn many<'s>() -> Parser Fn(&'a str) + 's> { + Parser::new(|_| ()) + } +} + +fn main() {}