diff --git a/.gitignore b/.gitignore index 1c50d9b054ddc..5f7135e38d113 100644 --- a/.gitignore +++ b/.gitignore @@ -31,6 +31,7 @@ __pycache__/ /inst/ /llvm/ /mingw-build/ +/src/tools/x/target # Created by default with `src/ci/docker/run.sh`: /obj/ /unicode-downloads diff --git a/Cargo.toml b/Cargo.toml index c27e5c469cf4b..e1a36d880867b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,11 +29,17 @@ members = [ "src/tools/unicode-table-generator", "src/tools/expand-yaml-anchors", ] + exclude = [ "build", "compiler/rustc_codegen_cranelift", # HACK(eddyb) This hardcodes the fact that our CI uses `/checkout/obj`. "obj", + # The `x` binary is a thin wrapper that calls `x.py`, which initializes + # submodules, before which workspace members cannot be invoked because + # not all `Cargo.toml` files are available, so we exclude the `x` binary, + # so it can be invoked before the current checkout is set up. + "src/tools/x", ] [profile.release.package.compiler_builtins] diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index ff7a145c2668d..1d0d6980b7a89 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -2801,6 +2801,7 @@ declare_lint_pass! { UNSTABLE_NAME_COLLISIONS, IRREFUTABLE_LET_PATTERNS, BROKEN_INTRA_DOC_LINKS, + PRIVATE_INTRA_DOC_LINKS, INVALID_CODEBLOCK_ATTRIBUTES, MISSING_CRATE_LEVEL_DOCS, MISSING_DOC_CODE_EXAMPLES, diff --git a/library/alloc/src/collections/btree/map/tests.rs b/library/alloc/src/collections/btree/map/tests.rs index d5b1c600d93eb..4fea6adf54107 100644 --- a/library/alloc/src/collections/btree/map/tests.rs +++ b/library/alloc/src/collections/btree/map/tests.rs @@ -42,7 +42,7 @@ fn test_all_refs<'a, T: 'a>(dummy: &mut T, iter: impl Iterator } } -impl<'a, K: 'a, V: 'a> BTreeMap { +impl BTreeMap { /// Panics if the map (or the code navigating it) is corrupted. fn check(&self) where @@ -54,14 +54,14 @@ impl<'a, K: 'a, V: 'a> BTreeMap { assert!(root_node.ascend().is_err()); root_node.assert_back_pointers(); - let counted = root_node.assert_ascending(); - assert_eq!(self.length, counted); assert_eq!(self.length, root_node.calc_length()); root_node.assert_min_len(if root_node.height() > 0 { 1 } else { 0 }); } else { assert_eq!(self.length, 0); } + + self.assert_ascending(); } /// Returns the height of the root, if any. @@ -79,10 +79,28 @@ impl<'a, K: 'a, V: 'a> BTreeMap { String::from("not yet allocated") } } + + /// Asserts that the keys are in strictly ascending order. + fn assert_ascending(&self) + where + K: Copy + Debug + Ord, + { + let mut num_seen = 0; + let mut keys = self.keys(); + if let Some(mut previous) = keys.next() { + num_seen = 1; + for next in keys { + assert!(previous < next, "{:?} >= {:?}", previous, next); + previous = next; + num_seen += 1; + } + } + assert_eq!(num_seen, self.len()); + } } impl<'a, K: 'a, V: 'a> NodeRef, K, V, marker::LeafOrInternal> { - pub fn assert_min_len(self, min_len: usize) { + fn assert_min_len(self, min_len: usize) { assert!(self.len() >= min_len, "{} < {}", self.len(), min_len); if let node::ForceResult::Internal(node) = self.force() { for idx in 0..=node.len() { diff --git a/library/alloc/src/collections/btree/node.rs b/library/alloc/src/collections/btree/node.rs index c8d3de9e5cd5c..433074027e7f7 100644 --- a/library/alloc/src/collections/btree/node.rs +++ b/library/alloc/src/collections/btree/node.rs @@ -1608,15 +1608,19 @@ pub mod marker { unsafe fn slice_insert(slice: &mut [T], idx: usize, val: T) { unsafe { - ptr::copy(slice.as_ptr().add(idx), slice.as_mut_ptr().add(idx + 1), slice.len() - idx); - ptr::write(slice.get_unchecked_mut(idx), val); + let len = slice.len(); + let slice_ptr = slice.as_mut_ptr(); + ptr::copy(slice_ptr.add(idx), slice_ptr.add(idx + 1), len - idx); + ptr::write(slice_ptr.add(idx), val); } } unsafe fn slice_remove(slice: &mut [T], idx: usize) -> T { unsafe { - let ret = ptr::read(slice.get_unchecked(idx)); - ptr::copy(slice.as_ptr().add(idx + 1), slice.as_mut_ptr().add(idx), slice.len() - idx - 1); + let len = slice.len(); + let slice_ptr = slice.as_mut_ptr(); + let ret = ptr::read(slice_ptr.add(idx)); + ptr::copy(slice_ptr.add(idx + 1), slice_ptr.add(idx), len - idx - 1); ret } } diff --git a/library/alloc/src/collections/btree/node/tests.rs b/library/alloc/src/collections/btree/node/tests.rs index d6527057c5d77..38c75de34eeeb 100644 --- a/library/alloc/src/collections/btree/node/tests.rs +++ b/library/alloc/src/collections/btree/node/tests.rs @@ -17,43 +17,6 @@ impl<'a, K: 'a, V: 'a> NodeRef, K, V, marker::LeafOrInternal> } } - /// Asserts that the keys are in strictly ascending order. - /// Returns how many keys it encountered. - pub fn assert_ascending(self) -> usize - where - K: Copy + Debug + Ord, - { - struct SeriesChecker { - num_seen: usize, - previous: Option, - } - impl SeriesChecker { - fn is_ascending(&mut self, next: T) { - if let Some(previous) = self.previous { - assert!(previous < next, "{:?} >= {:?}", previous, next); - } - self.previous = Some(next); - self.num_seen += 1; - } - } - - let mut checker = SeriesChecker { num_seen: 0, previous: None }; - self.visit_nodes_in_order(|pos| match pos { - navigate::Position::Leaf(node) => { - for idx in 0..node.len() { - let key = *unsafe { node.key_at(idx) }; - checker.is_ascending(key); - } - } - navigate::Position::InternalKV(kv) => { - let key = *kv.into_kv().0; - checker.is_ascending(key); - } - navigate::Position::Internal(_) => {} - }); - checker.num_seen - } - pub fn dump_keys(self) -> String where K: Debug, diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index 405667e0b2aa6..15092d463ec44 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -119,7 +119,6 @@ #![feature(raw_ref_op)] #![feature(rustc_attrs)] #![feature(receiver_trait)] -#![feature(renamed_spin_loop)] #![feature(min_specialization)] #![feature(slice_ptr_get)] #![feature(slice_ptr_len)] diff --git a/library/core/src/cell.rs b/library/core/src/cell.rs index ef2a5dd570f73..5eaecce67eb7f 100644 --- a/library/core/src/cell.rs +++ b/library/core/src/cell.rs @@ -406,7 +406,8 @@ impl Cell { /// assert_eq!(five, 5); /// ``` #[stable(feature = "move_cell", since = "1.17.0")] - pub fn into_inner(self) -> T { + #[rustc_const_unstable(feature = "const_cell_into_inner", issue = "78729")] + pub const fn into_inner(self) -> T { self.value.into_inner() } } @@ -668,12 +669,11 @@ impl RefCell { /// let five = c.into_inner(); /// ``` #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_unstable(feature = "const_cell_into_inner", issue = "78729")] #[inline] - pub fn into_inner(self) -> T { + pub const fn into_inner(self) -> T { // Since this function takes `self` (the `RefCell`) by value, the // compiler statically verifies that it is not currently borrowed. - // Therefore the following assertion is just a `debug_assert!`. - debug_assert!(self.borrow.get() == UNUSED); self.value.into_inner() } @@ -1682,7 +1682,8 @@ impl UnsafeCell { /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] - pub fn into_inner(self) -> T { + #[rustc_const_unstable(feature = "const_cell_into_inner", issue = "78729")] + pub const fn into_inner(self) -> T { self.value } } diff --git a/library/core/src/hint.rs b/library/core/src/hint.rs index 454fb34e77e49..979a5f8cf5075 100644 --- a/library/core/src/hint.rs +++ b/library/core/src/hint.rs @@ -1,6 +1,7 @@ #![stable(feature = "core_hint", since = "1.27.0")] //! Hints to compiler that affects how code should be emitted or optimized. +//! Hints may be compile time or runtime. use crate::intrinsics; @@ -24,7 +25,6 @@ use crate::intrinsics; /// Otherwise, consider using the [`unreachable!`] macro, which does not allow /// optimizations but will panic when executed. /// -/// /// # Example /// /// ``` @@ -51,18 +51,62 @@ pub const unsafe fn unreachable_unchecked() -> ! { unsafe { intrinsics::unreachable() } } -/// Emits a machine instruction hinting to the processor that it is running in busy-wait -/// spin-loop ("spin lock"). +/// Emits a machine instruction to signal the processor that it is running in +/// a busy-wait spin-loop ("spin lock"). +/// +/// Upon receiving the spin-loop signal the processor can optimize its behavior by, +/// for example, saving power or switching hyper-threads. +/// +/// This function is different from [`thread::yield_now`] which directly +/// yields to the system's scheduler, whereas `spin_loop` does not interact +/// with the operating system. +/// +/// A common use case for `spin_loop` is implementing bounded optimistic +/// spinning in a CAS loop in synchronization primitives. To avoid problems +/// like priority inversion, it is strongly recommended that the spin loop is +/// terminated after a finite amount of iterations and an appropriate blocking +/// syscall is made. +/// +/// **Note**: On platforms that do not support receiving spin-loop hints this +/// function does not do anything at all. +/// +/// # Examples /// -/// For a discussion of different locking strategies and their trade-offs, see -/// [`core::sync::atomic::spin_loop_hint`]. +/// ``` +/// use std::sync::atomic::{AtomicBool, Ordering}; +/// use std::sync::Arc; +/// use std::{hint, thread}; +/// +/// // A shared atomic value that threads will use to coordinate +/// let live = Arc::new(AtomicBool::new(false)); +/// +/// // In a background thread we'll eventually set the value +/// let bg_work = { +/// let live = live.clone(); +/// thread::spawn(move || { +/// // Do some work, then make the value live +/// do_some_work(); +/// live.store(true, Ordering::Release); +/// }) +/// }; /// -/// **Note**: On platforms that do not support receiving spin-loop hints this function does not -/// do anything at all. +/// // Back on our current thread, we wait for the value to be set +/// while live.load(Ordering::Acquire) { +/// // The spin loop is a hint to the CPU that we're waiting, but probably +/// // not for very long +/// hint::spin_loop(); +/// } +/// +/// // The value is now set +/// # fn do_some_work() {} +/// do_some_work(); +/// bg_work.join()?; +/// # Ok::<(), Box>(()) +/// ``` /// -/// [`core::sync::atomic::spin_loop_hint`]: crate::sync::atomic::spin_loop_hint +/// [`thread::yield_now`]: ../../std/thread/fn.yield_now.html #[inline] -#[unstable(feature = "renamed_spin_loop", issue = "55002")] +#[stable(feature = "renamed_spin_loop", since = "1.49.0")] pub fn spin_loop() { #[cfg(all(any(target_arch = "x86", target_arch = "x86_64"), target_feature = "sse2"))] { diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index 069e6e7e71881..ea560288c28bd 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -70,6 +70,7 @@ #![feature(cfg_target_has_atomic)] #![feature(const_alloc_layout)] #![feature(const_discriminant)] +#![feature(const_cell_into_inner)] #![feature(const_checked_int_methods)] #![feature(const_euclidean_int_methods)] #![feature(const_float_classify)] diff --git a/library/core/src/sync/atomic.rs b/library/core/src/sync/atomic.rs index 2edeb81011af9..d48c02bf59c64 100644 --- a/library/core/src/sync/atomic.rs +++ b/library/core/src/sync/atomic.rs @@ -115,23 +115,13 @@ use crate::hint::spin_loop; /// Signals the processor that it is inside a busy-wait spin-loop ("spin lock"). /// -/// Upon receiving spin-loop signal the processor can optimize its behavior by, for example, saving -/// power or switching hyper-threads. -/// -/// This function is different from [`std::thread::yield_now`] which directly yields to the -/// system's scheduler, whereas `spin_loop_hint` does not interact with the operating system. -/// -/// A common use case for `spin_loop_hint` is implementing bounded optimistic spinning in a CAS -/// loop in synchronization primitives. To avoid problems like priority inversion, it is strongly -/// recommended that the spin loop is terminated after a finite amount of iterations and an -/// appropriate blocking syscall is made. +/// This function is expected to be deprecated in favor of +/// [`hint::spin_loop`]. /// /// **Note**: On platforms that do not support receiving spin-loop hints this function does not /// do anything at all. /// -/// [`std::thread::yield_now`]: ../../../std/thread/fn.yield_now.html -/// [`std::thread::sleep`]: ../../../std/thread/fn.sleep.html -/// [`std::sync::Mutex`]: ../../../std/sync/struct.Mutex.html +/// [`hint::spin_loop`]: crate::hint::spin_loop #[inline] #[stable(feature = "spin_loop_hint", since = "1.24.0")] pub fn spin_loop_hint() { @@ -365,7 +355,8 @@ impl AtomicBool { /// ``` #[inline] #[stable(feature = "atomic_access", since = "1.15.0")] - pub fn into_inner(self) -> bool { + #[rustc_const_unstable(feature = "const_cell_into_inner", issue = "78729")] + pub const fn into_inner(self) -> bool { self.v.into_inner() != 0 } @@ -941,7 +932,8 @@ impl AtomicPtr { /// ``` #[inline] #[stable(feature = "atomic_access", since = "1.15.0")] - pub fn into_inner(self) -> *mut T { + #[rustc_const_unstable(feature = "const_cell_into_inner", issue = "78729")] + pub const fn into_inner(self) -> *mut T { self.p.into_inner() } @@ -1462,7 +1454,8 @@ assert_eq!(some_var.into_inner(), 5); ```"), #[inline] #[$stable_access] - pub fn into_inner(self) -> $int_type { + #[rustc_const_unstable(feature = "const_cell_into_inner", issue = "78729")] + pub const fn into_inner(self) -> $int_type { self.v.into_inner() } } diff --git a/library/core/src/task/poll.rs b/library/core/src/task/poll.rs index 4e987a53b2cb2..6851f3fcd2fc7 100644 --- a/library/core/src/task/poll.rs +++ b/library/core/src/task/poll.rs @@ -39,15 +39,17 @@ impl Poll { /// Returns `true` if this is `Poll::Ready` #[inline] + #[rustc_const_stable(feature = "const_poll", since = "1.49.0")] #[stable(feature = "futures_api", since = "1.36.0")] - pub fn is_ready(&self) -> bool { + pub const fn is_ready(&self) -> bool { matches!(*self, Poll::Ready(_)) } /// Returns `true` if this is `Poll::Pending` #[inline] + #[rustc_const_stable(feature = "const_poll", since = "1.49.0")] #[stable(feature = "futures_api", since = "1.36.0")] - pub fn is_pending(&self) -> bool { + pub const fn is_pending(&self) -> bool { !self.is_ready() } } diff --git a/library/core/tests/cell.rs b/library/core/tests/cell.rs index 40be01f443931..77517879dd250 100644 --- a/library/core/tests/cell.rs +++ b/library/core/tests/cell.rs @@ -422,3 +422,15 @@ fn refcell_format() { let msg = format!("{name} {}", &*what.borrow(), name = &*name.borrow()); assert_eq!(msg, "rust rocks".to_string()); } + +#[allow(dead_code)] +fn const_cells() { + const UNSAFE_CELL: UnsafeCell = UnsafeCell::new(3); + const _: i32 = UNSAFE_CELL.into_inner(); + + const REF_CELL: RefCell = RefCell::new(3); + const _: i32 = REF_CELL.into_inner(); + + const CELL: Cell = Cell::new(3); + const _: i32 = CELL.into_inner(); +} diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs index 0c4ce867f542d..c9f9b890c3938 100644 --- a/library/core/tests/lib.rs +++ b/library/core/tests/lib.rs @@ -9,6 +9,7 @@ #![feature(box_syntax)] #![feature(cell_update)] #![feature(const_assume)] +#![feature(const_cell_into_inner)] #![feature(core_intrinsics)] #![feature(core_private_bignum)] #![feature(core_private_diy_float)] @@ -91,5 +92,6 @@ mod result; mod slice; mod str; mod str_lossy; +mod task; mod time; mod tuple; diff --git a/library/core/tests/task.rs b/library/core/tests/task.rs new file mode 100644 index 0000000000000..d71fef9e5c87d --- /dev/null +++ b/library/core/tests/task.rs @@ -0,0 +1,14 @@ +use core::task::Poll; + +#[test] +fn poll_const() { + // test that the methods of `Poll` are usable in a const context + + const POLL: Poll = Poll::Pending; + + const IS_READY: bool = POLL.is_ready(); + assert!(!IS_READY); + + const IS_PENDING: bool = POLL.is_pending(); + assert!(IS_PENDING); +} diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs index 161bfe3795c2c..c256f556b3c8f 100644 --- a/library/std/src/fs.rs +++ b/library/std/src/fs.rs @@ -1701,10 +1701,14 @@ pub fn copy, Q: AsRef>(from: P, to: Q) -> io::Result { /// The `dst` path will be a link pointing to the `src` path. Note that systems /// often require these two paths to both be located on the same filesystem. /// +/// If `src` names a symbolic link, it is platform-specific whether the symbolic +/// link is followed. On platforms where it's possible to not follow it, it is +/// not followed, and the created hard link points to the symbolic link itself. +/// /// # Platform-specific behavior /// -/// This function currently corresponds to the `link` function on Unix -/// and the `CreateHardLink` function on Windows. +/// This function currently corresponds to the `linkat` function with no flags +/// on Unix and the `CreateHardLink` function on Windows. /// Note that, this [may change in the future][changes]. /// /// [changes]: io#platform-specific-behavior diff --git a/library/std/src/fs/tests.rs b/library/std/src/fs/tests.rs index 38fd470a1c322..0642dca8e48a7 100644 --- a/library/std/src/fs/tests.rs +++ b/library/std/src/fs/tests.rs @@ -1336,3 +1336,54 @@ fn metadata_access_times() { } } } + +/// Test creating hard links to symlinks. +#[test] +fn symlink_hard_link() { + let tmpdir = tmpdir(); + + // Create "file", a file. + check!(fs::File::create(tmpdir.join("file"))); + + // Create "symlink", a symlink to "file". + check!(symlink_file("file", tmpdir.join("symlink"))); + + // Create "hard_link", a hard link to "symlink". + check!(fs::hard_link(tmpdir.join("symlink"), tmpdir.join("hard_link"))); + + // "hard_link" should appear as a symlink. + assert!(check!(fs::symlink_metadata(tmpdir.join("hard_link"))).file_type().is_symlink()); + + // We sould be able to open "file" via any of the above names. + let _ = check!(fs::File::open(tmpdir.join("file"))); + assert!(fs::File::open(tmpdir.join("file.renamed")).is_err()); + let _ = check!(fs::File::open(tmpdir.join("symlink"))); + let _ = check!(fs::File::open(tmpdir.join("hard_link"))); + + // Rename "file" to "file.renamed". + check!(fs::rename(tmpdir.join("file"), tmpdir.join("file.renamed"))); + + // Now, the symlink and the hard link should be dangling. + assert!(fs::File::open(tmpdir.join("file")).is_err()); + let _ = check!(fs::File::open(tmpdir.join("file.renamed"))); + assert!(fs::File::open(tmpdir.join("symlink")).is_err()); + assert!(fs::File::open(tmpdir.join("hard_link")).is_err()); + + // The symlink and the hard link should both still point to "file". + assert!(fs::read_link(tmpdir.join("file")).is_err()); + assert!(fs::read_link(tmpdir.join("file.renamed")).is_err()); + assert_eq!(check!(fs::read_link(tmpdir.join("symlink"))), Path::new("file")); + assert_eq!(check!(fs::read_link(tmpdir.join("hard_link"))), Path::new("file")); + + // Remove "file.renamed". + check!(fs::remove_file(tmpdir.join("file.renamed"))); + + // Now, we can't open the file by any name. + assert!(fs::File::open(tmpdir.join("file")).is_err()); + assert!(fs::File::open(tmpdir.join("file.renamed")).is_err()); + assert!(fs::File::open(tmpdir.join("symlink")).is_err()); + assert!(fs::File::open(tmpdir.join("hard_link")).is_err()); + + // "hard_link" should still appear as a symlink. + assert!(check!(fs::symlink_metadata(tmpdir.join("hard_link"))).file_type().is_symlink()); +} diff --git a/library/std/src/io/cursor.rs b/library/std/src/io/cursor.rs index 5733735dc4ab4..bbee2cc98425e 100644 --- a/library/std/src/io/cursor.rs +++ b/library/std/src/io/cursor.rs @@ -94,7 +94,8 @@ impl Cursor { /// # force_inference(&buff); /// ``` #[stable(feature = "rust1", since = "1.0.0")] - pub fn new(inner: T) -> Cursor { + #[rustc_const_unstable(feature = "const_io_structs", issue = "78812")] + pub const fn new(inner: T) -> Cursor { Cursor { pos: 0, inner } } @@ -130,7 +131,8 @@ impl Cursor { /// let reference = buff.get_ref(); /// ``` #[stable(feature = "rust1", since = "1.0.0")] - pub fn get_ref(&self) -> &T { + #[rustc_const_unstable(feature = "const_io_structs", issue = "78812")] + pub const fn get_ref(&self) -> &T { &self.inner } @@ -175,7 +177,8 @@ impl Cursor { /// assert_eq!(buff.position(), 1); /// ``` #[stable(feature = "rust1", since = "1.0.0")] - pub fn position(&self) -> u64 { + #[rustc_const_unstable(feature = "const_io_structs", issue = "78812")] + pub const fn position(&self) -> u64 { self.pos } diff --git a/library/std/src/io/cursor/tests.rs b/library/std/src/io/cursor/tests.rs index 80d88ca66f669..5da31ce0ba761 100644 --- a/library/std/src/io/cursor/tests.rs +++ b/library/std/src/io/cursor/tests.rs @@ -514,3 +514,10 @@ fn test_eq() { let _: AssertEq>> = AssertEq(Cursor::new(Vec::new())); } + +#[allow(dead_code)] +fn const_cursor() { + const CURSOR: Cursor<&[u8]> = Cursor::new(&[0]); + const _: &&[u8] = CURSOR.get_ref(); + const _: u64 = CURSOR.position(); +} diff --git a/library/std/src/io/util.rs b/library/std/src/io/util.rs index dc05b9648fd6b..2b1f371129eaf 100644 --- a/library/std/src/io/util.rs +++ b/library/std/src/io/util.rs @@ -102,7 +102,8 @@ pub struct Empty { /// assert!(buffer.is_empty()); /// ``` #[stable(feature = "rust1", since = "1.0.0")] -pub fn empty() -> Empty { +#[rustc_const_unstable(feature = "const_io_structs", issue = "78812")] +pub const fn empty() -> Empty { Empty { _priv: () } } @@ -159,7 +160,8 @@ pub struct Repeat { /// assert_eq!(buffer, [0b101, 0b101, 0b101]); /// ``` #[stable(feature = "rust1", since = "1.0.0")] -pub fn repeat(byte: u8) -> Repeat { +#[rustc_const_unstable(feature = "const_io_structs", issue = "78812")] +pub const fn repeat(byte: u8) -> Repeat { Repeat { byte } } @@ -226,7 +228,8 @@ pub struct Sink { /// assert_eq!(num_bytes, 5); /// ``` #[stable(feature = "rust1", since = "1.0.0")] -pub fn sink() -> Sink { +#[rustc_const_unstable(feature = "const_io_structs", issue = "78812")] +pub const fn sink() -> Sink { Sink { _priv: () } } diff --git a/library/std/src/io/util/tests.rs b/library/std/src/io/util/tests.rs index e5e32ecb40531..9450b1ee1240c 100644 --- a/library/std/src/io/util/tests.rs +++ b/library/std/src/io/util/tests.rs @@ -1,5 +1,5 @@ use crate::io::prelude::*; -use crate::io::{copy, empty, repeat, sink}; +use crate::io::{copy, empty, repeat, sink, Empty, Repeat, Sink}; #[test] fn copy_copies() { @@ -43,3 +43,10 @@ fn take_some_bytes() { assert_eq!(repeat(4).take(100).bytes().next().unwrap().unwrap(), 4); assert_eq!(repeat(1).take(10).chain(repeat(2).take(10)).bytes().count(), 20); } + +#[allow(dead_code)] +fn const_utils() { + const _: Empty = empty(); + const _: Repeat = repeat(b'c'); + const _: Sink = sink(); +} diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 96a7755c68821..bc218b77c878f 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -242,6 +242,7 @@ #![feature(const_fn_transmute)] #![feature(const_fn)] #![feature(const_fn_fn_ptr_basics)] +#![feature(const_io_structs)] #![feature(const_ip)] #![feature(const_ipv6)] #![feature(const_raw_ptr_deref)] @@ -296,7 +297,6 @@ #![feature(raw)] #![feature(raw_ref_macros)] #![feature(ready_macro)] -#![feature(renamed_spin_loop)] #![feature(rustc_attrs)] #![feature(rustc_private)] #![feature(shrink_to)] diff --git a/library/std/src/sys/unix/fs.rs b/library/std/src/sys/unix/fs.rs index d27d6e2c5659b..96594095cc36d 100644 --- a/library/std/src/sys/unix/fs.rs +++ b/library/std/src/sys/unix/fs.rs @@ -1081,7 +1081,20 @@ pub fn symlink(src: &Path, dst: &Path) -> io::Result<()> { pub fn link(src: &Path, dst: &Path) -> io::Result<()> { let src = cstr(src)?; let dst = cstr(dst)?; - cvt(unsafe { libc::link(src.as_ptr(), dst.as_ptr()) })?; + cfg_if::cfg_if! { + if #[cfg(any(target_os = "vxworks", target_os = "redox", target_os = "android"))] { + // VxWorks, Redox, and old versions of Android lack `linkat`, so use + // `link` instead. POSIX leaves it implementation-defined whether + // `link` follows symlinks, so rely on the `symlink_hard_link` test + // in library/std/src/fs/tests.rs to check the behavior. + cvt(unsafe { libc::link(src.as_ptr(), dst.as_ptr()) })?; + } else { + // Use `linkat` with `AT_FDCWD` instead of `link` as `linkat` gives + // us a flag to specify how symlinks should be handled. Pass 0 as + // the flags argument, meaning don't follow symlinks. + cvt(unsafe { libc::linkat(libc::AT_FDCWD, src.as_ptr(), libc::AT_FDCWD, dst.as_ptr(), 0) })?; + } + } Ok(()) } diff --git a/library/std/src/sys/unix/net.rs b/library/std/src/sys/unix/net.rs index 74c7db27226ef..378d690f8bfd7 100644 --- a/library/std/src/sys/unix/net.rs +++ b/library/std/src/sys/unix/net.rs @@ -55,9 +55,18 @@ impl Socket { pub fn new_raw(fam: c_int, ty: c_int) -> io::Result { unsafe { cfg_if::cfg_if! { - if #[cfg(target_os = "linux")] { - // On Linux we pass the SOCK_CLOEXEC flag to atomically create - // the socket and set it as CLOEXEC, added in 2.6.27. + if #[cfg(any( + target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "illumos", + target_os = "linux", + target_os = "netbsd", + target_os = "opensbd", + ))] { + // On platforms that support it we pass the SOCK_CLOEXEC + // flag to atomically create the socket and set it as + // CLOEXEC. On Linux this was added in 2.6.27. let fd = cvt(libc::socket(fam, ty | libc::SOCK_CLOEXEC, 0))?; Ok(Socket(FileDesc::new(fd))) } else { @@ -83,7 +92,15 @@ impl Socket { let mut fds = [0, 0]; cfg_if::cfg_if! { - if #[cfg(target_os = "linux")] { + if #[cfg(any( + target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "illumos", + target_os = "linux", + target_os = "netbsd", + target_os = "opensbd", + ))] { // Like above, set cloexec atomically cvt(libc::socketpair(fam, ty | libc::SOCK_CLOEXEC, 0, fds.as_mut_ptr()))?; Ok((Socket(FileDesc::new(fds[0])), Socket(FileDesc::new(fds[1])))) @@ -174,13 +191,28 @@ impl Socket { pub fn accept(&self, storage: *mut sockaddr, len: *mut socklen_t) -> io::Result { // Unfortunately the only known way right now to accept a socket and // atomically set the CLOEXEC flag is to use the `accept4` syscall on - // Linux. This was added in 2.6.28, glibc 2.10 and musl 0.9.5. + // platforms that support it. On Linux, this was added in 2.6.28, + // glibc 2.10 and musl 0.9.5. cfg_if::cfg_if! { - if #[cfg(target_os = "linux")] { + if #[cfg(any( + target_os = "dragonfly", + target_os = "freebsd", + target_os = "illumos", + target_os = "linux", + target_os = "netbsd", + target_os = "opensbd", + ))] { let fd = cvt_r(|| unsafe { libc::accept4(self.0.raw(), storage, len, libc::SOCK_CLOEXEC) })?; Ok(Socket(FileDesc::new(fd))) + // While the Android kernel supports the syscall, + // it is not included in all versions of Android's libc. + } else if #[cfg(target_os = "android")] { + let fd = cvt_r(|| unsafe { + libc::syscall(libc::SYS_accept4, self.0.raw(), storage, len, libc::SOCK_CLOEXEC) + })?; + Ok(Socket(FileDesc::new(fd as c_int))) } else { let fd = cvt_r(|| unsafe { libc::accept(self.0.raw(), storage, len) })?; let fd = FileDesc::new(fd); diff --git a/library/test/src/helpers/concurrency.rs b/library/test/src/helpers/concurrency.rs index 7e9bd50f5566e..c39a9b0ec0233 100644 --- a/library/test/src/helpers/concurrency.rs +++ b/library/test/src/helpers/concurrency.rs @@ -1,18 +1,14 @@ //! Helper module which helps to determine amount of threads to be used //! during tests execution. -use std::env; -use std::thread; +use std::{env, num::NonZeroUsize, thread}; -#[allow(deprecated)] pub fn get_concurrency() -> usize { - match env::var("RUST_TEST_THREADS") { - Ok(s) => { - let opt_n: Option = s.parse().ok(); - match opt_n { - Some(n) if n > 0 => n, - _ => panic!("RUST_TEST_THREADS is `{}`, should be a positive integer.", s), - } + if let Ok(value) = env::var("RUST_TEST_THREADS") { + match value.parse::().ok() { + Some(n) => n.get(), + _ => panic!("RUST_TEST_THREADS is `{}`, should be a positive integer.", value), } - Err(..) => thread::available_concurrency().map(|n| n.get()).unwrap_or(1), + } else { + thread::available_concurrency().map(|n| n.get()).unwrap_or(1) } } diff --git a/src/bootstrap/test.rs b/src/bootstrap/test.rs index b48e9696c9a4d..2b87c4b91af83 100644 --- a/src/bootstrap/test.rs +++ b/src/bootstrap/test.rs @@ -1205,17 +1205,6 @@ note: if you're sure you want to do this, please open an issue as to why. In the // Only pass correct values for these flags for the `run-make` suite as it // requires that a C++ compiler was configured which isn't always the case. if !builder.config.dry_run && matches!(suite, "run-make" | "run-make-fulldeps") { - cmd.arg("--cc") - .arg(builder.cc(target)) - .arg("--cxx") - .arg(builder.cxx(target).unwrap()) - .arg("--cflags") - .arg(builder.cflags(target, GitRepo::Rustc).join(" ")); - copts_passed = true; - if let Some(ar) = builder.ar(target) { - cmd.arg("--ar").arg(ar); - } - // The llvm/bin directory contains many useful cross-platform // tools. Pass the path to run-make tests so they can use them. let llvm_bin_path = llvm_config @@ -1241,6 +1230,21 @@ note: if you're sure you want to do this, please open an issue as to why. In the } } + // Only pass correct values for these flags for the `run-make` suite as it + // requires that a C++ compiler was configured which isn't always the case. + if !builder.config.dry_run && matches!(suite, "run-make" | "run-make-fulldeps") { + cmd.arg("--cc") + .arg(builder.cc(target)) + .arg("--cxx") + .arg(builder.cxx(target).unwrap()) + .arg("--cflags") + .arg(builder.cflags(target, GitRepo::Rustc).join(" ")); + copts_passed = true; + if let Some(ar) = builder.ar(target) { + cmd.arg("--ar").arg(ar); + } + } + if !llvm_components_passed { cmd.arg("--llvm-components").arg(""); } diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index 5eca54199d6c8..4cad6418d6a5a 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -322,7 +322,8 @@ pub fn run_core( let cpath = Some(input.clone()); let input = Input::File(input); - let intra_link_resolution_failure_name = lint::builtin::BROKEN_INTRA_DOC_LINKS.name; + let broken_intra_doc_links = lint::builtin::BROKEN_INTRA_DOC_LINKS.name; + let private_intra_doc_links = lint::builtin::PRIVATE_INTRA_DOC_LINKS.name; let missing_docs = rustc_lint::builtin::MISSING_DOCS.name; let missing_doc_example = rustc_lint::builtin::MISSING_DOC_CODE_EXAMPLES.name; let private_doc_tests = rustc_lint::builtin::PRIVATE_DOC_TESTS.name; @@ -336,7 +337,8 @@ pub fn run_core( // In addition to those specific lints, we also need to allow those given through // command line, otherwise they'll get ignored and we don't want that. let lints_to_show = vec![ - intra_link_resolution_failure_name.to_owned(), + broken_intra_doc_links.to_owned(), + private_intra_doc_links.to_owned(), missing_docs.to_owned(), missing_doc_example.to_owned(), private_doc_tests.to_owned(), @@ -349,9 +351,8 @@ pub fn run_core( ]; let (lint_opts, lint_caps) = init_lints(lints_to_show, lint_opts, |lint| { - if lint.name == intra_link_resolution_failure_name - || lint.name == invalid_codeblock_attributes_name - { + // FIXME: why is this necessary? + if lint.name == broken_intra_doc_links || lint.name == invalid_codeblock_attributes_name { None } else { Some((lint.name_lower(), lint::Allow)) diff --git a/src/tools/x/Cargo.lock b/src/tools/x/Cargo.lock new file mode 100644 index 0000000000000..723d6cb25ed6a --- /dev/null +++ b/src/tools/x/Cargo.lock @@ -0,0 +1,5 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "x" +version = "0.1.0" diff --git a/src/tools/x/Cargo.toml b/src/tools/x/Cargo.toml new file mode 100644 index 0000000000000..72c4948c617fa --- /dev/null +++ b/src/tools/x/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "x" +version = "0.1.0" +description = "Run x.py slightly more conveniently" +authors = ["Casey Rodarmor "] +edition = "2018" +publish = false diff --git a/src/tools/x/README.md b/src/tools/x/README.md new file mode 100644 index 0000000000000..3b3cf2847c200 --- /dev/null +++ b/src/tools/x/README.md @@ -0,0 +1,3 @@ +# x + +`x` invokes `x.py` from any subdirectory. diff --git a/src/tools/x/src/main.rs b/src/tools/x/src/main.rs new file mode 100644 index 0000000000000..6c0311433d676 --- /dev/null +++ b/src/tools/x/src/main.rs @@ -0,0 +1,92 @@ +//! Run `x.py` from any subdirectory of a rust compiler checkout. +//! +//! We prefer `exec`, to avoid adding an extra process in the process tree. +//! However, since `exec` isn't available on Windows, we indirect through +//! `exec_or_status`, which will call `exec` on unix and `status` on Windows. +//! +//! We use `python`, `python3`, or `python2` as the python interpreter to run +//! `x.py`, in that order of preference. + +use std::{ + env, io, + process::{self, Command, ExitStatus}, +}; + +const PYTHON: &str = "python"; +const PYTHON2: &str = "python2"; +const PYTHON3: &str = "python3"; + +fn python() -> &'static str { + let val = match env::var_os("PATH") { + Some(val) => val, + None => return PYTHON, + }; + + let mut python2 = false; + let mut python3 = false; + + for dir in env::split_paths(&val) { + if dir.join(PYTHON).exists() { + return PYTHON; + } + + python2 |= dir.join(PYTHON2).exists(); + python3 |= dir.join(PYTHON3).exists(); + } + + if python3 { + PYTHON3 + } else if python2 { + PYTHON2 + } else { + PYTHON + } +} + +#[cfg(unix)] +fn exec_or_status(command: &mut Command) -> io::Result { + use std::os::unix::process::CommandExt; + Err(command.exec()) +} + +#[cfg(not(unix))] +fn exec_or_status(command: &mut Command) -> io::Result { + command.status() +} + +fn main() { + let current = match env::current_dir() { + Ok(dir) => dir, + Err(err) => { + eprintln!("Failed to get current directory: {}", err); + process::exit(1); + } + }; + + for dir in current.ancestors() { + let candidate = dir.join("x.py"); + + if candidate.exists() { + let mut python = Command::new(python()); + + python.arg(&candidate).args(env::args().skip(1)).current_dir(dir); + + let result = exec_or_status(&mut python); + + match result { + Err(error) => { + eprintln!("Failed to invoke `{}`: {}", candidate.display(), error); + } + Ok(status) => { + process::exit(status.code().unwrap_or(1)); + } + } + } + } + + eprintln!( + "x.py not found. Please run inside of a checkout of `https://github.com/rust-lang/rust`." + ); + + process::exit(1); +}